import React, { useState, useEffect, useRef, useCallback } from "react";
import { createRoot } from "react-dom/client";
import {
  LayoutDashboard, Microscope, ClipboardCheck, Radar, ListTodo, UserCircle,
  Plus, Sparkles, TrendingUp, TrendingDown, Minus, Upload, X, Check, ChevronRight,
  Building2, Target, Palette, Users, Megaphone, FileText, Search, Loader2,
  AlertTriangle, CheckCircle2, Circle, Clock, Wrench, Globe, Presentation,
  Trash2, Edit3, Save, LogOut, Eye, Flag, ArrowUpRight, Briefcase, Zap, Maximize2, Minimize2, Copy, ClipboardPaste,
  LayoutTemplate, Video, Award, Share2, Mail, Image, PlayCircle, CalendarDays, Radio, Send, Tag as TagIcon,
  PenLine, Lightbulb, Download, Bot, Workflow, Settings, BarChart3, GripVertical, BookOpen, RefreshCw, Sun, Moon, Type,
} from "lucide-react";

const LOGO = "/logo_white.png"; // wit REX-logo; het donkere logo valt weg tegen de achtergrond

/* ============================== THEME ============================== */
// Twee thema's. Donker is de standaard; licht is er voor wie liever op een witte
// achtergrond werkt. De zijbalk blijft in beide thema's donker: dat is een bewuste
// keuze, want het scheidt de navigatie van het werkgebied en het witte logo blijft leesbaar.
const THEMES = {
  dark: {
    yellow: "#8B5CF6", yellowDark: "#7C3AED",
    ink: "#FFFFFF", ink2: "#9CA3AF",
    paper: "#0A0A0A", card: "#1A1A1A", line: "#2A2A2A", muted: "#9CA3AF",
    good: "#4ADE80", bad: "#F87171", blue: "#60A5FA",
    surface: "#0A0A0A", sidebar: "#1A1A1A",
  },
  light: {
    yellow: "#8B5CF6", yellowDark: "#7C3AED",
    ink: "#111827", ink2: "#4B5563",
    paper: "#F4F4F6", card: "#FFFFFF", line: "#E5E7EB", muted: "#6B7280",
    // Iets dieper dan in het donkere thema, anders te licht op wit.
    good: "#16A34A", bad: "#DC2626", blue: "#2563EB",
    surface: "#111827", sidebar: "#1A1A1A",
  },
};

// C wordt van waarden voorzien en kan van thema wisselen. Alle stijlen lezen C tijdens
// het tekenen, dus na het wisselen is één hertekening genoeg om alles mee te laten kleuren.
const C = { ...THEMES.dark };
let CURRENT_THEME = "dark";
function applyTheme(mode) {
  const t = THEMES[mode] || THEMES.dark;
  Object.assign(C, t);
  CURRENT_THEME = THEMES[mode] ? mode : "dark";
  if (typeof document !== "undefined" && document.body) {
    document.body.classList.toggle("rex-light", CURRENT_THEME === "light");
  }
}

const FONT_DISPLAY = "'Inter', system-ui, sans-serif";
const FONT_BODY = "'Inter', system-ui, sans-serif";

/* ============================== STORAGE ============================== */
const store = {
  async get(key, def) {
    try {
      const r = await fetch("/api/store/" + encodeURIComponent(key), { credentials: "include" });
      if (!r.ok) return def;
      const d = await r.json();
      return d.value === null || d.value === undefined ? def : d.value;
    } catch { return def; }
  },
  async set(key, value) {
    try {
      await fetch("/api/store/" + encodeURIComponent(key), {
        method: "PUT", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ value }),
      });
    } catch (e) { console.error(e); }
  },
};

/* ============================== AI ============================== */
function parseMaybeJSON(text) {
  const clean = (text || "").replace(/```json/g, "").replace(/```/g, "").trim();
  try { return JSON.parse(clean); } catch {
    const m = clean.match(/[\{\[][\s\S]*[\}\]]/);
    if (m) { try { return JSON.parse(m[0]); } catch {} }
    return null;
  }
}

// Volledige variant: geeft { data, sources, error } terug (data = geparste JSON of tekst).
// De foutmelding van de server wordt meegegeven, zodat schermen kunnen tonen wát er misging
// (bijvoorbeeld een leeg AI-tegoed) in plaats van een algemene melding.
async function callAIFull(prompt, { json = false, system = "", useSearch = false, model = "" } = {}) {
  // Eerst vragen of het tegoed hiervoor gebruikt mag worden.
  if (AI_GATE && !AI_GATE_SKIP) {
    const ok = await AI_GATE(aiCostLabel({ useSearch, model }));
    if (!ok) return { data: json ? null : "", sources: [], error: "", cancelled: true };
  }
  const res = await fetch("/api/ai", {
    method: "POST", credentials: "include",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ prompt, system, json, useSearch, model }),
  });
  if (!res.ok) {
    if (res.status === 401) { window.location.reload(); }
    let msg = "";
    try { msg = (await res.json()).error || ""; } catch {}
    if (/credit balance|too low/i.test(msg)) msg = "Je AI-tegoed bij Anthropic is op. Vul het aan en probeer opnieuw.";
    return { data: json ? null : "Er ging iets mis bij de AI-aanroep.", sources: [], error: msg || `De AI-aanroep gaf een fout (status ${res.status}).` };
  }
  const payload = await res.json();
  const text = payload.text || "";
  const sources = payload.sources || [];
  if (!json) return { data: text, sources };
  const parsed = parseMaybeJSON(text);
  if (parsed === null) {
    // Het model gaf wel antwoord, maar niet in het gevraagde format. Meld dat, en geef
    // de ruwe tekst mee zodat een scherm er eventueel nog iets uit kan halen.
    return { data: null, sources, raw: text, error: "Het antwoord van de AI was niet in het verwachte format. Probeer het opnieuw; lukt het dan nog niet, formuleer je opdracht dan iets korter." };
  }
  return { data: parsed, sources };
}

// Eenvoudige variant (achterwaarts compatibel): geeft alleen data terug.
async function callAI(prompt, opts = {}) {
  const { data } = await callAIFull(prompt, opts);
  return data;
}

// Grove kosteninschatting vooraf per actietype, in euro (richtbedrag).
// Wat een actie ongeveer kost. Schattingen; het echte verbruik hangt af van de lengte
// van het antwoord en het aantal zoekopdrachten.
const COST_HINT = {
  desk: "~€ 0,03–0,09",      // AI + web search, groter antwoord
  search: "~€ 0,03–0,09",    // audit/monitor met web search
  light: "~€ 0,01–0,02",     // AI zonder web search (samenvatting, analyse)
};
function CostHint({ kind }) {
  return <span style={{ fontSize: 11.5, color: C.muted, fontWeight: 600 }}>{COST_HINT[kind] || COST_HINT.light}</span>;
}

// Zet de markdown waarin content wordt geschreven om naar HTML, want dat is wat
// WordPress verwacht. Bewust eenvoudig: koppen, opsommingen, vet, schuin en links.
function mdToHtml(md) {
  const esc = (t) => String(t).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  const inline = (t) => esc(t)
    .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
    .replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>")
    .replace(/\[(.+?)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2">$1</a>');
  const out = [];
  let list = null;
  for (const raw of String(md || "").split("\n")) {
    const line = raw.trimEnd();
    const bullet = line.match(/^\s*[-*]\s+(.*)$/);
    const numbered = line.match(/^\s*\d+[.)]\s+(.*)$/);
    const heading = line.match(/^(#{1,4})\s+(.*)$/);
    if (bullet || numbered) {
      const want = bullet ? "ul" : "ol";
      if (list !== want) { if (list) out.push(`</${list}>`); out.push(`<${want}>`); list = want; }
      out.push(`<li>${inline((bullet || numbered)[1])}</li>`);
      continue;
    }
    if (list) { out.push(`</${list}>`); list = null; }
    if (!line.trim()) continue;
    if (heading) { const lvl = Math.min(heading[1].length + 1, 6); out.push(`<h${lvl}>${inline(heading[2])}</h${lvl}>`); continue; }
    out.push(`<p>${inline(line)}</p>`);
  }
  if (list) out.push(`</${list}>`);
  return out.join("\n");
}

// ---- Drempel voor AI-acties ----
// Alle AI-aanroepen lopen via callAIFull, dus zetten we de vraag "dit kost tegoed,
// akkoord?" op dat ene punt. Zo geldt hij overal, ook voor functies die later bijkomen.
let AI_GATE = null;              // wordt door de app gezet; toont het venster
let AI_GATE_SKIP = false;        // "niet meer vragen" voor deze sessie
function setAiGate(fn) { AI_GATE = fn; }
function skipAiGate(v) { AI_GATE_SKIP = !!v; }

function aiCostLabel({ useSearch, model }) {
  if (useSearch) return { tier: "search", text: COST_HINT.search, what: "AI met web search" };
  if (model === "light") return { tier: "light", text: COST_HINT.light, what: "AI, klein model" };
  return { tier: "light", text: COST_HINT.light, what: "AI zonder web search" };
}

async function fetchUsage() {
  try {
    const r = await fetch("/api/usage", { credentials: "include" });
    if (r.ok) return await r.json();
  } catch {}
  return null;
}
const eur = (n) => "€ " + (n || 0).toLocaleString("nl-NL", { minimumFractionDigits: 2, maximumFractionDigits: 2 });

/* ============================== UI PRIMITIVES ============================== */
const Btn = ({ children, onClick, variant = "primary", size = "md", icon: Icon, disabled, style }) => {
  const base = {
    display: "inline-flex", alignItems: "center", gap: 8, border: "none", cursor: disabled ? "not-allowed" : "pointer",
    fontFamily: FONT_BODY, fontWeight: 600, borderRadius: 50, transition: "all .3s ease",
    opacity: disabled ? 0.5 : 1,
    padding: size === "sm" ? "10px 20px" : "13px 28px", fontSize: size === "sm" ? 13 : 14.5,
  };
  // Paars is de actiekleur; de overige varianten zijn rustig met een rand, zoals op rexdigital.nl.
  const variants = {
    primary: { background: `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})`, color: "#fff", boxShadow: "0 4px 20px rgba(139,92,246,.30)" },
    yellow: { background: `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})`, color: "#fff", boxShadow: "0 4px 20px rgba(139,92,246,.30)" },
    ghost: { background: "transparent", color: C.ink, border: `1px solid ${C.line}` },
    danger: { background: "transparent", color: C.bad, border: `1px solid ${C.bad}66` },
  };
  return (
    <button onClick={disabled ? undefined : onClick} style={{ ...base, ...variants[variant], ...style }}
      onMouseEnter={e => !disabled && (e.currentTarget.style.transform = "translateY(-1px)")}
      onMouseLeave={e => (e.currentTarget.style.transform = "translateY(0)")}>
      {Icon && <Icon size={size === "sm" ? 15 : 17} />} {children}
    </button>
  );
};

const Card = ({ children, style, onClick, hover }) => (
  <div onClick={onClick} style={{
    background: C.card, border: `1px solid ${C.line}`, borderRadius: 16, padding: 20,
    cursor: onClick ? "pointer" : "default", transition: "all .18s ease", ...style,
  }}
    onMouseEnter={e => hover && (e.currentTarget.style.boxShadow = "0 16px 40px rgba(139,92,246,.15)", e.currentTarget.style.borderColor = "rgba(139,92,246,.5)")}
    onMouseLeave={e => hover && (e.currentTarget.style.boxShadow = "none", e.currentTarget.style.borderColor = C.line)}>
    {children}
  </div>
);

const Tag = ({ children, color = C.muted, bg }) => (
  <span style={{ fontSize: 11.5, fontWeight: 700, color, background: bg || color + "18", padding: "3px 9px", borderRadius: 20, letterSpacing: .2 }}>{children}</span>
);

const Field = ({ label, value, onChange, multiline, placeholder, rows = 3 }) => (
  <div style={{ marginBottom: 16 }}>
    <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>{label}</label>
    {multiline
      ? <textarea value={value || ""} onChange={e => onChange(e.target.value)} placeholder={placeholder} rows={rows}
          style={{ width: "100%", boxSizing: "border-box", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, resize: "vertical", outline: "none", color: C.ink, lineHeight: 1.5 }}
          onFocus={e => e.target.style.borderColor = C.yellow} onBlur={e => e.target.style.borderColor = C.line} />
      : <input value={value || ""} onChange={e => onChange(e.target.value)} placeholder={placeholder}
          style={{ width: "100%", boxSizing: "border-box", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", color: C.ink }}
          onFocus={e => e.target.style.borderColor = C.yellow} onBlur={e => e.target.style.borderColor = C.line} />}
  </div>
);

const Spinner = ({ label }) => (
  <div style={{ display: "flex", alignItems: "center", gap: 10, color: C.muted, fontSize: 14, padding: 12 }}>
    <Loader2 size={18} className="spin" style={{ animation: "spin 1s linear infinite" }} /> {label || "AI is aan het werk…"}
  </div>
);

const AIBadge = () => (
  <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11, fontWeight: 700, color: C.ink, background: C.yellow, padding: "2px 8px", borderRadius: 20 }}>
    <Sparkles size={11} /> AI
  </span>
);

const Empty = ({ icon: Icon, title, sub }) => (
  <div style={{ textAlign: "center", padding: "48px 20px", color: C.muted }}>
    {Icon && <Icon size={36} style={{ opacity: .4, marginBottom: 12 }} />}
    <div style={{ fontWeight: 700, color: C.ink2, fontSize: 15 }}>{title}</div>
    {sub && <div style={{ fontSize: 13.5, marginTop: 4 }}>{sub}</div>}
  </div>
);

/* ============================== LOGIN ============================== */
function Login({ onLogin }) {
  const [u, setU] = useState(""); const [p, setP] = useState(""); const [err, setErr] = useState("");
  const [loading, setLoading] = useState(false);
  const submit = async () => {
    setErr(""); setLoading(true);
    try {
      const r = await fetch("/api/login", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ username: u, password: p }),
      });
      if (r.ok) onLogin();
      else setErr("Onjuiste inloggegevens.");
    } catch { setErr("Kan geen verbinding maken met de server."); }
    setLoading(false);
  };
  return (
    <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: C.surface, fontFamily: FONT_BODY, position: "relative", overflow: "hidden" }}>
      <div style={{ position: "absolute", inset: 0, background: `radial-gradient(circle at 30% 20%, ${C.yellow}22, transparent 45%), radial-gradient(circle at 80% 80%, ${C.yellow}11, transparent 40%)` }} />
      <div style={{ position: "relative", background: C.card, borderRadius: 24, padding: 40, width: 380, boxShadow: "0 30px 80px rgba(0,0,0,.4)" }}>
        <img src={LOGO} alt="REX.digital" style={{ height: 38, marginBottom: 26 }} />
        <div style={{ fontFamily: FONT_DISPLAY, fontSize: 23, fontWeight: 700, color: C.ink, letterSpacing: -.5 }}>Brand Command Center</div>
        <div style={{ fontSize: 13.5, color: C.muted, marginTop: 4, marginBottom: 26 }}>Het werkstation voor merk, content en overnames.</div>
        <Field label="Gebruikersnaam" value={u} onChange={setU} placeholder="admin" />
        <div style={{ marginBottom: 8 }}>
          <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Wachtwoord</label>
          <input type="password" value={p} onChange={e => setP(e.target.value)} onKeyDown={e => e.key === "Enter" && submit()} placeholder="••••"
            style={{ width: "100%", boxSizing: "border-box", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }}
            onFocus={e => e.target.style.borderColor = C.yellow} onBlur={e => e.target.style.borderColor = C.line} />
        </div>
        {err && <div style={{ color: C.bad, fontSize: 13, marginTop: 8, fontWeight: 600 }}>{err}</div>}
        <div style={{ marginTop: 22 }}>
          <Btn variant="yellow" onClick={submit} disabled={loading} style={{ width: "100%", justifyContent: "center" }} icon={Zap}>{loading ? "Bezig…" : "Inloggen"}</Btn>
        </div>
        <div style={{ fontSize: 11.5, color: C.muted, marginTop: 16, textAlign: "center" }}>Veilig ingelogd — sessie via de server</div>
      </div>
    </div>
  );
}

/* ============================== APP SHELL ============================== */
// De structuur volgt de vier pijlers: fundament (bedrijfsprofiel, vast in de zijbalk),
// richting (Strategy), executie (Marketing ops) en herijken (Rebalance).
const NAV = [
  { id: "dashboard", label: "Dashboard", icon: LayoutDashboard },
  { group: "strategy", label: "Strategy", icon: Target, children: [
    { id: "opportunity", label: "Opportunity agent", icon: TrendingUp },
    { id: "research", label: "Research agent", icon: Microscope },
    { id: "budget", label: "Budget & plan", icon: Zap },
  ] },
  { group: "ops", label: "Marketing ops", icon: Workflow, children: [
    { id: "operations", label: "Operations agent", icon: ListTodo },
    { id: "migration", label: "Migration agent", icon: Workflow },
    { id: "content", label: "Copy agent", icon: PenLine },
    { id: "design", label: "Studio agent", icon: Image },
    { id: "searchads", label: "Search ads agent", icon: BarChart3 },
  ] },
  { group: "rebalance", label: "Rebalance", icon: RefreshCw, children: [
    { id: "monitor", label: "Monitor", icon: Radar },
    { id: "trends", label: "Trends", icon: TrendingUp },
    { id: "contentperf", label: "Content performance", icon: BarChart3 },
    { id: "rebalanceplan", label: "Rebalance plan", icon: ListTodo },
  ] },
];

// Werkruimte-wisselaar in de sidebar: omgeving kiezen of naar het beheer-tabblad gaan.
function WorkspaceSwitcher({ onManage }) {
  const [list, setList] = useState([]);
  const [active, setActive] = useState(null);
  const [open, setOpen] = useState(false);
  const [busy, setBusy] = useState(false);

  const reload = async () => {
    try { const r = await fetch("/api/workspaces", { credentials: "include" }); const d = await r.json(); setList(d.workspaces || []); setActive(d.active || null); }
    catch {}
  };
  useEffect(() => { reload(); }, []);

  const activeName = (list.find(w => w.id === active) || {}).name || "—";
  const switchTo = async (id) => {
    if (id === active) { setOpen(false); return; }
    setBusy(true);
    try { await fetch("/api/workspaces/active", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id }) }); window.location.reload(); }
    catch { setBusy(false); }
  };

  return (
    <div style={{ marginBottom: 18 }}>
      <div style={{ fontSize: 10.5, fontWeight: 700, color: "#9CA3AF", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 5, paddingLeft: 2 }}>Omgeving</div>
      <button onClick={() => setOpen(o => !o)} style={{ display: "flex", alignItems: "center", gap: 8, width: "100%", padding: "9px 11px", borderRadius: 9, border: "1.5px solid #34332F", background: "#1A1A1A", color: "#fff", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, textAlign: "left" }}>
        <Building2 size={15} style={{ color: C.yellow, flexShrink: 0 }} />
        <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{activeName}</span>
        <ChevronRight size={14} style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .15s", color: "#9CA3AF" }} />
      </button>
      {open && (
        <div style={{ marginTop: 6, background: "#1A1A1A", borderRadius: 9, padding: 6 }}>
          {list.map(w => (
            <button key={w.id} onClick={() => switchTo(w.id)} disabled={busy} style={{ display: "flex", alignItems: "center", gap: 8, width: "100%", padding: "8px 10px", borderRadius: 7, border: "none", background: w.id === active ? "#ffffff14" : "transparent", color: w.id === active ? "#fff" : "#9CA3AF", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, textAlign: "left" }}>
              {w.id === active ? <Check size={13} style={{ color: C.yellow }} /> : <span style={{ width: 13 }} />}
              <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{w.name}</span>
            </button>
          ))}
          <button onClick={() => { setOpen(false); onManage && onManage(); }} style={{ display: "flex", alignItems: "center", gap: 8, width: "100%", padding: "8px 10px", borderRadius: 7, border: "none", background: "transparent", color: C.yellow, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, textAlign: "left", marginTop: 2 }}>
            <Settings size={13} /> Omgevingen beheren
          </button>
        </div>
      )}
    </div>
  );
}

// Volledig beheer van omgevingen, als tabblad onder Instellingen.
// ---- Demo-omgeving ----
// Alles hierin is fictief. Het bedrijf lijkt bewust op de doelgroep (software voor
// factuur- en documentverwerking), zodat een demo herkenbaar is bij een gesprek,
// maar "Ledgerly" bestaat niet. Er komt geen AI aan te pas: dit is kant-en-klare
// inhoud, dus het aanmaken van een demo kost geen credits.
const DEMO_DATA = () => {
  const iso = (d) => new Date(Date.now() - d * 864e5).toISOString().slice(0, 10);
  return {
    wv_profile: {
      filled: true,
      proposition: "Ledgerly maakt de financiële administratie van middelgrote bedrijven grotendeels zelfsturend. Facturen, bonnen en contracten worden automatisch herkend, gecontroleerd en klaargezet voor akkoord, zodat finance-teams zich met de uitzonderingen bezighouden in plaats van met het invoerwerk.",
      goals: "Marktleider worden in de Benelux voor slimme factuurverwerking binnen drie jaar.\nGroeien van 400 naar 1.000 klanten.\nTwee overnames per jaar integreren zonder dat klanten er last van hebben.",
      usps: "Werkt samen met de boekhoudpakketten die klanten al gebruiken.\nGegarandeerde controle op elke factuur, met een duidelijke uitzonderingenlijst.\nLive binnen vier weken, zonder ingrijpend implementatietraject.\nNederlandse support met kennis van de administratieve praktijk.",
      products: "Factuurverwerking: herkennen, coderen en klaarzetten voor akkoord.\nContractbeheer: contracten vastleggen met signalering van opzegtermijnen.\nUitgavenbeheer: bonnen en declaraties via de app.\nKoppelingen: standaardverbindingen met gangbare ERP- en boekhoudsystemen.",
      tone: "Nuchter, concreet en zonder opsmuk. We schrijven in gewone taal, vermijden Engelse buzzwords en beloven niets wat we niet kunnen waarmaken. Liever een cijfer dan een bijvoeglijk naamwoord.",
      icps: "Financieel manager bij een bedrijf met 50 tot 500 medewerkers.\nAdministratief teamleider die verantwoordelijk is voor de factuurstroom.\nIT-verantwoordelijke die let op koppelingen en beveiliging.",
      competitors: "Factura Cloud, DocuFlow, Klarvis, Stapel Software",
      colors: "#8B5CF6, #0A0A0A",
      brandTemplates: [],
    },
    wv_companies: [
      { id: 9001, name: "Factura Cloud", segment: "Concurrent", country: "Nederland", monitor: true, status: "standalone",
        whatTheyDo: "Factuurverwerking voor het mkb, sterk op prijs, minder op koppelingen.", audience: "Kleine bedrijven tot 50 medewerkers",
        usps: "Lage instapprijs, snelle zelfservice", markets: "Nederland, België", techstack: "Eigen herkenning, beperkte integraties",
        notes: "Wint vaak op prijs bij kleinere organisaties." },
      { id: 9002, name: "DocuFlow", segment: "Concurrent", country: "Duitsland", monitor: true, status: "standalone",
        whatTheyDo: "Documentmanagement met factuurmodule, sterk bij grotere organisaties.", audience: "Enterprise, 500+ medewerkers",
        usps: "Breed platform, veel maatwerk", markets: "DACH, Benelux", techstack: "Eigen platform, uitgebreide API",
        notes: "Zwaar implementatietraject; wij winnen op snelheid van live gaan." },
      { id: 9003, name: "Bonnetje", segment: "Overgenomen", country: "Nederland", monitor: true, status: "rebranded",
        whatTheyDo: "App voor bonnen en declaraties, populair bij kleinere teams.", audience: "mkb, 10 tot 100 medewerkers",
        usps: "Prettige app, snel te gebruiken", markets: "Nederland", techstack: "Mobiele app, eigen herkenning",
        notes: "Overgenomen om het uitgavenbeheer te versterken. Merk gaat op in Ledgerly." },
    ],
    wv_content: [
      { id: 8001, kind: "blog", title: "Waarom factuurverwerking vastloopt bij groei", phase: "published", date: iso(21),
        body: "# Waarom factuurverwerking vastloopt bij groei\n\nBij vijftig facturen per maand valt handmatig werk nog mee. Bij vijfhonderd niet meer.\n\n## Het kantelpunt\n\nDe meeste finance-teams merken het pas als het te laat is: betalingen die blijven liggen, leveranciers die bellen, en een maandafsluiting die uitloopt.\n\n## Wat helpt\n\n- Herkenning die je eigen codering leert\n- Een duidelijke uitzonderingenlijst\n- Akkoord geven vanaf je telefoon\n\n## Tot slot\n\nAutomatiseren betekent niet dat je de controle kwijtraakt. Het betekent dat je alleen nog kijkt naar wat afwijkt." },
      { id: 8002, kind: "whitepaper", title: "Van factuur tot betaling in vier weken live", phase: "done", date: iso(12),
        body: "# Van factuur tot betaling in vier weken live\n\n## Managementsamenvatting\n\nEen implementatie hoeft geen half jaar te duren.\n\n## De vier weken\n\n1. Inventarisatie van je factuurstroom\n2. Koppeling met je boekhoudpakket\n3. Meelopen met echte facturen\n4. Live met begeleiding\n\n## Wat je nodig hebt\n\nEen contactpersoon binnen finance en toegang tot je boekhoudsysteem. Meer niet." },
      { id: 8003, kind: "blog", title: "Wat verandert er voor klanten van Bonnetje", phase: "todo", date: iso(4), body: "" },
      { id: 8004, kind: "landingspagina", title: "Uitgavenbeheer voor finance-teams", phase: "backlog", date: iso(2), body: "" },
    ],
    wv_monitors: [
      { id: 7001, typeId: "competitors", subject: "Factura Cloud", freq: "manual", notify: false, lastAuto: null,
        findings: [{ title: "Nieuwe prijspagina online", text: "Instaptarief verlaagd en een gratis proefperiode van 30 dagen toegevoegd.", severity: "midden", date: iso(6) }] },
      { id: 7002, typeId: "mentions", subject: "Ledgerly", freq: "manual", notify: false, lastAuto: null,
        findings: [{ title: "Genoemd in vakblad over finance-automatisering", text: "Ledgerly wordt genoemd als voorbeeld van laagdrempelige factuurverwerking.", severity: "laag", date: iso(9) }] },
    ],
    wv_audits: [
      { id: 6001, typeId: "acquisition", typeLabel: "Overname-audit", subject: "Bonnetje", compareName: "Bonnetje", date: iso(15),
        summary: "Bonnetje vult het uitgavenbeheer van Ledgerly goed aan. De doelgroep overlapt sterk, de technologie is eenvoudig over te nemen, maar de propositie is smaller en de kanalen verschillen: Bonnetje leunt op de app-winkels, Ledgerly op directe verkoop.",
        overallFit: 74,
        axes: [
          { key: "propositie", score: 62, us: "Volledige administratie zelfsturend maken", them: "Bonnen en declaraties makkelijk indienen",
            similarities: ["Beide gericht op minder handwerk voor finance", "Beide beloven snelheid boven volledigheid"],
            differences: ["Ledgerly dekt de hele factuurstroom, Bonnetje alleen declaraties", "Bonnetje richt zich op de medewerker, Ledgerly op de finance-afdeling"],
            integration: "Positioneer Bonnetje als de module Uitgavenbeheer binnen Ledgerly, niet als apart product." },
          { key: "markt", score: 85, us: "mkb, 50 tot 500 medewerkers", them: "mkb, 10 tot 100 medewerkers",
            similarities: ["Zelfde land en zelfde type klant", "Beide via finance de organisatie in"],
            differences: ["Bonnetje zit gemiddeld bij kleinere bedrijven"],
            integration: "Goede kans op doorverkoop: de kleinere klanten van Bonnetje groeien naar het volledige pakket." },
          { key: "producten", score: 70, us: "Facturen, contracten, uitgaven", them: "Uitsluitend bonnen en declaraties",
            similarities: ["Herkenning van documenten als kern"],
            differences: ["Geen contractfunctie bij Bonnetje", "Bonnetje heeft een sterkere mobiele app"],
            integration: "De app van Bonnetje is de beste ingang voor mobiel akkoord geven in Ledgerly." },
          { key: "technologie", score: 78, us: "Webplatform met koppelingen", them: "Mobiele app met eigen herkenning",
            similarities: ["Beide in de cloud", "Vergelijkbare aanpak van documentherkenning"],
            differences: ["Aparte herkenningsmotor die samengevoegd moet worden"],
            integration: "Eén herkenningsmotor aanhouden; plan hiervoor een technisch traject van enkele maanden." },
          { key: "kanalen", score: 55, us: "Directe verkoop en partners", them: "App-winkels en zelfservice",
            similarities: ["Beide actief op LinkedIn"],
            differences: ["Bonnetje heeft geen verkoopteam", "Andere manier van klanten werven"],
            integration: "Zelfservice-instroom van Bonnetje behouden als goedkoop kanaal naar het grotere pakket." },
        ],
        actions: [
          { title: "Positionering vastleggen", text: "Bepaal binnen twee weken of Bonnetje een module of een merk blijft.", severity: "hoog" },
          { title: "Herkenningsmotoren vergelijken", text: "Laat techniek beoordelen welke motor leidend wordt.", severity: "midden" },
          { title: "Klanten informeren", text: "Stuur een bericht aan klanten van Bonnetje over wat er verandert.", severity: "hoog" },
        ],
        sources: [],
      },
    ],
    wv_settings: { notifyEmail: "", digest: false },
  };
};

// Welke taken uit de blauwdruk in de demo al zijn afgevinkt.
const DEMO_DONE_TASKS = new Set([
  "Persbericht opstellen / nalezen", "Nieuwsbericht op website", "Persbericht uitsturen (via Main)",
  "Bericht naar klanten overgenomen bedrijf", "Bericht naar Whitevision klanten", "Nieuwsbericht op socials",
  "About us pagina aanpassen", "Contact met marketingteam overgenomen bedrijf", "Goodiebag regelen",
  "Inventarisatie marketing tooling", "Inventarisatie marketingbudget", "Toegang regelen tot tools & socials",
]);

function WorkspacesSettings({ confirm }) {
  const [list, setList] = useState([]);
  const [active, setActive] = useState(null);
  const [newName, setNewName] = useState("");
  const [busy, setBusy] = useState(false);
  const [renaming, setRenaming] = useState(null);

  const reload = async () => {
    try { const r = await fetch("/api/workspaces", { credentials: "include" }); const d = await r.json(); setList(d.workspaces || []); setActive(d.active || null); }
    catch {}
  };
  useEffect(() => { reload(); }, []);

  const switchTo = async (id) => {
    if (id === active) return;
    setBusy(true);
    try { await fetch("/api/workspaces/active", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id }) }); window.location.reload(); }
    catch { setBusy(false); }
  };
  const create = async () => {
    const name = newName.trim(); if (!name) return;
    setBusy(true);
    try { await fetch("/api/workspaces", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }) }); setNewName(""); await reload(); }
    catch {}
    setBusy(false);
  };
  const doRename = async () => {
    if (!renaming || !renaming.name.trim()) return;
    setBusy(true);
    try { await fetch("/api/workspaces/rename", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: renaming.id, name: renaming.name.trim() }) }); setRenaming(null); await reload(); }
    catch {}
    setBusy(false);
  };
  // Maakt een nieuwe omgeving aan en vult die met voorbeelddata, zodat je iets kunt
  // laten zien zonder eerst alles in te vullen. Gebruikt geen AI, dus kost niets.
  const createDemo = async () => {
    setBusy(true);
    try {
      const r = await fetch("/api/workspaces", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name: "Demo — Ledgerly" }),
      });
      const d = await r.json();
      if (!r.ok || !d.id) { window.alert(d.error || "Kon de demo-omgeving niet aanmaken."); setBusy(false); return; }

      // Naar de nieuwe omgeving schakelen, want de opslag werkt op de actieve omgeving.
      await fetch("/api/workspaces/active", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ id: d.id }),
      });

      for (const [key, value] of Object.entries(DEMO_DATA())) {
        await store.set(key, value);
      }
      // Overname-checklist met de eerste fasen afgevinkt, zodat de voortgang zichtbaar is.
      const taken = blueprintTaskList().map(t => DEMO_DONE_TASKS.has(t.label) ? { ...t, done: true } : t);
      await store.set("wv_acq_progress", { 9003: taken });

      window.location.reload();
    } catch {
      window.alert("Er ging iets mis bij het maken van de demo-omgeving.");
      setBusy(false);
    }
  };

  const remove = (w) => {
    confirm(`Omgeving "${w.name}" en alle data erin verwijderen? Dit kan niet ongedaan worden gemaakt.`, async () => {
      setBusy(true);
      try { const r = await fetch("/api/workspaces/delete", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: w.id }) }); if (r.ok) { if (w.id === active) { window.location.reload(); return; } await reload(); } else { const d = await r.json(); window.alert(d.error || "Kon niet verwijderen."); } }
      catch {}
      setBusy(false);
    });
  };

  return (
    <div>
      <div style={{ fontSize: 13, color: C.muted, marginBottom: 18, lineHeight: 1.5, maxWidth: 720 }}>Elke omgeving is een apart bedrijf met eigen data en koppelingen, strikt gescheiden van de andere. Je wisselt van omgeving via de keuze linksboven in het menu, of hieronder.</div>

      <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 22 }}>
        {list.map(w => (
          <div key={w.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "12px 14px", border: `1.5px solid ${w.id === active ? C.yellow : C.line}`, borderRadius: 11, background: w.id === active ? C.yellow + "10" : C.card }}>
            <Building2 size={17} style={{ color: C.yellowDark, flexShrink: 0 }} />
            {renaming && renaming.id === w.id
              ? <input value={renaming.name} onChange={e => setRenaming({ ...renaming, name: e.target.value })} onKeyDown={e => e.key === "Enter" && doRename()} autoFocus style={{ flex: 1, padding: "8px 10px", border: `1.5px solid ${C.yellow}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
              : <span style={{ flex: 1, fontWeight: 700, fontSize: 14.5 }}>{w.name}{w.id === active ? <span style={{ fontSize: 11.5, color: C.good, fontWeight: 700, marginLeft: 10 }}>actief</span> : null}</span>}
            {renaming && renaming.id === w.id
              ? <><Btn size="sm" variant="yellow" onClick={doRename} disabled={busy}>Opslaan</Btn><Btn size="sm" variant="ghost" onClick={() => setRenaming(null)}>Annuleren</Btn></>
              : <>
                  {w.id !== active && <Btn size="sm" variant="ghost" onClick={() => switchTo(w.id)} disabled={busy}>Naar deze omgeving</Btn>}
                  <button onClick={() => setRenaming({ id: w.id, name: w.name })} title="Hernoemen" style={{ background: "none", border: "none", cursor: "pointer", color: C.ink2 }}><Edit3 size={15} /></button>
                  <button onClick={() => remove(w)} title="Verwijderen" disabled={list.length <= 1} style={{ background: "none", border: "none", cursor: list.length <= 1 ? "default" : "pointer", color: list.length <= 1 ? C.line : C.bad }}><Trash2 size={15} /></button>
                </>}
          </div>
        ))}
      </div>

      <Card style={{ border: `1.5px solid ${C.line}` }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 10, textTransform: "uppercase" }}>Nieuwe omgeving</div>
        <div style={{ display: "flex", gap: 8 }}>
          <input value={newName} onChange={e => setNewName(e.target.value)} onKeyDown={e => e.key === "Enter" && create()} placeholder="bedrijfsnaam" style={{ flex: 1, padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
          <Btn variant="yellow" icon={Plus} onClick={create} disabled={busy || !newName.trim()}>Aanmaken</Btn>
        </div>
        <div style={{ fontSize: 12, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Een nieuwe omgeving begint leeg, met een eigen merkprofiel, eigen lijsten en eigen koppelingen. Wissel ernaartoe om hem in te richten.</div>

        <div style={{ marginTop: 18, paddingTop: 16, borderTop: `1px solid ${C.line}` }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>Demo-omgeving</div>
          <div style={{ fontSize: 12.5, color: C.ink2, marginBottom: 10, lineHeight: 1.55 }}>
            Maakt een gevulde omgeving aan met een fictief bedrijf (Ledgerly), inclusief bedrijfsprofiel, concurrenten, een overname met deels afgevinkte checklist, voorbeeldcontent en een uitgewerkte overname-audit. Handig om de tool te laten zien zonder eerst alles in te vullen. Er wordt geen AI gebruikt, dus dit kost geen tegoed.
          </div>
          <Btn variant="ghost" icon={Sparkles} onClick={createDemo} disabled={busy}>Demo-omgeving aanmaken</Btn>
        </div>
      </Card>
    </div>
  );
}

function App() {
  const [authed, setAuthed] = useState(false);
  const [authChecked, setAuthChecked] = useState(false);
  const [tab, setTab] = useState("dashboard");
  const [confirmState, setConfirmState] = useState(null); // { message, onYes }
  const [aiAsk, setAiAsk] = useState(null);   // { cost, resolve } — vraag vóór een AI-actie
  const [aiBal, setAiBal] = useState(null);   // resterend saldo, om in de vraag te tonen
  const [gateOn, setGateOn] = useState(true); // of we het überhaupt vragen

  // De drempel registreren zodat elke AI-aanroep er langskomt.
  useEffect(() => {
    store.get("wv_ai_gate", true).then(v => {
      const on = v !== false;
      setGateOn(on);
      if (!on) { setAiGate(null); return; }
      setAiGate((cost) => new Promise(async (resolve) => {
        try { const u = await fetchUsage(); setAiBal(u); } catch { setAiBal(null); }
        setAiAsk({ cost, resolve });
      }));
    }).catch(() => {});
  }, []);
  const answerAi = (ok, remember) => {
    if (remember) skipAiGate(true);
    const r = aiAsk?.resolve; setAiAsk(null); r && r(ok);
  };
  const [openGroup, setOpenGroup] = useState(null);
  const [showSettings, setShowSettings] = useState(false);
  const confirm = (message, onYes) => setConfirmState({ message, onYes });
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    fetch("/api/me", { credentials: "include" })
      .then(r => r.json())
      .then(d => { if (d.authed) setAuthed(true); })
      .catch(() => {})
      .finally(() => setAuthChecked(true));
  }, []);

  // central shared state
  const [profile, setProfile] = useState(null);
  const [metrics, setMetrics] = useState(null);
  const [research, setResearch] = useState(null);
  const [audits, setAudits] = useState(null);
  const [monitors, setMonitors] = useState(null);
  const [actions, setActions] = useState(null);
  const [companies, setCompanies] = useState(null);
  const [users, setUsers] = useState(null);
  const [appSettings, setAppSettings] = useState(null);
  const [logoVer, setLogoVer] = useState(0); // bumpt om het logo te herladen na wijziging

  // Thema (donker of licht). De keuze wordt bewaard, zodat hij blijft staan.
  const [theme, setThemeState] = useState(CURRENT_THEME);
  useEffect(() => {
    let on = true;
    store.get("wv_theme", "dark").then(t => {
      if (!on) return;
      const mode = t === "light" ? "light" : "dark";
      applyTheme(mode);
      setThemeState(mode);
    }).catch(() => {});
    return () => { on = false; };
  }, []);
  const setTheme = (mode) => {
    applyTheme(mode);
    setThemeState(mode);
    store.set("wv_theme", mode);
  };
  const [surveys, setSurveys] = useState(null);
  const [frameworks, setFrameworks] = useState(null);
  const [integrations, setIntegrations] = useState(null);
  const [campaigns, setCampaigns] = useState(null);
  const [content, setContent] = useState(null);
  const [plans, setPlans] = useState(null);
  const [strategyBoards, setStrategyBoards] = useState(null);

  useEffect(() => {
    if (!authed) return;
    let cancelled = false;
    (async () => {
      const [
        _profile, _metrics, _research, _audits, _monitors, _actions, _companies, _users,
        _surveys, _frameworks, _integrations, _campaigns, _content, _plans, _strategyBoards, _appSettings,
      ] = await Promise.all([
        store.get("wv_profile", DEFAULT_PROFILE),
        store.get("wv_metrics", DEFAULT_METRICS),
        store.get("wv_research", []),
        store.get("wv_audits", []),
        store.get("wv_monitors", []),
        store.get("wv_actions", []),
        store.get("wv_companies", DEFAULT_COMPANIES),
        store.get("wv_users", DEFAULT_USERS),
        store.get("wv_surveys", []),
        store.get("wv_frameworks", []),
        store.get("wv_integrations", []),
        store.get("wv_campaigns", []),
        store.get("wv_content", []),
        store.get("wv_plans", []),
        store.get("wv_strategyboards", []),
        store.get("wv_appsettings", { channelLibrary: [] }),
      ]);
      if (cancelled) return;
      setProfile(_profile); setMetrics(_metrics); setResearch(_research); setAudits(_audits);
      setMonitors(_monitors); setActions(_actions); setCompanies(_companies); setUsers(_users);
      setSurveys(_surveys); setFrameworks(_frameworks); setIntegrations(_integrations); setCampaigns(_campaigns);
      setContent(_content); setPlans(_plans); setStrategyBoards(_strategyBoards); setAppSettings(_appSettings);
      setLoaded(true);
    })();
    return () => { cancelled = true; };
  }, [authed]);

  // persisters
  const saveProfile = v => { setProfile(v); store.set("wv_profile", v); };
  const saveMetrics = v => { setMetrics(v); store.set("wv_metrics", v); };
  const saveResearch = v => { setResearch(v); store.set("wv_research", v); };
  const saveAudits = v => { setAudits(v); store.set("wv_audits", v); };
  const saveMonitors = v => { setMonitors(v); store.set("wv_monitors", v); };
  const saveActions = v => { setActions(v); store.set("wv_actions", v); };
  const saveCompanies = v => { setCompanies(v); store.set("wv_companies", v); };
  const saveUsers = v => { setUsers(v); store.set("wv_users", v); };
  const saveSurveys = v => { setSurveys(v); store.set("wv_surveys", v); };
  const saveFrameworks = v => { setFrameworks(v); store.set("wv_frameworks", v); };
  const saveIntegrations = v => { setIntegrations(v); store.set("wv_integrations", v); };
  const saveCampaigns = v => { setCampaigns(v); store.set("wv_campaigns", v); };
  const saveContent = v => { setContent(v); store.set("wv_content", v); };
  const savePlans = v => { setPlans(v); store.set("wv_plans", v); };
  const saveStrategyBoards = v => { setStrategyBoards(v); store.set("wv_strategyboards", v); };
  const saveAppSettings = v => { setAppSettings(v); store.set("wv_appsettings", v); };

  const addAction = (a) => {
    const next = [{ id: Date.now() + Math.random(), status: "todo", createdAt: Date.now(), ...a }, ...(actions || [])];
    saveActions(next);
  };

  if (!authChecked) return (<><GlobalCSS /><div style={{ minHeight: "100vh", display: "grid", placeItems: "center", background: C.surface }} /></>);
  if (!authed) return (<><GlobalCSS /><Login onLogin={() => setAuthed(true)} /></>);
  if (!loaded) return (<><GlobalCSS /><div style={{ minHeight: "100vh", display: "grid", placeItems: "center", background: C.paper }}><Spinner label="Werkstation laden…" /></div></>);

  const ctx = {
    profile, saveProfile, metrics, saveMetrics, research, saveResearch,
    audits, saveAudits, monitors, saveMonitors, actions, saveActions,
    companies, saveCompanies, users, saveUsers, surveys, saveSurveys,
    frameworks, saveFrameworks, integrations, saveIntegrations,
    campaigns, saveCampaigns, content, saveContent, plans, savePlans, strategyBoards, saveStrategyBoards, appSettings, saveAppSettings, addAction, confirm, goTo: setTab,
    refreshLogo: () => setLogoVer(v => v + 1),
    theme, setTheme,
  };

  return (
    <>
      <GlobalCSS />
      <div style={{ display: "flex", minHeight: "100vh", background: C.paper, fontFamily: FONT_BODY, color: C.ink }}>
        {/* Sidebar */}
        <aside style={{ width: 232, background: C.sidebar, borderRight: `1px solid ${C.line}`, display: "flex", flexDirection: "column", padding: "22px 16px", position: "sticky", top: 0, height: "100vh", boxSizing: "border-box" }}>
          <div style={{ borderRadius: 12, padding: "10px 12px", marginBottom: 14 }}>
            <img src={`/api/app/logo?v=${logoVer}`} alt="logo" onError={(e) => { if (!e.target.src.endsWith(LOGO)) e.target.src = LOGO; }} style={{ height: 42, maxWidth: "100%", display: "block", objectFit: "contain" }} />
          </div>
          <WorkspaceSwitcher onManage={() => { if (typeof window !== "undefined") window.__settingsTab = "omgevingen"; setTab("settings"); }} />
          <nav style={{ display: "flex", flexDirection: "column", gap: 4, flex: 1, minHeight: 0, overflowY: "auto", overflowX: "hidden" }}>
            {NAV.map(n => {
              if (n.children) {
                const childActive = n.children.some(c => c.id === tab);
                const open = openGroup === n.group || (openGroup === null && childActive);
                return (
                  <div key={n.group}>
                    <button onClick={() => {
                      if (open) { setOpenGroup("__none__"); }
                      else { setOpenGroup(n.group); setTab(n.children[0].id); }
                    }} style={{
                      display: "flex", alignItems: "center", gap: 11, width: "100%", padding: "11px 13px", borderRadius: 10, border: "none",
                      cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5, fontWeight: childActive ? 700 : 500, textAlign: "left",
                      background: "transparent", color: childActive ? "#fff" : "#9CA3AF",
                    }}>
                      <n.icon size={18} /> {n.label}
                      <ChevronRight size={15} style={{ marginLeft: "auto", transform: open ? "rotate(90deg)" : "none", transition: "transform .15s" }} />
                    </button>
                    {open && n.children.map(c => {
                      const active = tab === c.id;
                      return (
                        <button key={c.id} onClick={() => { setOpenGroup(n.group); setTab(c.id); }} style={{
                          display: "flex", alignItems: "center", gap: 10, width: "100%", padding: "9px 13px 9px 30px", borderRadius: 10, border: "none",
                          cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: active ? 700 : 500, textAlign: "left",
                          background: active ? "#ffffff14" : "transparent", color: active ? "#fff" : "#9CA3AF",
                        }}>
                          <c.icon size={15} /> {c.label}
                        </button>
                      );
                    })}
                  </div>
                );
              }
              const active = tab === n.id;
              return (
                <button key={n.id} onClick={() => setTab(n.id)} style={{
                  display: "flex", alignItems: "center", gap: 11, padding: "11px 13px", borderRadius: 10, border: "none",
                  cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5, fontWeight: active ? 700 : 500, textAlign: "left",
                  background: active ? "#ffffff14" : "transparent",
                  color: active ? "#fff" : "#9CA3AF", flexShrink: 0,
                }}>
                  <n.icon size={18} /> {n.label}
                </button>
              );
            })}
          </nav>
          <div style={{ flexShrink: 0 }}>
            <button onClick={() => setTab("profile")} style={{
              display: "flex", alignItems: "center", gap: 11, padding: "11px 13px", borderRadius: 10, border: "none", cursor: "pointer",
              fontFamily: FONT_BODY, fontSize: 14.5, fontWeight: tab === "profile" ? 700 : 500, textAlign: "left", width: "100%",
              background: tab === "profile" ? C.yellow : "transparent", color: tab === "profile" ? C.ink : "#9CA3AF", marginBottom: 6,
            }}>
              <UserCircle size={18} /> Bedrijfsprofiel
            </button>
            <SidebarUsage onOpen={() => { if (typeof window !== "undefined") window.__settingsTab = "usage"; setTab("settings"); }} />
            <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}
              title={theme === "light" ? "Overschakelen naar donker" : "Overschakelen naar licht"}
              style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 13px", borderRadius: 10, border: "none", background: "transparent", color: "#9CA3AF", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, marginTop: 12, width: "100%", textAlign: "left" }}>
              {theme === "light" ? <Moon size={16} /> : <Sun size={16} />} {theme === "light" ? "Donker thema" : "Licht thema"}
            </button>
            <button onClick={async () => { try { await fetch("/api/logout", { method: "POST", credentials: "include" }); } catch {} setAuthed(false); }} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 13px", borderRadius: 10, border: "none", background: "transparent", color: "#9CA3AF", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, marginTop: 12, width: "100%", textAlign: "left" }}>
              <LogOut size={16} /> Uitloggen
            </button>
          </div>
        </aside>

        {/* Main */}
        <main style={{ flex: 1, padding: "30px 38px", maxWidth: 1180, margin: "0 auto", width: "100%", boxSizing: "border-box", position: "relative" }}>
          <button onClick={() => setTab("settings")} title="Instellingen" style={{ position: "absolute", top: 22, right: 30, zIndex: 5, width: 38, height: 38, borderRadius: 10, border: `1px solid ${C.line}`, background: tab === "settings" ? C.yellow : C.card, cursor: "pointer", display: "grid", placeItems: "center", color: C.ink2 }}>
            <Settings size={18} />
          </button>
          {tab === "dashboard" && <Dashboard ctx={ctx} />}
          {/* Strategy */}
          {tab === "opportunity" && <BenchmarkAgent ctx={ctx} />}
          {tab === "research" && <Research ctx={ctx} />}
          {tab === "channels" && <ChannelAuditAgent ctx={ctx} />}
          {/* Marketing ops */}
          {tab === "operations" && <OperationsAgent ctx={ctx} />}
          {tab === "budget" && <BudgetSection ctx={ctx} />}
          {tab === "migration" && <MigrationAgent ctx={ctx} />}
          {tab === "content" && <Content ctx={ctx} />}
          {tab === "design" && <Design ctx={ctx} />}
          {tab === "searchads" && <SearchAdsAgent ctx={ctx} />}
          {/* Rebalance */}
          {tab === "monitor" && <Monitor ctx={ctx} />}
          {tab === "trends" && <TrendsSection ctx={ctx} />}
          {tab === "contentperf" && <ContentPerformance ctx={ctx} />}
          {tab === "rebalanceplan" && <RebalancePlan ctx={ctx} />}
          {/* Fundament + instellingen */}
          {tab === "profile" && <Profile ctx={ctx} />}
          {tab === "settings" && <SettingsPage ctx={ctx} />}
        </main>
      </div>
      {confirmState && (
        <Modal onClose={() => setConfirmState(null)} title="Weet je het zeker?">
          <div style={{ fontSize: 14.5, color: C.ink2, lineHeight: 1.5, marginBottom: 18 }}>{confirmState.message || "Deze actie kan niet ongedaan worden gemaakt."}</div>
          <div style={{ display: "flex", gap: 10 }}>
            <Btn variant="danger" icon={Trash2} onClick={() => { const fn = confirmState.onYes; setConfirmState(null); fn && fn(); }}>Ja, verwijderen</Btn>
            <Btn variant="ghost" onClick={() => setConfirmState(null)}>Annuleren</Btn>
          </div>
        </Modal>
      )}

      {aiAsk && (
        <Modal onClose={() => answerAi(false)} title="Deze actie gebruikt AI-tegoed">
          <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.6, marginBottom: 14 }}>
            {aiAsk.cost.what}. Geschatte kosten voor deze actie: <b style={{ color: C.ink }}>{aiAsk.cost.text}</b>.
          </div>
          {aiBal && typeof aiBal.remainingEUR === "number" && (
            <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "10px 13px", background: C.paper, borderRadius: 10, marginBottom: 14, fontSize: 13 }}>
              <Zap size={15} style={{ color: C.yellowDark }} />
              <span style={{ color: C.ink2 }}>Nog beschikbaar: <b style={{ color: aiBal.remainingEUR <= 0 ? C.bad : C.ink }}>{eur(aiBal.remainingEUR)}</b></span>
            </div>
          )}
          <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
            <Btn variant="yellow" icon={Check} onClick={() => answerAi(true)}>Doorgaan</Btn>
            <Btn variant="ghost" onClick={() => answerAi(false)}>Annuleren</Btn>
            <Btn variant="ghost" size="sm" onClick={() => answerAi(true, true)}>Doorgaan, niet meer vragen deze sessie</Btn>
          </div>
          <div style={{ fontSize: 11.5, color: C.muted, marginTop: 12, lineHeight: 1.5 }}>
            Deze vraag kun je uitzetten bij Instellingen → Verbruik. Functies zonder AI, zoals de checklist, templates en het omzetten van presentaties, kosten nooit tegoed.
          </div>
        </Modal>
      )}
    </>
  );
}

// Alleen wat de app zelf nodig heeft aan bewegingen. Lettertype, kleuren, formuliervelden
// en schuifbalken komen uit rex-brand.css en rex-tool.css (ingeladen in index.html).
const GlobalCSS = () => (
  <style>{`
    * { box-sizing: border-box; }
    @keyframes spin { to { transform: rotate(360deg); } }
    .spin { animation: spin 1s linear infinite; }
    @keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
  `}</style>
);

/* ============================== DEFAULT DATA ============================== */
const DEFAULT_PROFILE = {
  filled: false,
  proposition: "",
  goals: "",
  usps: "",
  products: "",
  tone: "",
  icps: "",
  colors: "#F9C112, #1C1C1A",
  // Typografie: welke letters horen bij het merk, en waar valt de tool op terug als
  // een letter niet beschikbaar is op het apparaat van de lezer.
  fontPrimary: "",
  fontFallbacks: "",
  fontNotes: "",
  competitors: "",
  positioning: "",
  mission: "",
  techstack: "",
  website: "",
  // Cijfers per jaar (hoofdlijnen) en optioneel per product of markt.
  figures: {},        // { "2027": { revenue, result, fte, note } }
  figureDetail: [],   // [ { id, year, dimension: "product"|"markt", name, revenue, result, fte } ]
  // Kanalen, platformen en events: wat je inzet en wat je interessant vindt.
  channels: [],       // [ { id, name, type, status, note, cost } ]
  brandAssets: [],
  brandTemplates: [],
  notes: "",
};
const DEFAULT_METRICS = [
  { id: 1, label: "Merkbekendheid (unaided)", value: null, unit: "%", prev: null, target: null },
  { id: 2, label: "Share of Voice", value: null, unit: "%", prev: null, target: null },
  { id: 3, label: "NPS", value: null, unit: "", prev: null, target: null },
  { id: 4, label: "Merkperceptie (favorability)", value: null, unit: "%", prev: null, target: null },
  { id: 5, label: "Overgenomen bedrijven", value: null, unit: "", prev: null, target: null },
  { id: 6, label: "Geïntegreerde merken", value: null, unit: "", prev: null, target: null },
];
const DEFAULT_COMPANIES = [];
const DEFAULT_USERS = [
  { id: 1, name: "Admin", email: "admin@whitevision.com", role: "Brand Manager" },
];

/* ============================== SHARED: Action capture ============================== */
function ActionButton({ onAdd, label = "Actie", small }) {
  const [added, setAdded] = useState(false);
  return (
    <button
      onClick={(e) => { e.stopPropagation(); if (added) return; onAdd(); setAdded(true); setTimeout(() => setAdded(false), 2200); }}
      style={{ flexShrink: 0, display: "inline-flex", alignItems: "center", gap: 5, fontSize: small ? 11.5 : 12.5, fontWeight: 700, color: added ? "#fff" : C.ink, background: added ? C.good : C.yellow, border: "none", padding: small ? "5px 9px" : "6px 11px", borderRadius: 8, cursor: added ? "default" : "pointer", transition: "all .15s" }}>
      {added ? <><Check size={13} /> Toegevoegd</> : <><Flag size={13} /> {label}</>}
    </button>
  );
}

function severityColor(sev) {
  const s = (sev || "").toLowerCase();
  return s.includes("hoog") || s.includes("kans") || s.includes("risico") || s.includes("negatief") ? C.bad
    : s.includes("midden") || s.includes("neutraal") ? C.yellowDark : C.blue;
}

function Modal({ children, onClose, title }) {
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,.7)", display: "grid", placeItems: "center", zIndex: 1000, padding: 24 }}>
      <div onClick={e => e.stopPropagation()} style={{ background: C.card, borderRadius: 18, maxWidth: 640, width: "100%", maxHeight: "82vh", overflowY: "auto", boxShadow: "0 30px 80px rgba(0,0,0,.35)" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "18px 22px", borderBottom: `1px solid ${C.line}`, position: "sticky", top: 0, background: C.card, borderRadius: "18px 18px 0 0" }}>
          <span style={{ fontWeight: 700, fontSize: 16, fontFamily: FONT_DISPLAY }}>{title}</span>
          <button onClick={onClose} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted }}><X size={20} /></button>
        </div>
        <div style={{ padding: 22 }}>{children}</div>
      </div>
    </div>
  );
}

function SourceLinks({ sources }) {
  const [open, setOpen] = useState(false);
  if (!sources || !sources.length) return null;
  return (
    <div style={{ marginTop: 14 }}>
      <button onClick={() => setOpen(o => !o)} style={{ display: "flex", alignItems: "center", gap: 6, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: .3 }}>
        <ChevronRight size={13} style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .15s" }} />
        Bronnen ({sources.length})
      </button>
      {open && (
        <div style={{ display: "flex", flexDirection: "column", gap: 6, marginTop: 8 }}>
          {sources.map((s, i) => (
            <a key={i} href={s.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: 13, color: C.blue, textDecoration: "none", display: "flex", alignItems: "center", gap: 6, lineHeight: 1.4 }}>
              <Globe size={13} style={{ flexShrink: 0 }} /> <span style={{ wordBreak: "break-word" }}>{s.title || s.url}</span>
            </a>
          ))}
        </div>
      )}
    </div>
  );
}

// Detailpagina voor één bevinding/observatie: volledige tekst, bron, advies, en AI-verdieping.
function InsightDetail({ item, prefix, profile, onAction, onToProfile, onClose }) {
  const [deep, setDeep] = useState(null);
  const [busy, setBusy] = useState(false);
  const col = severityColor(item.severity);

  const dive = async () => {
    setBusy(true);
    const prompt = `${profileContext(profile)}\n\nCONTEXT: dit is een bevinding uit "${prefix}".\nTITEL: ${item.title || "—"}\nINHOUD: ${item.text || item.detail || item.suggestion || ""}\n${item.source ? "Genoemde bron: " + item.source : ""}\n\nVerdiep dit punt met web search. Geef: concrete onderbouwing met echte bronnen (URLs), bij welke concurrenten/partijen dit speelt, welke specifieke pagina's of voorbeelden relevant zijn, en 2-3 concrete vervolgstappen voor de brand manager.\n\nJSON: {"analysis":"grondige analyse in 4-6 zinnen","examples":[{"who":"concurrent/partij","detail":"wat en waar","url":"bron-URL"}],"steps":["stap 1","stap 2"]}`;
    const { data, sources } = await callAIFull(prompt, { json: true, useSearch: true });
    setDeep({ ...(data || {}), sources });
    setBusy(false);
  };

  return (
    <Modal onClose={onClose} title={item.title || (prefix + ": detail")}>
      <div style={{ display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap" }}>
        {item.severity && <Tag color={col}>{item.severity}</Tag>}
        {item.tag && <Tag>{item.tag}</Tag>}
        {item.sentiment && <Tag color={severityColor(item.sentiment)}>{item.sentiment}</Tag>}
        {item.date && <Tag color={C.muted}>{item.date}</Tag>}
      </div>
      <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{item.text || item.detail || item.suggestion}</div>
      {item.profileRef && (
        <div style={{ marginTop: 14, display: "flex", gap: 10, alignItems: "stretch" }}>
          <div style={{ width: 4, background: C.bad, borderRadius: 4, flexShrink: 0 }} />
          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: C.bad, textTransform: "uppercase", marginBottom: 3 }}>Wijkt af van je merkprofiel</div>
            <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, fontStyle: "italic" }}>"{item.profileRef}"</div>
          </div>
        </div>
      )}
      {item.source && (
        <div style={{ marginTop: 12, fontSize: 13, color: C.muted }}>
          Bron: {/^https?:\/\//.test(item.source)
            ? <a href={item.source} target="_blank" rel="noopener noreferrer" style={{ color: C.blue }}>{item.source}</a>
            : <span>{item.source}</span>}
        </div>
      )}
      {item.advice && (
        <div style={{ marginTop: 14, padding: "12px 14px", background: C.yellow + "14", borderRadius: 10, border: `1px solid ${C.yellow}44` }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: C.yellowDark, textTransform: "uppercase", marginBottom: 4 }}>Advies</div>
          <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{item.advice}</div>
        </div>
      )}

      {!deep && (
        <div style={{ marginTop: 18 }}>
          <Btn variant="yellow" size="sm" icon={Sparkles} onClick={dive} disabled={busy}>{busy ? "Verdiepen…" : "Verdiep met bronnen"}</Btn>
          {busy && <div style={{ marginTop: 10 }}><Spinner label="AI doet diepteonderzoek met web search…" /></div>}
        </div>
      )}
      {deep && (
        <div style={{ marginTop: 18, borderTop: `1px solid ${C.line}`, paddingTop: 16 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}><AIBadge /> <span style={{ fontWeight: 700 }}>Verdieping</span></div>
          {deep.analysis && <div style={{ fontSize: 14, lineHeight: 1.6, color: C.ink2 }}>{deep.analysis}</div>}
          {deep.examples?.length > 0 && (
            <div style={{ marginTop: 12, display: "flex", flexDirection: "column", gap: 8 }}>
              {deep.examples.map((ex, i) => (
                <div key={i} style={{ padding: "10px 12px", background: C.paper, borderRadius: 10, border: `1px solid ${C.line}` }}>
                  <div style={{ fontWeight: 700, fontSize: 13.5 }}>{ex.who}</div>
                  <div style={{ fontSize: 13, color: C.ink2, marginTop: 2, lineHeight: 1.45 }}>{ex.detail}</div>
                  {ex.url && <a href={ex.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: 12.5, color: C.blue, display: "inline-flex", alignItems: "center", gap: 4, marginTop: 5 }}><Globe size={12} /> bron</a>}
                </div>
              ))}
            </div>
          )}
          {deep.steps?.length > 0 && (
            <div style={{ marginTop: 12 }}>
              <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 6 }}>Vervolgstappen</div>
              {deep.steps.map((s, i) => (
                <div key={i} style={{ display: "flex", gap: 8, alignItems: "flex-start", marginBottom: 8 }}>
                  <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, flex: 1 }}>• {s}</div>
                  <ActionButton small onAdd={() => onAction({ title: s, detail: item.title, source: prefix })} />
                </div>
              ))}
            </div>
          )}
          <SourceLinks sources={deep.sources} />
        </div>
      )}

      <div style={{ marginTop: 20, display: "flex", gap: 10, borderTop: `1px solid ${C.line}`, paddingTop: 16 }}>
        <ActionButton onAdd={() => onAction({ title: item.title || (prefix + ": actie"), detail: item.text || item.detail || item.suggestion, source: prefix })} label="Markeer als actie" />
        {onToProfile && (
          <button onClick={() => { onToProfile(`${item.title || ""}: ${item.text || item.detail || item.suggestion || ""}`, prefix); onClose(); }}
            style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12.5, fontWeight: 700, color: C.ink, background: C.card, border: `1.5px solid ${C.yellow}`, padding: "6px 11px", borderRadius: 8, cursor: "pointer" }}>
            <UserCircle size={13} /> Vastleggen in merkprofiel
          </button>
        )}
      </div>
    </Modal>
  );
}

function Insights({ items, onAction, prefix, profile, onToProfile }) {
  const [detail, setDetail] = useState(null);
  if (!items || !items.length) return null;
  return (
    <>
      <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 14 }}>
        {items.map((it, i) => {
          const col = severityColor(it.severity || it.type);
          return (
            <div key={i} onClick={() => setDetail(it)}
              style={{ display: "flex", gap: 12, alignItems: "flex-start", padding: "13px 15px", background: C.paper, borderRadius: 12, border: `1px solid ${C.line}`, cursor: "pointer", transition: "border-color .15s" }}
              onMouseEnter={e => e.currentTarget.style.borderColor = C.yellow}
              onMouseLeave={e => e.currentTarget.style.borderColor = C.line}>
              <div style={{ width: 7, height: 7, borderRadius: 8, background: col, marginTop: 7, flexShrink: 0 }} />
              <div style={{ flex: 1 }}>
                {it.title && <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 2 }}>{it.title}</div>}
                <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{it.text || it.suggestion || it.detail}</div>
                <div style={{ display: "flex", gap: 6, marginTop: 6, alignItems: "center", flexWrap: "wrap" }}>
                  {it.date && <span style={{ fontSize: 11.5, color: C.muted, fontWeight: 600 }}>{it.date}</span>}
                  {it.tag && <Tag color={col}>{it.tag}</Tag>}
                  {it.sentiment && <Tag color={severityColor(it.sentiment)}>{it.sentiment}</Tag>}
                  {it.profileRef && <Tag color={C.bad}>vs. merkprofiel</Tag>}
                  {it.source && <span style={{ fontSize: 11.5, color: C.muted, display: "inline-flex", alignItems: "center", gap: 3 }}><Globe size={11} /> bron</span>}
                  <span style={{ fontSize: 11.5, color: C.blue, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 2 }}>details <ChevronRight size={12} /></span>
                </div>
              </div>
              <ActionButton onAdd={() => onAction({ title: it.title || (prefix + ": actie"), detail: it.text || it.suggestion || it.detail, source: prefix })} />
            </div>
          );
        })}
      </div>
      {detail && <InsightDetail item={detail} prefix={prefix} profile={profile} onAction={onAction} onToProfile={onToProfile} onClose={() => setDetail(null)} />}
    </>
  );
}

function PageHead({ title, sub, right }) {
  return (
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 24, animation: "fadeUp .3s ease" }}>
      <div>
        <h1 style={{ fontFamily: FONT_DISPLAY, fontSize: 28, fontWeight: 700, margin: 0, letterSpacing: -.6 }}>{title}</h1>
        {sub && <p style={{ color: C.muted, fontSize: 14.5, margin: "5px 0 0" }}>{sub}</p>}
      </div>
      {right}
    </div>
  );
}

function profileContext(p) {
  if (!p || !p.filled) return "Het merkprofiel is nog niet ingevuld. Werk met algemene aannames over whitevision (AI-driven IDP, buy-and-build, P2P/spend/contractmanagement).";
  let s = `MERKPROFIEL whitevision (kennisbasis om tegen te spiegelen):
- Propositie: ${p.proposition}
- Doelstellingen: ${p.goals}
- USP's: ${p.usps}
- Producten: ${p.products}
- Tone of voice: ${p.tone}
- ICP's: ${p.icps}
- Huisstijlkleuren: ${p.colors}
- Bekende concurrenten: ${p.competitors}`;
  if (p.positioning) s += `\n- Positioneringsstatement: ${p.positioning}`;
  if (p.mission) s += `\n- Missie & visie: ${p.mission}`;
  if (p.techstack) s += `\n- Tech stack: ${p.techstack}`;
  if (p.website) s += `\n- Website: ${p.website}`;
  // De uitgewerkte producten meegeven; die zijn veel specifieker dan het vrije tekstveld.
  const prods = (p.productList || []).filter(x => x && x.name);
  if (prods.length) {
    s += `\n\nPRODUCTEN (uitgewerkt):`;
    prods.forEach(x => {
      const bits = [];
      if (x.status && x.status !== "actief") bits.push(x.status === "nieuw" ? "in ontwikkeling" : "wordt uitgefaseerd");
      if (x.pricing) bits.push(`prijsmodel ${x.pricing}`);
      if (x.price) bits.push(x.price);
      s += `\n- ${x.name}${bits.length ? ` (${bits.join(", ")})` : ""}`;
      if (x.proposition) s += `\n  Propositie: ${x.proposition}`;
      if (x.audience) s += `\n  Voor wie: ${String(x.audience).replace(/\n/g, "; ")}`;
      if (x.usps) s += `\n  Onderscheidend: ${String(x.usps).replace(/\n/g, "; ")}`;
      if (x.competitors) s += `\n  Concurrenten hierop: ${x.competitors}`;
    });
  }
  return s;
}

/* ============================== VERBRUIK ============================== */
function NotificationSettings() {
  const [s, setS] = useState(null);
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState("");
  const load = async () => { try { const r = await fetch("/api/settings", { credentials: "include" }); if (r.ok) setS(await r.json()); } catch {} };
  useEffect(() => { load(); }, []);
  const save = async (patch) => {
    setBusy(true);
    const next = { ...s, ...patch }; setS(next);
    await fetch("/api/settings", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(patch) });
    setBusy(false);
  };
  const test = async () => {
    setBusy(true); setMsg("");
    const r = await fetch("/api/settings/testmail", { method: "POST", credentials: "include" });
    const d = await r.json();
    setMsg(d.ok ? `Testmail verstuurd naar ${d.to}.` : (d.error || "Versturen mislukt."));
    setBusy(false);
  };
  if (!s) return null;
  return (
    <Card style={{ marginTop: 22 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Megaphone size={18} style={{ color: C.yellowDark }} />
        <span style={{ fontWeight: 700, fontSize: 16 }}>Notificaties</span>
      </div>
      {!s.mailConfigured && (
        <div style={{ ...mirrorWarn, marginTop: 10 }}><AlertTriangle size={15} /> E-mail is nog niet ingesteld op de server (.env: RESEND_API_KEY en MAIL_FROM). Tot die er zijn worden er geen mails verstuurd.</div>
      )}
      <div style={{ fontSize: 13.5, color: C.muted, lineHeight: 1.5, marginBottom: 14 }}>Ontvang een dagelijkse samenvatting per e-mail van nieuwe bevindingen uit je monitoring. Afzender: {s.mailFrom}.</div>
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 14 }}>
        <Toggle on={s.notifyEnabled} onChange={() => save({ notifyEnabled: !s.notifyEnabled })} />
        <span style={{ fontSize: 14, fontWeight: 600 }}>Dagelijkse notificaties {s.notifyEnabled ? "aan" : "uit"}</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 12, marginBottom: 14 }}>
        <div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Ontvanger</label>
          <input value={s.notifyEmail} onChange={e => setS({ ...s, notifyEmail: e.target.value })} onBlur={() => save({ notifyEmail: s.notifyEmail })} placeholder="jouw@adres.nl"
            style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
        </div>
        <div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Niveau</label>
          <select value={s.notifyLevel} onChange={e => save({ notifyLevel: e.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
            <option value="all">Alle bevindingen</option>
            <option value="high">Alleen urgentie hoog</option>
          </select>
        </div>
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        <Btn size="sm" variant="ghost" icon={Megaphone} onClick={test} disabled={busy || !s.mailConfigured}>Testmail sturen</Btn>
        {msg && <span style={{ fontSize: 13, color: C.ink2 }}>{msg}</span>}
      </div>
    </Card>
  );
}

function SidebarUsage({ onOpen }) {
  const [u, setU] = useState(null);
  const load = () => fetchUsage().then(setU);
  useEffect(() => { load(); const t = setInterval(load, 20000); return () => clearInterval(t); }, []);
  if (!u) return null;
  const hasBudget = u.startUSD > 0;
  const pct = hasBudget ? Math.min(100, Math.round((u.costUSD / u.startUSD) * 100)) : 0;
  const low = hasBudget && u.remainingUSD < u.startUSD * 0.15;
  return (
    <div onClick={onOpen} style={{ background: "#ffffff10", borderRadius: 12, padding: "12px 13px", cursor: "pointer", marginTop: 8 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: hasBudget ? 8 : 4 }}>
        <Zap size={14} style={{ color: C.yellow }} />
        <span style={{ fontSize: 12.5, fontWeight: 700, color: "#fff" }}>Verbruik</span>
      </div>
      {hasBudget ? (
        <>
          <div style={{ height: 6, background: "#ffffff22", borderRadius: 6, overflow: "hidden" }}>
            <div style={{ height: "100%", width: `${pct}%`, background: low ? C.bad : C.yellow, borderRadius: 6 }} />
          </div>
          <div style={{ fontSize: 11.5, color: low ? "#F87171" : "#9CA3AF", marginTop: 6 }}>
            nog {eur(u.remainingEUR)} van {eur(u.startEUR)}
          </div>
        </>
      ) : (
        <div style={{ fontSize: 11.5, color: "#9CA3AF" }}>tot nu toe {eur(u.costEUR)} · stel saldo in</div>
      )}
    </div>
  );
}

function UsagePanel() {
  const [u, setU] = useState(null);
  const [startInput, setStartInput] = useState("");
  const [busy, setBusy] = useState(false);
  const load = () => fetchUsage().then(d => { setU(d); if (d && d.startUSD) setStartInput(String(d.startUSD)); });
  useEffect(() => { load(); }, []);
  const calibrate = async (extra) => {
    setBusy(true);
    await fetch("/api/usage/calibrate", {
      method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
      body: JSON.stringify(extra),
    });
    await load(); setBusy(false);
  };
  if (!u) return null;
  const hasBudget = u.startUSD > 0;
  const pct = hasBudget ? Math.min(100, Math.round((u.costUSD / u.startUSD) * 100)) : 0;
  return (
    <Card style={{ marginTop: 22 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 14 }}>
        <Zap size={18} style={{ color: C.yellowDark }} />
        <span style={{ fontWeight: 700, fontSize: 16 }}>API-verbruik & saldo</span>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 12, marginBottom: 16 }}>
        <div style={usageStat}><div style={usageNum}>{eur(u.costEUR)}</div><div style={usageLbl}>verbruikt (geschat)</div></div>
        <div style={usageStat}><div style={usageNum}>{hasBudget ? eur(u.remainingEUR) : "—"}</div><div style={usageLbl}>resterend</div></div>
        <div style={usageStat}><div style={usageNum}>{u.calls}</div><div style={usageLbl}>AI-aanroepen</div></div>
      </div>
      {hasBudget && (
        <div style={{ marginBottom: 16 }}>
          <div style={{ height: 8, background: C.line, borderRadius: 8, overflow: "hidden" }}>
            <div style={{ height: "100%", width: `${pct}%`, background: pct > 85 ? C.bad : C.yellow, borderRadius: 8 }} />
          </div>
          <div style={{ fontSize: 12, color: C.muted, marginTop: 6 }}>{pct}% van je ingevoerde saldo gebruikt · {u.searches} webzoekopdrachten</div>
        </div>
      )}
      <div style={{ fontSize: 13, color: C.muted, lineHeight: 1.5, marginBottom: 12 }}>
        Anthropic geeft je actuele saldo niet vrij aan de tool. Lees je saldo af in de <a href="https://console.anthropic.com/settings/billing" target="_blank" rel="noopener noreferrer" style={{ color: C.blue }}>Anthropic console</a> en voer het hier in als startpunt; de tool trekt het geschatte verbruik er daarna van af.
      </div>
      <div style={{ display: "flex", gap: 8, alignItems: "flex-end", flexWrap: "wrap" }}>
        <div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Saldo in USD</label>
          <input value={startInput} onChange={e => setStartInput(e.target.value)} placeholder="bijv. 20"
            style={{ width: 120, padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
        </div>
        <Btn size="sm" variant="yellow" icon={Save} disabled={busy} onClick={() => calibrate({ startUSD: Number(startInput) || 0 })}>Saldo instellen</Btn>
        <Btn size="sm" variant="ghost" disabled={busy} onClick={() => calibrate({ startUSD: Number(startInput) || 0, resetUsage: true })}>Verbruik op nul</Btn>
      </div>
      <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10 }}>Bedragen zijn schattingen op basis van tokenverbruik (USD→EUR ≈ {u.usdToEur}). Zet "Verbruik op nul" als je je saldo opnieuw hebt bijgevuld.</div>
      <AutoAiSetting />
      <AiGateSetting />
      <CostOverview />
    </Card>
  );
}

// Harde stop op automatische AI-acties. Staat standaard aan: dan draait de
// achtergrondplanner niet, ongeacht wat er per bron is ingesteld.
function AutoAiSetting() {
  const [blocked, setBlocked] = useState(true);
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    fetch("/api/auto-ai", { credentials: "include" }).then(r => r.json())
      .then(d => { setBlocked(d.blocked !== false); setLoaded(true); }).catch(() => setLoaded(true));
  }, []);
  const toggle = async () => {
    const next = !blocked;
    setBlocked(next);
    try { await fetch("/api/auto-ai", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ blocked: next }) }); } catch {}
  };
  if (!loaded) return null;
  return (
    <div style={{ display: "flex", alignItems: "flex-start", gap: 12, marginTop: 18, paddingTop: 16, borderTop: `1px solid ${C.line}` }}>
      <Toggle on={blocked} onChange={toggle} />
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
          <span style={{ fontSize: 13.5, fontWeight: 600, color: C.ink }}>Automatische AI-acties blokkeren</span>
          <Tag color={blocked ? C.good : C.bad}>{blocked ? "geblokkeerd" : "toegestaan"}</Tag>
        </div>
        <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5, marginTop: 2 }}>
          {blocked
            ? "Er draait niets vanzelf. Ook bronnen die op dagelijks of wekelijks staan, worden niet automatisch ververst. Je verbruikt alleen tegoed als je zelf een actie start."
            : "De achtergrondplanner mag draaien volgens de frequentie die per bron is ingesteld. Dat verbruikt tegoed zonder dat je erbij bent."}
        </div>
      </div>
    </div>
  );
}

// Aan/uit voor de vraag vooraf bij AI-acties.
function AiGateSetting() {
  const [on, setOn] = useState(true);
  useEffect(() => { store.get("wv_ai_gate", true).then(v => setOn(v !== false)).catch(() => {}); }, []);
  const toggle = () => {
    const next = !on;
    setOn(next);
    store.set("wv_ai_gate", next);
    // Direct laten gelden, zonder de pagina te herladen.
    if (!next) setAiGate(null);
    else window.location.reload();
  };
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, marginTop: 18, paddingTop: 16, borderTop: `1px solid ${C.line}` }}>
      <Toggle on={on} onChange={toggle} />
      <div>
        <div style={{ fontSize: 13.5, fontWeight: 600, color: C.ink }}>Vooraf vragen bij AI-acties</div>
        <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5 }}>Toont vóór elke AI-actie wat die ongeveer kost en hoeveel tegoed er nog is, zodat je bewust akkoord geeft.</div>
      </div>
    </div>
  );
}

// Overzicht van wat wel en niet tegoed kost, zodat duidelijk is waar je Anthropic voor nodig hebt.
const FREE_FEATURES = [
  "Bedrijfsprofiel invullen en beheren",
  "Acquisities: checklist, voortgang en planning",
  "Brand templates en assets opslaan",
  "Presentatie omzetten naar je huisstijl",
  "Sitemaps ophalen en bewaren",
  "Analytics en Search Console (echte cijfers)",
  "Lijsten, notities en instellingen",
];
const PAID_FEATURES = [
  ["Overname-audit (Acquisition agent)", COST_HINT.search],
  ["Benchmark tegen concurrenten", COST_HINT.search],
  ["Monitor: een bron controleren", COST_HINT.search],
  ["Content schrijven of herschrijven", COST_HINT.light],
  ["Indeling voor een presentatie bedenken", COST_HINT.light],
  ["Onderzoek en strategische modellen", COST_HINT.light],
];
function CostOverview() {
  const [open, setOpen] = useState(false);
  return (
    <div style={{ marginTop: 16, paddingTop: 16, borderTop: `1px solid ${C.line}` }}>
      <button onClick={() => setOpen(o => !o)} style={{ display: "flex", alignItems: "center", gap: 7, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, color: C.ink }}>
        <ChevronRight size={14} style={{ transform: open ? "rotate(90deg)" : "none", transition: "transform .15s" }} />
        Wat kost tegoed en wat niet?
      </button>
      {open && (
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 12 }}>
          <div style={{ padding: 13, background: C.good + "0E", borderRadius: 10 }}>
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.good, marginBottom: 8, textTransform: "uppercase" }}>Werkt zonder tegoed</div>
            {FREE_FEATURES.map((f, i) => (
              <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 4, display: "flex", gap: 6 }}><Check size={13} style={{ color: C.good, flexShrink: 0, marginTop: 2 }} /> {f}</div>
            ))}
          </div>
          <div style={{ padding: 13, background: C.yellow + "12", borderRadius: 10 }}>
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.yellowDark, marginBottom: 8, textTransform: "uppercase" }}>Gebruikt AI-tegoed</div>
            {PAID_FEATURES.map(([f, c], i) => (
              <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 5 }}>
                <span style={{ display: "block" }}>{f}</span>
                <span style={{ fontSize: 11.5, color: C.muted, fontWeight: 600 }}>{c}</span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
const usageStat = { padding: "12px 14px", background: C.paper, borderRadius: 12, border: `1px solid ${C.line}` };
const usageNum = { fontFamily: FONT_DISPLAY, fontSize: 22, fontWeight: 700 };
const usageLbl = { fontSize: 12, color: C.muted, marginTop: 2 };

/* ============================== 1. DASHBOARD ============================== */
function Dashboard({ ctx }) {
  const { metrics, saveMetrics, actions, research, audits, monitors, profile, confirm } = ctx;
  const [editing, setEditing] = useState(false);
  const [summary, setSummary] = useState("");
  const [busy, setBusy] = useState(false);

  const trend = (m) => {
    if (m.value == null || m.prev == null || m.value === "" || m.prev === "") return { empty: true };
    const d = Number(m.value) - Number(m.prev);
    return { d, up: d > 0, flat: d === 0 };
  };
  const updateMetric = (id, field, val) => {
    saveMetrics(metrics.map(m => {
      if (m.id !== id) return m;
      if (field === "label" || field === "unit") return { ...m, [field]: val };
      return { ...m, [field]: val === "" ? null : Number(val) };
    }));
  };
  const addMetric = () => saveMetrics([...metrics, { id: Date.now(), label: "Nieuwe metric", value: null, unit: "", prev: null, target: null }]);
  const delMetric = (id) => confirm("Deze KPI wordt verwijderd van je dashboard.", () => saveMetrics(metrics.filter(m => m.id !== id)));

  const genSummary = async () => {
    setBusy(true);
    const filled = metrics.filter(m => m.value != null && m.value !== "");
    if (!filled.length) { setSummary("Vul eerst een paar KPI's in via 'Metrics bewerken'. Daarna schrijf ik hier een samenvatting voor de CCO."); setBusy(false); return; }
    const data = filled.map(m => `${m.label}: ${m.value}${m.unit} (vorige ${m.prev ?? "?"}${m.unit}, doel ${m.target ?? "?"}${m.unit})`).join("; ");
    const r = await callAI(`${profileContext(profile)}\n\nHier zijn de actuele merk-KPI's: ${data}.\n\nSchrijf een beknopte management-samenvatting (max 5 zinnen) voor de CCO: wat valt op, waar zit momentum, waar is aandacht nodig richting de doelen. Zakelijk en concreet.`);
    setSummary(r); setBusy(false);
  };

  const openActions = (actions || []).filter(a => a.status !== "done").length;

  return (
    <div>
      <PageHead title="Dashboard" sub="De staat van het merk in één oogopslag."
        right={<Btn variant={editing ? "yellow" : "ghost"} size="sm" icon={editing ? Save : Edit3} onClick={() => setEditing(!editing)}>{editing ? "Klaar" : "Metrics bewerken"}</Btn>} />

      {/* quick stats */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 14, marginBottom: 22 }}>
        {[
          { l: "Openstaande acties", v: openActions, i: ListTodo },
          { l: "Onderzoeken", v: (research || []).length, i: Microscope },
          { l: "Audits", v: (audits || []).length, i: ClipboardCheck },
          { l: "Actieve monitors", v: (monitors || []).length, i: Radar },
        ].map((s, i) => (
          <Card key={i} style={{ padding: 16 }}>
            <s.i size={18} style={{ color: C.muted }} />
            <div style={{ fontFamily: FONT_DISPLAY, fontSize: 30, fontWeight: 700, marginTop: 8, lineHeight: 1 }}>{s.v}</div>
            <div style={{ fontSize: 12.5, color: C.muted, marginTop: 4 }}>{s.l}</div>
          </Card>
        ))}
      </div>

      {/* metric cards */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 14 }}>
        {metrics.map(m => {
          const t = trend(m);
          const hasVal = m.value != null && m.value !== "";
          const pct = (m.target && hasVal) ? Math.min(100, Math.round((Number(m.value) / Number(m.target)) * 100)) : 0;
          return (
            <Card key={m.id} hover>
              {editing ? (
                <div>
                  <input value={m.label} onChange={e => updateMetric(m.id, "label", e.target.value)} style={inlineInput} />
                  <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
                    <input value={m.value ?? ""} onChange={e => updateMetric(m.id, "value", e.target.value)} style={{ ...inlineInput, width: 60 }} placeholder="nu" />
                    <input value={m.prev ?? ""} onChange={e => updateMetric(m.id, "prev", e.target.value)} style={{ ...inlineInput, width: 60 }} placeholder="vorig" />
                    <input value={m.target ?? ""} onChange={e => updateMetric(m.id, "target", e.target.value)} style={{ ...inlineInput, width: 60 }} placeholder="doel" />
                    <input value={m.unit} onChange={e => updateMetric(m.id, "unit", e.target.value)} style={{ ...inlineInput, width: 40 }} placeholder="eenh" />
                  </div>
                  <button onClick={() => delMetric(m.id)} style={{ marginTop: 10, fontSize: 12, color: C.bad, background: "none", border: "none", cursor: "pointer", display: "flex", alignItems: "center", gap: 4 }}><Trash2 size={12} /> Verwijderen</button>
                </div>
              ) : (
                <div>
                  <div style={{ fontSize: 13, color: C.muted, fontWeight: 600, minHeight: 34 }}>{m.label}</div>
                  {hasVal ? (
                    <>
                      <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginTop: 4 }}>
                        <span style={{ fontFamily: FONT_DISPLAY, fontSize: 34, fontWeight: 700, lineHeight: 1 }}>{m.value}{m.unit}</span>
                        {!t.empty && (
                          <span style={{ display: "inline-flex", alignItems: "center", gap: 2, fontSize: 13, fontWeight: 700, color: t.flat ? C.muted : t.up ? C.good : C.bad }}>
                            {t.flat ? <Minus size={13} /> : t.up ? <TrendingUp size={13} /> : <TrendingDown size={13} />}
                            {t.flat ? "" : `${t.up ? "+" : ""}${t.d}${m.unit}`}
                          </span>
                        )}
                      </div>
                      <div style={{ marginTop: 14 }}>
                        <div style={{ height: 6, background: C.line, borderRadius: 6, overflow: "hidden" }}>
                          <div style={{ height: "100%", width: `${pct}%`, background: C.yellow, borderRadius: 6 }} />
                        </div>
                        <div style={{ fontSize: 11.5, color: C.muted, marginTop: 5 }}>{m.target != null ? `${pct}% van doel (${m.target}${m.unit})` : "geen doel ingesteld"}</div>
                      </div>
                    </>
                  ) : (
                    <div style={{ marginTop: 8 }}>
                      <span style={{ fontFamily: FONT_DISPLAY, fontSize: 30, fontWeight: 700, color: C.line }}>—</span>
                      <div style={{ fontSize: 12, color: C.muted, marginTop: 8 }}>Nog niet ingevuld. Klik op <b>Metrics bewerken</b> om je eigen cijfers in te voeren.</div>
                    </div>
                  )}
                </div>
              )}
            </Card>
          );
        })}
        {editing && (
          <Card hover onClick={addMetric} style={{ display: "grid", placeItems: "center", borderStyle: "dashed", minHeight: 130 }}>
            <div style={{ textAlign: "center", color: C.muted }}><Plus size={22} /><div style={{ fontSize: 13, fontWeight: 600, marginTop: 4 }}>Metric toevoegen</div></div>
          </Card>
        )}
      </div>

      {/* AI summary */}
      <Card style={{ marginTop: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: summary ? 14 : 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
            <AIBadge /> <span style={{ fontWeight: 700, fontSize: 15 }}>Management-samenvatting</span>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <CostHint kind="light" />
            <Btn size="sm" variant="ghost" icon={Sparkles} onClick={genSummary} disabled={busy}>{summary ? "Opnieuw" : "Genereer"}</Btn>
          </div>
        </div>
        {busy && <Spinner label="Samenvatting schrijven…" />}
        {summary && !busy && <div style={{ fontSize: 14.5, lineHeight: 1.65, color: C.ink2 }}>{summary}</div>}
      </Card>
    </div>
  );
}
const inlineInput = { width: "100%", boxSizing: "border-box", padding: "7px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, outline: "none" };

/* ============================== 2. ONDERZOEK ============================== */
const RESEARCH_TYPES = [
  { id: "competitor", label: "Concurrentieonderzoek", icon: Building2, desc: "Positionering, sterktes en zwaktes van concurrenten in de IDP/P2P-markt.",
    prompt: "Voer een concurrentieonderzoek uit. Analyseer de belangrijkste concurrenten t.o.v. het merkprofiel: hoe positioneren zij zich, waar zijn ze sterk, waar liggen kansen voor whitevision om zich te onderscheiden.",
    methodDefault: "desk", models: ["compare", "porter", "positionering", "swot"],
    methodNote: "Deskresearch met web search naar concurrent-websites, reviewsites en vakmedia. Aanbevolen model: de vergelijkingstabel." },
  { id: "nps", label: "NPS / klanttevredenheid", icon: TrendingUp, desc: "Meet hoe waarschijnlijk klanten je aanbevelen. Bij voorkeur met een vragenlijst.",
    prompt: "Analyseer de Net Promoter Score en klanttevredenheid: wat drijft de score, waar zitten kansen om promotors te maken en criticasters te verminderen.",
    methodDefault: "field", fieldFirst: true, deskMode: "reviewaudit", models: [],
    methodNote: "NPS meet je het zuiverst met een eigen vragenlijst (field research). Wil je toch deskresearch, dan voert de tool een audit uit op externe reviewsites." },
  { id: "positioning", label: "Positioneringsonderzoek", icon: Target, desc: "Toets en scherp de positionering tussen IDP-specialist en breed platform.",
    prompt: "Voer een positioneringsonderzoek uit. Beoordeel de huidige positionering, de spanning tussen IDP-kern en breed P2P/spend-platform, en geef aanbevelingen voor een scherpe, geloofwaardige positionering.",
    methodDefault: "desk", models: ["positionering", "swot", "brandkey", "treacy"],
    methodNote: "Deskresearch. Aanbevolen model: het positioneringskruis om je positie t.o.v. concurrenten te plotten." },
  { id: "perception", label: "Imago-onderzoek", icon: Eye, desc: "Hoe wordt het merk gezien en welke associaties leven er in de markt.",
    prompt: "Voer een merkperceptie-onderzoek uit. Beschrijf hoe whitevision waarschijnlijk wordt gepercipieerd, welke associaties sterk/zwak zijn, en hoe de perceptie richting marktleiderschap te sturen.",
    methodDefault: "desk", deskMode: "reviewaudit", models: ["brandkey", "swot"],
    methodNote: "Desk = een scan van reviewsites en media om de externe perceptie te peilen. Field = een korte perceptievragenlijst. Aanbevolen model: Brand Key." },
  { id: "sov", label: "Share of Voice", icon: Megaphone, desc: "Hoe zichtbaar is whitevision t.o.v. concurrenten in de markt en media.",
    prompt: "Voer een Share of Voice-analyse uit: schat met web search in hoe zichtbaar/aanwezig whitevision is ten opzichte van de belangrijkste concurrenten in online media, vakpers, zoekresultaten en social. Geef per speler een relatieve inschatting van het aandeel in de 'stem' van de markt, met onderbouwing en bronnen. Wees expliciet dat dit een inschatting is, geen exacte meting.",
    methodDefault: "desk", deskOnlyMethod: true, models: ["sov", "compare"],
    methodNote: "Deskresearch met web search. De AI schat de relatieve zichtbaarheid per speler in; dit is een indicatie, geen exacte meting via een mediatool." },
  { id: "audience", label: "Doelgroeponderzoek (ICP)", icon: Users, desc: "Verdiep de ideale klantprofielen en hun beslissingscriteria.",
    prompt: "Voer een doelgroeponderzoek uit op basis van de ICP's. Beschrijf hun belangrijkste pijnpunten, beslissingscriteria en welke boodschap bij hen aanslaat.",
    methodDefault: "desk", models: ["vpc", "journey", "segmentatie"],
    methodNote: "Deskresearch op basis van je ICP's. Aanbevolen model: het Value Proposition Canvas. Een doelgroep-vragenlijst kan via field research." },
  { id: "category", label: "Categorie / marktonderzoek", icon: Globe, desc: "Trends, marktontwikkeling en categoriedefinitie voor AI-driven IDP.",
    prompt: "Voer een categorie-onderzoek uit naar de AI-driven IDP- en P2P-markt: trends, groei, hoe de categorie te claimen en te definiëren als marktleider.",
    methodDefault: "desk", deskOnlyMethod: true, models: ["pestel", "porter", "bcg", "abell"],
    methodNote: "Deskresearch met web search. Aanbevolen model: DESTEP voor de macro-omgeving." },
  { id: "market", label: "Marktkansen (M&A)", icon: Briefcase, desc: "Vind bedrijven die je doelstellingen aanvullen of overlappen, met fit-score.",
    prompt: "", deskOnly: true, candidates: true, methodDefault: "desk",
    methodNote: "Deskresearch met web search naar concrete overnamekandidaten, met fit-score per kandidaat." },
];

const PLAIN = "Schrijf in heldere, gewone taal. Vermijd jargon en Engelse buzzwords; leg alles uit in begrijpelijke woorden.";
const PROFILE_KEYS = ["proposition", "positioning", "mission", "goals", "usps", "products", "tone", "icps", "competitors"];
const PROFILE_LABELS = { proposition: "Propositie", positioning: "Positioneringsstatement", mission: "Missie & visie", goals: "Doelstellingen", usps: "USP's", products: "Producten", tone: "Tone of voice", icps: "ICP's", competitors: "Concurrenten" };

// Strategische modellen voor desk research. Elk model levert een eigen visuele structuur.
// ── Modellenbibliotheek. Elk model: id, label, groep, viz (visueel type), instr, schema.
// De AI vult per model een vaste JSON-structuur; ModelView tekent die.
const MODEL_GROUPS = [
  "Analyse", "Strategie & positionering", "Merk & propositie", "Klant & communicatie", "Marketingmix & uitvoering",
];
const MODELS = [
  { id: "none", label: "Vrij rapport (geen model)", group: null, viz: "none" },

  // Analyse
  { id: "swot", label: "SWOT-analyse", group: "Analyse", viz: "quad4",
    instr: "Structureer als SWOT: sterktes, zwaktes, kansen, bedreigingen.",
    schema: '"model":{"a":["sterktes..."],"b":["zwaktes..."],"c":["kansen..."],"d":["bedreigingen..."]}', labels: ["Sterktes", "Zwaktes", "Kansen", "Bedreigingen"] },
  { id: "confrontatie", label: "Confrontatiematrix", group: "Analyse", viz: "confront",
    instr: "Maak een confrontatiematrix: combineer sterktes/zwaktes (rijen) met kansen/bedreigingen (kolommen) en benoem de strategische issues per cel.",
    schema: '"model":{"rows":["sterkte/zwakte..."],"cols":["kans/bedreiging..."],"cells":[{"row":0,"col":0,"text":"strategisch issue","weight":"hoog|midden|laag"}],"issues":["belangrijkste strategische issue..."]}' },
  { id: "pestel", label: "DESTEP / PESTEL", group: "Analyse", viz: "rows",
    instr: "Analyseer de macro-omgeving via DESTEP: Demografisch, Economisch, Sociaal-cultureel, Technologisch, Ecologisch, Politiek-juridisch.",
    schema: '"model":{"rows":[{"k":"Demografisch","v":"..."},{"k":"Economisch","v":"..."},{"k":"Sociaal-cultureel","v":"..."},{"k":"Technologisch","v":"..."},{"k":"Ecologisch","v":"..."},{"k":"Politiek-juridisch","v":"..."}]}' },
  { id: "porter", label: "Porter's Vijfkrachtenmodel", group: "Analyse", viz: "rows",
    instr: "Analyseer met Porter's vijf krachten.",
    schema: '"model":{"rows":[{"k":"Macht leveranciers","v":"..."},{"k":"Macht afnemers","v":"..."},{"k":"Nieuwe toetreders","v":"..."},{"k":"Substituten","v":"..."},{"k":"Interne concurrentie","v":"..."}]}' },
  { id: "sevens", label: "7S-model (McKinsey)", group: "Analyse", viz: "rows",
    instr: "Analyseer de organisatie via de 7 S'en van McKinsey.",
    schema: '"model":{"rows":[{"k":"Strategy","v":"..."},{"k":"Structure","v":"..."},{"k":"Systems","v":"..."},{"k":"Shared values","v":"..."},{"k":"Skills","v":"..."},{"k":"Style","v":"..."},{"k":"Staff","v":"..."}]}' },
  { id: "valuechain", label: "Value Chain (Porter)", group: "Analyse", viz: "chain",
    instr: "Breng de waardeketen in kaart: primaire activiteiten en ondersteunende activiteiten, met waar waarde wordt toegevoegd.",
    schema: '"model":{"primary":[{"k":"Inkomende logistiek","v":"..."},{"k":"Operations","v":"..."},{"k":"Uitgaande logistiek","v":"..."},{"k":"Marketing & sales","v":"..."},{"k":"Service","v":"..."}],"support":[{"k":"Infrastructuur","v":"..."},{"k":"HRM","v":"..."},{"k":"Technologie","v":"..."},{"k":"Inkoop","v":"..."}]}' },
  { id: "abell", label: "Abell-model", group: "Analyse", viz: "rows",
    instr: "Definieer het business domein volgens Abell: afnemersgroepen, afnemersbehoeften, technologieën.",
    schema: '"model":{"rows":[{"k":"Afnemersgroepen (wie)","v":"..."},{"k":"Behoeften (wat)","v":"..."},{"k":"Technologieën (hoe)","v":"..."},{"k":"Business scope","v":"..."}]}' },
  { id: "interne", label: "Interne analyse", group: "Analyse", viz: "list2",
    instr: "Breng interne sterktes en zwaktes in kaart.",
    schema: '"model":{"a":["sterktes..."],"b":["zwaktes..."]}', labels: ["Sterktes", "Zwaktes"] },
  { id: "balanced", label: "Balanced Scorecard", group: "Analyse", viz: "quad4",
    instr: "Vul de vier perspectieven van de Balanced Scorecard met doelen/KPI's.",
    schema: '"model":{"a":["financieel..."],"b":["klant..."],"c":["interne processen..."],"d":["leren & groei..."]}', labels: ["Financieel", "Klant", "Interne processen", "Leren & groei"] },

  // Strategie & positionering
  { id: "ansoff", label: "Ansoff-groeimatrix", group: "Strategie & positionering", viz: "matrix2x2",
    instr: "Plaats groeistrategieën in de Ansoff-matrix (bestaande/nieuwe markten × bestaande/nieuwe producten).",
    schema: '"model":{"axisX":"Producten","axisY":"Markten","q":[{"label":"Marktpenetratie","text":"..."},{"label":"Productontwikkeling","text":"..."},{"label":"Marktontwikkeling","text":"..."},{"label":"Diversificatie","text":"..."}]}' },
  { id: "bcg", label: "BCG-matrix", group: "Strategie & positionering", viz: "matrix2x2",
    instr: "Plaats producten/proposities in de BCG-matrix (marktgroei × relatief marktaandeel).",
    schema: '"model":{"axisX":"Marktaandeel","axisY":"Marktgroei","q":[{"label":"Stars","text":"..."},{"label":"Question marks","text":"..."},{"label":"Cash cows","text":"..."},{"label":"Dogs","text":"..."}]}' },
  { id: "treacy", label: "Waardestrategieën (Treacy & Wiersema)", group: "Strategie & positionering", viz: "list3",
    instr: "Beoordeel de drie waardestrategieën en welke whitevision zou moeten kiezen.",
    schema: '"model":{"a":["Operational excellence..."],"b":["Product leadership..."],"c":["Customer intimacy..."],"choice":"aanbevolen keuze met onderbouwing"}', labels: ["Operational excellence", "Product leadership", "Customer intimacy"] },
  { id: "positionering", label: "Positioneringskruis", group: "Strategie & positionering", viz: "scatter",
    instr: "Maak een positioneringskruis met twee relevante assen en plaats whitevision en concurrenten erop (x/y van 0-100).",
    schema: '"model":{"axisX":"as horizontaal","axisY":"as verticaal","points":[{"name":"whitevision","x":70,"y":60,"us":true},{"name":"concurrent","x":40,"y":50}]}' },
  { id: "missievisie", label: "Missie & visie", group: "Strategie & positionering", viz: "rows",
    instr: "Formuleer missie, visie en kernwaarden.",
    schema: '"model":{"rows":[{"k":"Missie","v":"..."},{"k":"Visie","v":"..."},{"k":"Kernwaarden","v":"..."},{"k":"Strategische koers","v":"..."}]}' },

  // Merk & propositie
  { id: "brandkey", label: "Brand Key-model", group: "Merk & propositie", viz: "rows",
    instr: "Vul het Brand Key-model.",
    schema: '"model":{"rows":[{"k":"Root strengths","v":"..."},{"k":"Concurrentie","v":"..."},{"k":"Doelgroep","v":"..."},{"k":"Inzicht","v":"..."},{"k":"Voordelen","v":"..."},{"k":"Waarden & persoonlijkheid","v":"..."},{"k":"Reason to believe","v":"..."},{"k":"Onderscheider","v":"..."},{"k":"Essentie","v":"..."}]}' },
  { id: "golden", label: "Golden Circle (Sinek)", group: "Merk & propositie", viz: "circle",
    instr: "Vul de Golden Circle: Why, How, What.",
    schema: '"model":{"why":"...","how":"...","what":"..."}' },
  { id: "bmc", label: "Business Model Canvas", group: "Merk & propositie", viz: "canvas9",
    instr: "Vul het Business Model Canvas (9 bouwstenen).",
    schema: '"model":{"partners":"...","activities":"...","resources":"...","value":"...","relationships":"...","channels":"...","segments":"...","costs":"...","revenue":"..."}' },
  { id: "vpc", label: "Value Proposition Canvas", group: "Merk & propositie", viz: "vpc",
    instr: "Vul het Value Proposition Canvas: klantprofiel (jobs, pains, gains) en waardekaart (producten, pain relievers, gain creators).",
    schema: '"model":{"jobs":["..."],"pains":["..."],"gains":["..."],"products":["..."],"relievers":["..."],"creators":["..."]}' },

  // Klant & communicatie
  { id: "journey", label: "Customer Journey", group: "Klant & communicatie", viz: "steps",
    instr: "Breng de klantreis in fasen in kaart met per fase het klantgedrag en kansen.",
    schema: '"model":{"steps":[{"title":"Bewustwording","text":"..."},{"title":"Overweging","text":"..."},{"title":"Aankoop","text":"..."},{"title":"Gebruik","text":"..."},{"title":"Loyaliteit","text":"..."}]}' },
  { id: "funnel", label: "Sales funnel", group: "Klant & communicatie", viz: "funnel",
    instr: "Beschrijf de sales funnel van breed naar smal, met per fase de focus.",
    schema: '"model":{"steps":[{"title":"Awareness","text":"..."},{"title":"Interest","text":"..."},{"title":"Consideration","text":"..."},{"title":"Intent","text":"..."},{"title":"Purchase","text":"..."}]}' },
  { id: "aida", label: "AIDA-model", group: "Klant & communicatie", viz: "steps",
    instr: "Analyseer langs AIDA: Attention, Interest, Desire, Action.",
    schema: '"model":{"steps":[{"title":"Attention","text":"..."},{"title":"Interest","text":"..."},{"title":"Desire","text":"..."},{"title":"Action","text":"..."}]}' },
  { id: "gantt", label: "Gantt-planning", group: "Klant & communicatie", viz: "gantt",
    instr: "Maak een eenvoudige planning met taken over een aantal periodes (bijv. maanden 1-6). Geef per taak start en duur in periodes.",
    schema: '"model":{"periods":["M1","M2","M3","M4","M5","M6"],"tasks":[{"name":"taak","start":0,"len":2}]}' },

  // Marketingmix & uitvoering
  { id: "marketingmix", label: "Marketingmix (4P)", group: "Marketingmix & uitvoering", viz: "quad4",
    instr: "Vul de 4 P's: Product, Prijs, Plaats, Promotie.",
    schema: '"model":{"a":["Product..."],"b":["Prijs..."],"c":["Plaats..."],"d":["Promotie..."]}', labels: ["Product", "Prijs", "Plaats", "Promotie"] },
  { id: "segmentatie", label: "Marktsegmentatie", group: "Marketingmix & uitvoering", viz: "list3",
    instr: "Beschrijf relevante segmenten op basis van segmentatiecriteria, en welke prioriteit krijgen.",
    schema: '"model":{"a":["Segment 1: ..."],"b":["Segment 2: ..."],"c":["Segment 3: ..."],"choice":"aanbevolen prioritaire segmenten"}', labels: ["Segment A", "Segment B", "Segment C"] },
  { id: "compare", label: "Concurrentie-vergelijking (tabel)", group: "Marketingmix & uitvoering", viz: "compare",
    instr: "Maak een vergelijkingstabel van whitevision tegenover concurrenten, en benoem wat zij wél en wij niet hebben en andersom.",
    schema: '"model":{"competitors":["naam1","naam2"],"criteria":[{"name":"criterium","whitevision":"...","values":["c1","c2"]}],"theyHaveWeDont":["..."],"weHaveTheyDont":["..."]}' },
  { id: "b2b", label: "B2B vs. B2C", group: "Marketingmix & uitvoering", viz: "compare2",
    instr: "Vergelijk de B2B- en B2C-benadering voor whitevision op relevante criteria.",
    schema: '"model":{"criteria":[{"name":"criterium","b2b":"...","b2c":"..."}]}' },
  { id: "sov", label: "Share of Voice (zoekwoorden)", group: "Marketingmix & uitvoering", viz: "sov",
    instr: "Schat per belangrijk zoekwoord het maandelijkse zoekvolume en de geschatte share of voice van whitevision (aandeel organische zichtbaarheid t.o.v. het totaal). Wees expliciet dat dit schattingen zijn.",
    schema: '"model":{"keywords":[{"term":"zoekwoord","volume":1000,"sovPct":12}],"note":"toelichting en aannames"}' },
];

// Planopbouw: hoofdstukken met gekoppelde modellen (zoals de SMP-structuur).
const PLAN_CHAPTERS = [
  { id: "plan", label: "Marketingplan", models: ["abell", "bcg", "porter", "pestel", "swot", "confrontatie", "interne"] },
  { id: "strategie", label: "Marketingstrategie", models: ["balanced", "sevens", "positionering", "ansoff", "treacy", "valuechain"] },
  { id: "communicatie", label: "Communicatieplan", models: ["aida", "journey", "funnel", "golden", "gantt"] },
  { id: "mix", label: "Marketingmix", models: ["marketingmix", "segmentatie", "compare", "b2b"] },
  { id: "ondernemingsplan", label: "Ondernemingsplan", models: ["missievisie", "bmc", "vpc"] },
];

function computeNPS(scores) {
  if (!scores.length) return null;
  const prom = scores.filter(s => s >= 9).length;
  const det = scores.filter(s => s <= 6).length;
  return Math.round(((prom - det) / scores.length) * 100);
}

function SovStudy({ ctx, onBack }) {
  const { profile, confirm } = ctx;
  const [studies, setStudies] = useState(null);
  const [openId, setOpenId] = useState(null);
  const [creating, setCreating] = useState(false);
  const [url, setUrl] = useState(profile?.website || "");
  const [picked, setPicked] = useState([]);
  const [busy, setBusy] = useState(false);

  useEffect(() => { (async () => { try { setStudies(await store.get("wv_sov", [])); } catch { setStudies([]); } })(); }, []);
  const save = (v) => { setStudies(v); store.set("wv_sov", v); };
  const competitors = (profile?.competitors || "").split(",").map(s => s.trim()).filter(Boolean);

  const startStudy = async () => {
    setBusy(true);
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Op basis van de website ${url || profile?.website || "(onbekend)"} en wat whitevision aanbiedt, stel 8-12 relevante zoekwoorden voor die je zou meenemen in een Share of Voice-onderzoek. Kies commerciële en informatieve zoekwoorden die passen bij de proposities en doelgroep.\n\nJSON: {"keywords":["zoekwoord 1","zoekwoord 2","..."]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: !!url });
    const rows = (r?.keywords || []).map((kw, i) => ({ id: Date.now() + i, term: kw, volume: "", impressions: "", clicks: "" }));
    const study = { id: Date.now(), url: url || profile?.website || "", competitors: picked, rows, date: new Date().toISOString().slice(0, 10) };
    save([study, ...(studies || [])]);
    setBusy(false); setCreating(false); setPicked([]); setOpenId(study.id);
  };

  // ---- detail ----
  if (openId) {
    const s = (studies || []).find(x => x.id === openId);
    if (!s) { setOpenId(null); return null; }
    const updRow = (id, patch) => save(studies.map(x => x.id === s.id ? { ...x, rows: x.rows.map(r => r.id === id ? { ...r, ...patch } : r) } : x));
    const addRow = () => save(studies.map(x => x.id === s.id ? { ...x, rows: [...x.rows, { id: Date.now(), term: "", volume: "", impressions: "", clicks: "" }] } : x));
    const delRow = (id) => save(studies.map(x => x.id === s.id ? { ...x, rows: x.rows.filter(r => r.id !== id) } : x));
    const totalVol = s.rows.reduce((a, r) => a + (parseFloat(r.volume) || 0), 0);
    const totalImpr = s.rows.reduce((a, r) => a + (parseFloat(r.impressions) || 0), 0);
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Share of Voice</button>
        <PageHead title="Share of Voice" sub={`${s.url || ""}${s.competitors?.length ? " · vs " + s.competitors.join(", ") : ""} · ${s.date}`}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => confirm("Dit SOV-onderzoek verwijderen?", () => { save(studies.filter(x => x.id !== s.id)); setOpenId(null); })}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 13, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Vul per zoekwoord het maandelijkse zoekvolume, je vertoningen en je kliks in. Share of Voice = jouw vertoningen gedeeld door het zoekvolume. Later koppelbaar aan Semrush, Ahrefs of Google Search Console om dit automatisch te vullen.</div>
          <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={cmpTh}>Zoekwoord</th>
                <th style={{ ...cmpTh, textAlign: "right" }}>Zoekvolume</th>
                <th style={{ ...cmpTh, textAlign: "right" }}>Vertoningen</th>
                <th style={{ ...cmpTh, textAlign: "right" }}>Kliks</th>
                <th style={{ ...cmpTh, textAlign: "right" }}>SOV</th>
                <th style={cmpTh}></th>
              </tr></thead>
              <tbody>{s.rows.map((r, i) => {
                const vol = parseFloat(r.volume) || 0, impr = parseFloat(r.impressions) || 0;
                const sov = vol > 0 ? Math.round((impr / vol) * 100) : null;
                const cell = (k, w) => <input value={r[k]} onChange={e => updRow(r.id, { [k]: e.target.value })} style={{ width: w, padding: "6px 8px", border: `1px solid ${C.line}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", textAlign: k === "term" ? "left" : "right" }} />;
                return (
                  <tr key={r.id} style={{ background: i % 2 ? C.paper : C.card }}>
                    <td style={cmpTd}>{cell("term", "100%")}</td>
                    <td style={{ ...cmpTd, textAlign: "right" }}>{cell("volume", 80)}</td>
                    <td style={{ ...cmpTd, textAlign: "right" }}>{cell("impressions", 80)}</td>
                    <td style={{ ...cmpTd, textAlign: "right" }}>{cell("clicks", 70)}</td>
                    <td style={{ ...cmpTd, textAlign: "right", fontWeight: 700, color: sov == null ? C.muted : sov >= 25 ? C.good : sov >= 10 ? C.yellowDark : C.bad }}>{sov == null ? "—" : sov + "%"}</td>
                    <td style={{ ...cmpTd, textAlign: "center" }}><button onClick={() => delRow(r.id)} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><X size={14} /></button></td>
                  </tr>
                );
              })}</tbody>
              <tfoot><tr style={{ background: C.paper, fontWeight: 700 }}>
                <td style={{ ...cmpTd, fontWeight: 700 }}>Totaal / gemiddeld</td>
                <td style={{ ...cmpTd, textAlign: "right" }}>{totalVol.toLocaleString("nl-NL")}</td>
                <td style={{ ...cmpTd, textAlign: "right" }}>{totalImpr.toLocaleString("nl-NL")}</td>
                <td style={cmpTd}></td>
                <td style={{ ...cmpTd, textAlign: "right", fontWeight: 700, color: C.ink }}>{totalVol > 0 ? Math.round((totalImpr / totalVol) * 100) + "%" : "—"}</td>
                <td style={cmpTd}></td>
              </tr></tfoot>
            </table>
          </div>
          <div style={{ marginTop: 12 }}><Btn variant="ghost" size="sm" icon={Plus} onClick={addRow}>Zoekwoord toevoegen</Btn></div>
        </Card>
      </div>
    );
  }

  // ---- overzicht / nieuw ----
  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar onderzoek</button>
      <PageHead title="Share of Voice" sub="Meet je zichtbaarheid op zoekwoordniveau: zet vertoningen en kliks af tegen het zoekvolume, vergeleken met je concurrenten."
        right={<Btn variant="yellow" icon={Plus} onClick={() => { setCreating(true); setUrl(profile?.website || ""); }}>Nieuw onderzoek</Btn>} />

      {creating && (
        <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuw Share of Voice-onderzoek</span></div>
          <Field label="Website / URL" value={url} onChange={setUrl} placeholder="https://www.whitevision.nl" />
          <div style={{ marginTop: 6, marginBottom: 6 }}>
            <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>Concurrenten om mee te vergelijken</label>
            {competitors.length === 0
              ? <div style={{ fontSize: 12.5, color: C.muted }}>Geen concurrenten in het merkprofiel. Voeg ze daar toe om te vergelijken.</div>
              : <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                {competitors.map(c => {
                  const on = picked.includes(c);
                  return <button key={c} onClick={() => setPicked(on ? picked.filter(x => x !== c) : [...picked, c])}
                    style={{ fontSize: 12.5, fontWeight: 600, padding: "6px 11px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${on ? C.yellow : C.line}`, background: on ? C.yellow + "22" : C.card, color: C.ink }}>{c}</button>;
                })}
              </div>}
          </div>
          <div style={{ fontSize: 12.5, color: C.muted, margin: "10px 0 14px", lineHeight: 1.5 }}>De AI stelt op basis van de URL relevante zoekwoorden voor. De zoekvolumes vul je nu handmatig in; later koppelbaar aan een zoekwoordtool.</div>
          <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
            <Btn variant="yellow" icon={Sparkles} onClick={startStudy} disabled={busy}>{busy ? "Zoekwoorden ophalen…" : "Stel zoekwoorden voor"}</Btn>
            <Btn variant="ghost" onClick={() => setCreating(false)}>Annuleren</Btn>
            <CostHint kind="desk" />
          </div>
          {busy && <div style={{ marginTop: 12 }}><Spinner label="AI stelt zoekwoorden voor…" /></div>}
        </Card>
      )}

      {studies == null ? <Spinner label="Laden…" /> : studies.length === 0 && !creating
        ? <Empty icon={Megaphone} title="Nog geen SOV-onderzoek" sub="Start een onderzoek: kies een URL en concurrenten, en laat de AI zoekwoorden voorstellen." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(studies || []).map(s => (
            <Card key={s.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                <div onClick={() => setOpenId(s.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}><Megaphone size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15 }}>{s.url || "Share of Voice"}</span></div>
                  <span style={{ fontSize: 12.5, color: C.muted }}>{(s.rows || []).length} zoekwoorden{s.competitors?.length ? " · vs " + s.competitors.join(", ") : ""} · {s.date}</span>
                </div>
                <button onClick={() => confirm("Dit SOV-onderzoek verwijderen?", () => save(studies.filter(x => x.id !== s.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                <button onClick={() => setOpenId(s.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              </div>
            </Card>
          ))}
        </div>}
    </div>
  );
}

function Research({ ctx }) {
  const { research, saveResearch, surveys, saveSurveys, profile, saveProfile, addAction, metrics, saveMetrics, confirm } = ctx;
  const [open, setOpen] = useState(null);
  const [openEntry, setOpenEntry] = useState(null);
  const [running, setRunning] = useState(false);
  const [inputBrief, setInputBrief] = useState("");
  const [showNew, setShowNew] = useState(null);
  const [mode, setMode] = useState(null);
  const [modelId, setModelId] = useState("none");
  const [planView, setPlanView] = useState(false);
  const [runningModel, setRunningModel] = useState(null);

  // Eén specifiek model draaien (gebruikt in de planopbouw).
  const runModel = async (m, chapter) => {
    setRunningModel(m.id + ":" + (chapter || ""));
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Vul het ${m.label} in voor whitevision. ${m.instr}\n\nLever JSON: {"summary":"korte toelichting in gewone taal", ${m.schema}}`;
    const { data: r, sources } = await callAIFull(prompt, { json: true });
    const entry = {
      id: Date.now(), typeId: chapter ? "plan" : "models", typeLabel: chapter ? `Plan: ${chapter}` : "Modellenbibliotheek", method: "desk",
      brief: m.label, chapter: chapter || null,
      modelId: m.id, modelLabel: m.label, modelViz: m.viz, modelLabels: m.labels || null,
      date: new Date().toISOString().slice(0, 10),
      summary: r?.summary || "Geen toelichting gegenereerd.",
      findings: [], actions: [], sources: sources || [], model: r && r.model ? r.model : null,
    };
    saveResearch([entry, ...research]);
    setRunningModel(null); setOpenEntry(entry.id);
  };

  const runDesk = async (type) => {
    setRunning(true);
    if (type.candidates) {
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Identificeer met web search concrete overname-/marktkansen.${inputBrief ? " Richting/doel: " + inputBrief : " Gebruik de doelstellingen uit het merkprofiel."}\n\nZoek echte bedrijven die passen, met per kandidaat een geschatte fit-score (0-100%), of het aanvult of overlapt, de relevantie t.o.v. de doelstelling, en indien vindbaar of het bedrijf mogelijk overnamebereid is ("in de etalage"). Vermeld een bron-URL per kandidaat.\n\nJSON: {"summary":"...","candidates":[{"name":"bedrijfsnaam","fit":0-100,"relation":"aanvullend|overlappend","rationale":"waarom","tag":"land/segment","source":"bron-URL"}],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
      const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch: true });
      const entry = {
        id: Date.now(), typeId: type.id, typeLabel: type.label, method: "desk", brief: inputBrief,
        date: new Date().toISOString().slice(0, 10),
        summary: r?.summary || "Geen samenvatting gegenereerd.",
        findings: [], actions: r?.actions || [], candidates: r?.candidates || [], sources: sources || [],
      };
      saveResearch([entry, ...research]);
      setRunning(false); setInputBrief(""); setShowNew(null); setMode(null); setModelId("none"); setOpen(type.id); setOpenEntry(entry.id);
      return;
    }
    if (type.deskMode === "reviewaudit") {
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Voer een audit uit op externe reviewsites en openbare bronnen om de klantbeleving en merkperceptie van whitevision (en waar relevant concurrenten) te peilen.${inputBrief ? " Focus: " + inputBrief : ""}\n\nKies zelf met web search de meest relevante reviewsites en bronnen voor B2B-software (bijvoorbeeld G2, Trustpilot, Capterra, Gartner Peer Insights, Google Reviews, vakfora) en zoek daar naar ervaringen. Schat op basis daarvan het sentiment en de NPS-richting in (let op: dit is een inschatting uit reviews, geen gemeten NPS). Geef terugkerende thema's, positieve punten, kritiekpunten, en per bevinding de bron-URL.\n\nJSON: {"summary":"samenvatting in gewone taal","sentiment":"positief|gemengd|negatief","npsEstimate":"grove inschatting NPS-richting met onderbouwing","findings":[{"title":"thema","text":"wat zeggen reviewers","tag":"positief|kritiek","source":"bron-URL","severity":"hoog|midden|laag"}],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
      const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch: true });
      const entry = {
        id: Date.now(), typeId: type.id, typeLabel: type.label, method: "desk", deskMode: "reviewaudit", brief: inputBrief,
        date: new Date().toISOString().slice(0, 10),
        summary: (r?.summary || "Geen samenvatting gegenereerd.") + (r?.sentiment ? `\n\nAlgemeen sentiment uit reviews: ${r.sentiment}.` : "") + (r?.npsEstimate ? ` Ingeschatte NPS-richting: ${r.npsEstimate}` : ""),
        findings: r?.findings || [], actions: r?.actions || [], sources: sources || [],
      };
      saveResearch([entry, ...research]);
      setRunning(false); setInputBrief(""); setShowNew(null); setMode(null); setModelId("none"); setOpen(type.id); setOpenEntry(entry.id);
      return;
    }
    const model = MODELS.find(m => m.id === modelId) || MODELS[0];
    const useModel = model.viz !== "none";
    const modelInstr = useModel
      ? `\n\nGebruik het ${model.label}. ${model.instr} Neem het resultaat op onder de sleutel "model" exact volgens onderstaand schema.`
      : "";
    const modelSchema = useModel ? ", " + model.schema : "";
    // Bij concurrentieonderzoek altijd een vergelijkingstabel meeleveren (los van het gekozen model).
    const wantsCompare = type.id === "competitor" && model.id !== "compare";
    const compareSchema = wantsCompare ? ', "compare":{"competitors":["naam1","naam2"],"criteria":[{"name":"criterium","whitevision":"...","values":["c1","c2"]}],"theyHaveWeDont":["..."],"weHaveTheyDont":["..."]}' : "";
    const compareInstr = wantsCompare ? " Maak daarnaast een vergelijkingstabel van whitevision tegenover de belangrijkste concurrenten op relevante criteria, en benoem wat zij wél en wij niet hebben en andersom (sleutel \"compare\")." : "";
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: ${type.prompt}\n${inputBrief ? "Aanvullende context van de brand manager: " + inputBrief : ""}${modelInstr}${compareInstr}\n\nVoer een gedegen onderzoek uit met web search en gebruik echte, actuele bronnen. Onderbouw bevindingen met concrete voorbeelden (welke concurrenten/partijen, welke pagina's). Vermeld per bevinding waar mogelijk een bron-URL. Schrijf schone zinnen zonder bron-tags in de tekst.\n\nLever het resultaat als JSON:
{"summary": "korte samenvatting: wat is onderzocht, waarnaar is gekeken, welke aanpak/model is gebruikt (2-4 zinnen)", "findings": [{"title": "kort", "text": "bevinding met onderbouwing", "tag": "label", "source": "bron-URL indien van toepassing", "severity": "hoog|midden|laag"}], "conclusion": "korte conclusie met aanbevolen strategische richting (2-4 zinnen)", "actions": [{"title": "kort", "text": "concrete vervolgactie", "severity": "hoog|midden|laag"}]${modelSchema}${compareSchema}}`;
    const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch: true });
    const entry = {
      id: Date.now(), typeId: type.id, typeLabel: type.label, method: "desk", brief: inputBrief,
      modelId: model.id, modelLabel: useModel ? model.label : null, modelViz: useModel ? model.viz : null, modelLabels: model.labels || null,
      date: new Date().toISOString().slice(0, 10),
      summary: r?.summary || "Geen samenvatting gegenereerd.",
      findings: r?.findings || [], actions: r?.actions || [], sources: sources || [],
      model: r && r.model ? r.model : null,
      compare: r && r.compare ? r.compare : (model.id === "compare" && r && r.model ? r.model : null),
      conclusion: r?.conclusion || null,
    };
    saveResearch([entry, ...research]);
    setRunning(false); setInputBrief(""); setShowNew(null); setMode(null); setModelId("none"); setOpen(type.id); setOpenEntry(entry.id);
  };

  const deleteEntry = (id) => confirm("Dit onderzoek wordt definitief verwijderd.", () => { saveResearch(research.filter(r => r.id !== id)); setOpenEntry(null); });

  const createSurvey = (type) => {
    const isNPS = type.id === "nps";
    const survey = {
      id: Date.now(), typeId: type.id, typeLabel: type.label, title: type.label + " whitevision",
      intro: "We horen graag je mening. Het invullen duurt minder dan een minuut.",
      thanks: "Bedankt voor je feedback!",
      active: true, createdAt: new Date().toISOString().slice(0, 10),
      questions: isNPS
        ? [
            { id: "q_nps", type: "nps", label: "Hoe waarschijnlijk is het dat je whitevision aanbeveelt aan een collega of relatie?" },
            { id: "q_why", type: "text", label: "Wat is de belangrijkste reden voor je score?" },
          ]
        : [
            { id: "q1", type: "scale", label: "Hoe tevreden ben je over whitevision?" },
            { id: "q_open", type: "text", label: "Wat kan beter?" },
          ],
    };
    saveSurveys([survey, ...(surveys || [])]);
    setShowNew(null); setMode(null); setOpen(type.id); setOpenEntry({ survey: survey.id });
  };

  const analyseResponses = async (survey) => {
    setRunning(true);
    let responses = [];
    try {
      const res = await fetch("/api/responses/" + survey.id, { credentials: "include" });
      if (res.ok) responses = (await res.json()).responses || [];
    } catch {}
    const npsQ = survey.questions.find(q => q.type === "nps");
    const scores = npsQ ? responses.map(r => Number(r.answers[npsQ.id])).filter(n => !isNaN(n)) : [];
    const nps = computeNPS(scores);
    const textData = responses.map((r, i) => {
      const parts = survey.questions.map(q => `${q.label}: ${r.answers[q.id] ?? "—"}`);
      return `Respondent ${i + 1}: ${parts.join(" | ")}`;
    }).join("\n");

    let entry;
    if (responses.length === 0) {
      entry = {
        id: Date.now(), typeId: survey.typeId, typeLabel: survey.typeLabel, method: "field", surveyId: survey.id,
        date: new Date().toISOString().slice(0, 10), respondents: 0,
        summary: "Er zijn nog geen reacties binnengekomen op dit formulier.",
        findings: [], actions: [], sources: [], nps,
      };
    } else {
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Analyseer de volgende ${responses.length} reacties uit een vragenlijst (${survey.typeLabel}).${nps != null ? " De berekende NPS-score is " + nps + "." : ""} Vat samen wat respondenten zeggen, welke patronen er zijn, wat goed gaat en wat beter moet.\n\nREACTIES:\n${textData}\n\nJSON: {"summary":"samenvatting in gewone taal","findings":[{"title":"kort","text":"patroon/inzicht","tag":"thema","severity":"hoog|midden|laag"}],"actions":[{"title":"kort","text":"vervolgactie","severity":"hoog|midden|laag"}]}`;
      const { data: r } = await callAIFull(prompt, { json: true, useSearch: false });
      entry = {
        id: Date.now(), typeId: survey.typeId, typeLabel: survey.typeLabel, method: "field", surveyId: survey.id,
        date: new Date().toISOString().slice(0, 10), respondents: responses.length,
        summary: r?.summary || "Analyse niet beschikbaar.",
        findings: r?.findings || [], actions: r?.actions || [], sources: [], nps,
      };
    }
    saveResearch([entry, ...research]);
    // NPS automatisch doorzetten naar het dashboard
    if (nps != null) {
      const npsMetric = (metrics || []).find(m => (m.label || "").toLowerCase().includes("nps"));
      if (npsMetric) saveMetrics(metrics.map(m => m.id === npsMetric.id ? { ...m, prev: m.value, value: nps } : m));
      else saveMetrics([...(metrics || []), { id: Date.now() + 1, label: "NPS", value: nps, unit: "", prev: null, target: null }]);
    }
    setRunning(false); setOpenEntry(entry.id);
  };

  const startNew = (type) => { setShowNew(type); setMode(type.methodDefault || (type.fieldFirst ? "field" : null)); setModelId((type.models && type.models[0]) || "none"); };

  if (openEntry && openEntry.survey) {
    const survey = (surveys || []).find(s => s.id === openEntry.survey);
    if (!survey) { setOpenEntry(null); return null; }
    return <SurveyEditor ctx={ctx} survey={survey} onBack={() => { setOpenEntry(null); setOpen(survey.typeId); }} onAnalyse={() => analyseResponses(survey)} running={running} />;
  }

  if (openEntry) {
    const r = research.find(x => x.id === openEntry);
    if (!r) { setOpenEntry(null); return null; }
    const type = RESEARCH_TYPES.find(t => t.id === r.typeId) || { label: r.typeLabel };
    const npsMetric = (metrics || []).find(m => m.label.toLowerCase().includes("nps"));
    const coupleNPS = () => {
      if (r.nps == null) return;
      if (npsMetric) saveMetrics(metrics.map(m => m.id === npsMetric.id ? { ...m, prev: m.value, value: r.nps } : m));
      else saveMetrics([...(metrics || []), { id: Date.now(), label: "NPS", value: r.nps, unit: "", prev: null, target: null }]);
    };
    return (
      <div>
        <button onClick={() => setOpenEntry(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar overzicht</button>
        <PageHead title={type.label} sub={`${r.date}${r.method === "field" ? " · vragenlijst" : " · deskresearch"}${r.respondents != null ? " · " + r.respondents + " reacties" : ""}`}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => deleteEntry(r.id)}>Verwijderen</Btn>} />
        <Card>
          {r.nps != null && (
            <div style={{ display: "flex", alignItems: "center", gap: 16, padding: "16px 18px", background: C.surface, borderRadius: 12, marginBottom: 16 }}>
              <div style={{ fontFamily: FONT_DISPLAY, fontSize: 40, fontWeight: 700, color: r.nps >= 50 ? C.good : r.nps >= 0 ? C.yellow : C.bad, lineHeight: 1 }}>{r.nps}</div>
              <div style={{ flex: 1 }}>
                <div style={{ color: "#fff", fontWeight: 700, fontSize: 15 }}>Berekende NPS-score</div>
                <div style={{ color: "#9CA3AF", fontSize: 12.5 }}>op basis van {r.respondents} reacties</div>
              </div>
              <Btn variant="yellow" size="sm" icon={LayoutDashboard} onClick={coupleNPS}>Koppel aan dashboard</Btn>
            </div>
          )}
          <div style={sectionLabel}>Samenvatting</div>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2, marginTop: 6 }}>{r.summary}</div>

          {r.model && r.modelViz && <>
            <div style={sectionLabel}>{r.modelLabel || "Model"}</div>
            <div style={{ marginTop: 10 }}><ModelView viz={r.modelViz} model={r.model} labels={r.modelLabels} /></div>
          </>}

          {r.compare && (r.compare.criteria?.length > 0 || r.compare.competitors?.length > 0) && <>
            <div style={sectionLabel}>Vergelijking met concurrenten</div>
            <div style={{ marginTop: 10 }}><ModelView viz="compare" model={r.compare} /></div>
          </>}

          {r.candidates?.length > 0 && <>
            <div style={sectionLabel}>Kandidaten</div>
            <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 10 }}>
              {r.candidates.map((c, i) => (
                <div key={i} style={{ display: "flex", alignItems: "center", gap: 14, padding: "12px 15px", background: C.paper, borderRadius: 12, border: `1px solid ${C.line}` }}>
                  <div style={{ position: "relative", width: 46, height: 46, flexShrink: 0 }}>
                    <svg width="46" height="46" style={{ transform: "rotate(-90deg)" }}>
                      <circle cx="23" cy="23" r="19" fill="none" stroke={C.line} strokeWidth="5" />
                      <circle cx="23" cy="23" r="19" fill="none" stroke={c.fit >= 70 ? C.good : c.fit >= 40 ? C.yellowDark : C.bad} strokeWidth="5" strokeDasharray={`${(c.fit / 100) * 119} 119`} strokeLinecap="round" />
                    </svg>
                    <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", fontSize: 12.5, fontWeight: 700 }}>{c.fit}%</div>
                  </div>
                  <div style={{ flex: 1 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                      <span style={{ fontWeight: 700, fontSize: 14.5 }}>{c.name}</span>
                      <Tag color={c.relation === "overlappend" ? C.bad : C.good}>{c.relation}</Tag>
                      {c.tag && <Tag>{c.tag}</Tag>}
                    </div>
                    <div style={{ fontSize: 13, color: C.ink2, marginTop: 3, lineHeight: 1.45 }}>{c.rationale}</div>
                    {c.source && <a href={c.source} target="_blank" rel="noopener noreferrer" style={{ fontSize: 12, color: C.blue, display: "inline-flex", alignItems: "center", gap: 4, marginTop: 5 }}><Globe size={11} /> bron</a>}
                  </div>
                  <ActionButton onAdd={() => addAction({ title: `Verken: ${c.name}`, detail: c.rationale, source: "Marktkansen" })} />
                </div>
              ))}
            </div>
          </>}

          {r.findings?.length > 0 && <>
            <div style={sectionLabel}>Bevindingen <span style={{ fontWeight: 400, textTransform: "none" }}>— klik voor verdieping</span></div>
            <Insights items={r.findings} onAction={addAction} prefix={type.label} profile={profile} />
          </>}

          {r.conclusion && <>
            <div style={sectionLabel}>Conclusie</div>
            <div style={{ marginTop: 8, padding: "14px 16px", background: C.surface, borderRadius: 12, color: "#fff", fontSize: 14.5, lineHeight: 1.6 }}>{r.conclusion}</div>
          </>}

          {r.actions?.length > 0 && <>
            <div style={sectionLabel}>Actiepunten <span style={{ fontWeight: 400, textTransform: "none" }}>— voeg toe aan je actielijst of negeer</span></div>
            <Insights items={r.actions} onAction={addAction} prefix={type.label} profile={profile} />
          </>}

          <SourceLinks sources={r.sources} />
        </Card>
      </div>
    );
  }

  if (open === "sov") {
    return <SovStudy ctx={ctx} onBack={() => setOpen(null)} />;
  }

  if (open) {
    const type = RESEARCH_TYPES.find(t => t.id === open);
    const history = research.filter(r => r.typeId === open);
    const typeSurveys = (surveys || []).filter(s => s.typeId === open);
    return (
      <div>
        <button onClick={() => setOpen(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar onderzoek</button>
        <PageHead title={type.label} sub={type.desc}
          right={<Btn variant="yellow" icon={Sparkles} onClick={() => startNew(type)}>Nieuw onderzoek</Btn>} />

        {showNew && (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 10 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuw {type.label.toLowerCase()}</span></div>
            {type.methodNote && <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5, marginBottom: 14, padding: "10px 12px", background: C.yellow + "12", borderRadius: 10, border: `1px solid ${C.yellow}33` }}>{type.methodNote}</div>}
            {!type.deskOnly && !type.deskOnlyMethod && <>
              <div style={{ fontSize: 13, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: .3, marginBottom: 8 }}>Aanbevolen methode: {type.methodDefault === "field" ? "vragenlijst" : "deskresearch"}</div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 }}>
                <div onClick={() => setMode("desk")} style={methodCard(mode === "desk")}>
                  <Sparkles size={18} style={{ color: C.ink }} />
                  <div style={{ fontWeight: 700, fontSize: 14.5, marginTop: 6 }}>Desk research {type.methodDefault === "desk" && <Tag color={C.ink} bg={C.yellow}>aanbevolen</Tag>}</div>
                  <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, lineHeight: 1.4 }}>{type.deskMode === "reviewaudit" ? "AI scant externe reviewsites en media om de perceptie te peilen." : "AI gaat de diepte in met web search en levert een onderbouwd rapport."}</div>
                </div>
                <div onClick={() => setMode("field")} style={methodCard(mode === "field")}>
                  <Users size={18} style={{ color: C.ink }} />
                  <div style={{ fontWeight: 700, fontSize: 14.5, marginTop: 6 }}>Field research {type.methodDefault === "field" && <Tag color={C.ink} bg={C.yellow}>aanbevolen</Tag>}</div>
                  <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, lineHeight: 1.4 }}>Maak een vragenlijst, verzamel reacties en analyseer ze met AI.</div>
                </div>
              </div>
            </>}
            {mode === "desk" && <>
              {!type.candidates && type.deskMode !== "reviewaudit" && (type.models && type.models.length > 0) && (
                <div style={{ marginBottom: 16 }}>
                  <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Model</label>
                  <select value={modelId} onChange={e => setModelId(e.target.value)} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
                    {type.models.map((mid, i) => { const m = MODELS.find(x => x.id === mid); return m ? <option key={mid} value={mid}>{m.label}{i === 0 ? " — aanbevolen" : ""}</option> : null; })}
                    <option value="none">Vrij rapport (geen model)</option>
                  </select>
                  <div style={{ fontSize: 12, color: C.muted, marginTop: 6 }}>Deze modellen passen het best bij {type.label.toLowerCase()}. Het bovenste is de aanbevolen keuze.</div>
                </div>
              )}
              {type.deskMode === "reviewaudit" && (
                <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5, marginBottom: 14 }}>De AI kiest zelf de relevante reviewsites en bronnen en levert een sentiment- en thema-overzicht met bronvermelding.</div>
              )}
              <Field label={type.candidates ? "Richting / doel (optioneel)" : "Brief / focus (optioneel)"} value={inputBrief} onChange={setInputBrief} multiline placeholder={type.candidates ? "Bijv. 'overnamekandidaten in de zorgbranche in DACH'…" : "Bijv. 'focus op de zorgbranche' of 'vergelijk met ABBYY en Hyperscience'…"} />
              <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
                <Btn variant="yellow" icon={Sparkles} onClick={() => runDesk(type)} disabled={running}>{running ? "Bezig…" : (type.deskMode === "reviewaudit" ? "Start reviewsite-audit" : "Start deskresearch")}</Btn>
                <Btn variant="ghost" onClick={() => { setShowNew(null); setMode(null); }}>Annuleren</Btn>
                <CostHint kind="desk" />
              </div>
              {running && <div style={{ marginTop: 12 }}><Spinner label="AI doet gedegen onderzoek met web search… dit kan even duren." /></div>}
            </>}
            {mode === "field" && <>
              <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, marginBottom: 14 }}>Je maakt een vragenlijst{type.id === "nps" ? " (met de standaard NPS-vraag)" : ""}. Daarna krijg je een deelbare link en een insluitcode, en kun je reacties met AI analyseren.</div>
              <div style={{ display: "flex", gap: 10 }}>
                <Btn variant="yellow" icon={Plus} onClick={() => createSurvey(type)}>Vragenlijst maken</Btn>
                <Btn variant="ghost" onClick={() => { setShowNew(null); setMode(null); }}>Annuleren</Btn>
              </div>
            </>}
          </Card>
        )}

        {typeSurveys.length > 0 && <>
          <div style={sectionLabel}>Vragenlijsten (field research)</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 8 }}>
            {typeSurveys.map(s => (
              <Card key={s.id} hover style={{ padding: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                  <div onClick={() => setOpenEntry({ survey: s.id })} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}>
                      <Tag color={s.active ? C.good : C.muted}>{s.active ? "open" : "gesloten"}</Tag>
                      <span style={{ fontSize: 12.5, color: C.muted }}>{s.createdAt}</span>
                    </div>
                    <div style={{ fontWeight: 700, fontSize: 14.5 }}>{s.title}</div>
                  </div>
                  <Users size={16} style={{ color: C.muted }} />
                  <button onClick={() => setOpenEntry({ survey: s.id })} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                </div>
              </Card>
            ))}
          </div>
        </>}

        <div style={sectionLabel}>Uitgevoerde onderzoeken</div>
        {history.length === 0 && <Empty icon={Microscope} title="Nog geen rapporten" sub="Start een nieuw onderzoek hierboven." />}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {history.map(r => (
            <Card key={r.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                <div onClick={() => setOpenEntry(r.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                    <Tag color={C.ink} bg={C.line}>{r.date}</Tag>
                    <Tag color={r.method === "field" ? C.blue : C.muted}>{r.method === "field" ? "vragenlijst" : "deskresearch"}</Tag>
                    {r.nps != null && <span style={{ fontSize: 12, fontWeight: 700, color: C.ink }}>NPS {r.nps}</span>}
                    {r.findings?.length > 0 && <span style={{ fontSize: 12, color: C.muted }}>{r.findings.length} bevindingen</span>}
                  </div>
                  <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>
                    {r.brief ? <b>{r.brief}: </b> : null}{r.summary}
                  </div>
                </div>
                <button onClick={() => setOpenEntry(r.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                <button onClick={() => deleteEntry(r.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
              </div>
            </Card>
          ))}
        </div>
      </div>
    );
  }

  // ---- Planopbouw / modellenbibliotheek ----
  if (planView) {
    const planEntries = research.filter(r => r.typeId === "plan");
    return (
      <div>
        <button onClick={() => setPlanView(false)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar onderzoek</button>
        <PageHead title="Modellenbibliotheek & planopbouw" sub="Bouw een strategisch marketingplan op via de hoofdstukken en modellen. Elk model wordt door AI ingevuld en visueel weergegeven." />
        {PLAN_CHAPTERS.map(ch => {
          const done = planEntries.filter(r => r.chapter === ch.label);
          return (
            <Card key={ch.id} style={{ marginBottom: 16 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }}>
                <span style={{ fontWeight: 700, fontSize: 16, fontFamily: FONT_DISPLAY }}>{ch.label}</span>
                {done.length > 0 && <Tag color={C.ink} bg={C.yellow}>{done.length} ingevuld</Tag>}
              </div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginTop: 10 }}>
                {ch.models.map(mid => {
                  const m = MODELS.find(x => x.id === mid);
                  if (!m) return null;
                  const ran = done.find(r => r.modelId === mid);
                  const busy = runningModel === m.id + ":" + ch.label;
                  return (
                    <div key={mid} style={{ display: "flex", alignItems: "center", gap: 6, border: `1.5px solid ${ran ? C.good : C.line}`, borderRadius: 9, padding: "7px 11px", background: ran ? C.good + "0E" : C.card }}>
                      <span style={{ fontSize: 13, fontWeight: 600 }}>{m.label}</span>
                      {ran && <button onClick={() => setOpenEntry(ran.id)} style={{ ...iconBtn(C.good), padding: 0 }}><Eye size={14} /></button>}
                      <button onClick={() => runModel(m, ch.label)} disabled={!!runningModel} style={{ background: "none", border: "none", cursor: runningModel ? "default" : "pointer", color: busy ? C.muted : C.yellowDark, padding: 0, display: "grid", placeItems: "center" }}>
                        {busy ? <Loader2 size={14} className="spin" /> : <Sparkles size={14} />}
                      </button>
                    </div>
                  );
                })}
              </div>
            </Card>
          );
        })}
        <CostHint kind="desk" /> <span style={{ fontSize: 12, color: C.muted }}>per model</span>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Onderzoek" sub="AI-aangedreven onderzoek, met deskresearch of een eigen vragenlijst." />
      <Card hover onClick={() => setPlanView(true)} style={{ marginBottom: 16, border: `1.5px solid ${C.yellow}`, background: C.yellow + "0C" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
          <div style={{ width: 42, height: 42, borderRadius: 11, background: C.yellow, display: "grid", placeItems: "center" }}><LayoutDashboard size={21} style={{ color: C.ink }} /></div>
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 700, fontSize: 16.5 }}>Modellenbibliotheek & planopbouw</div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 3 }}>~25 strategische modellen (SWOT, BCG, Business Model Canvas, customer journey…) en een opbouw van je marketingplan.</div>
          </div>
          <ArrowUpRight size={18} style={{ color: C.ink }} />
        </div>
      </Card>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 16 }}>
        {RESEARCH_TYPES.map(t => {
          const count = research.filter(r => r.typeId === t.id).length;
          const openForms = (surveys || []).filter(s => s.typeId === t.id && s.active).length;
          return (
            <Card key={t.id} hover onClick={() => setOpen(t.id)}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
                <div style={{ width: 42, height: 42, borderRadius: 11, background: C.yellow + "22", display: "grid", placeItems: "center" }}>
                  <t.icon size={21} style={{ color: C.ink }} />
                </div>
                <div style={{ display: "flex", gap: 6, flexDirection: "column", alignItems: "flex-end" }}>
                  {count > 0 && <Tag color={C.ink} bg={C.yellow}>{count} uitgevoerd</Tag>}
                  {openForms > 0 && <Tag color="#fff" bg={C.blue}>● {openForms} formulier{openForms > 1 ? "en" : ""} open</Tag>}
                </div>
              </div>
              <div style={{ fontWeight: 700, fontSize: 16.5, marginTop: 14 }}>{t.label}</div>
              <div style={{ fontSize: 13.5, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{t.desc}</div>
              <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>
                Openen <ArrowUpRight size={15} />
              </div>
            </Card>
          );
        })}
      </div>
    </div>
  );
}
const methodCard = (sel) => ({ padding: 15, border: `1.5px solid ${sel ? C.yellow : C.line}`, background: sel ? "#8B5CF622" : C.card, borderRadius: 12, cursor: "pointer", transition: "all .15s" });

// Visuele weergave van een gekozen model. 'viz' bepaalt de vorm.
function ModelView({ viz, model, labels }) {
  if (!model) return null;
  const QUAD_COLORS = [C.good, C.bad, C.blue, C.yellowDark];
  const box = (title, items, color) => (
    <div style={{ background: color + "0E", border: `1px solid ${color}33`, borderRadius: 12, padding: 14 }}>
      <div style={{ fontSize: 12.5, fontWeight: 700, color, textTransform: "uppercase", marginBottom: 8 }}>{title}</div>
      {(Array.isArray(items) ? items : [items]).filter(Boolean).map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, marginBottom: 5, display: "flex", gap: 7 }}><span style={{ color }}>•</span> {x}</div>)}
      {(!items || (Array.isArray(items) && !items.length)) && <div style={{ fontSize: 13, color: C.muted }}>—</div>}
    </div>
  );
  const wrap = (children) => <div style={{ marginBottom: 16 }}>{children}</div>;

  // 4-vaks (SWOT, BSC, 4P)
  if (viz === "quad4") {
    const L = labels || ["A", "B", "C", "D"];
    return wrap(<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
      {box(L[0], model.a, QUAD_COLORS[0])}{box(L[1], model.b, QUAD_COLORS[1])}
      {box(L[2], model.c, QUAD_COLORS[2])}{box(L[3], model.d, QUAD_COLORS[3])}
    </div>);
  }
  if (viz === "list2") {
    const L = labels || ["A", "B"];
    return wrap(<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>{box(L[0], model.a, C.good)}{box(L[1], model.b, C.bad)}</div>);
  }
  if (viz === "list3") {
    const L = labels || ["A", "B", "C"];
    return wrap(<>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12 }}>{box(L[0], model.a, C.blue)}{box(L[1], model.b, C.yellowDark)}{box(L[2], model.c, C.good)}</div>
      {model.choice && <div style={{ marginTop: 12, padding: "12px 14px", background: C.yellow + "14", borderRadius: 10, border: `1px solid ${C.yellow}44`, fontSize: 13.5 }}><b>Aanbeveling:</b> {model.choice}</div>}
    </>);
  }
  // label/value rijen (PESTEL, Porter, 7S, Abell, Brand Key, missie/visie)
  if (viz === "rows") {
    return wrap(<div style={{ border: `1px solid ${C.line}`, borderRadius: 12, overflow: "hidden" }}>
      {(model.rows || []).map((r, i) => (
        <div key={i} style={{ display: "flex", borderBottom: i < model.rows.length - 1 ? `1px solid ${C.line}` : "none", background: i % 2 ? C.paper : C.card }}>
          <div style={{ width: 190, padding: "10px 13px", fontWeight: 700, fontSize: 13, flexShrink: 0, background: C.surface, color: "#fff" }}>{r.k}</div>
          <div style={{ padding: "10px 13px", fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{r.v}</div>
        </div>
      ))}
    </div>);
  }
  // 2x2 matrix met kwadrantlabels en assen (Ansoff, BCG)
  if (viz === "matrix2x2") {
    const q = model.q || [];
    const cell = (i, color) => (
      <div style={{ background: color + "0E", border: `1px solid ${color}33`, borderRadius: 10, padding: 13, minHeight: 90 }}>
        <div style={{ fontSize: 12.5, fontWeight: 700, color, marginBottom: 5 }}>{q[i]?.label}</div>
        <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.45 }}>{q[i]?.text}</div>
      </div>
    );
    return wrap(<div>
      <div style={{ display: "flex", gap: 8 }}>
        <div style={{ writingMode: "vertical-rl", transform: "rotate(180deg)", fontSize: 12, fontWeight: 700, color: C.muted, display: "grid", placeItems: "center" }}>{model.axisY}</div>
        <div style={{ flex: 1 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>{cell(0, C.good)}{cell(1, C.blue)}{cell(2, C.yellowDark)}{cell(3, C.bad)}</div>
          <div style={{ textAlign: "center", fontSize: 12, fontWeight: 700, color: C.muted, marginTop: 8 }}>{model.axisX}</div>
        </div>
      </div>
    </div>);
  }
  // scatter/positioneringskruis
  if (viz === "scatter") {
    const pts = model.points || [];
    return wrap(<div>
      <div style={{ position: "relative", height: 320, border: `1px solid ${C.line}`, borderRadius: 12, background: C.paper }}>
        <div style={{ position: "absolute", left: "50%", top: 0, bottom: 0, width: 1, background: C.line }} />
        <div style={{ position: "absolute", top: "50%", left: 0, right: 0, height: 1, background: C.line }} />
        {pts.map((p, i) => (
          <div key={i} title={p.name} style={{ position: "absolute", left: `calc(${Math.max(2, Math.min(98, p.x))}% - 6px)`, top: `calc(${100 - Math.max(2, Math.min(98, p.y))}% - 6px)` }}>
            <div style={{ width: 12, height: 12, borderRadius: 12, background: p.us ? C.yellow : C.surface, border: p.us ? `2px solid ${C.ink}` : "none" }} />
            <div style={{ fontSize: 11, fontWeight: p.us ? 700 : 500, color: C.ink, whiteSpace: "nowrap", marginTop: 2, transform: "translateX(-40%)" }}>{p.name}</div>
          </div>
        ))}
        <div style={{ position: "absolute", right: 8, top: "50%", fontSize: 11, color: C.muted, transform: "translateY(-50%)" }}>{model.axisX} →</div>
        <div style={{ position: "absolute", left: "50%", top: 6, fontSize: 11, color: C.muted, transform: "translateX(-50%)" }}>↑ {model.axisY}</div>
      </div>
    </div>);
  }
  // confrontatiematrix
  if (viz === "confront") {
    const rows = model.rows || [], cols = model.cols || [], cells = model.cells || [];
    const find = (r, c) => cells.find(x => x.row === r && x.col === c);
    return wrap(<>
      <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
          <thead><tr style={{ background: C.surface }}><th style={cmpTh}></th>{cols.map((c, i) => <th key={i} style={cmpTh}>{c}</th>)}</tr></thead>
          <tbody>{rows.map((r, ri) => (
            <tr key={ri} style={{ background: ri % 2 ? C.paper : C.card }}>
              <td style={{ ...cmpTd, fontWeight: 700, background: C.line }}>{r}</td>
              {cols.map((_, ci) => { const cell = find(ri, ci); const w = cell?.weight || ""; const col = w.includes("hoog") ? C.bad : w.includes("midden") ? C.yellowDark : C.muted; return <td key={ci} style={{ ...cmpTd, color: col }}>{cell?.text || "—"}</td>; })}
            </tr>
          ))}</tbody>
        </table>
      </div>
      {model.issues?.length > 0 && <div style={{ marginTop: 12 }}>{box("Strategische issues", model.issues, C.bad)}</div>}
    </>);
  }
  // value chain
  if (viz === "chain") {
    return wrap(<>
      <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Primaire activiteiten</div>
      <div style={{ display: "flex", gap: 6, overflowX: "auto", marginBottom: 12 }}>
        {(model.primary || []).map((a, i) => (
          <div key={i} style={{ minWidth: 130, flex: 1, background: C.yellow + "18", border: `1px solid ${C.yellow}55`, borderRadius: 10, padding: 11 }}>
            <div style={{ fontWeight: 700, fontSize: 12.5 }}>{a.k}</div><div style={{ fontSize: 12, color: C.ink2, marginTop: 3, lineHeight: 1.4 }}>{a.v}</div>
          </div>
        ))}
      </div>
      <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Ondersteunend</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 8 }}>
        {(model.support || []).map((a, i) => (
          <div key={i} style={{ background: C.paper, border: `1px solid ${C.line}`, borderRadius: 10, padding: 11 }}>
            <div style={{ fontWeight: 700, fontSize: 12.5 }}>{a.k}</div><div style={{ fontSize: 12, color: C.ink2, marginTop: 3, lineHeight: 1.4 }}>{a.v}</div>
          </div>
        ))}
      </div>
    </>);
  }
  // golden circle
  if (viz === "circle") {
    const ring = (label, text, size, bg, color) => (
      <div style={{ width: size, height: size, borderRadius: "50%", background: bg, display: "grid", placeItems: "center", textAlign: "center", margin: "0 auto", position: "relative" }}>
        <div style={{ position: "absolute", top: 14 }}><span style={{ fontWeight: 700, fontSize: 12.5, color }}>{label}</span></div>
      </div>
    );
    return wrap(<div style={{ display: "flex", gap: 20, alignItems: "center", flexWrap: "wrap" }}>
      <div style={{ position: "relative", width: 220, height: 220, flexShrink: 0 }}>
        <div style={{ position: "absolute", inset: 0, borderRadius: "50%", background: C.surface }} />
        <div style={{ position: "absolute", inset: 36, borderRadius: "50%", background: C.yellowDark }} />
        <div style={{ position: "absolute", inset: 74, borderRadius: "50%", background: C.yellow }} />
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", justifyContent: "space-between", alignItems: "center", padding: "12px 0" }}>
          <span style={{ color: "#fff", fontWeight: 700, fontSize: 12 }}>WHAT</span>
          <span style={{ color: "#fff", fontWeight: 700, fontSize: 12 }}>HOW</span>
          <span style={{ color: C.ink, fontWeight: 700, fontSize: 12, marginBottom: 8 }}>WHY</span>
        </div>
      </div>
      <div style={{ flex: 1, minWidth: 200 }}>
        <div style={{ marginBottom: 10 }}><div style={{ fontWeight: 700, fontSize: 13, color: C.yellowDark }}>Why</div><div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{model.why}</div></div>
        <div style={{ marginBottom: 10 }}><div style={{ fontWeight: 700, fontSize: 13 }}>How</div><div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{model.how}</div></div>
        <div><div style={{ fontWeight: 700, fontSize: 13 }}>What</div><div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{model.what}</div></div>
      </div>
    </div>);
  }
  // business model canvas (9 blokken)
  if (viz === "canvas9") {
    const cell = (k, label) => (
      <div style={{ background: C.card, border: `1px solid ${C.line}`, borderRadius: 8, padding: 10 }}>
        <div style={{ fontSize: 11, fontWeight: 700, color: C.yellowDark, textTransform: "uppercase", marginBottom: 4 }}>{label}</div>
        <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.4 }}>{model[k]}</div>
      </div>
    );
    return wrap(<div style={{ display: "grid", gridTemplateColumns: "repeat(5,1fr)", gridAutoRows: "minmax(70px,auto)", gap: 6 }}>
      <div style={{ gridRow: "1 / 3" }}>{cell("partners", "Partners")}</div>
      <div>{cell("activities", "Activiteiten")}</div>
      <div style={{ gridRow: "1 / 3" }}>{cell("value", "Waardepropositie")}</div>
      <div>{cell("relationships", "Klantrelaties")}</div>
      <div style={{ gridRow: "1 / 3" }}>{cell("segments", "Klantsegmenten")}</div>
      <div>{cell("resources", "Resources")}</div>
      <div>{cell("channels", "Kanalen")}</div>
      <div style={{ gridColumn: "1 / 4" }}>{cell("costs", "Kostenstructuur")}</div>
      <div style={{ gridColumn: "4 / 6" }}>{cell("revenue", "Inkomsten")}</div>
    </div>);
  }
  // value proposition canvas
  if (viz === "vpc") {
    return wrap(<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
      <div style={{ border: `1px solid ${C.line}`, borderRadius: 12, padding: 14 }}>
        <div style={{ fontWeight: 700, fontSize: 13, marginBottom: 10 }}>Waardekaart (wij)</div>
        {box("Producten & diensten", model.products, C.blue)}<div style={{ height: 8 }} />
        {box("Pijn-verzachters", model.relievers, C.good)}<div style={{ height: 8 }} />
        {box("Voordeel-versterkers", model.creators, C.yellowDark)}
      </div>
      <div style={{ border: `1px solid ${C.line}`, borderRadius: 12, padding: 14 }}>
        <div style={{ fontWeight: 700, fontSize: 13, marginBottom: 10 }}>Klantprofiel</div>
        {box("Klanttaken", model.jobs, C.ink)}<div style={{ height: 8 }} />
        {box("Pijnpunten", model.pains, C.bad)}<div style={{ height: 8 }} />
        {box("Gewenste voordelen", model.gains, C.good)}
      </div>
    </div>);
  }
  // steps (journey, AIDA)
  if (viz === "steps") {
    return wrap(<div>
      {(model.steps || []).map((s, i) => (
        <div key={i} style={{ display: "flex", gap: 12, alignItems: "flex-start", marginBottom: 10 }}>
          <div style={{ width: 30, height: 30, borderRadius: 30, background: C.yellow, display: "grid", placeItems: "center", fontWeight: 700, fontSize: 13, flexShrink: 0 }}>{i + 1}</div>
          <div><div style={{ fontWeight: 700, fontSize: 14 }}>{s.title}</div><div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{s.text}</div></div>
        </div>
      ))}
    </div>);
  }
  // funnel (afnemende breedte)
  if (viz === "funnel") {
    const steps = model.steps || [];
    return wrap(<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}>
      {steps.map((s, i) => {
        const w = 100 - (i * (60 / Math.max(1, steps.length - 1)));
        return (
          <div key={i} style={{ width: `${w}%`, background: i % 2 ? C.surface : C.yellowDark, color: "#fff", borderRadius: 8, padding: "11px 14px", textAlign: "center" }}>
            <div style={{ fontWeight: 700, fontSize: 13.5 }}>{s.title}</div>
            <div style={{ fontSize: 12, opacity: .85, lineHeight: 1.4 }}>{s.text}</div>
          </div>
        );
      })}
    </div>);
  }
  // gantt
  if (viz === "gantt") {
    const periods = model.periods || [], tasks = model.tasks || [];
    const n = periods.length || 1;
    return wrap(<div style={{ overflowX: "auto" }}>
      <div style={{ display: "grid", gridTemplateColumns: `160px repeat(${n}, 1fr)`, gap: 4, minWidth: 480 }}>
        <div />
        {periods.map((p, i) => <div key={i} style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textAlign: "center" }}>{p}</div>)}
        {tasks.map((t, i) => (
          <React.Fragment key={i}>
            <div style={{ fontSize: 12.5, fontWeight: 600, padding: "6px 0", lineHeight: 1.3 }}>{t.name}</div>
            <div style={{ gridColumn: `2 / span ${n}`, position: "relative", height: 26 }}>
              <div style={{ position: "absolute", left: `${(t.start / n) * 100}%`, width: `${(Math.max(1, t.len) / n) * 100}%`, top: 4, bottom: 4, background: C.yellow, borderRadius: 6 }} />
            </div>
          </React.Fragment>
        ))}
      </div>
    </div>);
  }
  // comparison table (whitevision vs concurrenten)
  if (viz === "compare") {
    const comps = model.competitors || [];
    return wrap(<>
      <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
          <thead><tr style={{ background: C.surface }}><th style={cmpTh}>Criterium</th><th style={{ ...cmpTh, color: C.yellow }}>whitevision</th>{comps.map((c, i) => <th key={i} style={cmpTh}>{c}</th>)}</tr></thead>
          <tbody>{(model.criteria || []).map((row, i) => (
            <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
              <td style={{ ...cmpTd, fontWeight: 700 }}>{row.name}</td>
              <td style={{ ...cmpTd, background: C.yellow + "14" }}>{row.whitevision}</td>
              {(row.values || []).map((v, j) => <td key={j} style={cmpTd}>{v}</td>)}
            </tr>
          ))}</tbody>
        </table>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 12 }}>
        {box("Zij hebben, wij niet", model.theyHaveWeDont, C.bad)}{box("Wij hebben, zij niet", model.weHaveTheyDont, C.good)}
      </div>
    </>);
  }
  // b2b vs b2c
  if (viz === "compare2") {
    return wrap(<div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
      <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
        <thead><tr style={{ background: C.surface }}><th style={cmpTh}>Criterium</th><th style={cmpTh}>B2B</th><th style={cmpTh}>B2C</th></tr></thead>
        <tbody>{(model.criteria || []).map((row, i) => (
          <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
            <td style={{ ...cmpTd, fontWeight: 700 }}>{row.name}</td><td style={cmpTd}>{row.b2b}</td><td style={cmpTd}>{row.b2c}</td>
          </tr>
        ))}</tbody>
      </table>
    </div>);
  }
  if (viz === "sov") {
    const kws = model.keywords || [];
    return wrap(<div>
      <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
          <thead><tr style={{ background: C.surface }}><th style={cmpTh}>Zoekwoord</th><th style={{ ...cmpTh, textAlign: "right" }}>Zoekvolume/mnd</th><th style={{ ...cmpTh, textAlign: "right" }}>Share of Voice</th></tr></thead>
          <tbody>{kws.map((k, i) => (
            <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
              <td style={{ ...cmpTd, fontWeight: 700 }}>{k.term}</td>
              <td style={{ ...cmpTd, textAlign: "right" }}>{k.volume != null ? Number(k.volume).toLocaleString("nl-NL") : "—"}</td>
              <td style={{ ...cmpTd, textAlign: "right" }}>
                <span style={{ display: "inline-block", minWidth: 42, fontWeight: 700, color: (k.sovPct || 0) >= 25 ? C.good : (k.sovPct || 0) >= 10 ? C.yellowDark : C.bad }}>{k.sovPct != null ? k.sovPct + "%" : "—"}</span>
              </td>
            </tr>
          ))}</tbody>
        </table>
      </div>
      {model.note && <div style={{ fontSize: 12.5, color: C.muted, marginTop: 8, fontStyle: "italic", lineHeight: 1.5 }}>{model.note}</div>}
      <div style={{ fontSize: 12, color: C.muted, marginTop: 8 }}>Schattingen. Voor exacte cijfers koppel je later Google Search Console of een zoekwoordtool; dan vul je organische vertoningen en kliks af tegen het zoekvolume.</div>
    </div>);
  }
  return null;
}
const cmpTh = { padding: "10px 12px", textAlign: "left", color: "#fff", fontWeight: 700, fontSize: 12.5 };
const cmpTd = { padding: "9px 12px", verticalAlign: "top", get borderTop() { return `1px solid ${C.line}`; }, get color() { return C.ink2; } };

const QTYPES = [
  { id: "nps", label: "NPS (0–10)" },
  { id: "scale", label: "Schaal (1–5)" },
  { id: "choice", label: "Keuze" },
  { id: "short", label: "Kort antwoord" },
  { id: "text", label: "Open tekst" },
];
function SurveyEditor({ ctx, survey, onBack, onAnalyse, running }) {
  const { surveys, saveSurveys } = ctx;
  const [s, setS] = useState(survey);
  const [respCount, setRespCount] = useState(null);
  const [responses, setResponses] = useState([]);
  const [copied, setCopied] = useState("");
  const base = typeof window !== "undefined" ? window.location.origin : "";
  const shareUrl = `${base}/f/${s.id}`;
  const embedCode = `<iframe src="${base}/embed/${s.id}" style="width:100%;max-width:560px;height:560px;border:none" title="${s.title}"></iframe>`;

  const persist = (next) => { setS(next); saveSurveys((surveys || []).map(x => x.id === next.id ? next : x)); };
  const setF = (k, v) => persist({ ...s, [k]: v });
  const updateQ = (qid, k, v) => persist({ ...s, questions: s.questions.map(q => q.id === qid ? { ...q, [k]: v } : q) });
  const addQ = () => persist({ ...s, questions: [...s.questions, { id: "q" + Date.now(), type: "text", label: "Nieuwe vraag" }] });
  const delQ = (qid) => persist({ ...s, questions: s.questions.filter(q => q.id !== qid) });

  const copy = (text, what) => { try { navigator.clipboard.writeText(text); setCopied(what); setTimeout(() => setCopied(""), 1800); } catch {} };
  const loadResponses = async () => {
    try {
      const r = await fetch("/api/responses/" + s.id, { credentials: "include" });
      if (r.ok) { const list = (await r.json()).responses || []; setResponses(list); setRespCount(list.length); }
    } catch {}
  };
  useEffect(() => { loadResponses(); }, []);
  const npsQ = s.questions.find(q => q.type === "nps");
  const liveScores = npsQ ? responses.map(r => Number(r.answers?.[npsQ.id])).filter(n => !isNaN(n)) : [];
  const liveNps = computeNPS(liveScores);

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug</button>
      <PageHead title="Vragenlijst" sub={s.typeLabel}
        right={<Toggle on={s.active} onChange={() => setF("active", !s.active)} label={s.active ? "Open" : "Gesloten"} />} />

      <Card style={{ marginBottom: 16 }}>
        <Field label="Titel" value={s.title} onChange={v => setF("title", v)} />
        <Field label="Introtekst" value={s.intro} onChange={v => setF("intro", v)} multiline rows={2} />
        <div style={sectionLabel}>Vragen</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 10 }}>
          {s.questions.map(q => (
            <div key={q.id} style={{ padding: 13, background: C.paper, borderRadius: 10, border: `1px solid ${C.line}` }}>
              <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }}>
                <input value={q.label} onChange={e => updateQ(q.id, "label", e.target.value)} style={{ flex: 1, padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
                <select value={q.type} onChange={e => updateQ(q.id, "type", e.target.value)} style={{ padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, outline: "none", background: C.card }}>
                  {QTYPES.map(t => <option key={t.id} value={t.id}>{t.label}</option>)}
                </select>
                <button onClick={() => delQ(q.id)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
              </div>
              {q.type === "choice" && (
                <input value={(q.options || []).join(", ")} onChange={e => updateQ(q.id, "options", e.target.value.split(",").map(x => x.trim()).filter(Boolean))}
                  placeholder="Opties, gescheiden door komma's" style={{ width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, outline: "none" }} />
              )}
            </div>
          ))}
        </div>
        <div style={{ marginTop: 12 }}><Btn variant="ghost" size="sm" icon={Plus} onClick={addQ}>Vraag toevoegen</Btn></div>
      </Card>

      <Card style={{ marginBottom: 16 }}>
        <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 4 }}>Delen & insluiten</div>
        <div style={{ fontSize: 13, color: C.muted, marginBottom: 14 }}>Deel de link met respondenten, of sluit het formulier in op een webpagina (bijv. whitevision.com/feedback/). Respondenten hoeven niet in te loggen.</div>
        <label style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Deelbare link</label>
        <div style={{ display: "flex", gap: 8, margin: "6px 0 14px" }}>
          <input readOnly value={shareUrl} style={{ flex: 1, padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", color: C.ink2 }} />
          <Btn size="sm" variant={copied === "link" ? "yellow" : "ghost"} icon={copied === "link" ? Check : FileText} onClick={() => copy(shareUrl, "link")}>{copied === "link" ? "Gekopieerd" : "Kopieer"}</Btn>
          <Btn size="sm" variant="ghost" icon={ArrowUpRight} onClick={() => window.open(shareUrl, "_blank")}>Open</Btn>
        </div>
        <label style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Insluitcode (iframe)</label>
        <div style={{ display: "flex", gap: 8, marginTop: 6 }}>
          <textarea readOnly value={embedCode} rows={2} style={{ flex: 1, padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: "monospace", fontSize: 12, outline: "none", color: C.ink2, resize: "none" }} />
          <Btn size="sm" variant={copied === "embed" ? "yellow" : "ghost"} icon={copied === "embed" ? Check : FileText} onClick={() => copy(embedCode, "embed")}>{copied === "embed" ? "Gekopieerd" : "Kopieer"}</Btn>
        </div>
      </Card>

      <Card>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <div>
            <div style={{ fontWeight: 700, fontSize: 15 }}>Reacties</div>
            <div style={{ fontSize: 13, color: C.muted, marginTop: 2 }}>{respCount == null ? "laden…" : `${respCount} reactie${respCount === 1 ? "" : "s"} binnengekomen`}</div>
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <Btn size="sm" variant="ghost" icon={Radar} onClick={loadResponses}>Ververs</Btn>
            <Btn size="sm" variant="yellow" icon={Sparkles} onClick={onAnalyse} disabled={running}>{running ? "Analyseren…" : "Analyseer met AI"}</Btn>
          </div>
        </div>

        {liveNps != null && (
          <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 14, padding: "12px 14px", background: C.paper, borderRadius: 12, border: `1px solid ${C.line}` }}>
            <div style={{ fontFamily: FONT_DISPLAY, fontSize: 34, fontWeight: 700, lineHeight: 1, color: liveNps >= 50 ? C.good : liveNps >= 0 ? C.yellowDark : C.bad }}>{liveNps}</div>
            <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.4 }}>Actuele NPS uit {liveScores.length} score{liveScores.length === 1 ? "" : "s"}.<br />Bij "Analyseer met AI" wordt dit ook op je dashboard bijgewerkt.</div>
          </div>
        )}

        {responses.length > 0 && (
          <div style={{ marginTop: 14 }}>
            <div style={sectionLabel}>Binnengekomen reacties</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 8 }}>
              {responses.map((resp, i) => (
                <div key={i} style={{ padding: "11px 13px", background: C.card, border: `1px solid ${C.line}`, borderRadius: 10 }}>
                  <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 6 }}>Reactie {i + 1}{resp.at ? " · " + new Date(resp.at).toLocaleDateString("nl-NL") : ""}</div>
                  {s.questions.map(q => {
                    const ans = resp.answers?.[q.id];
                    if (ans == null || ans === "") return null;
                    const isNps = q.type === "nps";
                    return (
                      <div key={q.id} style={{ marginBottom: 6 }}>
                        <div style={{ fontSize: 12, fontWeight: 700, color: C.ink2 }}>{q.label}</div>
                        {isNps
                          ? <span style={{ display: "inline-block", marginTop: 2, padding: "2px 9px", borderRadius: 6, fontWeight: 700, fontSize: 13, color: "#fff", background: Number(ans) >= 9 ? C.good : Number(ans) >= 7 ? C.yellowDark : C.bad }}>{ans} / 10</span>
                          : <div style={{ fontSize: 13.5, color: C.ink, lineHeight: 1.45 }}>{String(ans)}</div>}
                      </div>
                    );
                  })}
                </div>
              ))}
            </div>
          </div>
        )}
        {responses.length === 0 && respCount === 0 && <div style={{ marginTop: 12, fontSize: 13, color: C.muted }}>Nog geen reacties. Deel de link om reacties te verzamelen.</div>}
        {running && <div style={{ marginTop: 12 }}><Spinner label="AI analyseert de reacties…" /></div>}
      </Card>
    </div>
  );
}
const backLink = { display: "inline-flex", alignItems: "center", gap: 4, fontSize: 13.5, fontWeight: 600, background: "none", border: "none", cursor: "pointer", marginBottom: 14, padding: 0, get color() { return C.muted; } };
const sectionLabel = { fontSize: 12.5, fontWeight: 700, marginTop: 16, marginBottom: 2, textTransform: "uppercase", letterSpacing: .4, get color() { return C.muted; } };
const iconBtn = (color) => ({ flexShrink: 0, background: "none", border: "none", cursor: "pointer", color, padding: 4, display: "grid", placeItems: "center" });

/* ============================== 3. AUDIT ============================== */
const AUDIT_TYPES = [
  { id: "acquisition", label: "Overname-audit", icon: ClipboardCheck, desc: "Vergelijk een overgenomen bedrijf met whitevision op propositie, markt, producten, technologie en kanalen, met een fit-score per as.", input: "company" },
  { id: "website", label: "Website content audit", icon: Globe, desc: "Toets website-content tegen je positionering, tone-of-voice en brandbook.", input: "url" },
  { id: "presentation", label: "Presentatie audit", icon: Presentation, desc: "Plak presentatie-inhoud en laat AI merkconsistentie beoordelen.", input: "text" },
  { id: "market", label: "Marktkansen (M&A)", icon: Briefcase, desc: "Vind bedrijven die je doelstellingen aanvullen of overlappen, met fit-score.", input: "market", moved: true },
  { id: "portfolio", label: "Overgenomen bedrijven", icon: Building2, desc: "Analyseer het aanbodverschil: wat bieden zij dat wij niet, en andersom.", input: "company" },
  { id: "collateral", label: "Materiaal audit", icon: FileText, desc: "Beoordeel een brochure, e-mail of andere uiting op merkfit.", input: "text" },
];
// Vaste assen voor de overname-audit.
const ACQ_AXES = [
  { key: "propositie", label: "Propositie" },
  { key: "markt", label: "Markt" },
  { key: "producten", label: "Producten" },
  { key: "technologie", label: "Technologie" },
  { key: "kanalen", label: "Kanalen" },
];

function Audit({ ctx }) {
  const { audits, saveAudits, profile, companies, addAction, confirm } = ctx;
  const [open, setOpen] = useState(null);
  const [openEntry, setOpenEntry] = useState(null);
  const [running, setRunning] = useState(false);
  const [val, setVal] = useState("");
  const [marketGoal, setMarketGoal] = useState("");
  const [selectedCo, setSelectedCo] = useState("");

  const runAudit = async (type) => {
    setRunning(true);
    let prompt;
    let useSearch = false;
    if (type.id === "market") {
      useSearch = true;
      prompt = `${profileContext(profile)}\n\nOPDRACHT: Identificeer met web search concrete overname-/marktkansen. Doel van de brand manager: "${marketGoal || profile?.goals || "groei en marktuitbreiding"}".\n\nZoek naar echte bedrijven die passen, met per kandidaat een geschatte fit-score (0-100%), of het aanvult of overlapt, de relevantie t.o.v. de doelstelling, en indien vindbaar of het bedrijf mogelijk overnamebereid is ("in de etalage"). Vermeld een bron-URL per kandidaat.\n\nJSON: {"summary": "...", "candidates": [{"name": "bedrijfsnaam", "fit": 0-100, "relation": "aanvullend|overlappend", "rationale": "waarom", "tag": "land/segment", "source": "bron-URL"}], "actions": [{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    } else if (type.id === "website") {
      useSearch = true;
      prompt = `${profileContext(profile)}\n\nOPDRACHT: Voer een website content audit uit voor: "${val}". Bekijk met web search de daadwerkelijke content en beoordeel of die aansluit op de positionering, tone-of-voice en het merkprofiel. Markeer afwijkingen met verwijzing naar specifieke pagina's (URL).\n\nJSON: {"summary": "...", "findings": [{"title":"...","text":"afwijking of observatie","severity":"hoog|midden|laag","tag":"categorie","source":"pagina-URL"}], "actions": [{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    } else if (type.id === "acquisition") {
      useSearch = true;
      const co = companies.find(c => String(c.id) === String(selectedCo));
      const coDetail = co ? [
        co.whatTheyDo ? `Wat ze doen: ${co.whatTheyDo}` : "",
        co.audience ? `Doelgroep: ${co.audience}` : "",
        co.usps ? `USP's: ${co.usps}` : "",
        co.markets ? `Markten: ${co.markets}` : "",
        co.techstack ? `Tech stack: ${co.techstack}` : "",
        co.segment ? `Segment: ${co.segment}` : "",
        co.country ? `Land: ${co.country}` : "",
        co.notes ? `Notities: ${co.notes}` : "",
      ].filter(Boolean).join("\n") : "";
      prompt = `${profileContext(profile)}\n\nOPDRACHT: Voer een overname-audit uit waarin je het overgenomen bedrijf "${co?.name || selectedCo}" vergelijkt met whitevision. Combineer de hieronder vastgelegde gegevens met eigen onderzoek via web search (hun website, nieuws, vacatures, reviews) om de gaten aan te vullen.\n\nWAT WE AL OVER DIT BEDRIJF WETEN:\n${coDetail || "(nog weinig vastgelegd; onderzoek zelf)"}\n\nBeoordeel het bedrijf op precies deze vijf assen: propositie, markt, producten, technologie, kanalen. Geef per as: een fit-score 0-100 (hoe dicht staan ze bij whitevision / hoe soepel is de integratie op die as), een korte typering van whitevision op die as, een korte typering van het bedrijf op die as, de belangrijkste overeenkomsten, de belangrijkste verschillen, en wat dit betekent voor de integratie. Wees concreet en onderbouw met bronnen waar mogelijk.\n\nGeef ook een totale integratie-fitscore (0-100) en een korte managementsamenvatting.\n\nJSON: {"summary":"managementsamenvatting","overallFit":0-100,"axes":[{"key":"propositie|markt|producten|technologie|kanalen","score":0-100,"us":"whitevision op deze as","them":"het bedrijf op deze as","similarities":["overeenkomst",...],"differences":["verschil",...],"integration":"wat dit betekent voor de integratie"}],"sources":["url",...],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    } else if (type.id === "portfolio") {
      useSearch = true;
      const co = companies.find(c => String(c.id) === String(selectedCo));
      const coDetail = co ? [
        co.whatTheyDo ? `Wat ze doen: ${co.whatTheyDo}` : "",
        co.audience ? `Doelgroep: ${co.audience}` : "",
        co.usps ? `USP's: ${co.usps}` : "",
        co.markets ? `Markten: ${co.markets}` : "",
        co.techstack ? `Tech stack: ${co.techstack}` : "",
        co.notes ? `Notities: ${co.notes}` : "",
      ].filter(Boolean).join("\n") : "";
      prompt = `${profileContext(profile)}\n\nOPDRACHT: Voer met web search een één-op-één vergelijking uit tussen whitevision en het overgenomen bedrijf "${co?.name || selectedCo}"${co?.segment ? " (segment: " + co.segment + ")" : ""}${co?.country ? ", " + co.country : ""}.${coDetail ? "\n\nWat we al over dit bedrijf vastgelegd hebben:\n" + coDetail : ""}\n\nKies zelf de meest relevante vergelijkingscriteria (bijv. propositie, functionaliteit, doelgroep, prijsmodel, AI/technologie, tech stack, support, marktpositie). Vergelijk daarbij ook expliciet de technologie/tech stack van beide partijen waar bekend. Geef per criterium beknopt de situatie aan beide kanten en wie het sterker doet. Benoem daarnaast expliciet wat whitevision beter doet en wat het andere bedrijf beter doet, en betrek de tech stack in de acties (bijv. migratie, integratie, uitfaseren). Onderbouw met bronnen waar mogelijk.\n\nJSON: {"summary":"...", "headToHead":[{"criterion":"criterium","us":"whitevision","them":"hun situatie","winner":"wij|zij|gelijk"}], "weBetter":["wat wij beter doen..."], "theyBetter":["wat zij beter doen..."], "theyOffer":[{"title":"hun aanbod dat wij missen","text":"...","source":"URL","severity":"hoog|midden|laag"}], "weOffer":[{"title":"ons aanbod dat zij missen","text":"...","severity":"laag"}], "overlap":["overlappend gebied"], "conclusion":"korte conclusie wat dit betekent voor de integratie/strategie", "actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    } else {
      prompt = `${profileContext(profile)}\n\nOPDRACHT: Audit het volgende materiaal tegen de positionering, tone-of-voice en het merkprofiel. Markeer waar het afwijkt van het merk en wat sterk is.\n\nMATERIAAL:\n"""${val}"""\n\nJSON: {"summary": "...", "findings": [{"title":"...","text":"...","severity":"hoog|midden|laag","tag":"tone|inhoud|visueel|positionering"}], "actions": [{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    }
    const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch });
    const co = companies.find(c => String(c.id) === String(selectedCo));
    const entry = {
      id: Date.now(), typeId: type.id, typeLabel: type.label,
      subject: type.id === "market" ? (marketGoal || "doelstelling") : type.id === "portfolio" ? (co?.name || "—") : (val.slice(0, 80) || "—"),
      date: new Date().toISOString().slice(0, 10),
      summary: r?.summary || "Geen samenvatting.",
      findings: r?.findings || [], candidates: r?.candidates || [], actions: r?.actions || [],
      theyOffer: r?.theyOffer || [], weOffer: r?.weOffer || [], overlap: r?.overlap || [], sources: sources || [],
      headToHead: r?.headToHead || [], weBetter: r?.weBetter || [], theyBetter: r?.theyBetter || [], conclusion: r?.conclusion || null,
      axes: r?.axes || [], overallFit: typeof r?.overallFit === "number" ? r.overallFit : null,
      compareName: co?.name || null,
    };
    saveAudits([entry, ...audits]);
    setRunning(false); setVal(""); setMarketGoal(""); setSelectedCo(""); setOpenEntry(entry.id);
  };

  const deleteAudit = (id) => confirm("Deze audit wordt definitief verwijderd.", () => { saveAudits(audits.filter(a => a.id !== id)); setOpenEntry(null); });

  // ---- single audit detail ----
  if (openEntry) {
    const a = audits.find(x => x.id === openEntry);
    if (!a) { setOpenEntry(null); return null; }
    const type = AUDIT_TYPES.find(t => t.id === a.typeId) || { label: a.typeLabel };
    return (
      <div>
        <button onClick={() => setOpenEntry(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar overzicht</button>
        <PageHead title={type.label} sub={`${a.date} · ${a.subject}`}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => deleteAudit(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>
          {a.axes?.length > 0 && <>
            {typeof a.overallFit === "number" && (
              <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 16, padding: "14px 16px", background: C.surface, borderRadius: 12 }}>
                <div style={{ position: "relative", width: 54, height: 54, flexShrink: 0 }}>
                  <svg width="54" height="54" style={{ transform: "rotate(-90deg)" }}>
                    <circle cx="27" cy="27" r="22" fill="none" stroke="#ffffff22" strokeWidth="6" />
                    <circle cx="27" cy="27" r="22" fill="none" stroke={a.overallFit >= 70 ? C.good : a.overallFit >= 40 ? C.yellow : C.bad} strokeWidth="6" strokeDasharray={`${(a.overallFit / 100) * 138} 138`} strokeLinecap="round" />
                  </svg>
                  <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", fontSize: 14, fontWeight: 700, color: "#fff" }}>{a.overallFit}%</div>
                </div>
                <div>
                  <div style={{ color: "#fff", fontWeight: 700, fontSize: 15 }}>Integratie-fit met whitevision</div>
                  <div style={{ color: "#9CA3AF", fontSize: 12.5, marginTop: 2 }}>Gemiddelde fit over de vijf assen; hoger betekent soepeler samen te voegen.</div>
                </div>
              </div>
            )}
            <div style={sectionLabel}>Scores per as</div>
            <div style={{ marginTop: 10, overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead>
                  <tr style={{ background: C.surface }}>
                    <th style={cmpTh}>As</th>
                    <th style={{ ...cmpTh, color: C.yellow }}>whitevision</th>
                    <th style={cmpTh}>{a.compareName || a.subject}</th>
                    <th style={{ ...cmpTh, width: 120 }}>Fit</th>
                  </tr>
                </thead>
                <tbody>
                  {a.axes.map((ax, i) => {
                    const lbl = (ACQ_AXES.find(x => x.key === ax.key) || {}).label || ax.key;
                    const sc = typeof ax.score === "number" ? ax.score : 0;
                    return (
                      <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
                        <td style={{ ...cmpTd, fontWeight: 700 }}>{lbl}</td>
                        <td style={cmpTd}>{ax.us}</td>
                        <td style={cmpTd}>{ax.them}</td>
                        <td style={cmpTd}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                            <div style={{ flex: 1, height: 7, background: C.line, borderRadius: 4, overflow: "hidden", minWidth: 44 }}>
                              <div style={{ width: `${sc}%`, height: "100%", background: sc >= 70 ? C.good : sc >= 40 ? C.yellowDark : C.bad }} />
                            </div>
                            <span style={{ fontWeight: 700, fontSize: 12.5 }}>{sc}%</span>
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
            <div style={sectionLabel}>Details per as</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 10 }}>
              {a.axes.map((ax, i) => {
                const lbl = (ACQ_AXES.find(x => x.key === ax.key) || {}).label || ax.key;
                return (
                  <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 12, padding: 15 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
                      <span style={{ fontWeight: 700, fontSize: 15 }}>{lbl}</span>
                      <Tag color={ax.score >= 70 ? C.good : ax.score >= 40 ? C.yellowDark : C.bad}>{typeof ax.score === "number" ? ax.score + "% fit" : "—"}</Tag>
                    </div>
                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 10 }}>
                      <div style={{ padding: 11, background: C.good + "0E", borderRadius: 10 }}>
                        <div style={{ fontSize: 11.5, fontWeight: 700, color: C.good, marginBottom: 6, textTransform: "uppercase" }}>Overeenkomsten</div>
                        {(ax.similarities || []).length ? (ax.similarities || []).map((s, j) => <div key={j} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 4, display: "flex", gap: 6 }}><span style={{ color: C.good }}>•</span> {s}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
                      </div>
                      <div style={{ padding: 11, background: C.bad + "0E", borderRadius: 10 }}>
                        <div style={{ fontSize: 11.5, fontWeight: 700, color: C.bad, marginBottom: 6, textTransform: "uppercase" }}>Verschillen</div>
                        {(ax.differences || []).length ? (ax.differences || []).map((s, j) => <div key={j} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 4, display: "flex", gap: 6 }}><span style={{ color: C.bad }}>•</span> {s}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
                      </div>
                    </div>
                    {ax.integration && <div style={{ display: "flex", gap: 8, alignItems: "flex-start", padding: "10px 12px", background: C.paper, borderRadius: 10 }}>
                      <ArrowUpRight size={15} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
                      <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}><span style={{ fontWeight: 700 }}>Voor de integratie: </span>{ax.integration}</div>
                    </div>}
                  </div>
                );
              })}
            </div>
          </>}
          {a.candidates?.length > 0 && (
            <div style={{ marginTop: 16, display: "flex", flexDirection: "column", gap: 10 }}>
              {a.candidates.map((c, i) => (
                <div key={i} style={{ display: "flex", alignItems: "center", gap: 14, padding: "12px 15px", background: C.paper, borderRadius: 12, border: `1px solid ${C.line}` }}>
                  <div style={{ position: "relative", width: 46, height: 46, flexShrink: 0 }}>
                    <svg width="46" height="46" style={{ transform: "rotate(-90deg)" }}>
                      <circle cx="23" cy="23" r="19" fill="none" stroke={C.line} strokeWidth="5" />
                      <circle cx="23" cy="23" r="19" fill="none" stroke={c.fit >= 70 ? C.good : c.fit >= 40 ? C.yellowDark : C.bad} strokeWidth="5" strokeDasharray={`${(c.fit / 100) * 119} 119`} strokeLinecap="round" />
                    </svg>
                    <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", fontSize: 12.5, fontWeight: 700 }}>{c.fit}%</div>
                  </div>
                  <div style={{ flex: 1 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                      <span style={{ fontWeight: 700, fontSize: 14.5 }}>{c.name}</span>
                      <Tag color={c.relation === "overlappend" ? C.bad : C.good}>{c.relation}</Tag>
                      {c.tag && <Tag>{c.tag}</Tag>}
                    </div>
                    <div style={{ fontSize: 13, color: C.ink2, marginTop: 3, lineHeight: 1.45 }}>{c.rationale}</div>
                    {c.source && <a href={c.source} target="_blank" rel="noopener noreferrer" style={{ fontSize: 12, color: C.blue, display: "inline-flex", alignItems: "center", gap: 4, marginTop: 5 }}><Globe size={11} /> bron</a>}
                  </div>
                  <ActionButton onAdd={() => addAction({ title: `Verken: ${c.name}`, detail: c.rationale, source: "Marktkansen" })} />
                </div>
              ))}
            </div>
          )}
          {a.headToHead?.length > 0 && <>
            <div style={sectionLabel}>Vergelijking: whitevision tegenover {a.compareName || a.subject}</div>
            <div style={{ marginTop: 10, overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead>
                  <tr style={{ background: C.surface }}>
                    <th style={cmpTh}>Criterium</th>
                    <th style={{ ...cmpTh, color: C.yellow }}>whitevision</th>
                    <th style={cmpTh}>{a.compareName || a.subject}</th>
                    <th style={{ ...cmpTh, width: 90 }}>Sterker</th>
                  </tr>
                </thead>
                <tbody>
                  {a.headToHead.map((row, i) => {
                    const w = (row.winner || "").toLowerCase();
                    const usWin = w.includes("wij"), themWin = w.includes("zij");
                    return (
                      <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
                        <td style={{ ...cmpTd, fontWeight: 700 }}>{row.criterion}</td>
                        <td style={{ ...cmpTd, background: usWin ? C.good + "14" : "transparent" }}>{row.us}</td>
                        <td style={{ ...cmpTd, background: themWin ? C.bad + "10" : "transparent" }}>{row.them}</td>
                        <td style={cmpTd}><Tag color={usWin ? C.good : themWin ? C.bad : C.muted}>{usWin ? "wij" : themWin ? "zij" : "gelijk"}</Tag></td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
            {(a.weBetter?.length > 0 || a.theyBetter?.length > 0) && (
              <div style={{ marginTop: 12, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
                <div style={{ padding: 14, background: C.good + "0E", borderRadius: 12, border: `1px solid ${C.good}22` }}>
                  <div style={{ fontSize: 12.5, fontWeight: 700, color: C.good, marginBottom: 8, textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6 }}><CheckCircle2 size={13} /> Wat wij beter doen</div>
                  {(a.weBetter || []).map((x, i) => <div key={i} style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5, marginBottom: 5, display: "flex", gap: 6 }}><span style={{ color: C.good }}>•</span> {x}</div>)}
                  {(!a.weBetter || !a.weBetter.length) && <div style={{ fontSize: 12.5, color: C.muted }}>—</div>}
                </div>
                <div style={{ padding: 14, background: C.bad + "0E", borderRadius: 12, border: `1px solid ${C.bad}22` }}>
                  <div style={{ fontSize: 12.5, fontWeight: 700, color: C.bad, marginBottom: 8, textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6 }}><TrendingUp size={13} /> Wat zij beter doen</div>
                  {(a.theyBetter || []).map((x, i) => <div key={i} style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5, marginBottom: 5, display: "flex", gap: 6 }}><span style={{ color: C.bad }}>•</span> {x}</div>)}
                  {(!a.theyBetter || !a.theyBetter.length) && <div style={{ fontSize: 12.5, color: C.muted }}>—</div>}
                </div>
              </div>
            )}
          </>}
          {(a.theyOffer?.length > 0 || a.weOffer?.length > 0) && (
            <div style={{ marginTop: 16, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div style={{ padding: 14, background: C.bad + "0E", borderRadius: 12, border: `1px solid ${C.bad}22` }}>
                <div style={{ fontSize: 12.5, fontWeight: 700, color: C.bad, marginBottom: 8, textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6 }}><TrendingUp size={13} /> Zij bieden, wij niet</div>
                {(a.theyOffer || []).map((x, i) => (
                  <div key={i} style={{ marginBottom: 10 }}>
                    <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start" }}>
                      <div><div style={{ fontWeight: 700, fontSize: 13.5 }}>{x.title}</div><div style={{ fontSize: 12.5, color: C.ink2, marginTop: 2, lineHeight: 1.45 }}>{x.text}</div></div>
                      <ActionButton small label="" onAdd={() => addAction({ title: x.title, detail: x.text, source: "Portfolio-gap" })} />
                    </div>
                  </div>
                ))}
                {(!a.theyOffer || !a.theyOffer.length) && <div style={{ fontSize: 12.5, color: C.muted }}>Geen gemeld.</div>}
              </div>
              <div style={{ padding: 14, background: C.good + "0E", borderRadius: 12, border: `1px solid ${C.good}22` }}>
                <div style={{ fontSize: 12.5, fontWeight: 700, color: C.good, marginBottom: 8, textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6 }}><CheckCircle2 size={13} /> Wij bieden, zij niet</div>
                {(a.weOffer || []).map((x, i) => (
                  <div key={i} style={{ marginBottom: 10 }}>
                    <div style={{ fontWeight: 700, fontSize: 13.5 }}>{x.title}</div><div style={{ fontSize: 12.5, color: C.ink2, marginTop: 2, lineHeight: 1.45 }}>{x.text}</div>
                  </div>
                ))}
                {(!a.weOffer || !a.weOffer.length) && <div style={{ fontSize: 12.5, color: C.muted }}>Geen gemeld.</div>}
              </div>
            </div>
          )}
          {a.overlap?.length > 0 && (
            <div style={{ marginTop: 12, display: "flex", flexWrap: "wrap", gap: 7, alignItems: "center" }}>
              <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Overlap:</span>
              {a.overlap.map((o, i) => <Tag key={i} color={C.blue}>{o}</Tag>)}
            </div>
          )}
          {a.conclusion && <>
            <div style={sectionLabel}>Conclusie</div>
            <div style={{ marginTop: 8, padding: "14px 16px", background: C.surface, borderRadius: 12, color: "#fff", fontSize: 14.5, lineHeight: 1.6 }}>{a.conclusion}</div>
          </>}
          {a.findings?.length > 0 && <>
            <div style={sectionLabel}>Bevindingen <span style={{ fontWeight: 400, textTransform: "none" }}>— klik voor verdieping</span></div>
            <Insights items={a.findings} onAction={addAction} prefix={type.label} profile={profile} />
          </>}
          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix={type.label} profile={profile} />
          </>}
          <SourceLinks sources={a.sources} />
        </Card>
      </div>
    );
  }

  // ---- type-view: form + compact list ----
  if (open) {
    const type = AUDIT_TYPES.find(t => t.id === open.id);
    const history = audits.filter(a => a.typeId === open.id);
    return (
      <div>
        <button onClick={() => setOpen(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar audits</button>
        <PageHead title={type.label} sub={type.desc} />
        {!profile?.filled && <div style={mirrorWarn}><AlertTriangle size={15} /> Vul eerst je merkprofiel in voor scherpere spiegeling.</div>}

        <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe audit</span></div>
          {type.input === "url" && <Field label="Website URL of sitemap" value={val} onChange={setVal} placeholder="https://whitevision.com" />}
          {type.input === "text" && <Field label="Plak hier de inhoud" value={val} onChange={setVal} multiline rows={6} placeholder="Plak de presentatietekst, brochure of e-mail…" />}
          {type.input === "market" && <Field label="Doelstelling / richting" value={marketGoal} onChange={setMarketGoal} placeholder="Bijv. 'uitbreiden naar de zorgbranche in DACH'" />}
          {type.input === "company" && (
            <div style={{ marginBottom: 16 }}>
              <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Kies een overgenomen bedrijf</label>
              {companies.length === 0
                ? <div style={{ fontSize: 13.5, color: C.muted }}>Voeg eerst overgenomen bedrijven toe op het merkprofiel.</div>
                : <select value={selectedCo} onChange={e => setSelectedCo(e.target.value)} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
                    <option value="">— Selecteer —</option>
                    {companies.map(c => <option key={c.id} value={c.id}>{c.name}{c.segment ? ` (${c.segment})` : ""}</option>)}
                  </select>}
            </div>
          )}
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Btn variant="yellow" icon={Sparkles} onClick={() => runAudit(type)} disabled={running || (type.input === "url" || type.input === "text" ? !val : false) || (type.input === "market" && !marketGoal && !profile?.goals) || (type.input === "company" && !selectedCo)}>
              {running ? "Bezig…" : "Start AI-audit"}
            </Btn>
            <CostHint kind={type.input === "text" ? "light" : "search"} />
          </div>
          {running && <div style={{ marginTop: 12 }}><Spinner label="AI voert de audit uit met web search & spiegelt tegen je merkprofiel…" /></div>}
        </Card>

        <div style={sectionLabel}>Eerdere audits</div>
        {history.length === 0 && <Empty icon={ClipboardCheck} title="Nog geen audits uitgevoerd" />}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {history.map(a => {
            const findCount = (a.findings?.length || 0) + (a.candidates?.length || 0) + (a.theyOffer?.length || 0) + (a.axes?.length || 0);
            return (
              <Card key={a.id} hover style={{ padding: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                  <div onClick={() => setOpenEntry(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                      <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                      <span style={{ fontSize: 12.5, color: C.muted }}>{a.subject}</span>
                      {findCount > 0 && <span style={{ fontSize: 12, color: C.muted }}>· {findCount} punten</span>}
                      {a.sources?.length > 0 && <span style={{ fontSize: 12, color: C.muted, display: "inline-flex", alignItems: "center", gap: 3 }}><Globe size={11} /> {a.sources.length}</span>}
                    </div>
                    <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
                  </div>
                  <button onClick={() => setOpenEntry(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                  <button onClick={() => deleteAudit(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                </div>
              </Card>
            );
          })}
        </div>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Audit" sub="Toets materiaal en marktkansen — alles gespiegeld tegen je merkprofiel." />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 16 }}>
        {AUDIT_TYPES.filter(t => !t.moved).map(t => {
          const count = audits.filter(a => a.typeId === t.id).length;
          return (
            <Card key={t.id} hover onClick={() => setOpen(t)}>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
                <div style={{ width: 42, height: 42, borderRadius: 11, background: C.surface, display: "grid", placeItems: "center" }}>
                  <t.icon size={21} style={{ color: C.yellow }} />
                </div>
                {count > 0 && <Tag color={C.ink} bg={C.yellow}>{count}×</Tag>}
              </div>
              <div style={{ fontWeight: 700, fontSize: 16.5, marginTop: 14 }}>{t.label}</div>
              <div style={{ fontSize: 13.5, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{t.desc}</div>
              <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>Openen <ArrowUpRight size={15} /></div>
            </Card>
          );
        })}
      </div>
    </div>
  );
}
const mirrorWarn = { display: "flex", alignItems: "center", gap: 8, fontSize: 13.5, padding: "10px 14px", borderRadius: 10, marginBottom: 18, fontWeight: 600, get color() { return C.yellowDark; }, get background() { return C.yellow + "18"; } };

/* ============================== 4. MONITOR ============================== */
const MONITOR_TYPES = [
  { id: "mentions", label: "Brand mentions", icon: Megaphone, desc: "Doorlopend volgen waar merknamen genoemd worden, met notificaties.",
    placeholder: "Merknaam of term (bijv. whitevision, WhiteVision IDP)…", addLabel: "Merknaam / term toevoegen", noun: "merk" },
  { id: "volume", label: "Brand volume", icon: BarChart3, desc: "Houd bij hoe vaak er op je merknaam gezocht wordt, per land. Nu handmatig; later koppelbaar aan een zoekwoordtool.",
    placeholder: "Merknaam + land (bijv. whitevision NL)…", addLabel: "Merknaam / markt toevoegen", noun: "merk/markt", manual: true },
  { id: "competitors", label: "Concurrenten", icon: Building2, desc: "Volg concurrenten: blogs, nieuwe features en social posts.",
    placeholder: "Naam van een concurrent (bijv. ABBYY)…", addLabel: "Concurrent toevoegen", noun: "concurrent" },
  { id: "acquired", label: "Overgenomen bedrijven", icon: Briefcase, desc: "Volg de overnames die je op het merkprofiel hebt aangezet.",
    placeholder: "", addLabel: "", noun: "overgenomen bedrijf" },
];

const VOL_COLORS = ["#E5E7EB", "#7C3AED", "#60A5FA"];

// Herbruikbare lijn-grafiek met y-as-waardes en hover-tooltip.
// labels: x-as labels (array). series: [{ name, color, values:[number|null] }]. fmtVal: opmaak van een waarde.
function HoverLineChart({ labels, series, fmtVal = (v) => Number(v).toLocaleString("nl-NL"), height = 240 }) {
  const [hover, setHover] = useState(null);
  const active = (series || []).filter(s => (s.values || []).some(v => v != null));
  if (!labels || !labels.length || !active.length) {
    return <div style={{ padding: "28px 0", textAlign: "center", fontSize: 13, color: C.muted, background: C.paper, borderRadius: 10 }}>Nog geen data om te tonen.</div>;
  }
  const allVals = active.flatMap(s => s.values).filter(v => v != null);
  const maxV = Math.max(1, ...allVals);
  const W = 680, H = height, padL = 48, padR = 16, padT = 16, padB = 34;
  const plotW = W - padL - padR, plotH = H - padT - padB;
  const x = (i) => labels.length <= 1 ? padL + plotW / 2 : padL + (plotW * i) / (labels.length - 1);
  const y = (v) => padT + plotH - (plotH * v) / maxV;
  const step = Math.ceil(labels.length / 8);
  const multi = active.length > 1;

  return (
    <div style={{ position: "relative" }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", height: "auto", display: "block" }} onMouseLeave={() => setHover(null)}>
        {[0, 0.25, 0.5, 0.75, 1].map((f, i) => {
          const val = Math.round(maxV * (1 - f)); const yy = padT + plotH * f;
          return <g key={i}>
            <line x1={padL} y1={yy} x2={W - padR} y2={yy} stroke={C.line} strokeWidth="1" />
            <text x={padL - 6} y={yy + 3} fontSize="10" fill={C.muted} textAnchor="end">{val.toLocaleString("nl-NL")}</text>
          </g>;
        })}
        {labels.map((l, i) => (i % step === 0 || i === labels.length - 1)
          ? <text key={i} x={x(i)} y={H - padB + 16} fontSize="10" fill={C.muted} textAnchor="middle">{String(l).slice(5, 10) || String(l)}</text>
          : null)}
        {active.map((s, si) => {
          const pts = s.values.map((v, i) => v == null ? null : `${x(i)},${y(v)}`).filter(Boolean).join(" ");
          return <polyline key={si} points={pts} fill="none" stroke={s.color || VOL_COLORS[si % VOL_COLORS.length]} strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" />;
        })}
        {hover != null && <line x1={x(hover)} y1={padT} x2={x(hover)} y2={padT + plotH} stroke={C.muted} strokeWidth="1" strokeDasharray="3 3" />}
        {hover != null && active.map((s, si) => s.values[hover] == null ? null : (
          <circle key={si} cx={x(hover)} cy={y(s.values[hover])} r="4" fill={s.color || VOL_COLORS[si % VOL_COLORS.length]} stroke="#fff" strokeWidth="1.5" />
        ))}
        {labels.map((l, i) => (
          <rect key={i} x={x(i) - (plotW / Math.max(labels.length, 1)) / 2} y={padT} width={Math.max(plotW / Math.max(labels.length, 1), 6)} height={plotH} fill="transparent" onMouseEnter={() => setHover(i)} />
        ))}
      </svg>
      {multi && (
        <div style={{ display: "flex", gap: 14, flexWrap: "wrap", marginTop: 6, paddingLeft: 24 }}>
          {active.map((s, si) => (
            <span key={si} style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: C.ink2 }}>
              <span style={{ width: 11, height: 3, borderRadius: 2, background: s.color || VOL_COLORS[si % VOL_COLORS.length] }} /> {s.name}
            </span>
          ))}
        </div>
      )}
      {hover != null && (
        <div style={{ position: "absolute", top: 8, left: "50%", transform: "translateX(-50%)", background: C.surface, color: "#fff", padding: "8px 12px", borderRadius: 8, fontSize: 12, pointerEvents: "none", whiteSpace: "nowrap", boxShadow: "0 4px 16px rgba(0,0,0,.2)" }}>
          <div style={{ fontWeight: 700, marginBottom: multi ? 4 : 0 }}>{labels[hover]}</div>
          {active.map((s, si) => (
            <div key={si} style={{ display: "flex", alignItems: "center", gap: 6 }}>
              {multi && <span style={{ width: 9, height: 9, borderRadius: 2, background: s.color || VOL_COLORS[si % VOL_COLORS.length] }} />}
              {multi ? s.name + ": " : ""}<b>{s.values[hover] == null ? "—" : fmtVal(s.values[hover])}</b>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// Multi-line grafiek voor branded zoekvolume met hover per maand.
function BrandVolumeChart({ terms }) {
  const active = (terms || []).filter(t => (t.series || []).length > 0);
  if (!active.length) {
    return <div style={{ padding: "28px 0", textAlign: "center", fontSize: 13, color: C.muted, background: C.paper, borderRadius: 10 }}>Nog geen data. Voeg een zoekterm toe en klik op Ophalen.</div>;
  }
  const monthsSet = new Set();
  active.forEach(t => t.series.forEach(s => monthsSet.add(s.month)));
  const months = Array.from(monthsSet).sort();
  const fmtMonth = (m) => { const [yr, mo] = m.split("-"); return `${["", "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec"][parseInt(mo)]} '${yr.slice(2)}`; };
  const series = active.map((t, idx) => {
    const byMonth = {}; t.series.forEach(s => byMonth[s.month] = s.impressions);
    return { name: t.term || "(naamloos)", color: VOL_COLORS[idx % VOL_COLORS.length], values: months.map(m => byMonth[m] ?? null) };
  });
  return <HoverLineChart labels={months.map(fmtMonth)} series={series} />;
}

/* ====================== STRATEGY: CHANNEL AUDIT AGENT ====================== */
// Kijkt in de breedte naar de kanaalstrategie: welke kanalen passen bij jouw markt en
// doelstellingen, waar zit je al, en wat laat je liggen. Gebruikt web search, want dit
// gaat over wat er nu in jouw markt speelt.
function ChannelAuditAgent({ ctx }) {
  const { audits, saveAudits, profile, saveProfile, addAction, confirm } = ctx;
  const [added, setAdded] = useState({});
  // Een gevonden kanaal opnemen in je bedrijfsprofiel, zodat het meetelt bij het budget.
  const addToProfile = (name, note, category, cost) => {
    const list = profile?.channels || [];
    if (list.some(c => (c.name || "").toLowerCase() === String(name).toLowerCase())) { setAdded(a => ({ ...a, [name]: "bestond al" })); return; }
    const type = category === "event" ? "event" : category === "platform" ? "platform" : "kanaal";
    saveProfile({
      ...profile, filled: true,
      channels: [...list, { id: "c" + Date.now() + Math.random().toString(36).slice(2, 5), name, type, status: "interessant", note: note || "", cost: cost || "" }],
    });
    setAdded(a => ({ ...a, [name]: "toegevoegd" }));
  };
  const [goal, setGoal] = useState("");
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const history = (audits || []).filter(a => a.typeId === "channels");

  const run = async () => {
    setRunning(true); setError("");
    const nextYear = new Date().getFullYear() + 1;
    const prompt = `${profileContext(profile)}\n\n${figuresContext(profile, nextYear)}\n\n${channelsContext(profile)}\n\nOPDRACHT: Doe een kanaalaudit voor dit bedrijf.${goal.trim() ? ` Let daarbij extra op: ${goal.trim()}.` : ""} Onderzoek via web search waar vergelijkbare bedrijven in deze markt zichtbaar zijn en welke kanalen, platformen en evenementen er in deze branche echt bestaan. Noem alleen kanalen, media en evenementen die daadwerkelijk bestaan en relevant zijn voor deze markt; verzin geen namen.\n\nBehandel deze drie soorten expliciet met 3 tot 5 items per soort (dus maximaal 15 in totaal). Houd de teksten kort en concreet; liever weinig en scherp dan veel en vaag:\n1. ADVERTENTIEKANALEN: betaalde kanalen zoals zoekadvertenties, social advertising, display, retargeting, adverteren in vakmedia of nieuwsbrieven.\n2. PLATFORMEN EN VAKMEDIA: specifieke websites, vakbladen, communities, nieuwsbrieven, podcasts, vergelijkings- en reviewsites waar de doelgroep zit. Noem ze bij naam, bijvoorbeeld een vakportaal voor financieel verantwoordelijken.\n3. EVENEMENTEN: beurzen, congressen, seminars, roundtables en netwerkbijeenkomsten waar de doelgroep komt. Noem ze bij naam waar mogelijk.\n\nNeem de kanalen die dit bedrijf al inzet mee in je beoordeling: zeg of dat verstandig is en of er iets aan moet veranderen. Beoordeel ook de kanalen die als interessant zijn aangemerkt. Houd rekening met de omzet- en resultaatdoelstelling: adviseer geen kanalen die niet in verhouding staan tot de schaal van dit bedrijf.\n\nGeef per kanaal: de soort ("advertentie", "platform" of "event"), een geschiktheidsscore 0-100, waarom het past of niet, wat er nodig is om te starten (inspanning laag/midden/hoog), een indicatie van de kosten per jaar als je die kunt inschatten, en of dit bedrijf er al actief is (dat weet je uit de lijst hierboven).\n\nGeef ook de drie grootste kansen, en welke kanalen je juist zou laten liggen met de reden.\n\nJSON: {"summary":"managementsamenvatting","channels":[{"name":"naam","category":"advertentie|platform|event","score":0,"why":"waarom passend","needed":"wat is er nodig","cost":"indicatie per jaar","effort":"laag|midden|hoog","active":false}],"topOpportunities":[{"channel":"...","category":"advertentie|platform|event","why":"..."}],"skip":[{"channel":"...","why":"..."}],"sources":["url"],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, sources, cancelled } = await callAIFull(prompt, { json: true, useSearch: true });
      if (cancelled) { setRunning(false); return; }
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r) { setError("De kanaalaudit kon niet worden opgesteld. Probeer opnieuw."); setRunning(false); return; }
      const entry = {
        id: Date.now(), typeId: "channels", typeLabel: "Kanaalaudit",
        subject: goal.trim() || "Kanaalstrategie", date: new Date().toISOString().slice(0, 10),
        summary: r.summary || "", channels: r.channels || [], topOpportunities: r.topOpportunities || [],
        skip: r.skip || [], actions: r.actions || [], sources: sources || [],
        forYear: new Date().getFullYear() + 1,
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id); setGoal("");
    } catch { setError("De kanaalaudit kon niet worden uitgevoerd."); }
    setRunning(false);
  };
  const del = (id) => confirm("Deze kanaalaudit wordt verwijderd.", () => { saveAudits((audits || []).filter(a => a.id !== id)); setOpenId(null); });
  const effColor = (e) => /hoog/i.test(e || "") ? C.bad : /midden/i.test(e || "") ? C.yellowDark : C.good;

  if (openId) {
    const a = history.find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar de agent</button>
        <PageHead title={`Kanaalaudit · ${a.subject}`} sub={a.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>

          {a.topOpportunities?.length > 0 && <>
            <div style={sectionLabel}>Grootste kansen</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 9, marginTop: 10 }}>
              {a.topOpportunities.map((o, i) => (
                <div key={i} style={{ display: "flex", gap: 10, padding: "11px 13px", background: C.yellow + "12", borderRadius: 10 }}>
                  <span style={{ fontWeight: 700, fontSize: 13.5, color: C.yellowDark, flexShrink: 0 }}>{i + 1}.</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontWeight: 700, fontSize: 13.5 }}>{o.channel}</div>
                    <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginTop: 2 }}>{o.why}</div>
                  </div>
                  {added[o.channel]
                    ? <Tag color={C.good}>{added[o.channel]}</Tag>
                    : <button onClick={() => addToProfile(o.channel, o.why, o.category)} title="Opnemen in je bedrijfsprofiel"
                        style={{ ...miniBtn(C.paper, C.ink), flexShrink: 0, alignSelf: "flex-start" }}><Plus size={12} /> naar profiel</button>}
                </div>
              ))}
            </div>
          </>}

          {a.channels?.length > 0 && [["advertentie", "Advertentiekanalen"], ["platform", "Platformen en vakmedia"], ["event", "Evenementen"]].map(([cat, label]) => {
            const group = a.channels.filter(c => (c.category || "advertentie") === cat).sort((x, y) => (y.score || 0) - (x.score || 0));
            if (!group.length) return null;
            return (
              <div key={cat}>
                <div style={sectionLabel}>{label} <Tag color={C.muted}>{group.length}</Tag></div>
                <div style={{ marginTop: 10, overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
                  <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                    <thead><tr style={{ background: C.surface }}>
                      <th style={cmpTh}>Naam</th><th style={{ ...cmpTh, width: 110 }}>Geschikt</th>
                      <th style={cmpTh}>Waarom</th><th style={cmpTh}>Nodig</th>
                      <th style={{ ...cmpTh, width: 110 }}>Kosten p/j</th><th style={{ ...cmpTh, width: 70 }}>Actief</th><th style={{ ...cmpTh, width: 110 }}></th>
                    </tr></thead>
                    <tbody>
                      {group.map((c, i) => (
                        <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
                          <td style={{ ...cmpTd, fontWeight: 700 }}>{c.name}</td>
                          <td style={cmpTd}>
                            <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
                              <div style={{ flex: 1, height: 6, background: C.line, borderRadius: 4, minWidth: 34 }}>
                                <div style={{ width: `${c.score || 0}%`, height: "100%", borderRadius: 4, background: (c.score || 0) >= 70 ? C.good : (c.score || 0) >= 40 ? C.yellowDark : C.bad }} />
                              </div>
                              <span style={{ fontWeight: 700, fontSize: 12 }}>{c.score || 0}</span>
                            </div>
                          </td>
                          <td style={cmpTd}>{c.why}</td>
                          <td style={cmpTd}>{c.needed} {c.effort && <Tag color={effColor(c.effort)}>{c.effort}</Tag>}</td>
                          <td style={{ ...cmpTd, textAlign: "right" }}>{c.cost || "—"}</td>
                          <td style={{ ...cmpTd, textAlign: "center" }}>{c.active ? <Check size={15} style={{ color: C.good }} /> : <span style={{ color: C.muted }}>—</span>}</td>
                          <td style={{ ...cmpTd, textAlign: "center" }}>
                            {added[c.name]
                              ? <Tag color={C.good}>{added[c.name]}</Tag>
                              : <button onClick={() => addToProfile(c.name, c.why, cat, c.cost)} title="Opnemen in je bedrijfsprofiel"
                                  style={{ ...miniBtn(C.paper, C.ink) }}><Plus size={12} /> profiel</button>}
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>
            );
          })}

          {a.skip?.length > 0 && <>
            <div style={sectionLabel}>Zou ik laten liggen</div>
            <div style={{ marginTop: 10, padding: 13, background: C.bad + "0E", borderRadius: 10 }}>
              {a.skip.map((sk, i) => (
                <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 5 }}>
                  <b style={{ color: C.ink }}>{sk.channel}: </b>{sk.why}
                </div>
              ))}
            </div>
          </>}

          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Kanaalaudit" profile={profile} />
          </>}
          <SourceLinks sources={a.sources} />
        </Card>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Channel audit agent" sub="Brengt in de breedte in kaart welke kanalen passen bij je markt en doelstellingen, waar je al zit en wat je laat liggen." />
      {!profile?.filled && <div style={mirrorWarn}><AlertTriangle size={15} /> Vul eerst je bedrijfsprofiel in; daar haalt de agent je markt en doelgroep uit.</div>}
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe kanaalaudit</span></div>
        <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Waar wil je op letten? (optioneel)</label>
        <textarea value={goal} onChange={e => setGoal(e.target.value)} rows={2} placeholder="bijv. 'we willen groeien in de UK' of 'meer instroom uit het mkb'"
          style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.5, marginBottom: 12 }} />
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={run} disabled={running}>{running ? "Bezig…" : "Start kanaalaudit"}</Btn>
          <CostHint kind="search" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent onderzoekt welke kanalen in jouw markt spelen…" /></div>}
      </Card>

      <div style={sectionLabel}>Eerdere audits</div>
      {history.length === 0 && <Empty icon={Share2} title="Nog geen kanaalaudits uitgevoerd" />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.subject}</span>
                  {a.channels?.length > 0 && <Tag color={C.muted}>{a.channels.length} kanalen</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ====================== MARKETING OPS: OPERATIONS AGENT ====================== */
// Zet gekozen kansen om in een uitvoerbare planning: wat ga je doen, in welke volgorde,
// wat kost het aan tijd, en wat mis je nog aan mensen, budget of kennis.
function OperationsAgent({ ctx }) {
  const { audits, plans, savePlans, profile, users, addAction, confirm } = ctx;
  const [form, setForm] = useState({ horizon: "kwartaal", budget: "", hours: "", skills: "", picked: [] });
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const history = (plans || []).filter(p => p.kind === "operations");

  // Kansen uit de Opportunity- en Channel audit agent, zodat je die kunt doorzetten.
  const opportunities = [];
  (audits || []).forEach(a => {
    if (a.typeId === "benchmark") (a.opportunities || []).forEach(o => opportunities.push({ id: `b${a.id}_${o.title}`, label: o.title, from: `Kansen · ${a.subject}`, why: o.why || "" }));
    if (a.typeId === "channels") (a.topOpportunities || []).forEach(o => opportunities.push({ id: `c${a.id}_${o.channel}`, label: o.channel, from: `Kanaalaudit · ${a.subject}`, why: o.why || "" }));
  });
  const toggle = (id) => setForm(f => ({ ...f, picked: f.picked.includes(id) ? f.picked.filter(x => x !== id) : [...f.picked, id] }));

  const run = async () => {
    const chosen = opportunities.filter(o => form.picked.includes(o.id));
    if (!chosen.length) { setError("Kies eerst een of meer kansen om uit te werken."); return; }
    setRunning(true); setError("");
    const team = (users || []).map(u => `${u.name}${u.role ? " (" + u.role + ")" : ""}`).join(", ");
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Maak een uitvoerbare planning voor de komende ${form.horizon} op basis van de gekozen kansen. Wees realistisch: plan niet meer dan er past binnen de capaciteit.\n\nGEKOZEN KANSEN:\n${chosen.map(o => `- ${o.label} (${o.from}): ${o.why}`).join("\n")}\n\nRANDVOORWAARDEN:\n${form.budget ? `Budget: ${form.budget}\n` : ""}${form.hours ? `Beschikbare capaciteit: ${form.hours}\n` : ""}${form.skills ? `Aanwezige kennis: ${form.skills}\n` : ""}${team ? `Team: ${team}\n` : ""}\nGeef een planning in fasen met per taak: wat het is, waarom het nodig is, een tijdsindicatie en welke vaardigheid ervoor nodig is. Benoem eerlijk wat NIET past binnen deze capaciteit, en wat je daarvoor extra nodig hebt (mensen, budget, kennis of hulp van buiten).\n\nJSON: {"summary":"korte toelichting","phases":[{"title":"fase, bijv. Maand 1","tasks":[{"title":"taak","why":"waarom","hours":"tijdsindicatie","skill":"benodigde vaardigheid"}]}],"gaps":[{"need":"wat je mist","why":"waarom nodig","suggestion":"hoe op te lossen"}],"notPlanned":["wat niet past en waarom"],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setRunning(false); return; }
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r) { setError("De planning kon niet worden opgesteld."); setRunning(false); return; }
      const entry = {
        id: Date.now(), kind: "operations", title: `Planning ${form.horizon}`,
        date: new Date().toISOString().slice(0, 10), horizon: form.horizon,
        summary: r.summary || "", phases: r.phases || [], gaps: r.gaps || [],
        notPlanned: r.notPlanned || [], actions: r.actions || [],
        basedOn: chosen.map(o => o.label),
      };
      savePlans([entry, ...(plans || [])]);
      setOpenId(entry.id); setForm(f => ({ ...f, picked: [] }));
    } catch { setError("Er ging iets mis bij het opstellen."); }
    setRunning(false);
  };
  const del = (id) => confirm("Deze planning wordt verwijderd.", () => { savePlans((plans || []).filter(p => p.id !== id)); setOpenId(null); });

  if (openId) {
    const a = history.find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar de agent</button>
        <PageHead title={a.title} sub={`${a.date}${a.basedOn?.length ? " · op basis van: " + a.basedOn.join(", ") : ""}`}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>

          {(a.phases || []).map((ph, i) => (
            <div key={i} style={{ marginTop: 16, border: `1px solid ${C.line}`, borderRadius: 12, padding: 14 }}>
              <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 10 }}>{ph.title}</div>
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                {(ph.tasks || []).map((t, j) => (
                  <div key={j} style={{ display: "flex", gap: 10, alignItems: "flex-start", paddingBottom: 8, borderBottom: j < ((ph.tasks || []).length - 1) ? `1px solid ${C.line}` : "none" }}>
                    <Circle size={13} style={{ color: C.muted, flexShrink: 0, marginTop: 4 }} />
                    <div style={{ flex: 1 }}>
                      <div style={{ fontWeight: 600, fontSize: 13.5 }}>{t.title}</div>
                      <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, marginTop: 2 }}>{t.why}</div>
                    </div>
                    <div style={{ display: "flex", gap: 6, flexShrink: 0, flexWrap: "wrap", justifyContent: "flex-end" }}>
                      {t.hours && <Tag color={C.blue}>{t.hours}</Tag>}
                      {t.skill && <Tag color={C.muted}>{t.skill}</Tag>}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))}

          {a.gaps?.length > 0 && <>
            <div style={sectionLabel}>Wat je nog nodig hebt</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 9, marginTop: 10 }}>
              {a.gaps.map((g, i) => (
                <div key={i} style={{ padding: 12, background: C.yellow + "12", borderRadius: 10 }}>
                  <div style={{ fontWeight: 700, fontSize: 13.5, marginBottom: 3 }}>{g.need}</div>
                  <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{g.why}</div>
                  {g.suggestion && <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, marginTop: 4 }}><b>Oplossing: </b>{g.suggestion}</div>}
                </div>
              ))}
            </div>
          </>}

          {a.notPlanned?.length > 0 && <>
            <div style={sectionLabel}>Bewust niet ingepland</div>
            <div style={{ marginTop: 10, padding: 13, background: C.paper, borderRadius: 10 }}>
              {a.notPlanned.map((n, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 4 }}>• {n}</div>)}
            </div>
          </>}

          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Planning" profile={profile} />
          </>}
        </Card>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Operations agent" sub="Zet gekozen kansen om in een planning die past bij je budget, capaciteit en kennis — en laat zien wat je nog mist." />
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe planning</span></div>

        <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Welke kansen wil je uitwerken?</label>
        {opportunities.length === 0
          ? <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>Er zijn nog geen kansen om te kiezen. Draai eerst de Opportunity agent of de Channel audit agent; de kansen daaruit verschijnen hier zodat je ze kunt doorzetten naar een planning.</div>
          : <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 16 }}>
              {opportunities.map(o => (
                <label key={o.id} style={{ display: "flex", gap: 9, alignItems: "flex-start", padding: "9px 11px", border: `1px solid ${form.picked.includes(o.id) ? C.yellow : C.line}`, borderRadius: 9, cursor: "pointer", background: form.picked.includes(o.id) ? C.yellow + "10" : "transparent" }}>
                  <input type="checkbox" checked={form.picked.includes(o.id)} onChange={() => toggle(o.id)} style={{ marginTop: 3 }} />
                  <div>
                    <div style={{ fontSize: 13.5, fontWeight: 600 }}>{o.label}</div>
                    <div style={{ fontSize: 11.5, color: C.muted, marginTop: 1 }}>{o.from}</div>
                  </div>
                </label>
              ))}
            </div>}

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10, marginBottom: 10 }}>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Periode</label>
            <select value={form.horizon} onChange={e => setForm(f => ({ ...f, horizon: e.target.value }))}
              style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", background: C.card }}>
              <option value="maand">Komende maand</option>
              <option value="kwartaal">Komend kwartaal</option>
              <option value="halfjaar">Komend halfjaar</option>
            </select>
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Budget</label>
            <input value={form.budget} onChange={e => setForm(f => ({ ...f, budget: e.target.value }))} placeholder="bijv. € 15.000"
              style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none" }} />
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Capaciteit</label>
            <input value={form.hours} onChange={e => setForm(f => ({ ...f, hours: e.target.value }))} placeholder="bijv. 2 dagen per week"
              style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none" }} />
          </div>
        </div>
        <input value={form.skills} onChange={e => setForm(f => ({ ...f, skills: e.target.value }))} placeholder="Welke kennis is aanwezig? bijv. 'content en SEO sterk, advertising beperkt, geen video'"
          style={{ width: "100%", boxSizing: "border-box", padding: "9px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", marginBottom: 14 }} />

        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={run} disabled={running || !opportunities.length}>{running ? "Bezig…" : "Maak planning"}</Btn>
          <CostHint kind="light" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent maakt een planning die past bij je capaciteit…" /></div>}
      </Card>

      <div style={sectionLabel}>Eerdere planningen</div>
      {history.length === 0 && <Empty icon={ListTodo} title="Nog geen planning gemaakt" />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.title}</span>
                  {a.phases?.length > 0 && <Tag color={C.muted}>{a.phases.length} fasen</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ====================== MARKETING OPS: SEARCH ADS AGENT ====================== */
// Analyseert je zoekcampagnes en geeft concrete verbeterpunten. Leest de cijfers uit
// Google Ads als dat gekoppeld is; anders kun je ze zelf plakken.
function SearchAdsAgent({ ctx }) {
  const { audits, saveAudits, profile, addAction, confirm } = ctx;
  const [manual, setManual] = useState("");
  const [adsOn, setAdsOn] = useState(null);
  const [structure, setStructure] = useState(null);
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const history = (audits || []).filter(a => a.typeId === "searchads");

  useEffect(() => {
    let on = true;
    fetch("/api/google/ads/status", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (on) setAdsOn(d && d.ok === true); })
      .catch(() => { if (on) setAdsOn(false); });
    return () => { on = false; };
  }, []);

  const loadStructure = async () => {
    setError("");
    try {
      const r = await fetch("/api/google/ads/structure", { credentials: "include" });
      const d = await r.json();
      if (!r.ok) { setError(d.error || "Kon de campagnegegevens niet ophalen."); return; }
      setStructure(d);
    } catch { setError("Kon de campagnegegevens niet ophalen."); }
  };

  const run = async () => {
    const data = structure ? JSON.stringify(structure).slice(0, 12000) : manual.trim();
    if (!data) { setError("Haal eerst je campagnegegevens op, of plak ze zelf."); return; }
    setRunning(true); setError("");
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Beoordeel de zoekcampagnes hieronder en geef concrete verbeterpunten. Wees specifiek: noem campagnes, advertentiegroepen of zoekwoorden bij naam waar dat uit de gegevens blijkt. Verzin geen cijfers die er niet staan.\n\nCAMPAGNEGEGEVENS:\n${data}\n\nGeef per verbeterpunt: wat er aan de hand is, waarom het geld kost of oplevert, wat je concreet moet aanpassen, hoe zwaar dat is (laag/midden/hoog) en welke impact je mag verwachten. Sorteer op impact. Benoem ook wat er goed gaat en wat je met rust moet laten.\n\nJSON: {"summary":"managementsamenvatting","findings":[{"title":"verbeterpunt","what":"wat is er aan de hand","why":"waarom het uitmaakt","fix":"wat je concreet aanpast","effort":"laag|midden|hoog","impact":"hoog|midden|laag"}],"good":["wat er goed gaat"],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setRunning(false); return; }
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r) { setError("De analyse kon niet worden opgesteld."); setRunning(false); return; }
      const entry = {
        id: Date.now(), typeId: "searchads", typeLabel: "Search ads",
        subject: structure ? "Uit Google Ads" : "Handmatig ingevoerd",
        date: new Date().toISOString().slice(0, 10),
        summary: r.summary || "", findings: r.findings || [], good: r.good || [],
        actions: r.actions || [], sources: [],
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id);
    } catch { setError("Er ging iets mis bij de analyse."); }
    setRunning(false);
  };
  const del = (id) => confirm("Deze analyse wordt verwijderd.", () => { saveAudits((audits || []).filter(a => a.id !== id)); setOpenId(null); });
  const impColor = (v) => /hoog/i.test(v || "") ? C.good : /midden/i.test(v || "") ? C.yellowDark : C.muted;
  const effColor = (v) => /hoog/i.test(v || "") ? C.bad : /midden/i.test(v || "") ? C.yellowDark : C.good;

  if (openId) {
    const a = history.find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar de agent</button>
        <PageHead title={`Search ads · ${a.subject}`} sub={a.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>

          {a.findings?.length > 0 && <>
            <div style={sectionLabel}>Verbeterpunten</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 10 }}>
              {a.findings.map((f, i) => (
                <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 11, padding: 13 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6, flexWrap: "wrap" }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>{f.title}</span>
                    {f.impact && <Tag color={impColor(f.impact)}>impact {f.impact}</Tag>}
                    {f.effort && <Tag color={effColor(f.effort)}>inspanning {f.effort}</Tag>}
                  </div>
                  <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.55 }}>{f.what}</div>
                  {f.why && <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.55, marginTop: 4 }}>{f.why}</div>}
                  {f.fix && <div style={{ display: "flex", gap: 8, alignItems: "flex-start", marginTop: 8, padding: "9px 11px", background: C.paper, borderRadius: 9 }}>
                    <ArrowUpRight size={14} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
                    <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}><b>Aanpassen: </b>{f.fix}</div>
                  </div>}
                </div>
              ))}
            </div>
          </>}

          {a.good?.length > 0 && <>
            <div style={sectionLabel}>Dit gaat goed</div>
            <div style={{ marginTop: 10, padding: 13, background: C.good + "0E", borderRadius: 10 }}>
              {a.good.map((g, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 4, display: "flex", gap: 6 }}><Check size={13} style={{ color: C.good, flexShrink: 0, marginTop: 2 }} /> {g}</div>)}
            </div>
          </>}

          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Search ads" profile={profile} />
          </>}
        </Card>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Search ads agent" sub="Beoordeelt je zoekcampagnes en geeft concrete verbeterpunten, gesorteerd op impact." />
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe analyse</span></div>

        {adsOn === null ? <div style={{ fontSize: 13, color: C.muted, marginBottom: 12 }}>Koppeling controleren…</div>
          : adsOn ? (
            <div style={{ marginBottom: 14 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: 8 }}>
                <Tag color={C.good}>Google Ads gekoppeld</Tag>
                <Btn size="sm" variant="ghost" icon={RefreshCw} onClick={loadStructure}>Campagnegegevens ophalen</Btn>
                {structure && <span style={{ fontSize: 12.5, color: C.good, fontWeight: 600 }}>gegevens geladen</span>}
              </div>
              <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5 }}>Het ophalen van je campagnegegevens kost geen AI-tegoed; alleen de analyse erna gebruikt AI.</div>
            </div>
          ) : (
            <div style={{ marginBottom: 14 }}>
              <div style={{ display: "flex", alignItems: "flex-start", gap: 9, padding: "11px 13px", background: C.blue + "12", border: `1px solid ${C.blue}33`, borderRadius: 10, marginBottom: 10 }}>
                <AlertTriangle size={15} style={{ color: C.blue, flexShrink: 0, marginTop: 2 }} />
                <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>
                  Google Ads is niet gekoppeld, dus de agent kan je cijfers niet zelf ophalen. Koppel Google bij Instellingen → Koppelingen, of plak je gegevens hieronder.
                </div>
              </div>
              <textarea value={manual} onChange={e => setManual(e.target.value)} rows={6}
                placeholder="Plak hier je campagnegegevens: campagnenamen, zoekwoorden, vertoningen, klikken, kosten en conversies. Een export uit Google Ads werkt goed."
                style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, resize: "vertical", outline: "none", lineHeight: 1.5 }} />
            </div>
          )}

        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={run} disabled={running || (!structure && !manual.trim())}>{running ? "Bezig…" : "Analyseer campagnes"}</Btn>
          <CostHint kind="light" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent beoordeelt je campagnes…" /></div>}

        <div style={{ fontSize: 11.5, color: C.muted, marginTop: 14, lineHeight: 1.55, paddingTop: 12, borderTop: `1px solid ${C.line}` }}>
          De agent geeft advies; hij past nog niets automatisch aan in je account. Wijzigingen doorvoeren vraagt schrijfrechten op Google Ads, en dat wil je bewust en met controle inrichten.
        </div>
      </Card>

      <div style={sectionLabel}>Eerdere analyses</div>
      {history.length === 0 && <Empty icon={BarChart3} title="Nog geen analyses uitgevoerd" />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.subject}</span>
                  {a.findings?.length > 0 && <Tag color={C.muted}>{a.findings.length} punten</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ====================== REBALANCE: TRENDS ====================== */
// Wat beweegt er in je markt: ontwikkelingen, verschuivingen in vraag, nieuwe spelers.
function TrendsSection({ ctx }) {
  const { audits, saveAudits, profile, addAction, confirm } = ctx;
  const [focus, setFocus] = useState("");
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const history = (audits || []).filter(a => a.typeId === "trends");

  const run = async () => {
    setRunning(true); setError("");
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Breng met web search in kaart wat er momenteel beweegt in de markt van dit bedrijf.${focus.trim() ? ` Focus daarbij op: ${focus.trim()}.` : ""} Kijk naar recente ontwikkelingen, verschuivingen in wat klanten vragen, nieuwe of veranderende spelers, technologie en regelgeving.\n\nGeef per trend: wat het is, hoe hard het signaal is (sterk/gemiddeld/zwak), wat het voor dit bedrijf betekent, en of het een kans of een risico is. Verzin niets; onderbouw met wat je vindt.\n\nJSON: {"summary":"managementsamenvatting","trends":[{"title":"trend","what":"wat het is","strength":"sterk|gemiddeld|zwak","meaning":"wat het voor ons betekent","type":"kans|risico"}],"sources":["url"],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, sources, cancelled } = await callAIFull(prompt, { json: true, useSearch: true });
      if (cancelled) { setRunning(false); return; }
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r) { setError("De trendanalyse kon niet worden opgesteld."); setRunning(false); return; }
      const entry = {
        id: Date.now(), typeId: "trends", typeLabel: "Trends",
        subject: focus.trim() || "Marktontwikkelingen", date: new Date().toISOString().slice(0, 10),
        summary: r.summary || "", trends: r.trends || [], actions: r.actions || [], sources: sources || [],
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id); setFocus("");
    } catch { setError("De trendanalyse kon niet worden uitgevoerd."); }
    setRunning(false);
  };
  const del = (id) => confirm("Deze trendanalyse wordt verwijderd.", () => { saveAudits((audits || []).filter(a => a.id !== id)); setOpenId(null); });

  if (openId) {
    const a = history.find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar trends</button>
        <PageHead title={`Trends · ${a.subject}`} sub={a.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>
          {a.trends?.length > 0 && <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 16 }}>
            {a.trends.map((t, i) => (
              <div key={i} style={{ border: `1px solid ${C.line}`, borderLeft: `3px solid ${/risico/i.test(t.type || "") ? C.bad : C.good}`, borderRadius: 11, padding: 13 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 5, flexWrap: "wrap" }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>{t.title}</span>
                  {t.type && <Tag color={/risico/i.test(t.type) ? C.bad : C.good}>{t.type}</Tag>}
                  {t.strength && <Tag color={C.muted}>signaal {t.strength}</Tag>}
                </div>
                <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.55 }}>{t.what}</div>
                {t.meaning && <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.55, marginTop: 5 }}><b>Voor ons: </b>{t.meaning}</div>}
              </div>
            ))}
          </div>}
          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Trends" profile={profile} />
          </>}
          <SourceLinks sources={a.sources} />
        </Card>
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Trends" sub="Wat beweegt er in je markt: ontwikkelingen, verschuivende vraag en nieuwe spelers." />
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}
      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe trendanalyse</span></div>
        <input value={focus} onChange={e => setFocus(e.target.value)} placeholder="Focus (optioneel), bijv. 'AI in financiële administratie'"
          style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", marginBottom: 12 }} />
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={run} disabled={running}>{running ? "Bezig…" : "Zoek trends"}</Btn>
          <CostHint kind="search" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent zoekt wat er speelt in je markt…" /></div>}
      </Card>
      <div style={sectionLabel}>Eerdere analyses</div>
      {history.length === 0 && <Empty icon={TrendingUp} title="Nog geen trendanalyses" />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.subject}</span>
                  {a.trends?.length > 0 && <Tag color={C.muted}>{a.trends.length} trends</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ====================== REBALANCE: REBALANCE PLAN ====================== */
// Alle acties die uit de agents komen, op één plek. Vanaf hier zet je aanpassingen
// door naar je bedrijfsprofiel, zodat de strategie meebeweegt met wat je leert.
function RebalancePlan({ ctx }) {
  const { actions, saveActions, profile, saveProfile, confirm, goTo } = ctx;
  const [filter, setFilter] = useState("open");
  const [pushing, setPushing] = useState(null);
  const list = actions || [];
  const shown = list.filter(a => filter === "alle" ? true : filter === "open" ? !a.done : a.done);

  const toggle = (id) => saveActions(list.map(a => a.id === id ? { ...a, done: !a.done } : a));
  const del = (a) => confirm("Deze actie wordt verwijderd.", () => saveActions(list.filter(x => x.id !== a.id)));

  // Een actie doorzetten naar het bedrijfsprofiel: hij wordt aan een veld toegevoegd,
  // zodat de kennisbasis meegroeit met wat je onderweg besluit.
  const PROFILE_FIELDS = [
    ["proposition", "Propositie"], ["goals", "Doelstellingen"], ["usps", "USP's"],
    ["products", "Producten"], ["tone", "Tone of voice"], ["icps", "ICP's"], ["competitors", "Concurrenten"],
  ];
  const pushToProfile = (a, field) => {
    const current = String(profile?.[field] || "").trim();
    const addition = a.title + (a.text ? `: ${a.text}` : "");
    saveProfile({ ...profile, [field]: current ? `${current}\n${addition}` : addition, filled: true });
    saveActions(list.map(x => x.id === a.id ? { ...x, done: true, pushedTo: field } : x));
    setPushing(null);
  };

  const sevColor = (s) => /hoog/i.test(s || "") ? C.bad : /midden/i.test(s || "") ? C.yellowDark : C.muted;

  return (
    <div>
      <PageHead title="Rebalance plan" sub="Alle acties uit je analyses op één plek. Vink af wat gedaan is, en zet aanpassingen door naar je bedrijfsprofiel." />

      <div style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap" }}>
        {[["open", "Openstaand"], ["done", "Afgerond"], ["alle", "Alles"]].map(([id, lbl]) => (
          <button key={id} onClick={() => setFilter(id)} style={{
            padding: "7px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 700,
            border: `1px solid ${filter === id ? "transparent" : C.line}`,
            background: filter === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
            color: filter === id ? "#fff" : C.muted,
          }}>{lbl}{id === "open" ? ` (${list.filter(a => !a.done).length})` : ""}</button>
        ))}
      </div>

      {shown.length === 0
        ? <Empty icon={ListTodo} title={filter === "open" ? "Geen openstaande acties" : "Nog niets hier"}
            sub="Acties komen uit de agents: bij een analyse kun je aanbevelingen als actie overnemen." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {shown.map(a => (
              <Card key={a.id} style={{ padding: "13px 15px" }}>
                <div style={{ display: "flex", gap: 11, alignItems: "flex-start" }}>
                  <button onClick={() => toggle(a.id)} style={{ width: 20, height: 20, borderRadius: 6, border: `1.5px solid ${a.done ? C.good : C.line}`, background: a.done ? C.good : "transparent", cursor: "pointer", display: "grid", placeItems: "center", flexShrink: 0, marginTop: 2 }}>
                    {a.done && <Check size={13} style={{ color: "#fff" }} />}
                  </button>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap", marginBottom: 3 }}>
                      <span style={{ fontWeight: 700, fontSize: 14, color: a.done ? C.muted : C.ink, textDecoration: a.done ? "line-through" : "none" }}>{a.title}</span>
                      {a.severity && <Tag color={sevColor(a.severity)}>{a.severity}</Tag>}
                      {a.source && <Tag color={C.muted}>{a.source}</Tag>}
                      {a.pushedTo && <Tag color={C.blue}>in profiel</Tag>}
                    </div>
                    {a.text && <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.55 }}>{a.text}</div>}

                    {pushing === a.id && (
                      <div style={{ marginTop: 10, padding: "11px 13px", border: `1px solid ${C.line}`, borderRadius: 10 }}>
                        <div style={{ fontSize: 12.5, color: C.ink2, marginBottom: 8, lineHeight: 1.5 }}>Aan welk onderdeel van je bedrijfsprofiel wil je dit toevoegen?</div>
                        <div style={{ display: "flex", gap: 7, flexWrap: "wrap" }}>
                          {PROFILE_FIELDS.map(([f, lbl]) => (
                            <button key={f} onClick={() => pushToProfile(a, f)} style={{ ...miniBtn(C.paper, C.ink) }}>{lbl}</button>
                          ))}
                          <button onClick={() => setPushing(null)} style={{ ...miniBtn("transparent", C.muted) }}>annuleren</button>
                        </div>
                      </div>
                    )}
                  </div>
                  <div style={{ display: "flex", gap: 6, flexShrink: 0 }}>
                    {!a.pushedTo && <button onClick={() => setPushing(pushing === a.id ? null : a.id)} title="Doorzetten naar bedrijfsprofiel" style={iconBtn(C.yellowDark)}><ArrowUpRight size={16} /></button>}
                    <button onClick={() => del(a)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                  </div>
                </div>
              </Card>
            ))}
          </div>}

      <Card style={{ marginTop: 20, background: C.paper, border: "none" }}>
        <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.6 }}>
          Zet je een actie door naar je bedrijfsprofiel, dan wordt hij toegevoegd aan het onderdeel dat je kiest en hier afgevinkt. Zo werkt herijken de andere kant op: wat je leert uit monitoring en analyses landt weer in je fundament.
          <button onClick={() => goTo && goTo("profile")} style={{ background: "none", border: "none", padding: 0, marginLeft: 6, color: C.blue, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, textDecoration: "underline" }}>Naar het bedrijfsprofiel</button>
        </div>
      </Card>
    </div>
  );
}

/* ====================== MARKETING OPS: BUDGET ====================== */
// Een marketing- en mediabudget opbouwen. Drie manieren: leeg beginnen, een bestaand
// Excel- of CSV-bestand inlezen, of door AI laten opzetten op basis van je doelstellingen.
// Die laatste levert in feite een mediaplan op, want de verdeling over kanalen zit erin.
// Vat de cijfers van een jaar samen voor de AI, inclusief de uitsplitsing als die er is.
function figuresContext(profile, year) {
  const f = (profile?.figures || {})[String(year)] || {};
  const prev = (profile?.figures || {})[String(Number(year) - 1)] || {};
  const det = (profile?.figureDetail || []).filter(d => String(d.year) === String(year));
  const lines = [`DOELSTELLINGEN ${year}:`];
  if (f.revenue) lines.push(`Netto omzet: ${f.revenue}`);
  if (f.result) lines.push(`Netto resultaat: ${f.result}`);
  if (f.fte) lines.push(`Vast personeel: ${f.fte} fte`);
  if (f.note) lines.push(`Toelichting: ${f.note}`);
  if (prev.revenue) lines.push(`Ter vergelijking, ${Number(year) - 1}: omzet ${prev.revenue}${prev.result ? `, resultaat ${prev.result}` : ""}${prev.fte ? `, ${prev.fte} fte` : ""}`);
  if (det.length) {
    lines.push(`Uitsplitsing ${year}:`);
    det.forEach(d => lines.push(`- ${d.dimension === "markt" ? "Markt" : "Product"} ${d.name}: omzet ${d.revenue || "-"}, resultaat ${d.result || "-"}${d.fte ? `, ${d.fte} fte` : ""}`));
  }
  return lines.length > 1 ? lines.join("\n") : `DOELSTELLINGEN ${year}: nog niet ingevuld in het bedrijfsprofiel.`;
}

// Vat de kanalen en events samen: wat loopt er al en wat is interessant.
function channelsContext(profile) {
  const list = profile?.channels || [];
  if (!list.length) return "KANALEN: nog niets vastgelegd in het bedrijfsprofiel.";
  const line = (c) => {
    const bits = [];
    if (c.type) bits.push(c.type);
    if (c.goal) bits.push(`doel ${c.goal}`);
    if (c.cost) bits.push(`${c.cost} p/j`);
    if (c.period) bits.push(c.period);
    let t = `${c.name}${bits.length ? ` (${bits.join(", ")})` : ""}`;
    if (c.audience) t += ` — bereikt: ${String(c.audience).replace(/\n/g, "; ")}`;
    if (c.experience) t += ` — ervaring: ${String(c.experience).replace(/\n/g, "; ")}`;
    else if (c.note) t += ` — ${c.note}`;
    return t;
  };
  const by = (st) => list.filter(c => c.status === st).map(line);
  const out = ["KANALEN, PLATFORMEN EN EVENTS:"];
  const act = by("actief"); if (act.length) out.push(`Zetten we in:\n- ${act.join("\n- ")}`);
  const int = by("interessant"); if (int.length) out.push(`Interessant, nog niet actief:\n- ${int.join("\n- ")}`);
  const stp = by("gestopt"); if (stp.length) out.push(`Gestopt:\n- ${stp.join("\n- ")}`);
  const no = by("afgewezen"); if (no.length) out.push(`Bewust niet:\n- ${no.join("\n- ")}`);
  return out.join("\n");
}

const BUDGET_TYPES = ["terugkerend", "eenmalig"];
const eurFmt = (n) => "€ " + (Number(n) || 0).toLocaleString("nl-NL", { minimumFractionDigits: 0, maximumFractionDigits: 0 });

function BudgetSection({ ctx }) {
  const { profile, confirm, goTo } = ctx;
  const [rows, setRows] = useState(null);
  const [meta, setMeta] = useState({ year: new Date().getFullYear(), total: "" });
  const [view, setView] = useState("table");     // table | supplier | ledger
  const [busy, setBusy] = useState("");
  const [err, setErr] = useState("");
  const [msg, setMsg] = useState("");
  const [aiForm, setAiForm] = useState({ total: "", focus: "" });
  const [planText, setPlanText] = useState("");
  const [planBusy, setPlanBusy] = useState(false);
  const [showAi, setShowAi] = useState(false);
  const fileRef = useRef();

  useEffect(() => {
    let on = true;
    store.get("wv_budget_plan", null).then(d => {
      if (!on) return;
      if (d && Array.isArray(d.rows)) { setRows(d.rows); setMeta(d.meta || { year: new Date().getFullYear(), total: "" }); setPlanText((d.meta && d.meta.plan) || ""); }
      else setRows([]);
    }).catch(() => setRows([]));
    return () => { on = false; };
  }, []);

  const save = (nextRows, nextMeta) => {
    const r = nextRows !== undefined ? nextRows : rows;
    const m = nextMeta !== undefined ? nextMeta : meta;
    setRows(r); setMeta(m);
    store.set("wv_budget_plan", { rows: r, meta: m });
  };
  const flash = (t) => { setMsg(t); setTimeout(() => setMsg(""), 2500); };

  const addRow = () => save([...(rows || []), { id: "b" + Date.now() + Math.random().toString(36).slice(2, 5), supplier: "", ledger: "", channel: "", amount: 0, type: "terugkerend", period: "", note: "" }]);
  const updRow = (id, patch) => save((rows || []).map(r => r.id === id ? { ...r, ...patch } : r));
  const delRow = (id) => save((rows || []).filter(r => r.id !== id));

  // Bedragen: een terugkerend bedrag is per maand, een eenmalig bedrag is voor het hele jaar.
  const yearAmount = (r) => (String(r.type).toLowerCase().startsWith("terug") ? (Number(r.amount) || 0) * 12 : (Number(r.amount) || 0));
  const monthAmount = (r) => (String(r.type).toLowerCase().startsWith("terug") ? (Number(r.amount) || 0) : 0);
  const totalYear = (rows || []).reduce((s, r) => s + yearAmount(r), 0);
  const totalMonth = (rows || []).reduce((s, r) => s + monthAmount(r), 0);
  const totalOnce = (rows || []).filter(r => !String(r.type).toLowerCase().startsWith("terug")).reduce((s, r) => s + (Number(r.amount) || 0), 0);

  const groupBy = (key) => {
    const g = {};
    (rows || []).forEach(r => {
      const k = (r[key] || "—").trim() || "—";
      if (!g[k]) g[k] = { year: 0, month: 0, count: 0 };
      g[k].year += yearAmount(r); g[k].month += monthAmount(r); g[k].count++;
    });
    return Object.entries(g).sort((a, b) => b[1].year - a[1].year);
  };

  // Bestand inlezen
  const importFile = async (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    setBusy("import"); setErr("");
    try {
      const buf = await f.arrayBuffer();
      const r = await fetch("/api/budget/import?filename=" + encodeURIComponent(f.name), {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf,
      });
      const d = await r.json();
      if (!r.ok) setErr(d.error || "Inlezen mislukte.");
      else {
        const imported = (d.rows || []).map((x, i) => ({
          id: "i" + Date.now() + i, supplier: x.supplier || "", ledger: x.ledger || "", channel: x.channel || "",
          amount: Number(x.amount) || 0, type: String(x.type || "").toLowerCase().startsWith("eenmal") ? "eenmalig" : "terugkerend",
          period: x.period || "", note: x.note || "",
        }));
        save([...(rows || []), ...imported]);
        flash(`${imported.length} regels ingelezen${d.skipped ? `, ${d.skipped} overgeslagen` : ""}.`);
      }
    } catch { setErr("Inlezen mislukte."); }
    if (fileRef.current) fileRef.current.value = "";
    setBusy("");
  };

  // Door AI laten opzetten: dit levert een verdeling over kanalen op, dus een mediaplan.
  const buildWithAi = async () => {
    if (!aiForm.total.trim()) { setErr("Vul eerst een totaalbudget in."); return; }
    setBusy("ai"); setErr("");
    const prompt = `${profileContext(profile)}\n\n${figuresContext(profile, meta.year)}\n\n${channelsContext(profile)}\n\nOPDRACHT: Stel een marketing- en mediabudget op voor ${meta.year}, met een totaal van ${aiForm.total.trim()}.${aiForm.focus.trim() ? ` Let daarbij op: ${aiForm.focus.trim()}.` : ""}\n\nGebruik de doelstellingen voor ${meta.year} als vertrekpunt: het budget moet die omzet- en resultaatdoelen aannemelijk maken. Neem de kanalen die al worden ingezet mee, en beoordeel of de kanalen die als interessant zijn aangemerkt budget verdienen. Wees kritisch: als een doelstelling niet haalbaar lijkt met dit budget, zeg dat.\n\nGeef per regel: de leverancier of het platform, de grootboekrekening of kostenpost (bijvoorbeeld leadgeneratie, merkbekendheid, events, tooling, content), het kanaal, het bedrag, en of het terugkerend (bedrag PER MAAND) of eenmalig (totaalbedrag) is.\n\nZorg dat het totaal ongeveer uitkomt op het opgegeven budget (terugkerend maal twaalf plus de eenmalige posten).\n\nSchrijf daarnaast een strategische toelichting van 300 tot 500 woorden: dit is het marketingplan achter het budget. Behandel daarin de doelstellingen voor ${meta.year} en hoe dit budget daaraan bijdraagt, de keuze van kanalen en waarom, wat je bewust niet doet, welke aannames eronder liggen en welke risico's er zijn. Schrijf in gewone taal, concreet, zonder buzzwords, geschikt om aan de directie voor te leggen. Gebruik markdown met ## koppen.\n\nJSON: {"summary":"korte onderbouwing in 2-3 zinnen","plan":"de strategische toelichting in markdown","assumptions":["aanname"],"risks":["risico"],"rows":[{"supplier":"...","ledger":"...","channel":"...","amount":0,"type":"terugkerend|eenmalig","period":"...","note":"waarom deze post"}]}`;
    try {
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setBusy(""); return; }
      if (aiErr) { setErr(aiErr); setBusy(""); return; }
      if (!r || !Array.isArray(r.rows) || !r.rows.length) { setErr("Er kon geen budget worden opgesteld. Probeer opnieuw."); setBusy(""); return; }
      const made = r.rows.map((x, i) => ({
        id: "a" + Date.now() + i, supplier: x.supplier || "", ledger: x.ledger || "", channel: x.channel || "",
        amount: Number(x.amount) || 0, type: String(x.type || "").toLowerCase().startsWith("eenmal") ? "eenmalig" : "terugkerend",
        period: x.period || "", note: x.note || "",
      }));
      const nextMeta = { ...meta, total: aiForm.total.trim(), summary: r.summary || "", assumptions: r.assumptions || [], risks: r.risks || [], plan: r.plan || "" };
      setPlanText(r.plan || "");
      save(made, nextMeta);
      setShowAi(false);
      flash(`Budget opgesteld met ${made.length} posten.`);
    } catch { setErr("Er ging iets mis bij het opstellen."); }
    setBusy("");
  };

  // De toelichting los laten schrijven op basis van het budget dat er staat.
  const writePlan = async () => {
    setPlanBusy(true); setErr("");
    const overview = (rows || []).map(r => `- ${r.supplier || "?"} | ${r.ledger || "?"} | ${r.channel || "?"} | ${r.type} | ${eurFmt(r.amount)}${String(r.type).startsWith("terug") ? " p/m" : ""}`).join("\n");
    const prompt = `${profileContext(profile)}\n\n${figuresContext(profile, meta.year)}\n\n${channelsContext(profile)}\n\nOPDRACHT: Schrijf de strategische toelichting bij het onderstaande marketingbudget voor ${meta.year}. Dit is het marketingplan achter de cijfers; het gaat naar de directie.\n\nBUDGET (jaartotaal ${eurFmt(totalYear)}):\n${overview}\n\nBehandel: wat we in ${meta.year} willen bereiken en hoe dit budget daaraan bijdraagt, de keuze van kanalen en waarom, wat we bewust niet doen, de aannames eronder en de risico's. Wees kritisch: als het budget niet in verhouding staat tot de doelstellingen, benoem dat.\n\nSchrijf 300 tot 500 woorden in gewone taal, concreet, zonder buzzwords. Markdown met ## koppen.\n\nGeef ALLEEN de tekst, geen JSON.`;
    try {
      const { data: text, error: aiErr, cancelled } = await callAIFull(prompt, {});
      if (cancelled) { setPlanBusy(false); return; }
      if (aiErr) { setErr(aiErr); setPlanBusy(false); return; }
      if (text && String(text).trim()) { setPlanText(String(text)); save(undefined, { ...meta, plan: String(text) }); flash("Toelichting geschreven."); }
    } catch { setErr("Kon de toelichting niet schrijven."); }
    setPlanBusy(false);
  };

  const exportCsv = () => {
    const head = ["Leverancier", "Grootboekrekening", "Kanaal", "Type", "Bedrag", "Periode", "Per jaar", "Notitie"];
    const lines = [head.join(";")].concat((rows || []).map(r => [
      r.supplier, r.ledger, r.channel, r.type, String(r.amount).replace(".", ","), r.period,
      String(yearAmount(r)).replace(".", ","), (r.note || "").replace(/[\r\n;]+/g, " "),
    ].join(";")));
    const blob = new Blob(["\uFEFF" + lines.join("\n")], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = `mediabudget-${meta.year || ""}.csv`;
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 3000);
  };

  const clearAll = () => confirm("Alle budgetregels worden verwijderd.", () => save([], { year: meta.year, total: "" }));

  if (rows === null) return <Spinner label="Budget laden…" />;

  const inputStyle = { width: "100%", boxSizing: "border-box", padding: "6px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card };

  return (
    <div>
      <PageHead title="Budget" sub="Bouw je marketing- en mediabudget op: leeg beginnen, een bestaand bestand inlezen, of laten opzetten op basis van je doelstellingen."
        right={<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn size="sm" variant="ghost" icon={Download} onClick={exportCsv} disabled={!rows.length}>Exporteren</Btn>
          <Btn size="sm" variant="yellow" icon={Plus} onClick={addRow}>Regel</Btn>
        </div>} />

      {err && <Card style={{ marginBottom: 14, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{err}</div></Card>}
      {msg && <Card style={{ marginBottom: 14, border: `1.5px solid ${C.good}`, background: C.good + "0E" }}><div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 13.5, color: C.good, fontWeight: 600 }}><Check size={15} /> {msg}</div></Card>}

      {/* Voor welk jaar, met de doelstellingen uit het bedrijfsprofiel erbij */}
      {(() => {
        const thisYear = new Date().getFullYear();
        const years = [thisYear, thisYear + 1, thisYear + 2];
        const f = (profile?.figures || {})[String(meta.year)] || {};
        const filled = f.revenue || f.result || f.fte;
        return (
          <Card style={{ marginBottom: 18 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
              <div>
                <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Budgetjaar</label>
                <select value={meta.year} onChange={e => save(undefined, { ...meta, year: Number(e.target.value) })}
                  style={{ padding: "9px 13px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 14, fontWeight: 700, outline: "none", background: C.card }}>
                  {years.map(y => <option key={y} value={y}>{y}</option>)}
                </select>
              </div>
              <div style={{ flex: 1, minWidth: 240 }}>
                <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Doelstellingen {meta.year}</div>
                {filled ? (
                  <div style={{ display: "flex", gap: 16, flexWrap: "wrap", fontSize: 13.5 }}>
                    {f.revenue && <span style={{ color: C.ink2 }}>Omzet <b style={{ color: C.ink }}>{f.revenue}</b></span>}
                    {f.result && <span style={{ color: C.ink2 }}>Resultaat <b style={{ color: C.ink }}>{f.result}</b></span>}
                    {f.fte && <span style={{ color: C.ink2 }}>Personeel <b style={{ color: C.ink }}>{f.fte} fte</b></span>}
                  </div>
                ) : (
                  <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap" }}>
                    <span style={{ fontSize: 12.5, color: C.muted }}>Nog niet ingevuld voor {meta.year}.</span>
                    <button onClick={() => goTo && goTo("profile")} style={{ background: "none", border: "none", padding: 0, cursor: "pointer", color: C.blue, fontFamily: FONT_BODY, fontSize: 12.5, textDecoration: "underline" }}>invullen bij het bedrijfsprofiel</button>
                  </div>
                )}
              </div>
            </div>
          </Card>
        );
      })()}

      {/* Totalen */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))", gap: 12, marginBottom: 18 }}>
        {[["Per jaar", totalYear, C.yellow], ["Per maand (terugkerend)", totalMonth, C.blue], ["Eenmalig", totalOnce, C.muted], ["Aantal posten", rows.length, C.good]].map(([label, val, col], i) => (
          <Card key={i} style={{ padding: "13px 15px" }}>
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: .3, marginBottom: 5 }}>{label}</div>
            <div style={{ fontSize: 21, fontWeight: 700, color: col === C.muted ? C.ink : col }}>{i === 3 ? val : eurFmt(val)}</div>
          </Card>
        ))}
      </div>

      {/* Manieren om te beginnen */}
      {rows.length === 0 && (
        <Card style={{ marginBottom: 18 }}>
          <div style={{ fontWeight: 700, fontSize: 15.5, marginBottom: 6 }}>Hoe wil je beginnen?</div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>Je kunt op drie manieren starten. Alles is daarna handmatig aan te passen.</div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(210px, 1fr))", gap: 12 }}>
            <div style={{ border: `1px solid ${C.line}`, borderRadius: 11, padding: 14 }}>
              <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 5 }}>Leeg beginnen</div>
              <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, marginBottom: 11 }}>Voeg zelf regel voor regel je posten toe.</div>
              <Btn size="sm" variant="ghost" icon={Plus} onClick={addRow}>Eerste regel</Btn>
            </div>
            <div style={{ border: `1px solid ${C.line}`, borderRadius: 11, padding: 14 }}>
              <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 5 }}>Bestand inlezen</div>
              <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, marginBottom: 11 }}>Upload je bestaande Excel of CSV. Kolomnamen mogen afwijken; kost geen tegoed.</div>
              <label style={{ ...miniBtn(C.paper, C.ink), cursor: busy ? "default" : "pointer" }}>
                <Upload size={13} /> {busy === "import" ? "Inlezen…" : "Bestand kiezen"}
                <input ref={fileRef} type="file" accept=".xlsx,.xlsm,.csv" onChange={importFile} disabled={!!busy} style={{ display: "none" }} />
              </label>
            </div>
            <div style={{ border: `1.5px solid ${C.yellow}`, borderRadius: 11, padding: 14 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}>
                <span style={{ fontWeight: 700, fontSize: 14 }}>Laten opzetten</span><AIBadge />
              </div>
              <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, marginBottom: 11 }}>Op basis van je doelstellingen, inclusief verdeling over kanalen. Dit is in feite je mediaplan.</div>
              <Btn size="sm" variant="yellow" icon={Sparkles} onClick={() => setShowAi(true)}>Opzetten</Btn>
            </div>
          </div>
        </Card>
      )}

      {/* Strategische toelichting: dit is het marketingplan achter het budget */}
      <Card style={{ marginBottom: 18 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, marginBottom: 6, flexWrap: "wrap" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
            <Target size={17} style={{ color: C.yellowDark }} />
            <span style={{ fontWeight: 700, fontSize: 15.5 }}>Strategische toelichting</span>
            {!planText.trim() && <Tag color={C.bad}>nog niet ingevuld</Tag>}
          </div>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            <Btn size="sm" variant="ghost" icon={planBusy ? Loader2 : Sparkles} onClick={writePlan} disabled={planBusy || !rows.length}>
              {planBusy ? "Schrijven…" : planText.trim() ? "Opnieuw laten schrijven" : "Laten schrijven"}
            </Btn>
            <Btn size="sm" variant="yellow" icon={Check} onClick={() => { save(undefined, { ...meta, plan: planText }); flash("Toelichting opgeslagen."); }}>Opslaan</Btn>
          </div>
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.55 }}>
          Een budget zonder verhaal is een lijst met bedragen. Leg hier vast waarom je het geld zo verdeelt, wat je met {meta.year} wilt bereiken, wat je bewust niet doet en welke risico's er zijn. Dit is je strategische marketingplan; je kunt het zelf schrijven of laten opzetten en daarna bijschaven.
        </div>
        <textarea value={planText} onChange={e => setPlanText(e.target.value)} rows={planText ? 16 : 6}
          placeholder={"## Doelstellingen " + meta.year + "\nWat willen we bereiken en hoe draagt dit budget daaraan bij?\n\n## Keuze van kanalen\nWaarom deze kanalen, en waarom niet de andere?\n\n## Aannames en risico's\nWaar gaan we van uit, en wat kan tegenvallen?"}
          style={{ width: "100%", boxSizing: "border-box", padding: "12px 14px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.7 }} />

        {(meta.assumptions || []).length > 0 && (
          <div style={{ marginTop: 12, display: "grid", gridTemplateColumns: (meta.risks || []).length ? "1fr 1fr" : "1fr", gap: 12 }}>
            <div style={{ padding: 12, background: C.paper, borderRadius: 10 }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Aannames</div>
              {meta.assumptions.map((a, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 3 }}>• {a}</div>)}
            </div>
            {(meta.risks || []).length > 0 && (
              <div style={{ padding: 12, background: C.bad + "0E", borderRadius: 10 }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: C.bad, marginBottom: 6, textTransform: "uppercase" }}>Risico's</div>
                {meta.risks.map((a, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5, marginBottom: 3 }}>• {a}</div>)}
              </div>
            )}
          </div>
        )}
      </Card>

      {rows.length > 0 && <>
        <div style={{ display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap", alignItems: "center" }}>
          {[["table", "Alle posten"], ["ledger", "Per grootboekrekening"], ["supplier", "Per leverancier"]].map(([id, lbl]) => (
            <button key={id} onClick={() => setView(id)} style={{
              padding: "7px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 700,
              border: `1px solid ${view === id ? "transparent" : C.line}`,
              background: view === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: view === id ? "#fff" : C.muted,
            }}>{lbl}</button>
          ))}
          <div style={{ marginLeft: "auto", display: "flex", gap: 8, flexWrap: "wrap" }}>
            <label style={{ ...miniBtn(C.paper, C.ink), cursor: busy ? "default" : "pointer" }}>
              <Upload size={13} /> {busy === "import" ? "Inlezen…" : "Bestand toevoegen"}
              <input type="file" accept=".xlsx,.xlsm,.csv" onChange={importFile} disabled={!!busy} style={{ display: "none" }} />
            </label>
            <button onClick={() => setShowAi(true)} style={{ ...miniBtn(C.yellow, "#fff") }}><Sparkles size={13} /> Laten opzetten</button>
            <button onClick={clearAll} style={{ ...miniBtn("transparent", C.bad) }}><Trash2 size={13} /> Leegmaken</button>
          </div>
        </div>

        {view === "table" && (
          <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={cmpTh}>Leverancier</th><th style={cmpTh}>Grootboekrekening</th><th style={cmpTh}>Kanaal</th>
                <th style={{ ...cmpTh, width: 130 }}>Type</th><th style={{ ...cmpTh, width: 110 }}>Bedrag</th>
                <th style={{ ...cmpTh, width: 100 }}>Periode</th><th style={{ ...cmpTh, width: 110 }}>Per jaar</th><th style={{ ...cmpTh, width: 40 }}></th>
              </tr></thead>
              <tbody>
                {rows.map((r, i) => (
                  <tr key={r.id} style={{ background: i % 2 ? C.paper : C.card }}>
                    <td style={cmpTd}><input value={r.supplier} onChange={e => updRow(r.id, { supplier: e.target.value })} placeholder="bijv. Google" style={inputStyle} /></td>
                    <td style={cmpTd}><input value={r.ledger} onChange={e => updRow(r.id, { ledger: e.target.value })} placeholder="bijv. leadgeneratie" style={inputStyle} /></td>
                    <td style={cmpTd}><input value={r.channel} onChange={e => updRow(r.id, { channel: e.target.value })} placeholder="bijv. search ads" style={inputStyle} /></td>
                    <td style={cmpTd}>
                      <select value={r.type} onChange={e => updRow(r.id, { type: e.target.value })} style={inputStyle}>
                        {BUDGET_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
                      </select>
                    </td>
                    <td style={cmpTd}><input type="number" value={r.amount} onChange={e => updRow(r.id, { amount: Number(e.target.value) })} style={{ ...inputStyle, textAlign: "right" }} /></td>
                    <td style={cmpTd}><input value={r.period} onChange={e => updRow(r.id, { period: e.target.value })} placeholder="bijv. Q2" style={inputStyle} /></td>
                    <td style={{ ...cmpTd, fontWeight: 700, textAlign: "right" }}>{eurFmt(yearAmount(r))}</td>
                    <td style={{ ...cmpTd, textAlign: "center" }}><button onClick={() => delRow(r.id)} style={iconBtn(C.bad)}><Trash2 size={14} /></button></td>
                  </tr>
                ))}
                <tr style={{ background: C.surface }}>
                  <td style={{ ...cmpTd, color: "#fff", fontWeight: 700 }} colSpan={6}>Totaal per jaar</td>
                  <td style={{ ...cmpTd, color: C.yellow, fontWeight: 700, textAlign: "right" }}>{eurFmt(totalYear)}</td>
                  <td style={cmpTd}></td>
                </tr>
              </tbody>
            </table>
          </div>
        )}

        {(view === "ledger" || view === "supplier") && (
          <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={cmpTh}>{view === "ledger" ? "Grootboekrekening" : "Leverancier"}</th>
                <th style={{ ...cmpTh, width: 90 }}>Posten</th>
                <th style={{ ...cmpTh, width: 130 }}>Per maand</th>
                <th style={{ ...cmpTh, width: 130 }}>Per jaar</th>
                <th style={{ ...cmpTh, width: 110 }}>Aandeel</th>
              </tr></thead>
              <tbody>
                {groupBy(view === "ledger" ? "ledger" : "supplier").map(([k, v], i) => (
                  <tr key={k} style={{ background: i % 2 ? C.paper : C.card }}>
                    <td style={{ ...cmpTd, fontWeight: 700 }}>{k}</td>
                    <td style={cmpTd}>{v.count}</td>
                    <td style={{ ...cmpTd, textAlign: "right" }}>{eurFmt(v.month)}</td>
                    <td style={{ ...cmpTd, fontWeight: 700, textAlign: "right" }}>{eurFmt(v.year)}</td>
                    <td style={cmpTd}>
                      <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
                        <div style={{ flex: 1, height: 6, background: C.line, borderRadius: 4, minWidth: 30 }}>
                          <div style={{ width: `${totalYear ? Math.round(v.year / totalYear * 100) : 0}%`, height: "100%", borderRadius: 4, background: C.yellow }} />
                        </div>
                        <span style={{ fontSize: 12, fontWeight: 700 }}>{totalYear ? Math.round(v.year / totalYear * 100) : 0}%</span>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10, lineHeight: 1.55 }}>
          Een terugkerend bedrag geldt per maand en wordt maal twaalf gerekend voor het jaartotaal. Een eenmalig bedrag telt één keer mee.
        </div>
      </>}

      {showAi && (
        <Modal onClose={() => setShowAi(false)} title="Budget laten opzetten">
          <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.6, marginBottom: 14 }}>
            Op basis van je bedrijfsprofiel en doelstellingen wordt een verdeling over kanalen gemaakt, met per post een leverancier, grootboekrekening en bedrag. Je krijgt er een onderbouwing bij. Alles is daarna aan te passen.
          </div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Totaalbudget per jaar</label>
          <input value={aiForm.total} onChange={e => setAiForm(f => ({ ...f, total: e.target.value }))} placeholder="bijv. € 120.000"
            style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", marginBottom: 12 }} />
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Waar wil je op letten? (optioneel)</label>
          <textarea value={aiForm.focus} onChange={e => setAiForm(f => ({ ...f, focus: e.target.value }))} rows={2} placeholder="bijv. 'zwaarder op leadgeneratie in de UK' of 'minder events, meer content'"
            style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.5, marginBottom: 14 }} />
          {rows.length > 0 && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 12, lineHeight: 1.5 }}>Let op: dit vervangt je huidige {rows.length} regels.</div>}
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
            <Btn variant="yellow" icon={busy === "ai" ? Loader2 : Sparkles} onClick={buildWithAi} disabled={!!busy || !aiForm.total.trim()}>{busy === "ai" ? "Opstellen…" : "Budget opstellen"}</Btn>
            <Btn variant="ghost" onClick={() => setShowAi(false)}>Annuleren</Btn>
            <CostHint kind="light" />
          </div>
          {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{err}</div>}
        </Modal>
      )}
    </div>
  );
}

function Monitor({ ctx }) {
  const { monitors, saveMonitors, profile, companies, saveCompanies, addAction, confirm } = ctx;
  const [monTab, setMonTab] = useState("sources");
  const [activeType, setActiveType] = useState(null);
  const [openItem, setOpenItem] = useState(null);
  const [openVolId, setOpenVolId] = useState(null); // brand volume detailpagina
  const [newName, setNewName] = useState("");
  const [busyId, setBusyId] = useState(null);
  const [gscVolBusy, setGscVolBusy] = useState(null); // id van item dat GSC-volume ophaalt
  const [gscVolMsg, setGscVolMsg] = useState({});     // {id: foutmelding}

  // Haal maandelijkse vertoningen voor één merkterm uit Search Console en leg ze vast (mergen met bestaande historie).
  const fetchGscVolume = async (item, termObj) => {
    const term = (termObj.term || "").trim();
    const key = `${item.id}:${termObj.id}`;
    if (!term) { setGscVolMsg(m => ({ ...m, [key]: "Vul eerst een zoekterm in (bijv. je merknaam)." })); return; }
    setGscVolBusy(key); setGscVolMsg(m => ({ ...m, [key]: "" }));
    try {
      const r = await fetch("/api/google/gsc/term-volume", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ term, months: 16 }) });
      const d = await r.json();
      if (!r.ok) { setGscVolMsg(m => ({ ...m, [key]: d.error || "Kon het zoekvolume niet ophalen. Is Search Console gekoppeld en een site gekozen?" })); }
      else if (!d.series || d.series.length === 0) { setGscVolMsg(m => ({ ...m, [key]: "Geen vertoningen gevonden voor deze term in deze periode." })); }
      else {
        // Merge: bestaande maanden bijwerken, nieuwe toevoegen, zo bouwt de historie zich op.
        setGscVolMsg(m => ({ ...m, [key]: "" }));
        saveMonitors(monitors.map(m => {
          if (m.id !== item.id) return m;
          const terms = (m.terms || []).map(t => {
            if (t.id !== termObj.id) return t;
            const byMonth = {};
            for (const s of (t.series || [])) byMonth[s.month] = s.impressions;
            for (const s of d.series) byMonth[s.month] = s.impressions; // nieuwe data wint
            const series = Object.keys(byMonth).sort().map(month => ({ month, impressions: byMonth[month] }));
            return { ...t, term, series, lastFetch: new Date().toISOString().slice(0, 10), lastFetchMonth: new Date().toISOString().slice(0, 7) };
          });
          return { ...m, terms };
        }));
      }
    } catch { setGscVolMsg(m => ({ ...m, [key]: "Kon het zoekvolume niet ophalen." })); }
    setGscVolBusy(null);
  };

  const itemsOf = (typeId) => (monitors || []).filter(m => m.typeId === typeId);

  const addItem = (typeId, name) => {
    if (!name.trim()) return;
    saveMonitors([{ id: Date.now(), typeId, name: name.trim(), notify: true, freq: "manual", lastCheck: null, lastAuto: null, activity: [], gaps: [], sources: [] }, ...monitors]);
    setNewName("");
  };
  const toggleNotify = (id) => saveMonitors(monitors.map(m => m.id === id ? { ...m, notify: !m.notify } : m));
  const setFreq = (id, freq) => saveMonitors(monitors.map(m => m.id === id ? { ...m, freq } : m));
  const removeItem = (id) => confirm("Deze bron wordt uit je monitoring verwijderd.", () => saveMonitors(monitors.filter(m => m.id !== id)));

  const acquiredMonitored = (companies || []).filter(c => c.monitor);

  const checkActivity = async (item, isCompany) => {
    setBusyId(item.id);
    let prompt = `${profileContext(profile)}\n\n`;
    if (item.typeId === "mentions") {
      prompt += `OPDRACHT: Zoek met web search naar recente online vermeldingen (mentions) van "${item.name}". Geef per mention: wat er gezegd is, op welk platform/welke bron (met URL), het sentiment, en markeer afwijkingen t.o.v. het merkprofiel. Voeg per punt een concreet advies toe hoe ermee om te gaan.`;
    } else if (item.typeId === "competitors") {
      prompt += `OPDRACHT: Zoek met web search naar recente activiteit van concurrent "${item.name}": blogposts, nieuwe features, persberichten, social posts, positioneringsverschuivingen. Geef per item de bron (URL). Breng in kaart waar zij afwijken of overlappen met whitevision, met advies.`;
    } else {
      prompt += `OPDRACHT: Zoek met web search naar recente activiteit en merksignalen van overgenomen bedrijf "${item.name}"${item.segment ? " (" + item.segment + ")" : ""}. Beoordeel merkconsistentie t.o.v. whitevision, met bron (URL) en advies.`;
    }
    prompt += `\n\nGebruik web search; baseer je uitsluitend op wat je echt vindt. Vind je niets, geef dan lege lijsten terug.\n\nJSON: {"activity":[{"title":"...","text":"wat en waar precies","source":"bron-URL","sentiment":"positief|neutraal|negatief","advice":"hoe mee omgaan","severity":"hoog|midden|laag"}], "gaps":[{"title":"afwijking t.o.v. merkprofiel","text":"...","advice":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch: true });
      const today = new Date().toISOString().slice(0, 10);
      if (r === null) {
        // parsing/aanroep mislukt
        const update = { lastCheck: today, lastError: "De check kwam niet goed terug. Probeer het zo nog eens." };
        if (isCompany) { saveCompanies(companies.map(c => c.id === item.id ? { ...c, ...update } : c)); setOpenItem(o => ({ ...(o || item), ...update, isCompany: true })); }
        else { saveMonitors(monitors.map(m => m.id === item.id ? { ...m, ...update } : m)); setOpenItem(o => ({ ...(o || item), ...update })); }
      } else {
        const activity = r?.activity || [], gaps = r?.gaps || [];
        const update = { activity, gaps, sources: sources || [], lastCheck: today, lastError: "",
          lastEmpty: (activity.length + gaps.length) === 0 };
        if (isCompany) { saveCompanies(companies.map(c => c.id === item.id ? { ...c, ...update } : c)); setOpenItem(o => ({ ...(o || item), ...update, isCompany: true })); }
        else { saveMonitors(monitors.map(m => m.id === item.id ? { ...m, ...update } : m)); setOpenItem(o => ({ ...(o || item), ...update })); }
      }
    } catch {
      const update = { lastError: "Er ging iets mis bij het ophalen." };
      if (isCompany) saveCompanies(companies.map(c => c.id === item.id ? { ...c, ...update } : c));
      else saveMonitors(monitors.map(m => m.id === item.id ? { ...m, ...update } : m));
      setOpenItem(o => ({ ...(o || item), ...update }));
    }
    setBusyId(null);
  };

  const sevDot = (list) => {
    if (!list || !list.length) return C.muted;
    if (list.some(x => (x.severity || "").includes("hoog"))) return C.bad;
    if (list.some(x => (x.severity || "").includes("midden"))) return C.yellowDark;
    return C.good;
  };
  const freqLabel = (f) => f === "daily" ? "elke dag" : f === "weekly" ? "elke week" : "handmatig";

  // ---- DETAIL VIEW: brand volume (grafiek + zoektermen) ----
  if (openVolId) {
    const it = (monitors || []).find(m => m.id === openVolId);
    if (!it) { setOpenVolId(null); return null; }
    const terms = it.terms || [];
    const addTerm = () => {
      if (terms.length >= 3) return;
      saveMonitors(monitors.map(m => m.id === it.id ? { ...m, terms: [...(m.terms || []), { id: Date.now() + Math.floor(Math.random() * 1000), term: "", series: [] }] } : m));
    };
    const updTerm = (tid, patch) => saveMonitors(monitors.map(m => m.id === it.id ? { ...m, terms: (m.terms || []).map(t => t.id === tid ? { ...t, ...patch } : t) } : m));
    const removeTerm = (tid) => saveMonitors(monitors.map(m => m.id === it.id ? { ...m, terms: (m.terms || []).filter(t => t.id !== tid) } : m));
    return (
      <div>
        <button onClick={() => setOpenVolId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Brand volume</button>
        <PageHead title={it.name} sub="Branded zoekvolume op basis van vertoningen in Search Console. Wordt elke maand automatisch bijgewerkt en vastgelegd." />
        <Card>
          <BrandVolumeChart terms={terms} />
          <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 16 }}>
            {terms.map((t, idx) => {
              const key = `${it.id}:${t.id}`;
              const color = VOL_COLORS[idx % VOL_COLORS.length];
              const captured = (t.series || []).length;
              return (
                <div key={t.id} style={{ padding: "12px 14px", background: C.paper, borderRadius: 10 }}>
                  <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                    <span style={{ width: 11, height: 11, borderRadius: 3, background: color, flexShrink: 0 }} />
                    <input value={t.term || ""} onChange={e => updTerm(t.id, { term: e.target.value })} placeholder="zoekterm, bijv. whitevision"
                      style={{ padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, outline: "none", width: 200 }} />
                    <Btn size="sm" variant="ghost" icon={Search} onClick={() => fetchGscVolume(it, t)} disabled={gscVolBusy === key}>{gscVolBusy === key ? "Ophalen…" : "Ophalen"}</Btn>
                    <button onClick={() => removeTerm(t.id)} style={iconBtn(C.bad)}><Trash2 size={14} /></button>
                  </div>
                  {gscVolMsg[key] && <div style={{ fontSize: 11.5, color: C.bad, marginTop: 8, lineHeight: 1.4 }}>{gscVolMsg[key]}</div>}
                  {captured > 0 && !gscVolMsg[key] && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 8 }}>{captured} maanden vastgelegd{t.lastFetch ? ` · laatst opgehaald ${t.lastFetch}` : ""}.</div>}
                </div>
              );
            })}
            {terms.length < 3 && <Btn size="sm" variant="ghost" icon={Plus} onClick={addTerm}>Zoekterm toevoegen{terms.length > 0 ? ` (${terms.length}/3)` : ""}</Btn>}
            {terms.length === 0 && <div style={{ fontSize: 12.5, color: C.muted }}>Nog geen zoektermen. Voeg er één toe om het branded zoekvolume te volgen.</div>}
          </div>
          <div style={{ fontSize: 11.5, color: C.muted, marginTop: 14, lineHeight: 1.5 }}>Vertoningen op je merkterm benaderen het zoekvolume. De tool werkt dit maandelijks automatisch bij; met "Ophalen" ververs je een term meteen.</div>
        </Card>
      </div>
    );
  }

  // ---- DETAIL VIEW: activity for one monitored item ----
  if (openItem) {
    return (
      <div>
        <button onClick={() => setOpenItem(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar monitor</button>
        <PageHead title={openItem.name} sub={openItem.lastCheck ? `Laatst gecontroleerd op ${openItem.lastCheck}` : "Nog niet gecontroleerd"}
          right={<Btn variant="yellow" size="sm" icon={Sparkles} onClick={() => checkActivity(openItem, openItem.isCompany)} disabled={busyId === openItem.id}>{busyId === openItem.id ? "Bezig…" : "Nu controleren"}</Btn>} />
        {busyId === openItem.id && <Spinner label="AI haalt recente activiteit op…" />}
        {busyId !== openItem.id && (!openItem.activity || !openItem.activity.length) && (
          openItem.lastError
            ? <Empty icon={AlertTriangle} title="Ophalen lukte niet" sub={openItem.lastError} />
            : openItem.lastEmpty
              ? <Empty icon={Radar} title="Niets gevonden" sub={`Geen recente vermeldingen of activiteit gevonden via web search${openItem.lastCheck ? " op " + openItem.lastCheck : ""}. Dit kan kloppen, of de bronnen zijn beperkt. Probeer het later nog eens.`} />
              : <Empty icon={Radar} title="Nog geen activiteit opgehaald" sub="Klik op 'Nu controleren'." />
        )}
        {openItem.activity?.length > 0 && (
          <Card>
            <div style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 2, textTransform: "uppercase" }}>Recente activiteit <span style={{ fontWeight: 400, textTransform: "none" }}>— klik voor bron & aanpak</span></div>
            <Insights items={openItem.activity} onAction={addAction} prefix={"Monitor: " + openItem.name} profile={profile} />
            {openItem.gaps?.length > 0 && <>
              <div style={{ fontSize: 12.5, fontWeight: 700, color: C.bad, marginTop: 18, marginBottom: 2, textTransform: "uppercase", display: "flex", alignItems: "center", gap: 6 }}><AlertTriangle size={13} /> Afwijkingen t.o.v. merkprofiel</div>
              <Insights items={openItem.gaps} onAction={addAction} prefix={"Monitor gap: " + openItem.name} profile={profile} />
            </>}
            <SourceLinks sources={openItem.sources} />
          </Card>
        )}
      </div>
    );
  }

  // ---- TYPE VIEW: list of monitored items for a type ----
  if (activeType) {
    const type = MONITOR_TYPES.find(t => t.id === activeType);
    const isAcquired = activeType === "acquired";
    const list = isAcquired ? acquiredMonitored : itemsOf(activeType);
    return (
      <div>
        <button onClick={() => setActiveType(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar monitor</button>
        <PageHead title={type.label} sub={type.desc} />

        {!isAcquired && (
          <Card style={{ marginBottom: 14 }}>
            <div style={{ display: "flex", gap: 10 }}>
              <input value={newName} onChange={e => setNewName(e.target.value)} onKeyDown={e => e.key === "Enter" && addItem(activeType, newName)}
                placeholder={type.placeholder} style={{ flex: 1, padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
              <Btn variant="primary" icon={Plus} onClick={() => addItem(activeType, newName)}>{type.addLabel}</Btn>
            </div>
          </Card>
        )}
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 18, lineHeight: 1.5 }}>
          Stel per bron in hoe vaak automatisch wordt gecontroleerd (handmatig, dagelijks of wekelijks). De server ververst bronnen op hun frequentie; met "Check" haal je nu direct nieuwe activiteit op.
        </div>
        {isAcquired && (
          <div style={mirrorWarn}><AlertTriangle size={15} /> Bedrijven beheer en activeer je op het <b style={{ marginLeft: 4 }}>Bedrijfsprofiel</b>. Zet daar "monitoren" aan om ze hier te zien.</div>
        )}

        {list.length === 0 && <Empty icon={Radar} title={isAcquired ? "Geen overgenomen bedrijven op monitoren gezet" : `Nog geen ${type.noun}en toegevoegd`} sub={isAcquired ? "Ga naar het merkprofiel om bedrijven toe te voegen." : "Voeg er hierboven één toe om te volgen."} />}

        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {list.map(item => {
            const it = isAcquired ? { ...item, typeId: "acquired", isCompany: true } : item;
            if (activeType === "volume") {
              const terms = it.terms || [];
              const totalCaptured = terms.reduce((a, t) => a + ((t.series || []).length), 0);
              return (
                <Card key={it.id} hover style={{ padding: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "15px 16px" }}>
                    <div onClick={() => setOpenVolId(it.id)} style={{ flex: 1, minWidth: 0, cursor: "pointer", display: "flex", alignItems: "center", gap: 12 }}>
                      <BarChart3 size={18} style={{ color: C.yellowDark, flexShrink: 0 }} />
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontWeight: 700, fontSize: 15 }}>{it.name}</div>
                        <div style={{ fontSize: 12.5, color: C.muted }}>{terms.length} zoekterm{terms.length === 1 ? "" : "en"}{totalCaptured > 0 ? ` · historie vastgelegd` : " · nog geen data"}</div>
                      </div>
                    </div>
                    <ChevronRight size={18} style={{ color: C.muted, cursor: "pointer" }} onClick={() => setOpenVolId(it.id)} />
                    <button onClick={() => removeItem(it.id)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                  </div>
                </Card>
              );
            }
            const actCount = (it.activity || []).length;
            const dot = sevDot([...(it.activity || []), ...(it.gaps || [])]);
            const freq = it.freq || "manual";
            const setItemFreq = (f) => {
              if (isAcquired) saveCompanies(companies.map(c => c.id === it.id ? { ...c, freq: f } : c));
              else setFreq(it.id, f);
            };
            return (
              <Card key={it.id} hover>
                <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
                  <div onClick={() => setOpenItem(it)} style={{ display: "flex", alignItems: "center", gap: 14, flex: 1, cursor: "pointer", minWidth: 0 }}>
                    <div style={{ width: 40, height: 40, borderRadius: 10, background: C.surface, display: "grid", placeItems: "center", color: C.yellow, fontWeight: 700, fontFamily: FONT_DISPLAY }}>{it.name?.[0]?.toUpperCase()}</div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 700, fontSize: 15, display: "flex", alignItems: "center", gap: 9 }}>
                        {it.name}
                        <span style={{ width: 8, height: 8, borderRadius: 8, background: dot }} />
                      </div>
                      <div style={{ fontSize: 12.5, color: C.muted, marginTop: 2 }}>
                        {actCount ? `${actCount} activiteit${actCount > 1 ? "en" : ""}` : "Nog niet gecontroleerd"}
                        {it.lastCheck ? ` · laatst ${it.lastCheck}` : ""}
                        {it.gaps?.length ? ` · ${it.gaps.length} afwijking(en)` : ""}
                      </div>
                    </div>
                  </div>
                  <select value={freq} onChange={e => setItemFreq(e.target.value)} title="Automatisch controleren staat uit bij 'Handmatig'. Kies wekelijks of dagelijks om het aan te zetten."
                    style={{ flexShrink: 0, padding: "7px 9px", border: `1.5px solid ${freq === "manual" ? C.line : C.yellow}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: freq === "manual" ? "#fff" : C.yellow + "18", color: freq === "manual" ? C.muted : C.ink, fontWeight: freq === "manual" ? 500 : 700 }}>
                    <option value="manual">Handmatig (uit)</option>
                    <option value="weekly">Automatisch · wekelijks</option>
                    <option value="daily">Automatisch · dagelijks</option>
                  </select>
                  {!isAcquired && <Toggle on={it.notify} onChange={() => toggleNotify(it.id)} />}
                  <Btn size="sm" variant="ghost" icon={Sparkles} onClick={() => checkActivity(it, isAcquired)} disabled={busyId === it.id}>{busyId === it.id ? "…" : "Check"}</Btn>
                  <button onClick={() => setOpenItem(it)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                  {!isAcquired && <button onClick={() => removeItem(it.id)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>}
                </div>
              </Card>
            );
          })}
        </div>
      </div>
    );
  }

  // ---- OVERVIEW ----
  const counts = {
    mentions: itemsOf("mentions").length,
    volume: itemsOf("volume").length,
    competitors: itemsOf("competitors").length,
    acquired: acquiredMonitored.length,
  };
  const notifyOn = (monitors || []).filter(m => m.notify).length;
  return (
    <div>
      <PageHead title="Monitor" sub="Bewaking van merk, concurrentie en overnames. Zet per bron op wekelijks of dagelijks om automatisch te laten controleren; op 'Handmatig' gebeurt er niets vanzelf en controleer je zelf met Check." />

      <div style={{ display: "flex", gap: 8, marginBottom: 20, borderBottom: `1.5px solid ${C.line}` }}>
        {[["sources", "Bronnen"], ["aivis", "AI visibility"]].map(([id, lbl]) => (
          <button key={id} onClick={() => setMonTab(id)} style={{
            padding: "10px 16px", border: "none", background: "none", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5,
            fontWeight: monTab === id ? 700 : 500, color: monTab === id ? C.ink : C.muted,
            borderBottom: `2.5px solid ${monTab === id ? C.yellow : "transparent"}`, marginBottom: -1.5,
          }}>{lbl}</button>
        ))}
      </div>

      {monTab === "aivis" && <AIVisibility ctx={ctx} />}
      {monTab === "sources" && <>
      <div style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: C.muted, marginBottom: 18 }}>
        <div style={{ width: 8, height: 8, borderRadius: 8, background: C.good }} /> {notifyOn} bron(nen) met notificaties aan
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 14 }}>
        {MONITOR_TYPES.map(t => (
          <Card key={t.id} hover onClick={() => setActiveType(t.id)}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
              <div style={{ width: 42, height: 42, borderRadius: 11, background: C.yellow + "22", display: "grid", placeItems: "center" }}><t.icon size={21} style={{ color: C.ink }} /></div>
              <Tag color={C.ink} bg={C.line}>{counts[t.id]} gevolgd</Tag>
            </div>
            <div style={{ fontWeight: 700, fontSize: 16, marginTop: 14 }}>{t.label}</div>
            <div style={{ fontSize: 13, color: C.muted, marginTop: 4, lineHeight: 1.45 }}>{t.desc}</div>
            <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>Openen <ArrowUpRight size={15} /></div>
          </Card>
        ))}
      </div>
      </>}
    </div>
  );
}

const Toggle = ({ on, onChange, label }) => (
  <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
    {label && <span style={{ fontSize: 12, color: C.muted, fontWeight: 600 }}>{label}</span>}
    <button onClick={onChange} style={{ width: 38, height: 22, borderRadius: 20, border: "none", cursor: "pointer", background: on ? C.good : C.line, position: "relative", transition: "all .15s", padding: 0 }}>
      <span style={{ position: "absolute", top: 2, left: on ? 18 : 2, width: 18, height: 18, borderRadius: 18, background: C.card, transition: "all .15s", boxShadow: "0 1px 3px rgba(0,0,0,.2)" }} />
    </button>
  </div>
);

/* ============================== 5. ACTIES ============================== */
const COLUMNS = [
  { id: "todo", label: "Te doen", color: C.muted },
  { id: "doing", label: "Mee bezig", color: C.blue },
  { id: "done", label: "Afgerond", color: C.good },
];

function Actions({ ctx }) {
  const { actions, saveActions, confirm } = ctx;
  const [busyId, setBusyId] = useState(null);
  const [adding, setAdding] = useState(false);
  const [newTitle, setNewTitle] = useState("");

  const move = (id, status) => saveActions(actions.map(a => a.id === id ? { ...a, status } : a));
  const del = (id) => confirm("Deze actie wordt verwijderd.", () => saveActions(actions.filter(a => a.id !== id)));
  const add = () => {
    if (!newTitle.trim()) return;
    saveActions([{ id: Date.now(), title: newTitle, status: "todo", createdAt: Date.now() }, ...actions]);
    setNewTitle(""); setAdding(false);
  };

  const estimate = async (a) => {
    setBusyId(a.id);
    const r = await callAI(`Een brand manager van whitevision heeft deze actie op de lijst: "${a.title}"${a.detail ? " — context: " + a.detail : ""}.\n\nGeef een realistische tijdsindicatie en welke hulpmiddelen/tools nodig zijn.\n\nJSON: {"time":"bijv. 2-3 uur of 1 dag","tools":["tool1","tool2"],"firstStep":"de logische eerste stap in 1 zin"}`, { json: true });
    saveActions(actions.map(x => x.id === a.id ? { ...x, estimate: r } : x));
    setBusyId(null);
  };

  return (
    <div>
      <PageHead title="Acties" sub="Je openstaande merk-acties — met AI-tijdsindicatie en benodigde tools."
        right={<Btn variant="yellow" icon={Plus} onClick={() => setAdding(true)}>Actie</Btn>} />
      {adding && (
        <Card style={{ marginBottom: 18, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ display: "flex", gap: 10 }}>
            <input value={newTitle} onChange={e => setNewTitle(e.target.value)} onKeyDown={e => e.key === "Enter" && add()} autoFocus placeholder="Wat moet er gebeuren?" style={{ flex: 1, padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none" }} />
            <Btn variant="primary" onClick={add}>Toevoegen</Btn>
            <Btn variant="ghost" onClick={() => setAdding(false)}><X size={16} /></Btn>
          </div>
        </Card>
      )}
      {(!actions || actions.length === 0) && <Empty icon={ListTodo} title="Nog geen acties" sub="Markeer inzichten in onderzoek, audit of monitor als actie — of voeg er handmatig een toe." />}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }}>
        {COLUMNS.map(col => {
          const items = (actions || []).filter(a => a.status === col.id);
          return (
            <div key={col.id}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
                <div style={{ width: 9, height: 9, borderRadius: 9, background: col.color }} />
                <span style={{ fontWeight: 700, fontSize: 14 }}>{col.label}</span>
                <span style={{ fontSize: 12.5, color: C.muted }}>({items.length})</span>
              </div>
              <div style={{ display: "flex", flexDirection: "column", gap: 12, minHeight: 60 }}>
                {items.map(a => (
                  <Card key={a.id} style={{ padding: 15 }}>
                    <div style={{ fontWeight: 700, fontSize: 14.5, lineHeight: 1.4 }}>{a.title}</div>
                    {a.source && <div style={{ marginTop: 6 }}><Tag color={C.ink} bg={C.line}>{a.source}</Tag></div>}
                    {a.detail && <div style={{ fontSize: 13, color: C.muted, marginTop: 7, lineHeight: 1.45 }}>{a.detail}</div>}
                    {a.estimate && (
                      <div style={{ marginTop: 12, padding: "10px 12px", background: C.paper, borderRadius: 10, border: `1px solid ${C.line}` }}>
                        <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 13, fontWeight: 700 }}><Clock size={13} style={{ color: C.yellowDark }} /> {a.estimate.time}</div>
                        {a.estimate.tools?.length > 0 && <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: C.ink2, marginTop: 6, flexWrap: "wrap" }}><Wrench size={12} style={{ color: C.muted }} /> {a.estimate.tools.join(", ")}</div>}
                        {a.estimate.firstStep && <div style={{ fontSize: 12.5, color: C.muted, marginTop: 6, fontStyle: "italic" }}>→ {a.estimate.firstStep}</div>}
                      </div>
                    )}
                    <div style={{ display: "flex", gap: 6, marginTop: 12, flexWrap: "wrap", alignItems: "center" }}>
                      {!a.estimate && (
                        <button onClick={() => estimate(a)} disabled={busyId === a.id} style={miniBtn(C.yellow, C.ink)}>
                          {busyId === a.id ? <Loader2 size={12} style={{ animation: "spin 1s linear infinite" }} /> : <Sparkles size={12} />} {busyId === a.id ? "" : "Inschatten"}
                        </button>
                      )}
                      {COLUMNS.filter(c => c.id !== a.status).map(c => (
                        <button key={c.id} onClick={() => move(a.id, c.id)} style={miniBtn(C.line, C.ink2)}>→ {c.label}</button>
                      ))}
                      <button onClick={() => del(a.id)} style={{ ...miniBtn("transparent", C.bad), marginLeft: "auto" }}><Trash2 size={12} /></button>
                    </div>
                  </Card>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
const miniBtn = (bg, color) => ({ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 12, fontWeight: 600, color, background: bg, border: bg === "transparent" ? "none" : "none", padding: "5px 9px", borderRadius: 7, cursor: "pointer" });

/* ============================== AGENTS ============================== */
const AGENTS = [
  { id: "acquisition", label: "Acquisition agent", role: "acquisition", icon: ClipboardCheck, active: true,
    desc: "Voert een overname-audit uit: vergelijkt een overgenomen bedrijf met whitevision op propositie, markt, producten, technologie en kanalen, met een fit-score per as.",
    skills: ["Overname-audit", "Fit-score per as", "Overeenkomsten & verschillen", "Integratie-advies"] },
  { id: "migration", label: "Website migration agent", role: "migration", icon: Workflow, active: true,
    desc: "Brengt bij een overname de content van beide sites in kaart en maakt een migratieplan plus gap-analyse.",
    skills: ["Content-inventaris", "URL-matching", "Migratieplan", "Gap-analyse"] },
  { id: "benchmark", label: "Benchmark agent", role: "benchmark", icon: TrendingUp, active: true,
    desc: "Vergelijkt whitevision met concurrenten op een onderwerp dat jij kiest, bijvoorbeeld AI-functies, en laat zien welke kansen er nog liggen.",
    skills: ["Vergelijking per concurrent", "Wat zij hebben en wij niet", "Kansen en witte vlekken", "Advies per kans"] },
  { id: "copy", label: "Content Agent", role: "copy", icon: PenLine, active: true,
    desc: "Schrijft en creëert alle benodigde marketinguitingen: van onderzoek en thema's naar uitgewerkte blogs, whitepapers en social posts.",
    skills: ["Onderzoek & thema's", "Blogs & whitepapers", "Social posts", "Tone of voice"] },
];

// De Benchmark agent vergelijkt whitevision met concurrenten op een onderwerp dat je zelf
// kiest (bijvoorbeeld AI-functies) en laat zien wat zij wel hebben en wij niet.
// Onderzoekt via web search, want dit gaat over wat er nu bij anderen te zien is.
const BENCH_FOCUS = [
  "AI-functies", "Productfunctionaliteit", "Prijsstelling en pakketten",
  "Positionering en boodschap", "Content en thought leadership",
  "Integraties en koppelingen", "Klantervaring en support",
];

function BenchmarkAgent({ ctx, onBack }) {
  const { audits, saveAudits, profile, companies, addAction, confirm } = ctx;
  const [oppTab, setOppTab] = useState("bench");
  const [focus, setFocus] = useState(BENCH_FOCUS[0]);
  const [ownFocus, setOwnFocus] = useState("");
  const [picked, setPicked] = useState([]);
  const [extra, setExtra] = useState("");
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const history = (audits || []).filter(a => a.typeId === "benchmark");

  // Concurrenten uit je eigen lijst, aangevuld met wat in het profiel staat.
  const fromCompanies = (companies || []).filter(c => (c.segment || "").toLowerCase().includes("concurrent")).map(c => c.name);
  const fromProfile = String(profile?.competitors || "").split(/[,\n]/).map(x => x.trim()).filter(Boolean);
  const known = [...new Set([...fromCompanies, ...fromProfile])];
  const toggle = (n) => setPicked(p => p.includes(n) ? p.filter(x => x !== n) : [...p, n]);

  // Laat de AI concurrenten voorstellen die je zelf nog niet in beeld had.
  const [sugg, setSugg] = useState([]);
  const [suggBusy, setSuggBusy] = useState(false);
  const suggest = async () => {
    setSuggBusy(true); setError("");
    const subject = (ownFocus.trim() || focus);
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Stel met web search 5 tot 8 bedrijven voor die op het onderwerp "${subject}" concurrenten of relevante vergelijkingspartijen zijn voor whitevision. Kijk naar partijen die actief zijn in dezelfde markt en dit onderwerp aantoonbaar aanbieden.${known.length ? " We kennen deze al, noem die niet opnieuw: " + known.join(", ") + "." : ""} Noem alleen bedrijven die echt bestaan.\n\nJSON: {"companies":[{"name":"bedrijfsnaam","why":"waarom relevant op dit onderwerp"}]}`;
    try {
      const { data: r, error: aiErr } = await callAIFull(prompt, { json: true, useSearch: true, model: "light" });
      if (aiErr) { setError(aiErr); setSuggBusy(false); return; }
      setSugg((r?.companies || []).filter(c => c && c.name));
    } catch { setError("Kon geen suggesties ophalen."); }
    setSuggBusy(false);
  };

  const run = async () => {
    const subject = (ownFocus.trim() || focus);
    const list = [...picked, ...extra.split(",").map(x => x.trim()).filter(Boolean)];
    if (!list.length) { setError("Kies of noem minstens één concurrent."); return; }
    setRunning(true); setError("");
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Voer een benchmark uit op het onderwerp "${subject}". Vergelijk whitevision met deze concurrenten: ${list.join(", ")}. Onderzoek via web search wat deze partijen op dit onderwerp daadwerkelijk aanbieden (hun website, productpagina's, nieuws, releases).\n\nGeef per concurrent: wat zij op dit onderwerp hebben, hoe zij het brengen, en hoe dat zich verhoudt tot whitevision. Wees concreet en noem waar mogelijk namen van functies of pakketten; verzin niets.\n\nGeef daarnaast: wat concurrenten hebben dat whitevision mist, wat whitevision heeft dat zij missen, en de belangrijkste kansen (witte vlekken) met per kans waarom die interessant is en hoe zwaar het is om te pakken.\n\nGeef ook een positiescore 0-100: hoe sterk staat whitevision op dit onderwerp ten opzichte van deze concurrenten.\n\nJSON: {"summary":"managementsamenvatting","position":0-100,"players":[{"name":"concurrent","offer":"wat zij hebben","angle":"hoe zij het brengen","vsUs":"hoe dit zich verhoudt tot ons"}],"theyHave":["wat zij hebben en wij niet",...],"weHave":["wat wij hebben en zij niet",...],"opportunities":[{"title":"kans","why":"waarom interessant","effort":"laag|midden|hoog"}],"sources":["url",...],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, sources } = await callAIFull(prompt, { json: true, useSearch: true });
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r) { setError("De benchmark kon niet worden opgesteld. Probeer opnieuw."); setRunning(false); return; }
      const entry = {
        id: Date.now(), typeId: "benchmark", typeLabel: "Benchmark",
        subject, date: new Date().toISOString().slice(0, 10),
        summary: r.summary || "Geen samenvatting.",
        position: typeof r.position === "number" ? r.position : null,
        players: r.players || [], theyHave: r.theyHave || [], weHave: r.weHave || [],
        opportunities: r.opportunities || [], actions: r.actions || [], sources: sources || [],
        against: list,
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id); setPicked([]); setExtra("");
    } catch { setError("De benchmark kon niet worden uitgevoerd."); }
    setRunning(false);
  };

  const del = (id) => confirm("Deze benchmark wordt definitief verwijderd.", () => { saveAudits((audits || []).filter(a => a.id !== id)); setOpenId(null); });

  if (openId) {
    const a = history.find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }
    const effColor = (e) => /hoog/i.test(e) ? C.bad : /midden/i.test(e) ? C.yellowDark : C.good;
    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar de agent</button>
        <PageHead title={`Kansen · ${a.subject}`} sub={`${a.date}${a.against?.length ? " · tegen " + a.against.join(", ") : ""}`}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>

          {typeof a.position === "number" && (
            <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 16, padding: "14px 16px", background: C.surface, borderRadius: 12 }}>
              <div style={{ position: "relative", width: 54, height: 54, flexShrink: 0 }}>
                <svg width="54" height="54" style={{ transform: "rotate(-90deg)" }}>
                  <circle cx="27" cy="27" r="22" fill="none" stroke="#ffffff22" strokeWidth="6" />
                  <circle cx="27" cy="27" r="22" fill="none" stroke={a.position >= 70 ? C.good : a.position >= 40 ? C.yellow : C.bad} strokeWidth="6" strokeDasharray={`${(a.position / 100) * 138} 138`} strokeLinecap="round" />
                </svg>
                <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", fontSize: 14, fontWeight: 700, color: "#fff" }}>{a.position}%</div>
              </div>
              <div>
                <div style={{ color: "#fff", fontWeight: 700, fontSize: 15 }}>Positie op {a.subject.toLowerCase()}</div>
                <div style={{ color: "#9CA3AF", fontSize: 12.5, marginTop: 2 }}>Hoe sterk we staan ten opzichte van de vergeleken partijen.</div>
              </div>
            </div>
          )}

          {a.players?.length > 0 && <>
            <div style={sectionLabel}>Per concurrent</div>
            <div style={{ marginTop: 10, overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead><tr style={{ background: C.surface }}>
                  <th style={cmpTh}>Partij</th><th style={cmpTh}>Wat ze hebben</th><th style={cmpTh}>Hoe ze het brengen</th><th style={cmpTh}>Versus ons</th>
                </tr></thead>
                <tbody>
                  {a.players.map((p, i) => (
                    <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
                      <td style={{ ...cmpTd, fontWeight: 700 }}>{p.name}</td>
                      <td style={cmpTd}>{p.offer}</td>
                      <td style={cmpTd}>{p.angle}</td>
                      <td style={cmpTd}>{p.vsUs}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </>}

          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 16 }}>
            <div style={{ padding: 13, background: C.bad + "0E", borderRadius: 10 }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, color: C.bad, marginBottom: 7, textTransform: "uppercase" }}>Zij wel, wij niet</div>
              {(a.theyHave || []).length ? a.theyHave.map((t, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 5, display: "flex", gap: 6 }}><span style={{ color: C.bad }}>•</span> {t}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
            </div>
            <div style={{ padding: 13, background: C.good + "0E", borderRadius: 10 }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, color: C.good, marginBottom: 7, textTransform: "uppercase" }}>Wij wel, zij niet</div>
              {(a.weHave || []).length ? a.weHave.map((t, i) => <div key={i} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 5, display: "flex", gap: 6 }}><span style={{ color: C.good }}>•</span> {t}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
            </div>
          </div>

          {a.opportunities?.length > 0 && <>
            <div style={sectionLabel}>Kansen</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 10 }}>
              {a.opportunities.map((o, i) => (
                <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 11, padding: 13 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 5, flexWrap: "wrap" }}>
                    <span style={{ fontWeight: 700, fontSize: 14 }}>{o.title}</span>
                    {o.effort && <Tag color={effColor(o.effort)}>inspanning {o.effort}</Tag>}
                  </div>
                  <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{o.why}</div>
                </div>
              ))}
            </div>
          </>}

          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Kansen" profile={profile} />
          </>}
          <SourceLinks sources={a.sources} />
        </Card>
      </div>
    );
  }

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Opportunity agent" sub="Waar liggen je kansen: tegenover concurrenten, en in kanalen en events. Kansen zet je door naar je bedrijfsprofiel, je budget of de Operations agent." />

      <div style={{ display: "flex", gap: 8, marginBottom: 22, borderBottom: `1.5px solid ${C.line}` }}>
        {[["bench", "Tegenover concurrenten"], ["chan", "Kanalen en events"], ["acq", "Acquisities"]].map(([id, lbl]) => (
          <button key={id} onClick={() => setOppTab(id)} style={{
            padding: "10px 16px", border: "none", background: "none", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5,
            fontWeight: oppTab === id ? 700 : 500, color: oppTab === id ? C.ink : C.muted,
            borderBottom: `2.5px solid ${oppTab === id ? C.yellow : "transparent"}`, marginBottom: -1.5,
          }}>{lbl}</button>
        ))}
      </div>

      {oppTab === "chan" && <ChannelAuditAgent ctx={ctx} />}
      {oppTab === "acq" && <AcquisitionAgent ctx={ctx} />}
      {oppTab === "bench" && <>
      {!profile?.filled && <div style={mirrorWarn}><AlertTriangle size={15} /> Vul eerst je bedrijfsprofiel in voor een scherpere vergelijking.</div>}
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 14 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuwe kansenanalyse</span></div>

        <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Waarop wil je vergelijken?</label>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 8 }}>
          {BENCH_FOCUS.map(f => (
            <button key={f} onClick={() => { setFocus(f); setOwnFocus(""); }} style={{
              padding: "7px 13px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 600,
              border: `1px solid ${!ownFocus && focus === f ? "transparent" : C.line}`,
              background: !ownFocus && focus === f ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: !ownFocus && focus === f ? "#fff" : C.muted,
            }}>{f}</button>
          ))}
        </div>
        <input value={ownFocus} onChange={e => setOwnFocus(e.target.value)} placeholder="of typ een eigen onderwerp, bijv. 'automatische boekingsregels'"
          style={{ width: "100%", boxSizing: "border-box", padding: "9px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", marginBottom: 18 }} />

        <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Met wie vergelijken?</label>
        {known.length > 0 && (
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 8 }}>
            {known.map(n => (
              <button key={n} onClick={() => toggle(n)} style={{
                padding: "7px 13px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 600,
                border: `1px solid ${picked.includes(n) ? "transparent" : C.line}`,
                background: picked.includes(n) ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                color: picked.includes(n) ? "#fff" : C.muted,
              }}>{n}</button>
            ))}
          </div>
        )}
        <input value={extra} onChange={e => setExtra(e.target.value)} placeholder="extra partijen, komma-gescheiden"
          style={{ width: "100%", boxSizing: "border-box", padding: "9px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", marginBottom: 10 }} />

        <div style={{ marginBottom: 16 }}>
          <button onClick={suggest} disabled={suggBusy} style={{ display: "inline-flex", alignItems: "center", gap: 6, background: "none", border: "none", padding: 0, cursor: suggBusy ? "default" : "pointer", color: C.blue, fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 600 }}>
            {suggBusy ? <Loader2 size={13} className="spin" /> : <Sparkles size={13} />} {suggBusy ? "Zoeken naar partijen…" : "Laat AI concurrenten voorstellen"}
          </button>
          {sugg.length > 0 && (
            <div style={{ marginTop: 10, border: `1px solid ${C.line}`, borderRadius: 10, padding: "10px 12px" }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>Voorgestelde partijen — klik om toe te voegen</div>
              <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
                {sugg.map((c, i) => (
                  <div key={i} style={{ display: "flex", alignItems: "flex-start", gap: 9 }}>
                    <button onClick={() => { toggle(c.name); setSugg(s => s.filter((_, j) => j !== i)); }}
                      style={{ ...miniBtn(C.yellow, "#fff"), flexShrink: 0 }}><Plus size={12} /> {c.name}</button>
                    <span style={{ fontSize: 12, color: C.muted, lineHeight: 1.45, paddingTop: 4 }}>{c.why}</span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>

        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={run} disabled={running}>{running ? "Bezig…" : "Zoek kansen"}</Btn>
          <CostHint kind="search" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent onderzoekt de concurrenten en vergelijkt ze met je profiel…" /></div>}
      </Card>

      <div style={sectionLabel}>Eerdere analyses</div>
      {history.length === 0 && <Empty icon={TrendingUp} title="Nog geen analyses uitgevoerd" />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.subject}</span>
                  {typeof a.position === "number" && <Tag color={a.position >= 70 ? C.good : a.position >= 40 ? C.yellowDark : C.bad}>{a.position}%</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
      </>}
    </div>
  );
}

// De Acquisition agent voert de overname-audit uit: bedrijf kiezen, AI-audit met web search,
// scoretabel + details per as. Resultaten worden bewaard in de gedeelde audits-opslag.
function AcquisitionAgent({ ctx, onBack }) {
  const { audits, saveAudits, profile, companies, saveCompanies, addAction, confirm } = ctx;
  const [selectedCo, setSelectedCo] = useState("");
  const [running, setRunning] = useState(false);
  const [openId, setOpenId] = useState(null);
  const [error, setError] = useState("");
  const [mode, setMode] = useState("audit");   // "audit" = bestaand bedrijf, "find" = kandidaten zoeken
  const history = (audits || []).filter(a => a.typeId === "acquisition" || a.typeId === "acqfind");

  // ---- Kandidaten zoeken ----
  // Zoekt bedrijven die de propositie zouden aanvullen. Gebruikt web search, want dit
  // gaat over partijen die nu bestaan.
  const [findForm, setFindForm] = useState({ focus: "", region: "" });
  const [addedCo, setAddedCo] = useState({});
  const findCandidates = async () => {
    setRunning(true); setError("");
    const nextYear = new Date().getFullYear() + 1;
    const known = (companies || []).map(c => c.name).filter(Boolean);
    const prompt = `${profileContext(profile)}\n\n${figuresContext(profile, nextYear)}\n\nOPDRACHT: Zoek met web search 5 tot 8 bedrijven die interessant kunnen zijn om over te nemen.${findForm.focus.trim() ? ` Let daarbij op: ${findForm.focus.trim()}.` : ""}${findForm.region.trim() ? ` Richt je op deze regio of markt: ${findForm.region.trim()}.` : ""}\n\nZoek partijen die de propositie aanvullen of versterken: een product dat wij missen, toegang tot een markt of doelgroep die wij niet hebben, of technologie die ons verder helpt. Kijk naar de schaal van ons bedrijf: stel geen partijen voor die veel te groot zijn.${known.length ? ` We kennen deze al, noem die niet opnieuw: ${known.join(", ")}.` : ""}\n\nNoem alleen bedrijven die echt bestaan en die je kunt onderbouwen met wat je vindt. Verzin geen namen en geen omzetcijfers; laat een veld leeg als je het niet weet.\n\nGeef per bedrijf: naam, wat ze doen, waarom ze passen, wat het ons zou brengen, een geschatte fit 0-100, wat de risico's van zo'n overname zijn, en land of regio.\n\nJSON: {"summary":"managementsamenvatting","candidates":[{"name":"...","whatTheyDo":"...","why":"waarom passend","brings":"wat het ons brengt","fit":0,"risks":"risico's","country":"..."}],"sources":["url"],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, error: aiErr, sources, cancelled } = await callAIFull(prompt, { json: true, useSearch: true });
      if (cancelled) { setRunning(false); return; }
      if (aiErr) { setError(aiErr); setRunning(false); return; }
      if (!r || !Array.isArray(r.candidates) || !r.candidates.length) { setError("Er konden geen kandidaten worden gevonden. Probeer een andere focus."); setRunning(false); return; }
      const entry = {
        id: Date.now(), typeId: "acqfind", typeLabel: "Overnamekandidaten",
        subject: findForm.focus.trim() || "Kandidaten", date: new Date().toISOString().slice(0, 10),
        summary: r.summary || "", candidates: r.candidates, actions: r.actions || [], sources: sources || [],
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id);
    } catch { setError("Het zoeken naar kandidaten is niet gelukt."); }
    setRunning(false);
  };

  // Een kandidaat opnemen in je lijst met bedrijven, zodat je hem kunt monitoren of later auditen.
  const addCandidate = (c) => {
    const list = companies || [];
    if (list.some(x => (x.name || "").toLowerCase() === String(c.name).toLowerCase())) { setAddedCo(a => ({ ...a, [c.name]: "bestond al" })); return; }
    saveCompanies([...list, {
      id: Date.now() + Math.random(), name: c.name, segment: "Kandidaat", country: c.country || "",
      monitor: false, status: "standalone", whatTheyDo: c.whatTheyDo || "",
      usps: c.brings || "", notes: [c.why, c.risks ? `Risico's: ${c.risks}` : ""].filter(Boolean).join("\n"),
    }]);
    setAddedCo(a => ({ ...a, [c.name]: "toegevoegd" }));
  };

  const run = async () => {
    const co = companies.find(c => String(c.id) === String(selectedCo));
    if (!co) return;
    setRunning(true); setError("");
    const coDetail = [
      co.whatTheyDo ? `Wat ze doen: ${co.whatTheyDo}` : "",
      co.audience ? `Doelgroep: ${co.audience}` : "",
      co.usps ? `USP's: ${co.usps}` : "",
      co.markets ? `Markten: ${co.markets}` : "",
      co.techstack ? `Tech stack: ${co.techstack}` : "",
      co.segment ? `Segment: ${co.segment}` : "",
      co.country ? `Land: ${co.country}` : "",
      co.notes ? `Notities: ${co.notes}` : "",
    ].filter(Boolean).join("\n");
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Voer een overname-audit uit waarin je het overgenomen bedrijf "${co.name}" vergelijkt met whitevision. Combineer de hieronder vastgelegde gegevens met eigen onderzoek via web search (hun website, nieuws, vacatures, reviews) om de gaten aan te vullen.\n\nWAT WE AL OVER DIT BEDRIJF WETEN:\n${coDetail || "(nog weinig vastgelegd; onderzoek zelf)"}\n\nBeoordeel het bedrijf op precies deze vijf assen: propositie, markt, producten, technologie, kanalen. Geef per as: een fit-score 0-100 (hoe dicht staan ze bij whitevision / hoe soepel is de integratie op die as), een korte typering van whitevision op die as, een korte typering van het bedrijf op die as, de belangrijkste overeenkomsten, de belangrijkste verschillen, en wat dit betekent voor de integratie. Wees concreet en onderbouw met bronnen waar mogelijk.\n\nGeef ook een totale integratie-fitscore (0-100) en een korte managementsamenvatting.\n\nJSON: {"summary":"managementsamenvatting","overallFit":0-100,"axes":[{"key":"propositie|markt|producten|technologie|kanalen","score":0-100,"us":"whitevision op deze as","them":"het bedrijf op deze as","similarities":["overeenkomst",...],"differences":["verschil",...],"integration":"wat dit betekent voor de integratie"}],"sources":["url",...],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    try {
      const { data: r, sources } = await callAIFull(prompt, { json: true, useSearch: true });
      const entry = {
        id: Date.now(), typeId: "acquisition", typeLabel: "Overname-audit",
        subject: co.name, date: new Date().toISOString().slice(0, 10),
        summary: r?.summary || "Geen samenvatting.",
        axes: r?.axes || [], overallFit: typeof r?.overallFit === "number" ? r.overallFit : null,
        actions: r?.actions || [], sources: sources || [], compareName: co.name,
      };
      saveAudits([entry, ...(audits || [])]);
      setOpenId(entry.id); setSelectedCo("");
    } catch (e) {
      setError("De audit kon niet worden uitgevoerd. Als je AI-tegoed op is, vul het aan en probeer opnieuw.");
    }
    setRunning(false);
  };

  const del = (id) => confirm("Deze audit wordt definitief verwijderd.", () => { saveAudits((audits || []).filter(a => a.id !== id)); setOpenId(null); });

  if (openId) {
    const a = history.find(x => x.id === openId) || (audits || []).find(x => x.id === openId);
    if (!a) { setOpenId(null); return null; }

    // Kandidatenlijst heeft een eigen weergave.
    if (a.typeId === "acqfind") {
      const fitColor = (f) => (f || 0) >= 70 ? C.good : (f || 0) >= 40 ? C.yellowDark : C.bad;
      return (
        <div>
          <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug</button>
          <PageHead title={`Overnamekandidaten · ${a.subject}`} sub={a.date}
            right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
          <Card>
            <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>
            <div style={sectionLabel}>Kandidaten</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 11, marginTop: 10 }}>
              {[...(a.candidates || [])].sort((x, y) => (y.fit || 0) - (x.fit || 0)).map((c, i) => (
                <div key={i} style={{ border: `1px solid ${C.line}`, borderLeft: `3px solid ${fitColor(c.fit)}`, borderRadius: 11, padding: 14 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6, flexWrap: "wrap" }}>
                    <span style={{ fontWeight: 700, fontSize: 15 }}>{c.name}</span>
                    {typeof c.fit === "number" && <Tag color={fitColor(c.fit)}>{c.fit}% fit</Tag>}
                    {c.country && <Tag color={C.muted}>{c.country}</Tag>}
                    <div style={{ marginLeft: "auto" }}>
                      {addedCo[c.name]
                        ? <Tag color={C.good}>{addedCo[c.name]}</Tag>
                        : <button onClick={() => addCandidate(c)} title="Toevoegen aan je bedrijvenlijst" style={{ ...miniBtn(C.paper, C.ink) }}><Plus size={12} /> naar bedrijven</button>}
                    </div>
                  </div>
                  {c.whatTheyDo && <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.55, marginBottom: 6 }}>{c.whatTheyDo}</div>}
                  <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
                    {c.why && <div style={{ padding: 10, background: C.good + "0E", borderRadius: 9 }}>
                      <div style={{ fontSize: 11, fontWeight: 700, color: C.good, marginBottom: 3, textTransform: "uppercase" }}>Waarom passend</div>
                      <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{c.why}</div>
                    </div>}
                    {c.brings && <div style={{ padding: 10, background: C.yellow + "12", borderRadius: 9 }}>
                      <div style={{ fontSize: 11, fontWeight: 700, color: C.yellowDark, marginBottom: 3, textTransform: "uppercase" }}>Wat het ons brengt</div>
                      <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{c.brings}</div>
                    </div>}
                  </div>
                  {c.risks && <div style={{ marginTop: 8, padding: 10, background: C.bad + "0E", borderRadius: 9 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: C.bad, marginBottom: 3, textTransform: "uppercase" }}>Risico's</div>
                    <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{c.risks}</div>
                  </div>}
                </div>
              ))}
            </div>
            {a.actions?.length > 0 && <>
              <div style={sectionLabel}>Aanbevolen acties</div>
              <Insights items={a.actions} onAction={addAction} prefix="Overnamekandidaten" profile={profile} />
            </>}
            <SourceLinks sources={a.sources} />
            <div style={{ fontSize: 11.5, color: C.muted, marginTop: 14, lineHeight: 1.55, paddingTop: 12, borderTop: `1px solid ${C.line}` }}>
              Dit zijn suggesties op basis van openbaar vindbare informatie. Controleer altijd zelf de cijfers en de situatie van een partij voordat je stappen zet.
            </div>
          </Card>
        </div>
      );
    }

    return (
      <div>
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar de agent</button>
        <PageHead title={`Overname-audit · ${a.subject}`} sub={a.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => del(a.id)}>Verwijderen</Btn>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2 }}>{a.summary}</div>
          {typeof a.overallFit === "number" && (
            <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 16, padding: "14px 16px", background: C.surface, borderRadius: 12 }}>
              <div style={{ position: "relative", width: 54, height: 54, flexShrink: 0 }}>
                <svg width="54" height="54" style={{ transform: "rotate(-90deg)" }}>
                  <circle cx="27" cy="27" r="22" fill="none" stroke="#ffffff22" strokeWidth="6" />
                  <circle cx="27" cy="27" r="22" fill="none" stroke={a.overallFit >= 70 ? C.good : a.overallFit >= 40 ? C.yellow : C.bad} strokeWidth="6" strokeDasharray={`${(a.overallFit / 100) * 138} 138`} strokeLinecap="round" />
                </svg>
                <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", fontSize: 14, fontWeight: 700, color: "#fff" }}>{a.overallFit}%</div>
              </div>
              <div>
                <div style={{ color: "#fff", fontWeight: 700, fontSize: 15 }}>Integratie-fit met whitevision</div>
                <div style={{ color: "#9CA3AF", fontSize: 12.5, marginTop: 2 }}>Gemiddelde fit over de vijf assen; hoger betekent soepeler samen te voegen.</div>
              </div>
            </div>
          )}
          {a.axes?.length > 0 && <>
            <div style={sectionLabel}>Scores per as</div>
            <div style={{ marginTop: 10, overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 12 }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                <thead>
                  <tr style={{ background: C.surface }}>
                    <th style={cmpTh}>As</th>
                    <th style={{ ...cmpTh, color: C.yellow }}>whitevision</th>
                    <th style={cmpTh}>{a.compareName || a.subject}</th>
                    <th style={{ ...cmpTh, width: 120 }}>Fit</th>
                  </tr>
                </thead>
                <tbody>
                  {a.axes.map((ax, i) => {
                    const lbl = (ACQ_AXES.find(x => x.key === ax.key) || {}).label || ax.key;
                    const sc = typeof ax.score === "number" ? ax.score : 0;
                    return (
                      <tr key={i} style={{ background: i % 2 ? C.paper : C.card }}>
                        <td style={{ ...cmpTd, fontWeight: 700 }}>{lbl}</td>
                        <td style={cmpTd}>{ax.us}</td>
                        <td style={cmpTd}>{ax.them}</td>
                        <td style={cmpTd}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                            <div style={{ flex: 1, height: 7, background: C.line, borderRadius: 4, overflow: "hidden", minWidth: 44 }}>
                              <div style={{ width: `${sc}%`, height: "100%", background: sc >= 70 ? C.good : sc >= 40 ? C.yellowDark : C.bad }} />
                            </div>
                            <span style={{ fontWeight: 700, fontSize: 12.5 }}>{sc}%</span>
                          </div>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
            <div style={sectionLabel}>Details per as</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 10 }}>
              {a.axes.map((ax, i) => {
                const lbl = (ACQ_AXES.find(x => x.key === ax.key) || {}).label || ax.key;
                return (
                  <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 12, padding: 15 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
                      <span style={{ fontWeight: 700, fontSize: 15 }}>{lbl}</span>
                      <Tag color={ax.score >= 70 ? C.good : ax.score >= 40 ? C.yellowDark : C.bad}>{typeof ax.score === "number" ? ax.score + "% fit" : "—"}</Tag>
                    </div>
                    <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 10 }}>
                      <div style={{ padding: 11, background: C.good + "0E", borderRadius: 10 }}>
                        <div style={{ fontSize: 11.5, fontWeight: 700, color: C.good, marginBottom: 6, textTransform: "uppercase" }}>Overeenkomsten</div>
                        {(ax.similarities || []).length ? (ax.similarities || []).map((s, j) => <div key={j} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 4, display: "flex", gap: 6 }}><span style={{ color: C.good }}>•</span> {s}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
                      </div>
                      <div style={{ padding: 11, background: C.bad + "0E", borderRadius: 10 }}>
                        <div style={{ fontSize: 11.5, fontWeight: 700, color: C.bad, marginBottom: 6, textTransform: "uppercase" }}>Verschillen</div>
                        {(ax.differences || []).length ? (ax.differences || []).map((s, j) => <div key={j} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.45, marginBottom: 4, display: "flex", gap: 6 }}><span style={{ color: C.bad }}>•</span> {s}</div>) : <div style={{ fontSize: 12, color: C.muted }}>—</div>}
                      </div>
                    </div>
                    {ax.integration && <div style={{ display: "flex", gap: 8, alignItems: "flex-start", padding: "10px 12px", background: C.paper, borderRadius: 10 }}>
                      <ArrowUpRight size={15} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
                      <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}><span style={{ fontWeight: 700 }}>Voor de integratie: </span>{ax.integration}</div>
                    </div>}
                  </div>
                );
              })}
            </div>
          </>}
          {a.actions?.length > 0 && <>
            <div style={sectionLabel}>Aanbevolen acties</div>
            <Insights items={a.actions} onAction={addAction} prefix="Overname-audit" profile={profile} />
          </>}
          <SourceLinks sources={a.sources} />
        </Card>
      </div>
    );
  }

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Acquisitie-kansen" sub="Analyseer een overgenomen of beoogd bedrijf, of laat kandidaten zoeken die je propositie aanvullen." />
      {!profile?.filled && <div style={mirrorWarn}><AlertTriangle size={15} /> Vul eerst je merkprofiel in voor een scherpere vergelijking.</div>}
      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Acquisitie-kansen</span></div>

        <div style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap" }}>
          {[["audit", "Bedrijf analyseren"], ["find", "Kandidaten zoeken"]].map(([id, lbl]) => (
            <button key={id} onClick={() => { setMode(id); setError(""); }} style={{
              padding: "8px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, fontWeight: 700,
              border: `1px solid ${mode === id ? "transparent" : C.line}`,
              background: mode === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: mode === id ? "#fff" : C.muted,
            }}>{lbl}</button>
          ))}
        </div>

        {mode === "find" ? <>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.55 }}>
            Zoekt bedrijven die je propositie zouden aanvullen: een product dat je mist, toegang tot een markt of doelgroep, of technologie die je verder helpt. Je krijgt per kandidaat een fit-score, wat het je brengt en de risico's. Kandidaten kun je direct opnemen in je bedrijvenlijst.
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 10, marginBottom: 14 }}>
            <div>
              <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Waar zoek je naar? (optioneel)</label>
              <input value={findForm.focus} onChange={e => setFindForm(f => ({ ...f, focus: e.target.value }))}
                placeholder="bijv. 'contractmanagement' of 'toegang tot de UK'"
                style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
            </div>
            <div>
              <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Regio of markt</label>
              <input value={findForm.region} onChange={e => setFindForm(f => ({ ...f, region: e.target.value }))} placeholder="bijv. Benelux"
                style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
            </div>
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Btn variant="yellow" icon={running ? Loader2 : Sparkles} onClick={findCandidates} disabled={running}>{running ? "Bezig…" : "Zoek kandidaten"}</Btn>
            <CostHint kind="search" />
          </div>
          {running && <div style={{ marginTop: 12 }}><Spinner label="De agent zoekt partijen die je propositie aanvullen…" /></div>}
        </> : <>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.55 }}>
          Vergelijkt een bedrijf uit je lijst met whitevision op propositie, markt, producten, technologie en kanalen, met een fit-score per as en wat het betekent voor de integratie.
        </div>
        <div style={{ marginBottom: 16 }}>
          <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Kies een bedrijf</label>
          {companies.length === 0
            ? <div style={{ fontSize: 13.5, color: C.muted }}>Voeg eerst overgenomen bedrijven toe op het merkprofiel.</div>
            : <select value={selectedCo} onChange={e => setSelectedCo(e.target.value)} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
                <option value="">— Selecteer —</option>
                {companies.map(c => <option key={c.id} value={c.id}>{c.name}{c.segment ? ` (${c.segment})` : ""}</option>)}
              </select>}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <Btn variant="yellow" icon={Sparkles} onClick={run} disabled={running || !selectedCo}>{running ? "Bezig…" : "Start overname-audit"}</Btn>
          <CostHint kind="search" />
        </div>
        {running && <div style={{ marginTop: 12 }}><Spinner label="De agent onderzoekt het bedrijf en vergelijkt het met je merkprofiel…" /></div>}
        </>}
      </Card>

      <div style={sectionLabel}>Eerder uitgevoerd</div>
      {history.length === 0 && <Empty icon={ClipboardCheck} title="Nog niets uitgevoerd" sub="Analyseer een bedrijf uit je lijst, of laat kandidaten zoeken." />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {history.map(a => (
          <Card key={a.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenId(a.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3, flexWrap: "wrap" }}>
                  <Tag color={C.ink} bg={C.line}>{a.date}</Tag>
                  <span style={{ fontSize: 13, fontWeight: 700 }}>{a.subject}</span>
                  <Tag color={C.muted}>{a.typeId === "acqfind" ? "kandidaten" : "analyse"}</Tag>
                  {a.typeId === "acqfind" && a.candidates?.length > 0 && <Tag color={C.blue}>{a.candidates.length} partijen</Tag>}
                  {typeof a.overallFit === "number" && <Tag color={a.overallFit >= 70 ? C.good : a.overallFit >= 40 ? C.yellowDark : C.bad}>{a.overallFit}% fit</Tag>}
                </div>
                <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{a.summary}</div>
              </div>
              <button onClick={() => setOpenId(a.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              <button onClick={() => del(a.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

function Agents({ ctx }) {
  const { profile, content, saveContent, confirm } = ctx;
  const [agent, setAgent] = useState(null);
  const [inboxCount, setInboxCount] = useState(null);

  useEffect(() => {
    let on = true;
    fetch("/api/agent/inbox", { credentials: "include" }).then(r => r.ok ? r.json() : null).then(d => { if (on && d) setInboxCount((d.runs || []).length); }).catch(() => {});
    return () => { on = false; };
  }, [agent]);

  if (agent === "copy") return <BrandAgent ctx={ctx} onBack={() => setAgent(null)} />;
  if (agent === "acquisition") return <AcquisitionAgent ctx={ctx} onBack={() => setAgent(null)} />;
  if (agent === "benchmark") return <BenchmarkAgent ctx={ctx} onBack={() => setAgent(null)} />;
  if (agent === "migration") return <MigrationAgent ctx={ctx} onBack={() => setAgent(null)} />;
  if (agent === "inbox") return <AgentInbox ctx={ctx} onBack={() => setAgent(null)} />;
  if (agent === "team") return <AgentTeam ctx={ctx} onBack={() => setAgent(null)} />;

  // Aantal uitgewerkte (nog niet gepubliceerde) stukken content, voor op de Content Agent-tegel.
  const doneCount = (content || []).filter(c => c.phase === "done" || (!c.phase && c.body)).length;

  return (
    <div>
      <PageHead title="Agents" sub="Je AI-marketingteam. Zet een specialist los in, of stel een team samen rond één vraagstuk. Jij houdt de regie en keurt goed."
        right={<Btn variant={inboxCount ? "yellow" : "ghost"} size="sm" icon={Workflow} onClick={() => setAgent("inbox")}>Postvak{inboxCount ? ` (${inboxCount})` : ""}</Btn>} />

      <div style={sectionLabel}>De specialisten</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 16, marginTop: 10, marginBottom: 24 }}>
        {AGENTS.map(a => (
          <Card key={a.id} hover={a.active} onClick={a.active ? () => setAgent(a.id) : undefined}
            style={{ opacity: a.active ? 1 : .65, cursor: a.active ? "pointer" : "default" }}>
            <div style={{ display: "flex", gap: 13, alignItems: "flex-start" }}>
              <div style={{ width: 46, height: 46, borderRadius: 13, background: C.yellow + "22", display: "grid", placeItems: "center", flexShrink: 0 }}>
                <a.icon size={23} style={{ color: C.ink }} />
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <span style={{ fontWeight: 700, fontSize: 16 }}>{a.label}</span>
                  {!a.active && <Tag color={C.muted}>binnenkort</Tag>}
                  {a.id === "copy" && doneCount > 0 && <Tag color={C.good}>{doneCount} uitgewerkt</Tag>}
                </div>
                <div style={{ fontSize: 13, color: C.muted, marginTop: 4, lineHeight: 1.5 }}>{a.desc}</div>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 10 }}>
                  {a.skills.map(s => <span key={s} style={{ fontSize: 11.5, fontWeight: 600, color: C.ink2, padding: "3px 9px", borderRadius: 7, background: C.paper, border: `1px solid ${C.line}` }}>{s}</span>)}
                </div>
                {a.active && <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 12 }}>Inschakelen <ArrowUpRight size={15} /></div>}
              </div>
            </div>
          </Card>
        ))}
      </div>

      <div style={sectionLabel}>Team rond een vraagstuk</div>
      <Card hover onClick={() => setAgent("team")} style={{ marginTop: 10, cursor: "pointer", border: `1.5px solid ${C.yellow}` }}>
        <div style={{ display: "flex", gap: 13, alignItems: "flex-start" }}>
          <div style={{ width: 46, height: 46, borderRadius: 13, background: C.yellow, display: "grid", placeItems: "center", flexShrink: 0 }}>
            <Users size={23} style={{ color: C.ink }} />
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontWeight: 700, fontSize: 16 }}>Stel een team samen</div>
            <div style={{ fontSize: 13, color: C.muted, marginTop: 4, lineHeight: 1.5 }}>Leg een vraagstuk voor en kies welke specialisten meedenken. Het team werkt het samen uit en legt een gezamenlijk advies voor.</div>
            <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 12 }}>Team samenstellen <ArrowUpRight size={15} /></div>
          </div>
        </div>
      </Card>

      <Card style={{ marginTop: 20, background: C.paper, border: "none" }}>
        <div style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
          <Workflow size={18} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
          <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.55 }}>
            <b>Op afroep of gepland:</b> je schakelt een specialist zelf in, of laat hem op een ritme draaien (bijv. wekelijks). Een geplande agent draait op de achtergrond — ook als je niet ingelogd bent — en zet zijn voorstellen klaar in het <b>Postvak</b>. Niets gaat naar buiten zonder jouw goedkeuring.
          </div>
        </div>
      </Card>
    </div>
  );
}

// Team rond een vraagstuk: kies specialisten, geef een briefing, het team werkt het samen uit.
function AgentTeam({ ctx, onBack }) {
  const { profile } = ctx;
  const [question, setQuestion] = useState("");
  const [team, setTeam] = useState(["copy"]);
  const [phase, setPhase] = useState("intro"); // intro | running | done
  const [busyRole, setBusyRole] = useState(null);
  const [contributions, setContributions] = useState([]); // {role, label, text}
  const [synthesis, setSynthesis] = useState("");

  const ROLE_BRIEF = {
    benchmark: "Je bent de Benchmark agent. Bekijk het vraagstuk vanuit de vergelijking met concurrenten: wat doen zij op dit vlak, wat hebben zij wel en wij niet, en welke kansen liggen er nog open.",
    acquisition: "Je bent de Acquisition agent. Bekijk het vraagstuk vanuit de overname-integratie: hoe verhoudt het overgenomen bedrijf zich tot whitevision op propositie, markt, producten, technologie en kanalen, en wat betekent dat voor het samenvoegen.",
    copy: "Je bent de Content Agent. Bekijk het vraagstuk vanuit content en thought leadership: welke verhalen, thema's en stukken content helpen hier, en hoe sluiten ze aan op het merk.",
    migration: "Je bent een website migration specialist. Bekijk het vraagstuk vanuit content-structuur en SEO: welke content behouden, samenvoegen of doorverwijzen, en waar zitten content-gaps.",
    product: "Je bent de Context Agent. Bekijk het vraagstuk vanuit markt, trends en doelgroepbehoeften: wat speelt er in de markt, wat wil de doelgroep, en hoe scherp je de positionering aan.",
    performance: "Je bent de Amplify Agent. Bekijk het vraagstuk vanuit distributie en promotie: via welke kanalen verspreid je de content, welke campagne-ideeën vergroten het bereik, en hoe meet en optimaliseer je dat.",
    orchestrator: "Je bent de Orchestrator Agent. Bekijk het vraagstuk vanuit regie en strategie: welke deeltaken zijn er, in welke volgorde, welke specialisten zijn nodig, en wat is de overkoepelende aanpak.",
    experience: "Je bent de Experience Agent. Bekijk het vraagstuk vanuit de klantervaring: de customer journey, de belangrijkste touchpoints, en waar de ervaring beter kan.",
    operations: "Je bent de Operations Agent. Bekijk het vraagstuk vanuit processen, data en workflows: hoe richt je het operationeel in, welke data heb je nodig, en hoe meet en rapporteer je de voortgang.",
  };
  const roleLabel = (r) => (AGENTS.find(a => a.role === r) || {}).label || r;

  const toggle = (r) => setTeam(team.includes(r) ? team.filter(x => x !== r) : [...team, r]);

  const run = async () => {
    if (!question.trim() || !team.length) return;
    setPhase("running"); setContributions([]); setSynthesis("");
    const out = [];
    for (const role of team) {
      setBusyRole(role);
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\n${ROLE_BRIEF[role]}\n\nVRAAGSTUK: ${question.trim()}\n\nGeef je analyse en concrete aanbevelingen vanuit jouw vakgebied, in 4-8 bondige punten. Schrijf in het Nederlands, gewone tekst met streepjes voor de punten, geen markdown-koppen.`;
      try {
        const { data: text } = await callAIFull(prompt, {});
        out.push({ role, label: roleLabel(role), text });
        setContributions([...out]);
      } catch { /* sla deze rol over */ }
    }
    setBusyRole("synth");
    const combined = out.map(c => `## ${c.label}\n${c.text}`).join("\n\n");
    const sPrompt = `${profileContext(profile)}\n\n${PLAIN}\n\nHet volgende zijn adviezen van verschillende marketingspecialisten op dit vraagstuk: "${question.trim()}".\n\n${combined}\n\nOPDRACHT: Breng dit samen tot één helder, gezamenlijk teamadvies voor de brand manager: de rode draad, de belangrijkste aanbevelingen en een concreet voorstel voor de volgende stappen. Schrijf in het Nederlands. Markdown: ## voor koppen, - voor opsommingen.`;
    try { const { data: text } = await callAIFull(sPrompt, { useSearch: false }); setSynthesis(text); } catch {}
    setBusyRole(null); setPhase("done");
  };

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Team rond een vraagstuk" sub="Kies de specialisten, geef je vraagstuk, en het team werkt het samen uit." />

      {phase === "intro" && <Card>
        <Field label="Wat is je vraagstuk?" value={question} onChange={setQuestion} multiline rows={3} placeholder="Bijv. 'Hoe verstevigen we onze positie in de UK-markt tegenover ABBYY in de komende twee kwartalen?'" />
        <div style={sectionLabel}>Wie zet je in je team?</div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, margin: "10px 0 16px" }}>
          {AGENTS.map(a => {
            const on = team.includes(a.role);
            return (
              <label key={a.id} style={{ display: "flex", gap: 10, alignItems: "flex-start", padding: "11px 13px", border: `1.5px solid ${on ? C.yellow : C.line}`, background: on ? C.yellow + "12" : C.card, borderRadius: 11, cursor: "pointer" }}>
                <input type="checkbox" checked={on} onChange={() => toggle(a.role)} style={{ marginTop: 3 }} />
                <div><div style={{ fontWeight: 700, fontSize: 13.5 }}>{a.label}</div><div style={{ fontSize: 12, color: C.muted, marginTop: 2, lineHeight: 1.4 }}>{a.desc}</div></div>
              </label>
            );
          })}
        </div>
        <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
          <Btn variant="yellow" icon={Users} onClick={run} disabled={!question.trim() || !team.length}>Laat het team werken ({team.length})</Btn>
          <CostHint kind="desk" />
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Elke specialist geeft eerst zijn eigen kijk, daarna brengt het team het samen tot één advies. Dit doet meerdere AI-stappen achter elkaar, dus het duurt even.</div>
      </Card>}

      {phase === "running" && <Card>
        <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 12 }}>Het team is aan het werk…</div>
        {team.map(role => {
          const done = contributions.some(c => c.role === role);
          const busy = busyRole === role;
          return <div key={role} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
            <div style={{ width: 22, height: 22, borderRadius: "50%", display: "grid", placeItems: "center", background: done ? C.good : busy ? C.yellow : C.line, color: done ? "#fff" : C.ink }}>
              {done ? <Check size={13} /> : busy ? <Loader2 size={13} className="spin" /> : <Circle size={9} />}
            </div>
            <span style={{ fontSize: 13.5, fontWeight: busy ? 700 : 500, color: (done || busy) ? C.ink : C.muted }}>{roleLabel(role)}</span>
          </div>;
        })}
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
          <div style={{ width: 22, height: 22, borderRadius: "50%", display: "grid", placeItems: "center", background: synthesis ? C.good : busyRole === "synth" ? C.yellow : C.line, color: synthesis ? "#fff" : C.ink }}>
            {synthesis ? <Check size={13} /> : busyRole === "synth" ? <Loader2 size={13} className="spin" /> : <Circle size={9} />}
          </div>
          <span style={{ fontSize: 13.5, fontWeight: busyRole === "synth" ? 700 : 500, color: C.ink2 }}>Teamadvies samenstellen</span>
        </div>
      </Card>}

      {phase === "done" && <>
        {synthesis && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}><Users size={17} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15.5 }}>Teamadvies</span></div>
          <div style={{ fontSize: 14, lineHeight: 1.6, color: C.ink }} dangerouslySetInnerHTML={{ __html: contentToHtml(synthesis) }} />
        </Card>}
        <div style={sectionLabel}>Inbreng per specialist</div>
        <div style={{ marginTop: 10 }}>
          {contributions.map(c => (
            <Card key={c.role} style={{ marginBottom: 12 }}>
              <details>
                <summary style={{ cursor: "pointer", fontWeight: 700, fontSize: 14.5 }}>{c.label}</summary>
                <div style={{ fontSize: 13.5, lineHeight: 1.6, color: C.ink2, marginTop: 10, whiteSpace: "pre-wrap" }}>{c.text}</div>
              </details>
            </Card>
          ))}
        </div>
        <div style={{ display: "flex", gap: 10, marginTop: 8 }}>
          <Btn variant="yellow" icon={Download} onClick={() => exportToWord("Teamadvies — " + question.slice(0, 60), contentToHtml(synthesis))}>Exporteer advies naar Word</Btn>
          <Btn variant="ghost" onClick={() => { setPhase("intro"); setContributions([]); setSynthesis(""); }}>Nieuw vraagstuk</Btn>
        </div>
      </>}
    </div>
  );
}

const BRAND_STEPS = [
  { key: "research", label: "Imago-onderzoek & marktscan" },
  { key: "themes", label: "Verbeterthema's afleiden" },
  { key: "topics", label: "Topics voorstellen" },
  { key: "concepts", label: "Concepten uitwerken" },
];

function BrandAgent({ ctx, onBack }) {
  const { profile, content, saveContent, confirm } = ctx;
  const [goal, setGoal] = useState("");
  const [phase, setPhase] = useState("intro"); // intro | running | review-topics | generating | review-concepts | done
  const [stepStatus, setStepStatus] = useState({}); // key -> "busy"|"done"
  const [research, setResearch] = useState(null); // { summary, themes:[] }
  const [topics, setTopics] = useState([]); // {id, title, kind, theme, angle, picked}
  const [concepts, setConcepts] = useState([]); // {id, title, kind, body, sources, approved}
  const [error, setError] = useState("");

  const today = new Date();
  const dateNL = today.toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" });
  const site = profile?.website ? profile.website.trim() : "";

  const runResearch = async () => {
    setPhase("running"); setError("");
    setStepStatus({ research: "busy" });
    try {
      // Stap 1+2: onderzoek en thema's
      const sitePart = site ? `Doorzoek met web search ${site} en de bredere markt/concurrenten.` : "Baseer je op het merkprofiel en algemene marktinzichten.";
      const p1 = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${dateNL}. OPDRACHT (stap imago-analyse): Voer een beknopte imago- en marktscan uit voor whitevision${goal ? ", met focus op: " + goal : ""}. ${sitePart} Bepaal hoe het merk er nu voorstaat en leid 3-4 concrete verbeterthema's af waarmee het imago versterkt kan worden.\n\nJSON: {"summary":"korte stand van zaken (3-5 zinnen)","themes":[{"theme":"naam thema","why":"waarom dit het imago verbetert"}]}`;
      const { data: r1 } = await callAIFull(p1, { json: true, useSearch: true });
      setResearch({ summary: r1?.summary || "", themes: r1?.themes || [] });
      setStepStatus({ research: "done", themes: "done", topics: "busy" });
      // Stap 3: topics
      const themeList = (r1?.themes || []).map(t => t.theme).join("; ");
      const p2 = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${dateNL}. OPDRACHT (stap topics): Op basis van deze verbeterthema's (${themeList}) en het doel "${goal || "imago versterken"}", stel 10 sterke contenttopics voor die het imago van whitevision verbeteren. Mix blogs, whitepapers en social posts. ${site ? "Vermijd met web search overlap met bestaande content op " + site + "." : ""}\n\nJSON: {"topics":[{"title":"titel","kind":"blog|whitepaper|social","theme":"bijbehorend thema","angle":"korte invalshoek"}]}`;
      const { data: r2 } = await callAIFull(p2, { json: true, useSearch: !!site });
      const list = (r2?.topics || []).map((t, i) => ({
        id: Date.now() + i, title: t.title, kind: ["blog", "whitepaper", "social"].includes(t.kind) ? t.kind : "blog",
        theme: t.theme || "", angle: t.angle || "", picked: true,
      }));
      setTopics(list);
      setStepStatus({ research: "done", themes: "done", topics: "done" });
      setPhase("review-topics");
    } catch (e) {
      setError("Er ging iets mis tijdens het uitvoeren. Probeer het opnieuw."); setPhase("intro");
    }
  };

  const togglePick = (id) => setTopics(topics.map(t => t.id === id ? { ...t, picked: !t.picked } : t));
  const setTopicField = (id, patch) => setTopics(topics.map(t => t.id === id ? { ...t, ...patch } : t));

  const runConcepts = async () => {
    const picked = topics.filter(t => t.picked);
    if (!picked.length) return;
    setPhase("generating"); setError("");
    setStepStatus(s => ({ ...s, concepts: "busy" }));
    const out = [];
    try {
      for (const t of picked) {
        const tpl = (profile?.brandTemplates || []).find(x => x.kind === t.kind && x.structure?.trim())
          || (profile?.brandTemplates || []).find(x => x.kind === "algemeen" && x.structure?.trim());
        const tplPart = tpl ? `\nVolg deze brand template voor structuur en stijl:\n${tpl.structure}\n` : "";
        const lengte = t.kind === "whitepaper" ? "grondig en gestructureerd (1500-2500 woorden)"
          : t.kind === "social" ? "kort en pakkend (een LinkedIn-post van 80-150 woorden, met een sterke openingszin en passende afsluiting)"
          : "bondig en pakkend (600-900 woorden)";
        const p = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${dateNL}; schrijf vanuit ${today.getFullYear()}. ${tplPart}\nOPDRACHT: Schrijf ${t.kind === "whitepaper" ? "een whitepaper" : t.kind === "social" ? "een social post" : "een blog"} voor whitevision over: "${t.title}". ${t.angle ? "Invalshoek: " + t.angle : ""} Schrijf ${lengte}, in onze tone of voice. Gebruik web search voor actuele feiten. Markdown: # titel, ## koppen, - opsommingen. Geen <cite>-tags. Geef ALLEEN de tekst in markdown.`;
        const { data: text, sources } = await callAIFull(p, { useSearch: true });
        out.push({ id: t.id, kind: t.kind, theme: t.theme, title: (text.match(/^#\s+(.+)$/m)?.[1] || t.title).trim(), body: text, sources: sources || [], approved: true });
        setConcepts([...out]);
      }
      setStepStatus(s => ({ ...s, concepts: "done" }));
      setPhase("review-concepts");
    } catch (e) {
      setError("Er ging iets mis bij het uitwerken. De al gemaakte concepten staan hieronder."); setPhase("review-concepts");
    }
  };

  const toggleApprove = (id) => setConcepts(concepts.map(c => c.id === id ? { ...c, approved: !c.approved } : c));
  const approveAll = () => {
    const approved = concepts.filter(c => c.approved);
    if (!approved.length) return;
    const entries = approved.map((c, i) => ({
      id: Date.now() + i, tile: "thought", phase: "done", kind: c.kind === "whitepaper" ? "whitepaper" : c.kind === "social" ? "social" : "blog",
      title: c.title, body: c.body, sources: c.sources, date: new Date().toISOString().slice(0, 10), fromAgent: "Content marketeer",
    }));
    saveContent([...entries, ...(content || [])]);
    setPhase("done");
  };

  const StepRow = ({ k, label }) => {
    const st = stepStatus[k];
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0" }}>
        <div style={{ width: 22, height: 22, borderRadius: "50%", display: "grid", placeItems: "center", flexShrink: 0,
          background: st === "done" ? C.good : st === "busy" ? C.yellow : C.line, color: st === "done" ? "#fff" : C.ink }}>
          {st === "done" ? <Check size={13} /> : st === "busy" ? <Loader2 size={13} className="spin" /> : <Circle size={9} />}
        </div>
        <span style={{ fontSize: 13.5, fontWeight: st === "busy" ? 700 : 500, color: st ? C.ink : C.muted }}>{label}</span>
      </div>
    );
  };

  const kindLabel = (k) => k === "whitepaper" ? "whitepaper" : k === "social" ? "social post" : "blog";

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Content marketeer" sub="Van doel naar uitgewerkte content, klaar voor jouw goedkeuring." />

      {error && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.bad}`, background: C.bad + "0E" }}><div style={{ fontSize: 13.5, color: C.bad }}>{error}</div></Card>}

      {phase === "intro" && (
        <>
        <TemplatePicker where="copy" />
        <Card>
          <div style={{ fontWeight: 700, fontSize: 15.5, marginBottom: 6 }}>Wat is je doel?</div>
          <div style={{ fontSize: 13, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Beschrijf wat je wilt bereiken. De agent doet onderzoek, leidt thema's af, stelt topics voor en werkt de gekozen topics uit tot concepten. Laat leeg voor een brede imagoversterking.</div>
          <Field label="Doel / opdracht" value={goal} onChange={setGoal} multiline rows={2} placeholder="Bijv. 'ons imago versterken rondom AI en compliance in de zorgsector'" />
          {!site && <div style={{ fontSize: 12.5, color: C.yellowDark, marginBottom: 12 }}>Tip: vul je website in het merkprofiel in, dan checkt de agent op bestaande content.</div>}
          <Btn variant="yellow" icon={Sparkles} onClick={runResearch}>Start de agent nu</Btn>
        </Card>
        <AgentSchedule goal={goal} />
        </>
      )}

      {(phase === "running" || phase === "generating") && (
        <Card>
          <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 6 }}>{phase === "generating" ? "Concepten worden uitgewerkt…" : "De agent is aan het werk…"}</div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 10 }}>Dit duurt even; de agent zet meerdere stappen achter elkaar.</div>
          {BRAND_STEPS.map(s => <StepRow key={s.key} k={s.key} label={s.label} />)}
          {phase === "generating" && concepts.length > 0 && <div style={{ fontSize: 12.5, color: C.muted, marginTop: 8 }}>{concepts.length} van de {topics.filter(t => t.picked).length} concepten klaar…</div>}
        </Card>
      )}

      {phase === "review-topics" && (
        <>
          {research?.summary && <Card style={{ marginBottom: 16 }}>
            <div style={sectionLabel}>Stand van zaken</div>
            <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.6, marginTop: 6 }}>{research.summary}</div>
            {research.themes?.length > 0 && <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12 }}>
              {research.themes.map((t, i) => <span key={i} title={t.why} style={{ fontSize: 12.5, fontWeight: 600, padding: "5px 11px", borderRadius: 8, background: C.yellow + "22", border: `1px solid ${C.yellow}` }}>{t.theme}</span>)}
            </div>}
          </Card>}
          <Card>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6 }}>
              <div style={{ fontWeight: 700, fontSize: 15.5 }}>Voorgestelde topics</div>
              <span style={{ fontSize: 12.5, color: C.muted }}>{topics.filter(t => t.picked).length} van {topics.length} geselecteerd</span>
            </div>
            <div style={{ fontSize: 13, color: C.muted, marginBottom: 12 }}>Vink aan wat de agent moet uitwerken. Je kunt titel, type en invalshoek nog aanpassen.</div>
            {topics.map(t => (
              <div key={t.id} style={{ border: `1.5px solid ${t.picked ? C.yellow : C.line}`, background: t.picked ? C.yellow + "0E" : C.card, borderRadius: 11, padding: 12, marginBottom: 8 }}>
                <div style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
                  <input type="checkbox" checked={t.picked} onChange={() => togglePick(t.id)} style={{ marginTop: 4 }} />
                  <div style={{ flex: 1 }}>
                    <input value={t.title} onChange={e => setTopicField(t.id, { title: e.target.value })}
                      style={{ width: "100%", boxSizing: "border-box", padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, marginBottom: 6, outline: "none" }} />
                    <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                      <select value={t.kind} onChange={e => setTopicField(t.id, { kind: e.target.value })} style={{ padding: "5px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12, background: C.card, outline: "none" }}>
                        <option value="blog">Blog</option><option value="whitepaper">Whitepaper</option><option value="social">Social post</option>
                      </select>
                      {t.theme && <Tag color={C.muted}>{t.theme}</Tag>}
                    </div>
                    {t.angle && <div style={{ fontSize: 12.5, color: C.ink2, marginTop: 6, lineHeight: 1.45 }}>{t.angle}</div>}
                  </div>
                </div>
              </div>
            ))}
            <div style={{ display: "flex", gap: 10, marginTop: 12, alignItems: "center" }}>
              <Btn variant="yellow" icon={Sparkles} onClick={runConcepts} disabled={!topics.some(t => t.picked)}>Werk {topics.filter(t => t.picked).length} concepten uit</Btn>
              <Btn variant="ghost" onClick={onBack}>Stoppen</Btn>
              <CostHint kind="desk" />
            </div>
          </Card>
        </>
      )}

      {phase === "review-concepts" && (
        <>
          <Card style={{ marginBottom: 16, background: C.paper, border: "none" }}>
            <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>De agent heeft {concepts.length} concept{concepts.length === 1 ? "" : "en"} klaargezet. Bekijk ze, vink aan wat je wilt behouden en keur goed. Goedgekeurde concepten komen in <b>Content</b> te staan (bij Uitgewerkt).</div>
          </Card>
          {concepts.map(c => (
            <Card key={c.id} style={{ marginBottom: 12, border: `1.5px solid ${c.approved ? C.good : C.line}` }}>
              <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "flex-start", marginBottom: 10 }}>
                <div style={{ display: "flex", gap: 9, alignItems: "flex-start" }}>
                  <input type="checkbox" checked={c.approved} onChange={() => toggleApprove(c.id)} style={{ marginTop: 5 }} />
                  <div>
                    <div style={{ fontWeight: 700, fontSize: 15 }}>{c.title}</div>
                    <div style={{ display: "flex", gap: 7, marginTop: 3 }}><Tag color={C.yellowDark}>{kindLabel(c.kind)}</Tag>{c.theme && <Tag color={C.muted}>{c.theme}</Tag>}</div>
                  </div>
                </div>
              </div>
              <details>
                <summary style={{ cursor: "pointer", fontSize: 13, color: C.blue, fontWeight: 600 }}>Bekijk concept</summary>
                <div style={{ fontSize: 14, lineHeight: 1.6, color: C.ink, marginTop: 10, paddingTop: 10, borderTop: `1px solid ${C.line}` }} dangerouslySetInnerHTML={{ __html: contentToHtml(c.body) }} />
              </details>
            </Card>
          ))}
          <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 8 }}>
            <Btn variant="yellow" icon={Check} onClick={approveAll} disabled={!concepts.some(c => c.approved)}>Keur {concepts.filter(c => c.approved).length} goed → naar Content</Btn>
            <Btn variant="ghost" onClick={onBack}>Sluiten zonder opslaan</Btn>
          </div>
        </>
      )}

      {phase === "done" && (
        <Card style={{ textAlign: "center", padding: "40px 20px" }}>
          <div style={{ width: 54, height: 54, borderRadius: "50%", background: C.good + "22", display: "grid", placeItems: "center", margin: "0 auto 14px" }}><Check size={26} style={{ color: C.good }} /></div>
          <div style={{ fontWeight: 700, fontSize: 17, fontFamily: FONT_DISPLAY }}>Klaar — concepten staan in Content</div>
          <div style={{ fontSize: 13.5, color: C.muted, marginTop: 6, lineHeight: 1.5 }}>De goedgekeurde concepten vind je terug bij Content, onder Uitgewerkt. Daar kun je ze openen, aanpassen en exporteren.</div>
          <div style={{ display: "flex", gap: 10, justifyContent: "center", marginTop: 18 }}>
            <Btn variant="yellow" icon={PenLine} onClick={() => ctx.goTo("content")}>Naar Content</Btn>
            <Btn variant="ghost" onClick={() => { setPhase("intro"); setGoal(""); setResearch(null); setTopics([]); setConcepts([]); setStepStatus({}); }}>Nieuwe run</Btn>
          </div>
        </Card>
      )}
    </div>
  );
}

// Planning van de geplande Brand-agent (serverzijde config).
// Echte CSV-parser: respecteert aanhalingstekens en velden met komma's/quotes erin.
function parseCSVRows(text) {
  text = String(text || "").replace(/^\uFEFF/, ""); // BOM weg
  const rows = []; let row = [], field = "", inQ = false;
  for (let i = 0; i < text.length; i++) {
    const c = text[i];
    if (inQ) {
      if (c === '"' && text[i + 1] === '"') { field += '"'; i++; }
      else if (c === '"') inQ = false;
      else field += c;
    } else {
      if (c === '"') inQ = true;
      else if (c === "," || c === ";" || c === "\t") { row.push(field); field = ""; }
      else if (c === "\r") { /* skip */ }
      else if (c === "\n") { row.push(field); rows.push(row); row = []; field = ""; }
      else field += c;
    }
  }
  if (field.length || row.length) { row.push(field); rows.push(row); }
  return rows;
}
function colIdx(header, names) {
  for (const n of names) { const i = header.findIndex(h => (h || "").trim().toLowerCase() === n); if (i >= 0) return i; }
  return -1;
}
// Haal indexeerbare 200-OK HTML-pagina's uit een crawl-export of sitemap.
// Retourneert objecten { url, title, desc, ga } zodat de caller kan kiezen hoe rijk hij ze meestuurt.
function extractPages(text) {
  const raw = String(text || "");
  // XML-sitemap: alleen URL's beschikbaar
  if (/<loc>/i.test(raw)) {
    const locs = raw.match(/<loc>\s*([^<]+?)\s*<\/loc>/gi) || [];
    const seen = new Set();
    const list = [];
    for (const l of locs) {
      const url = l.replace(/<\/?loc>/gi, "").trim();
      if (url && !seen.has(url)) { seen.add(url); list.push({ url, title: "", desc: "", ga: "" }); }
    }
    return list;
  }
  const rows = parseCSVRows(raw);
  if (!rows.length) return [];
  const header = rows[0];
  const looksLikeHeader = header.some(h => /address|url|status|indexability/i.test(h || ""));
  const iUrl = looksLikeHeader ? colIdx(header, ["address", "url", "original url"]) : 0;
  const iStatus = looksLikeHeader ? colIdx(header, ["status code"]) : -1;
  const iIndex = looksLikeHeader ? colIdx(header, ["indexability"]) : -1;
  const iType = looksLikeHeader ? colIdx(header, ["content type"]) : -1;
  const iGA = looksLikeHeader ? colIdx(header, ["ga4 sessions", "ga4 views", "sessions", "sessies"]) : -1;
  const iTitle = looksLikeHeader ? colIdx(header, ["title 1", "title", "page title"]) : -1;
  const iDesc = looksLikeHeader ? colIdx(header, ["meta description 1", "meta description", "description"]) : -1;
  const out = [];
  const seen = new Set();
  const start = looksLikeHeader ? 1 : 0;
  for (let r = start; r < rows.length; r++) {
    const row = rows[r]; if (!row) continue;
    const cell = (i) => (i >= 0 && i < row.length ? (row[i] || "").trim() : "");
    let url = iUrl >= 0 ? cell(iUrl) : "";
    if (!url) url = (row.find(c => /^https?:\/\//i.test((c || "").trim())) || "").trim();
    if (!/^https?:\/\//i.test(url) || seen.has(url)) continue;
    if (iStatus >= 0 && cell(iStatus) && cell(iStatus) !== "200") continue;
    const idx = cell(iIndex).toLowerCase();
    if (iIndex >= 0 && idx && idx !== "indexable") continue;
    const type = cell(iType).toLowerCase();
    if (iType >= 0 && type && !type.includes("html")) continue;
    seen.add(url);
    out.push({ url, title: cell(iTitle), desc: cell(iDesc), ga: cell(iGA) });
  }
  return out;
}
// Formatteer een pagina als compacte regel voor de AI (titel + description meegenomen, description ingekort).
function pageLine(p) {
  let s = p.url;
  if (p.title) s += ` | ${p.title}`;
  if (p.desc) s += ` | ${p.desc.slice(0, 160)}`;
  if (p.ga && p.ga !== "0") s += ` | bezoeken: ${p.ga}`;
  return s;
}
function slugOf(u) {
  try { const p = u.startsWith("http") ? new URL(u).pathname : u; return p.replace(/\/+$/, "") || "/"; }
  catch { return u; }
}

function MigrationAgent({ ctx, onBack }) {
  const { companies, profile, confirm, content, saveContent, goTo } = ctx;
  const [studies, setStudies] = useState(null);
  const [openId, setOpenId] = useState(null);
  const [creating, setCreating] = useState(false);
  const [name, setName] = useState("");
  const [acqLang, setAcqLang] = useState("en"); // taal overgenomen site: en | nl | mixed
  const [acqText, setAcqText] = useState("");
  const [oursText, setOursText] = useState("");
  const [busy, setBusy] = useState(false);
  const [progress, setProgress] = useState(null); // {done,total}
  const [smUrl, setSmUrl] = useState({ acq: "", ours: "" });
  const [smBusy, setSmBusy] = useState(null); // "acq" | "ours" | null
  const [smMsg, setSmMsg] = useState({ acq: "", ours: "" });
  const [smCached, setSmCached] = useState({ acq: false, ours: false });
  const acqRef = useRef(null), oursRef = useRef(null);

  const fetchSitemap = async (key, refresh = false) => {
    const url = (smUrl[key] || "").trim();
    if (!url) return;
    setSmBusy(key); setSmMsg(m => ({ ...m, [key]: "" }));
    try {
      const r = await fetch("/api/sitemap", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url, refresh }) });
      const d = await r.json();
      if (!r.ok || d.error) { setSmMsg(m => ({ ...m, [key]: d.error || "Ophalen mislukt." })); }
      else if (!d.pages || !d.pages.length) { setSmMsg(m => ({ ...m, [key]: d.note || "Geen URL's gevonden." })); }
      else {
        const text = d.pages.join("\n");
        if (key === "acq") setAcqText(text); else setOursText(text);
        const when = d.fetchedAt ? new Date(d.fetchedAt).toLocaleDateString("nl-NL", { day: "numeric", month: "short" }) : "";
        setSmMsg(m => ({ ...m, [key]: d.cached
          ? `${d.pages.length} URL's uit de bewaarde crawl van ${when}.`
          : `${d.pages.length} URL's opgehaald uit ${d.mapsFetched} sitemap(s).` }));
        setSmCached(c => ({ ...c, [key]: !!d.cached }));
      }
    } catch { setSmMsg(m => ({ ...m, [key]: "Ophalen mislukt. Probeer een bestand te uploaden." })); }
    setSmBusy(null);
  };

  useEffect(() => { (async () => { try { setStudies(await store.get("wv_migrations", [])); } catch { setStudies([]); } })(); }, []);
  const save = (v) => { setStudies(v); store.set("wv_migrations", v); };

  const onFile = (which, e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const r = new FileReader();
    r.onload = () => { const t = String(r.result || ""); if (which === "acq") setAcqText(t); else setOursText(t); };
    r.readAsText(f); e.target.value = "";
  };

  const run = async () => {
    if (!acqText.trim() || !oursText.trim()) return;
    setBusy(true); setProgress(null);
    // Client-side de pagina's extraheren (echte CSV-parser, filtert op indexeerbare 200-OK HTML).
    let acqUrls = [], ourUrls = [];
    try { acqUrls = extractPages(acqText); } catch {}
    try { ourUrls = extractPages(oursText); } catch {}
    const acqTotal = acqUrls.length, ourTotal = ourUrls.length;
    if (!acqTotal || !ourTotal) {
      const study = { id: Date.now(), name: name || "Migratie", date: new Date().toISOString().slice(0, 10), mapping: [], note: `Geen geschikte pagina's gevonden (${acqTotal} bij het overgenomen bedrijf, ${ourTotal} bij whitevision). Controleer of je een crawl-export met een 'Address'-kolom of een sitemap hebt geüpload.`, counts: { acq: acqTotal, ours: ourTotal } };
      save([study, ...(studies || [])]);
      setBusy(false); setCreating(false); setName(""); setAcqText(""); setOursText(""); setOpenId(study.id);
      return;
    }
    // De whitevision-lijst gaat als naslag mee in elk groepje (nodig om te kunnen matchen).
    // De overgenomen lijst knippen we op in groepjes zodat elke AI-aanroep behapbaar blijft.
    const ourPages = ourUrls.slice(0, 400);
    const ourList = ourPages.map(pageLine);
    const BATCH = 10;
    const batches = [];
    for (let i = 0; i < acqUrls.length; i += BATCH) batches.push(acqUrls.slice(i, i + BATCH));
    setProgress({ done: 0, total: batches.length });

    const allMapping = [];
    let fails = 0;
    for (let b = 0; b < batches.length; b++) {
      const acqList = batches[b].map(pageLine);
      const langLabel = acqLang === "nl" ? "Nederlands" : acqLang === "mixed" ? "gemengd (bepaal per pagina)" : "Engels";
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nCONTEXT: whitevision neemt ${name || "een bedrijf"} over. Je krijgt een GROEPJE pagina's van het overgenomen bedrijf, plus de VOLLEDIGE lijst van whitevision-pagina's om tegen te matchen. Elke regel heeft het formaat: URL | titel | omschrijving | bezoeken (titel/omschrijving/bezoeken kunnen ontbreken). Gebruik titel en omschrijving om de inhoud te begrijpen.\n\nDE OVERGENOMEN SITE IS: ${langLabel}.${acqLang === "mixed" ? " Bepaal de taal per pagina uit URL en inhoud." : " Behandel de overgenomen pagina's dus als deze taal, tenzij een losse pagina overduidelijk een andere taal is."}\n\nREGELS:\n- Koppel ELKE overgenomen pagina aan een doel-URL op whitevision. Geef ALTIJD een "to" (nooit leeg).\n- TAAL EERST: zoek het doel in DEZELFDE taal als de overgenomen pagina. whitevision heeft Nederlandse URL's en Engelse URL's (met /en/). Een Engelse overgenomen pagina hoort bij een Engelse whitevision-URL met /en/; een Nederlandse bij een Nederlandse URL. Als er een goede tegenhanger in de juiste taal bestaat, kies die ALTIJD boven de andere taal.\n- Bestaat er GÉÉN goed alternatief in de juiste taal, maar wel in een andere taal? Kies dan dat anderstalige doel, zet "langWarning":true, "langNote" kort (bijv. "alleen NL-versie") en actie "vertalen" (de doelpagina moet nog vertaald worden). Anders "langWarning":false.\n- PARTNERPAGINA'S (URL bevat /partner): nemen we over op onze site. Doel = onze partnerpagina (juiste taal indien aanwezig; anders andere taal met langWarning + actie kan dan "vertalen" of "content overnemen" zijn). Voor partners is de standaardactie "content overnemen".\n- "reason": heel kort (max ~8 woorden).\n- "importance": hoog/midden/laag (veel bezoeken = belangrijker).\n- "action": precies één van: "vervanger aanwezig" (er is al een whitevision-pagina die dit dekt; zet die URL in "to"), "1-op-1 doorzetten" (de tekst is waardevol en kan ongewijzigd mee), "content herschrijven" (inhoud is waardevol maar moet naar onze tone of voice of een andere opbouw), "nieuwe categorie" (past nergens onder; laat "to" leeg).\n- Geef voor ELKE pagina precies één regel. "from" is alleen de URL.\n\nGROEPJE OVERGENOMEN PAGINA'S (${name || "overgenomen"}, groep ${b + 1}/${batches.length}):\n${acqList.join("\n")}\n\nVOLLEDIGE WHITEVISION-LIJST om tegen te matchen (${ourTotal} pagina's${ourTotal > 400 ? ", eerste 400" : ""}):\n${ourList.join("\n")}\n\nJSON: {"mapping":[{"from":"https://...","to":"https://...","lang":"NL|EN","langWarning":true|false,"langNote":"kort, alleen bij waarschuwing","importance":"hoog|midden|laag","reason":"kort","action":"vervanger aanwezig|1-op-1 doorzetten|content herschrijven|nieuwe categorie"}]}`;
      try {
        let m = [];
        for (let attempt = 0; attempt < 2 && !m.length; attempt++) {
          const { data: r } = await callAIFull(prompt, { json: true, useSearch: false });
          m = Array.isArray(r?.mapping) ? r.mapping : [];
        }
        // Vangnet: partnerpagina's overnemen, tenzij ze vanwege taal eerst vertaald moeten worden.
        m = m.map(x => (/\/partner/i.test(x.from || "") && x.action !== "vertalen") ? { ...x, action: "content overnemen" } : x);
        if (!m.length) fails++;
        allMapping.push(...m);
      } catch { fails++; }
      setProgress({ done: b + 1, total: batches.length });
    }

    let note = `${acqTotal} overgenomen pagina's gematcht tegen ${ourTotal} whitevision-pagina's, in ${batches.length} groepjes.`;
    if (!allMapping.length) note = "De analyse leverde geen koppelingen op. Probeer het opnieuw; blijft dit gebeuren, laat het me weten.";
    else if (fails) note += ` Let op: ${fails} groepje(s) gaven geen resultaat; je kunt het onderzoek opnieuw draaien voor een volledig beeld.`;

    const study = { id: Date.now(), name: name || "Migratie", date: new Date().toISOString().slice(0, 10), mapping: allMapping, note, counts: { acq: acqTotal, ours: ourTotal } };
    save([study, ...(studies || [])]);
    setBusy(false); setProgress(null); setCreating(false); setName(""); setAcqText(""); setOursText(""); setOpenId(study.id);
  };

  const impColor = (i) => i === "hoog" ? C.bad : i === "midden" ? C.yellowDark : C.muted;

  // ---- detail ----
  if (openId) {
    const s = (studies || []).find(x => x.id === openId);
    if (!s) { setOpenId(null); return null; }
    return <MigrationDetail study={s} onBack={() => setOpenId(null)} impColor={impColor}
      onDelete={() => confirm("Dit migratie-onderzoek verwijderen?", () => { save(studies.filter(x => x.id !== s.id)); setOpenId(null); })}
      content={content} saveContent={saveContent} goTo={goTo} confirm={confirm}
      saveStudy={(next) => save(studies.map(x => x.id === s.id ? next : x))} />;
  }

  // ---- overzicht ----
  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Website migration agent" sub="Breng bij een overname beide sites in kaart en krijg een migratieplan plus content-gaps."
        right={<Btn variant="yellow" icon={Plus} onClick={() => { setCreating(true); setName(""); setAcqText(""); setOursText(""); }}>Nieuw onderzoek</Btn>} />

      {creating && (
        <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontSize: 13, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Upload van beide sites een crawl-export uit ScreamingFrog (CSV) <b>of</b> een sitemap.xml-bestand, of plak de inhoud. Een crawl geeft het rijkste resultaat (titels, omschrijvingen en bezoeken); een sitemap.xml werkt ook en is handig als je geen crawl kunt maken (dan zonder bezoekdata). De agent neemt van het overgenomen bedrijf alleen indexeerbare 200-OK-pagina's mee.</div>
          <div style={{ fontSize: 12, color: C.muted, marginBottom: 14, lineHeight: 1.5, padding: "8px 11px", background: C.paper, borderRadius: 8 }}>Tip: een sitemap.xml staat meestal in de root van een website, bijvoorbeeld <span style={{ fontFamily: "monospace" }}>https://voorbeeld.nl/sitemap.xml</span>. Open die in je browser, sla hem op en upload hem hier.</div>
          <input value={name} onChange={e => setName(e.target.value)} placeholder="Naam overgenomen bedrijf (bijv. Document Logistix)" list="wv-companies"
            style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, marginBottom: 12, outline: "none" }} />
          <datalist id="wv-companies">{(companies || []).map(c => <option key={c.id} value={c.name} />)}</datalist>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14, flexWrap: "wrap" }}>
            <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Taal van de overgenomen site:</span>
            {[["en", "Engels"], ["nl", "Nederlands"], ["mixed", "Gemengd"]].map(([v, lbl]) => (
              <button key={v} onClick={() => setAcqLang(v)}
                style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${acqLang === v ? C.yellow : C.line}`, background: acqLang === v ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
            ))}
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
            {[["acq", "Overgenomen bedrijf", acqText, acqRef], ["ours", "whitevision (onze site)", oursText, oursRef]].map(([key, label, val, ref]) => (
              <div key={key} style={{ border: `1.5px solid ${C.line}`, borderRadius: 12, padding: 14 }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>{label}</span>
                  <Btn size="sm" variant="ghost" icon={Upload} onClick={() => ref.current?.click()}>Upload</Btn>
                  <input ref={ref} type="file" accept=".csv,.txt,.xml,text/xml,application/xml" onChange={e => onFile(key, e)} style={{ display: "none" }} />
                </div>
                <textarea value={val} onChange={e => key === "acq" ? setAcqText(e.target.value) : setOursText(e.target.value)} rows={5} placeholder="…of plak hier de crawl/sitemap"
                  style={{ width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1px solid ${C.line}`, borderRadius: 8, fontFamily: "monospace", fontSize: 11, resize: "vertical", outline: "none" }} />
                <div style={{ display: "flex", gap: 6, marginTop: 8, alignItems: "center" }}>
                  <input value={smUrl[key]} onChange={e => setSmUrl(m => ({ ...m, [key]: e.target.value }))} placeholder="of haal op via URL (sitemap of site)"
                    style={{ flex: 1, minWidth: 0, padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12, outline: "none" }} />
                  <Btn size="sm" variant="ghost" icon={Workflow} onClick={() => fetchSitemap(key)} disabled={smBusy === key || !(smUrl[key] || "").trim()}>{smBusy === key ? "Bezig…" : "Ophalen"}</Btn>
                </div>
                {smMsg[key] && <div style={{ fontSize: 11.5, color: (smMsg[key].includes("opgehaald") || smMsg[key].includes("bewaarde")) ? C.good : C.bad, marginTop: 5, lineHeight: 1.4 }}>
                  {smMsg[key]}
                  {smCached[key] && <button onClick={() => fetchSitemap(key, true)} style={{ marginLeft: 8, background: "none", border: "none", padding: 0, cursor: "pointer", color: C.blue, fontFamily: FONT_BODY, fontSize: 11.5, textDecoration: "underline" }}>opnieuw ophalen</button>}
                </div>}
                <div style={{ fontSize: 12, color: val.trim() ? C.good : C.muted, marginTop: 6, fontWeight: 600 }}>{val.trim() ? `${val.split(/\r?\n/).filter(Boolean).length} regels` : "Nog niets"}</div>
              </div>
            ))}
          </div>
          <div style={{ marginTop: 14, display: "flex", gap: 10, alignItems: "center" }}>
            <Btn variant="yellow" icon={Sparkles} onClick={run} disabled={busy || !acqText.trim() || !oursText.trim()}>{busy ? "Bezig…" : "Maak migratieplan"}</Btn>
            <Btn variant="ghost" onClick={() => setCreating(false)}>Annuleren</Btn>
            <CostHint kind="desk" />
          </div>
          {busy && <div style={{ marginTop: 12 }}><Spinner label={progress ? `Bezig met matchen… groepje ${progress.done} van ${progress.total}` : "De agent leest beide sites in…"} /></div>}
        </Card>
      )}

      {studies == null ? <Spinner label="Laden…" /> : studies.length === 0 && !creating
        ? <Empty icon={Workflow} title="Nog geen migratie-onderzoek" sub="Start een onderzoek: upload de crawls van beide sites en laat de agent het migratieplan maken." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(studies || []).map(s => {
            const total = (s.mapping || []).length;
            const nieuw = (s.mapping || []).filter(m => m.action === "nieuwe content maken" || m.action === "content overnemen").length;
            return (
              <Card key={s.id} hover style={{ padding: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                  <div onClick={() => setOpenId(s.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}><Workflow size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15 }}>{s.name}</span></div>
                    <span style={{ fontSize: 12.5, color: C.muted }}>{total} pagina's · {nieuw} met content-actie · {s.date}</span>
                  </div>
                  <button onClick={() => confirm("Dit migratie-onderzoek verwijderen?", () => save(studies.filter(x => x.id !== s.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  <button onClick={() => setOpenId(s.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                </div>
              </Card>
            );
          })}
        </div>}
    </div>
  );
}

function MigrationDetail({ study, onBack, onDelete, impColor, content, saveContent, goTo, confirm, saveStudy }) {
  const [sel, setSel] = useState({});
  const s = study || { mapping: [], name: "", date: "" };
  const mapping = s.mapping || [];
  const selCount = Object.values(sel).filter(Boolean).length;

  // Kleur per actie: groen als de bestemming klaar is, oranje als er nog tekstwerk ligt,
  // rood als de bestemming nog bepaald moet worden.
  const actionColor = (a) => {
    const v = String(a || "");
    if (v === "nieuwe categorie") return C.bad;
    if (v === "content herschrijven") return C.yellowDark;
    if (v === "1-op-1 doorzetten" || v === "vervanger aanwezig") return C.good;
    return C.muted;
  };
  const shortFrom = (u) => (u || "").replace(/^https?:\/\/[^/]+/i, "") || "/";

  const exportExcel = () => {
    const sheet = {
      name: "Migratieplan",
      rows: [["Van (overgenomen)", "Naar (whitevision)", "Taal", "Vertaling ontbreekt", "Actie", "Toelichting"],
        ...mapping.map(m => [m.from || "", m.to || "", m.lang || "", m.langWarning ? (m.langNote || "ja") : "", normAction(m.action) || "", m.reason || ""])],
    };
    exportToExcel(`Migratieplan ${s.name || ""}`, [sheet]);
  };
  const exportPlan = () => {
    const rows = mapping.map(m => `<tr><td>${esc(m.from)}</td><td>${esc(m.to)}</td><td>${esc(m.lang || "")}${m.langWarning ? " ⚠ " + esc(m.langNote || "") : ""}</td><td>${esc(m.importance)}</td><td>${esc(m.reason)}</td><td>${esc(m.action)}</td></tr>`).join("");
    const html = `<h1>Migratieplan ${esc(s.name)} → whitevision</h1><table border="1" cellpadding="6" cellspacing="0"><tr><th>Van</th><th>Naar</th><th>Taal</th><th>Belang</th><th>Reden</th><th>Actie</th></tr>${rows}</table>${s.note ? `<p><i>${esc(s.note)}</i></p>` : ""}`;
    exportToWord(`Migratieplan ${s.name || ""}`, html);
  };

  const [catPick, setCatPick] = useState(false);
  const [flash, setFlash] = useState("");
  // Paginatypen uit de WordPress-instellingen; daarmee weten we waar een pagina hoort.
  const [wpTypes, setWpTypes] = useState([]);
  const [wpOn, setWpOn] = useState(false);
  useEffect(() => {
    let on = true;
    fetch("/api/wordpress/status", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (on && d) { setWpTypes(d.pageTypes || []); setWpOn(!!d.configured); } })
      .catch(() => {});
    return () => { on = false; };
  }, []);

  // Wijzigingen in de tabel bewaren.
  const updateRows = (fn) => {
    if (!saveStudy) return;
    saveStudy({ ...s, mapping: mapping.map((m, i) => (sel[i] ? fn(m, i) : m)) });
  };
  const updateRow = (idx, patch) => {
    if (!saveStudy) return;
    saveStudy({ ...s, mapping: mapping.map((m, i) => (i === idx ? { ...m, ...patch } : m)) });
  };

  // Bij "content overnemen" of "nieuwe content maken" komt de pagina niet op een
  // bestaande URL terecht, maar op een nieuwe. We stellen dan een pad voor op basis
  // van de oorspronkelijke URL, zodat je niet zelf hoeft te typen.
  const slugOf = (m) => decodeURIComponent((m.from || "").split("?")[0].split("/").filter(Boolean).pop() || "")
    .replace(/\.(html?|php|aspx?)$/i, "")
    .toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
  // De doel-URL volgt uit het paginatype: het pad daarvan plus de naam van de pagina.
  const proposeNewUrl = (m) => {
    const slug = slugOf(m);
    if (!slug) return "";
    const pt = wpTypes.find(t => t.id === m.pageType);
    const prefix = (pt?.pathPrefix || "").replace(/^\/*/, "/").replace(/\/+$/, "");
    return `https://www.whitevision.nl${prefix}/${slug}`;
  };
  // Per actie weten we wat de doel-URL is: te schatten uit de oude slug, al bekend
  // (er is een vervanger), of nog onbekend omdat de pagina nog gemaakt moet worden.
  const ACTIONS = [
    { id: "1-op-1 doorzetten", label: "1-op-1 doorzetten", url: "propose", hint: "Tekst gaat ongewijzigd mee. Doel-URL geschat uit de oude slug." },
    { id: "content herschrijven", label: "Content herschrijven", url: "propose", hint: "Inhoud overnemen, tekst herschrijven. Doel-URL geschat uit de oude slug." },
    { id: "vervanger aanwezig", label: "Vervanger aanwezig", url: "known", hint: "Er is al een pagina op onze site die dit vervangt. Doel-URL is bekend." },
    { id: "nieuwe categorie", label: "Nieuwe categorie", url: "unknown", hint: "Past nergens onder; de bestemming moet nog bepaald worden." },
  ];
  // Oudere onderzoeken gebruikten andere namen; die tonen we als de nieuwe actie,
  // zodat bestaande plannen niet met een leeg vakje in beeld komen.
  const ACTION_ALIAS = {
    "content geschikt": "vervanger aanwezig",
    "content updaten": "vervanger aanwezig",
    "content overnemen": "content herschrijven",
    "vertalen": "content herschrijven",
    "nieuwe content maken": "nieuwe categorie",
    "laten vervallen": "nieuwe categorie",
  };
  const normAction = (a) => {
    const v = String(a || "").trim();
    if (!v) return "";
    if (ACTIONS.some(x => x.id === v)) return v;
    return ACTION_ALIAS[v.toLowerCase()] || "";
  };
  const actionMeta = (a) => ACTIONS.find(x => x.id === a) || null;
  const NEEDS_NEW_URL = ACTIONS.filter(a => a.url === "propose").map(a => a.id);

  // Zet de actie en laat de doel-URL meebewegen met wat die actie betekent.
  const applyAction = (m, v) => {
    const meta = actionMeta(v);
    const next = { ...m, action: v };
    // De oorspronkelijke match onthouden voordat we de URL aanpassen.
    if (next.toOriginal === undefined) next.toOriginal = m.to || "";
    if (!meta) return next;
    if (meta.url === "propose") {
      if (!m.toEdited) {
        const proposed = proposeNewUrl(m);
        if (proposed) { next.to = proposed; next.toProposed = true; next.toUnknown = false; }
      }
    } else if (meta.url === "known") {
      // De match uit het onderzoek is hier leidend; die laten we staan.
      next.toProposed = false; next.toUnknown = false;
    } else if (meta.url === "unknown") {
      if (!m.toEdited) { next.to = ""; }
      next.toProposed = false; next.toUnknown = true;
    } else if (meta.url === "none") {
      next.toProposed = false; next.toUnknown = false;
    }
    return next;
  };
  const bulkAction = (v) => { updateRows(m => applyAction(m, v)); };
  const bulkSelectAll = () => {
    const all = mapping.every((_, i) => sel[i]);
    if (all) setSel({});
    else { const n = {}; mapping.forEach((_, i) => { n[i] = true; }); setSel(n); }
  };

  // Eén-op-één doorzetten naar WordPress: haalt de tekst van de oude pagina op en zet die
  // ongewijzigd als concept klaar. Geen AI, dus geen tegoed. Bedoeld voor blogs en andere
  // pagina's die je letterlijk wilt overnemen.
  const [pubBusy, setPubBusy] = useState(false);
  const [pubLog, setPubLog] = useState(null);
  const [wpHint, setWpHint] = useState(false);

  // ---- Sorteren en kolombreedtes ----
  // Sorteren verandert alleen de weergave; we houden de oorspronkelijke rijpositie bij,
  // zodat aanvinken en bewerken op de juiste regel blijven werken.
  const [fullScreen, setFullScreen] = useState(false);

  // ---- Statuscodes controleren ----
  // Vraagt per URL op wat de server antwoordt. Kost geen AI-tegoed. We doen het in
  // blokjes met voortgang, zodat een lange lijst niet in één keer hoeft.
  const [statusMap, setStatusMap] = useState({});
  const [statusBusy, setStatusBusy] = useState(false);
  const [statusProg, setStatusProg] = useState({ done: 0, total: 0 });
  const statusStop = useRef(false);

  useEffect(() => { setStatusMap((s.urlStatus && typeof s.urlStatus === "object") ? s.urlStatus : {}); }, [s.id]);

  const checkStatuses = async (which) => {
    // which: "from" (overgenomen pagina's), "to" (doel-URL's) of "both"
    const wanted = [];
    mapping.forEach(m => {
      if ((which === "from" || which === "both") && /^https?:\/\//i.test(m.from || "")) wanted.push(m.from);
      if ((which === "to" || which === "both") && /^https?:\/\//i.test(m.to || "")) wanted.push(m.to);
    });
    const list = [...new Set(wanted)];
    if (!list.length) return;
    setStatusBusy(true); statusStop.current = false;
    setStatusProg({ done: 0, total: list.length });
    const found = { ...statusMap };
    const CHUNK = 40;
    for (let i = 0; i < list.length; i += CHUNK) {
      if (statusStop.current) break;
      const slice = list.slice(i, i + CHUNK);
      try {
        const r = await fetch("/api/urls/status", {
          method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ urls: slice }),
        });
        const d = await r.json();
        (d.results || []).forEach(x => { found[x.url] = { code: x.status, redirect: x.redirect || "", error: x.error || "", at: Date.now() }; });
      } catch {
        slice.forEach(u => { found[u] = { code: 0, redirect: "", error: "onbereikbaar", at: Date.now() }; });
      }
      setStatusMap({ ...found });
      setStatusProg({ done: Math.min(i + CHUNK, list.length), total: list.length });
    }
    setStatusBusy(false);
    if (saveStudy) saveStudy({ ...s, urlStatus: found });
  };

  // Hoe een statuscode getoond wordt.
  const statusBadge = (url) => {
    if (!url || !/^https?:\/\//i.test(url)) return null;
    const st = statusMap[url];
    if (!st) return null;
    const c = st.code;
    const info = c === 0 ? { col: C.muted, txt: st.error || "?" }
      : c >= 200 && c < 300 ? { col: C.good, txt: String(c) }
      : c >= 300 && c < 400 ? { col: C.yellowDark, txt: String(c) }
      : c === 404 ? { col: C.bad, txt: "404" }
      : { col: C.bad, txt: String(c) };
    const tip = st.redirect ? `${c} → ${st.redirect}` : st.error ? `${st.error}` : `status ${c}`;
    return <span title={tip} style={{ display: "inline-block", fontSize: 9.5, fontWeight: 700, color: info.col, background: info.col + "1E", padding: "1px 5px", borderRadius: 5, marginLeft: 5, verticalAlign: "middle", cursor: "help" }}>{info.txt}</span>;
  };
  const [sortKey, setSortKey] = useState("");
  const [sortDir, setSortDir] = useState("asc");
  const sortValue = (m, key) => {
    if (key === "from") return String(m.from || "").toLowerCase();
    if (key === "to") return String(m.to || "").toLowerCase();
    if (key === "lang") return String(m.lang || "").toLowerCase();
    if (key === "action") return String(normAction(m.action) || "").toLowerCase();
    if (key === "pageType") {
      const t = wpTypes.find(x => x.id === m.pageType);
      return String(t?.name || "").toLowerCase();
    }
    return "";
  };
  const toggleSort = (key) => {
    if (sortKey === key) { setSortDir(d => d === "asc" ? "desc" : "asc"); }
    else { setSortKey(key); setSortDir("asc"); }
  };
  // Rijen met hun oorspronkelijke index, in de gewenste weergaveorde.
  const viewRows = mapping.map((m, i) => ({ m, i }));
  if (sortKey) {
    viewRows.sort((a, b) => {
      const va = sortValue(a.m, sortKey), vb = sortValue(b.m, sortKey);
      const c = typeof va === "number" ? va - vb : String(va).localeCompare(String(vb), "nl");
      return sortDir === "asc" ? c : -c;
    });
  }

  // Kolombreedtes, versleepbaar. Worden per onderzoek bewaard in de browser.
  const COLS = [
    { key: "_check", label: "", w: 34, sortable: false },
    { key: "from", label: "Van (overgenomen)", w: 250 },
    { key: "to", label: "Naar (whitevision)", w: 280 },
    { key: "lang", label: "Taal", w: 70, center: true },
    ...(wpOn && wpTypes.length > 0 ? [{ key: "pageType", label: "Paginatype", w: 150 }] : []),
    { key: "action", label: "Content GAP / actie", w: 190 },
  ];
  const [colW, setColW] = useState(null);
  useEffect(() => {
    const def = {}; COLS.forEach(c => { def[c.key] = c.w; });
    let stored = null;
    try { stored = JSON.parse(window.localStorage ? window.localStorage.getItem("wv_mig_cols") || "null" : "null"); } catch {}
    setColW({ ...def, ...(stored && typeof stored === "object" ? stored : {}) });
  }, [wpOn, wpTypes.length]);
  const startResize = (key, e) => {
    e.preventDefault(); e.stopPropagation();
    const startX = e.clientX;
    const startW = (colW && colW[key]) || 120;
    const move = (ev) => {
      const next = Math.max(50, startW + (ev.clientX - startX));
      setColW(w => ({ ...(w || {}), [key]: next }));
    };
    const up = () => {
      document.removeEventListener("mousemove", move);
      document.removeEventListener("mouseup", up);
      setColW(w => { try { window.localStorage && window.localStorage.setItem("wv_mig_cols", JSON.stringify(w)); } catch {} return w; });
    };
    document.addEventListener("mousemove", move);
    document.addEventListener("mouseup", up);
  };
  const resetCols = () => {
    const def = {}; COLS.forEach(c => { def[c.key] = c.w; });
    setColW(def);
    try { window.localStorage && window.localStorage.removeItem("wv_mig_cols"); } catch {}
  };
  const totalW = COLS.reduce((t, c) => t + ((colW && colW[c.key]) || c.w), 0);
  const publishDirect = async () => {
    const chosen = mapping.map((m, i) => ({ m, i })).filter(({ i }) => sel[i]);
    if (!chosen.length) return;
    setPubBusy(true); setPubLog(null);
    const results = [];
    for (const { m } of chosen) {
      const pt = wpTypes.find(t => t.id === m.pageType);
      const title = decodeURIComponent((m.from || "").split("?")[0].split("/").filter(Boolean).pop() || "Overgenomen pagina").replace(/[-_]/g, " ").replace(/\.(html?|php|aspx?)$/i, "");
      try {
        // Tekst van de oorspronkelijke pagina ophalen (server-kant, want de browser mag dat niet).
        const cr = await fetch("/api/page", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: m.from }) });
        const cd = await cr.json().catch(() => ({}));
        if (!cr.ok) { results.push({ title, ok: false, error: cd.error || `Kon de pagina niet ophalen (status ${cr.status}).` }); continue; }
        const body = (cd.text || "").trim();
        if (!body) { results.push({ title, ok: false, error: cd.note || "Geen bruikbare tekst op deze pagina gevonden." }); continue; }
        const html = body.split(/\n{2,}/).map(par => `<p>${par.replace(/\n/g, "<br>")}</p>`).join("\n");
        const pr = await fetch("/api/wordpress/publish", {
          method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ title, content: html, restBase: pt?.restBase || "posts", categoryId: pt?.categoryId || null, slug: slugOf(m), contentField: pt?.contentField || "" }),
        });
        const pd = await pr.json();
        results.push(pr.ok
          ? { title, ok: true, editLink: pd.editLink, warn: pd.fieldWritten === false ? `het veld "${pd.fieldName}" kon niet gevuld worden` : "" }
          : { title, ok: false, error: pd.error || "Publiceren mislukte." });
      } catch (e) { results.push({ title, ok: false, error: (e && e.message) ? String(e.message) : "Er ging iets mis bij het ophalen of doorzetten." }); }
    }
    setPubLog(results);
    setPubBusy(false);
  };

  const ACTION_OPTS = ACTIONS.map(a => a.id);
  const createInCategory = (kind) => {
    const chosen = mapping.filter((m, i) => sel[i]);
    if (!chosen.length) { setCatPick(false); return; }
    const entries = chosen.map((m, i) => ({
      id: Date.now() + i, tile: "copy", phase: "todo", kind,
      title: decodeURIComponent((m.from || "").split("/").filter(Boolean).pop() || "Nieuw onderwerp").replace(/[-_]/g, " "),
      angle: `Uit migratie ${s.name} (${m.action || "content"}): ${m.from}`,
      sourceUrl: m.from || "", targetUrl: m.to || "", lang: m.lang || "", pageType: m.pageType || "",
      date: new Date().toISOString().slice(0, 10),
    }));
    saveContent([...entries, ...(content || [])]);
    setCatPick(false); setSel({});
    setFlash(`${chosen.length} onderwerp(en) toegevoegd onder ${KIND_LABEL(kind)} in Copy → Uitwerken.`);
    setTimeout(() => setFlash(""), 6000);
  };

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar overzicht</button>
      <PageHead title={`Migratieplan — ${s.name}`} sub={`${mapping.length} pagina's · ${s.date}`}
        right={<div style={{ display: "flex", gap: 8 }}>
          <Btn size="sm" variant="yellow" icon={Download} onClick={exportExcel}>Excel</Btn>
          <Btn size="sm" variant="ghost" icon={Download} onClick={exportPlan}>Word</Btn>
          <Btn size="sm" variant="danger" icon={Trash2} onClick={onDelete}>Verwijderen</Btn>
        </div>} />
      {wpHint && (
        <Modal onClose={() => setWpHint(false)} title="WordPress is nog niet gekoppeld">
          <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.6, marginBottom: 14 }}>
            Om pagina's rechtstreeks als concept in WordPress te zetten, moet je je site eerst koppelen. Dat doe je één keer bij Instellingen → WordPress: je vult daar het adres van je site in, je gebruikersnaam en een applicatiewachtwoord dat je in WordPress aanmaakt.
          </div>
          <div style={{ display: "flex", gap: 10 }}>
            <Btn variant="yellow" icon={ArrowUpRight} onClick={() => { if (typeof window !== "undefined") window.__settingsTab = "wordpress"; setWpHint(false); goTo("settings"); }}>Naar de instellingen</Btn>
            <Btn variant="ghost" onClick={() => setWpHint(false)}>Later</Btn>
          </div>
        </Modal>
      )}

      {pubLog && (
        <Card style={{ marginBottom: 16, border: `1.5px solid ${pubLog.every(r => r.ok) ? C.good : C.bad}` }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
            <span style={{ fontWeight: 700, fontSize: 14 }}>Naar WordPress gezet — {pubLog.filter(r => r.ok).length} van {pubLog.length} gelukt</span>
            <button onClick={() => setPubLog(null)} style={iconBtn(C.muted)}><X size={15} /></button>
          </div>
          <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
            {pubLog.map((r, i) => (
              <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 12.5 }}>
                {r.ok ? <Check size={13} style={{ color: C.good, flexShrink: 0 }} /> : <AlertTriangle size={13} style={{ color: C.bad, flexShrink: 0 }} />}
                <span style={{ color: C.ink2 }}>{r.title}</span>
                {r.ok && r.editLink && <a href={r.editLink} target="_blank" rel="noopener noreferrer" style={{ color: C.blue, fontSize: 12 }}>openen in WordPress</a>}
                {r.ok && r.warn && <span style={{ color: C.yellowDark, fontSize: 12 }}>let op: {r.warn}</span>}
                {!r.ok && <span style={{ color: C.bad, fontSize: 12 }}>{r.error}</span>}
              </div>
            ))}
          </div>
          <div style={{ fontSize: 11.5, color: C.muted, marginTop: 9, lineHeight: 1.5 }}>Aangemaakt als concept. Loop ze na in WordPress voordat je publiceert.</div>
        </Card>
      )}
      {flash && <Card style={{ marginBottom: 16, border: `1.5px solid ${C.good}`, background: C.good + "12" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <CheckCircle2 size={17} style={{ color: C.good, flexShrink: 0 }} />
          <span style={{ fontSize: 13, color: C.ink, flex: 1 }}>{flash}</span>
          <Btn size="sm" variant="ghost" icon={ArrowUpRight} onClick={() => goTo("content")}>Naar Copy</Btn>
        </div>
      </Card>}
      {s.note && <Card style={{ marginBottom: 16, background: C.paper, border: "none" }}><div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>{s.note}</div></Card>}

      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10, gap: 12, flexWrap: "wrap" }}>
        <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5, maxWidth: 620 }}>Elke overgenomen pagina krijgt een doel-URL en een actie. Houd je muis op het belang-label voor de reden. Vink pagina's aan om ze in bulk aan te passen of er content voor te laten maken.</div>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn size="sm" variant="ghost" icon={pubBusy ? Loader2 : ArrowUpRight} onClick={() => wpOn ? publishDirect() : setWpHint(true)} disabled={pubBusy || (wpOn && !selCount)}
            title={wpOn ? "Neemt de tekst één op één over als concept in WordPress. Geen AI, dus gratis." : "WordPress is nog niet gekoppeld"}>
            {pubBusy ? "Bezig…" : `1-op-1 naar WordPress${wpOn ? ` (${selCount})` : ""}`}</Btn>
          <Btn size="sm" variant="yellow" icon={PenLine} onClick={() => setCatPick(true)} disabled={!selCount}>Naar Content om te herschrijven ({selCount})</Btn>
        </div>
      </div>

      {/* Bulkbewerking van de geselecteerde rijen */}
      <Card style={{ marginBottom: 12, padding: "11px 14px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
          <button onClick={bulkSelectAll} style={{ ...miniBtn(C.paper, C.ink) }}>
            {mapping.length > 0 && mapping.every((_, i) => sel[i]) ? <><Circle size={13} /> Selectie wissen</> : <><CheckCircle2 size={13} /> Alles selecteren</>}
          </button>
          <span style={{ fontSize: 12.5, color: selCount ? C.ink : C.muted, fontWeight: 600 }}>{selCount} geselecteerd</span>

          {wpOn && wpTypes.length > 0 && (
            <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
              <span style={{ fontSize: 12, color: C.muted }}>Paginatype:</span>
              <select value="" onChange={e => { if (e.target.value) { const v = e.target.value; updateRows(m => { const n = { ...m, pageType: v }; if (!m.toEdited) { const pt = wpTypes.find(t => t.id === v); const slug = slugOf(m); if (slug) { const pre = (pt?.pathPrefix || "").replace(/^\/*/, "/").replace(/\/+$/, ""); n.to = `https://www.whitevision.nl${pre}/${slug}`; n.toProposed = true; } } return n; }); e.target.value = ""; } }} disabled={!selCount}
                style={{ padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card, opacity: selCount ? 1 : .5 }}>
                <option value="">— kies —</option>
                {wpTypes.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
              </select>
            </div>
          )}

          <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
            <span style={{ fontSize: 12, color: C.muted }}>Actie:</span>
            <select value="" onChange={e => { if (e.target.value) { bulkAction(e.target.value); e.target.value = ""; } }} disabled={!selCount}
              style={{ padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card, opacity: selCount ? 1 : .5 }}>
              <option value="">— kies —</option>
              {ACTION_OPTS.map(o => <option key={o} value={o}>{o}</option>)}
            </select>
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 8, flexWrap: "wrap" }}>
          <div style={{ fontSize: 11.5, color: C.muted, lineHeight: 1.5, flex: 1, minWidth: 260 }}>
            Kies je een actie waarbij content op je eigen site komt (overnemen, nieuw maken of vertalen), dan stelt de tool meteen een nieuwe doel-URL voor. Klik op een kolomkop om te sorteren, en sleep de rand van een kop om die kolom breder te maken.
          </div>
          {sortKey && <button onClick={() => { setSortKey(""); }} style={{ ...miniBtn("transparent", C.muted) }}>sortering wissen</button>}
          <button onClick={resetCols} style={{ ...miniBtn("transparent", C.muted) }}>kolombreedtes terugzetten</button>
          <button onClick={() => setFullScreen(true)} style={{ ...miniBtn(C.paper, C.ink) }}><ArrowUpRight size={13} /> volledig scherm</button>
        </div>

        {/* Statuscodes: wat antwoordt de server op deze URL's? */}
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 10, paddingTop: 10, borderTop: `1px solid ${C.line}`, flexWrap: "wrap" }}>
          <span style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Statuscodes</span>
          {statusBusy ? <>
            <Loader2 size={14} className="spin" style={{ color: C.yellowDark }} />
            <span style={{ fontSize: 12.5, color: C.ink2 }}>{statusProg.done} van {statusProg.total} gecontroleerd…</span>
            <button onClick={() => { statusStop.current = true; }} style={{ ...miniBtn("transparent", C.bad) }}>stoppen</button>
          </> : <>
            <button onClick={() => checkStatuses("from")} style={{ ...miniBtn(C.paper, C.ink) }}>oude pagina's</button>
            <button onClick={() => checkStatuses("to")} style={{ ...miniBtn(C.paper, C.ink) }}>doel-URL's</button>
            <button onClick={() => checkStatuses("both")} style={{ ...miniBtn(C.yellow, "#fff") }}>allebei</button>
            {Object.keys(statusMap).length > 0 && <>
              <span style={{ fontSize: 12, color: C.muted }}>
                {(() => {
                  const vals = Object.values(statusMap);
                  const ok = vals.filter(v => v.code >= 200 && v.code < 300).length;
                  const red = vals.filter(v => v.code >= 300 && v.code < 400).length;
                  const bad = vals.filter(v => v.code === 0 || v.code >= 400).length;
                  return `${ok} in orde · ${red} doorverwijzing · ${bad} probleem`;
                })()}
              </span>
              <button onClick={() => { setStatusMap({}); if (saveStudy) saveStudy({ ...s, urlStatus: {} }); }} style={{ ...miniBtn("transparent", C.muted) }}>wissen</button>
            </>}
            <span style={{ fontSize: 11.5, color: C.muted, fontWeight: 600 }}>kost geen tegoed</span>
          </>}
        </div>
      </Card>

      {catPick && (
        <Modal onClose={() => setCatPick(false)} title="Kies een categorie">
          <div style={{ fontSize: 13, color: C.ink2, marginBottom: 14, lineHeight: 1.5 }}>In welke categorie wil je de {selCount} geselecteerde pagina('s) uitwerken? Ze komen bij Copy onder die categorie te staan, klaar om uit te werken.</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            {COPY_KINDS.map(k => (
              <button key={k.kind} onClick={() => createInCategory(k.kind)}
                style={{ display: "flex", alignItems: "center", gap: 8, padding: "12px 14px", border: `1.5px solid ${C.line}`, borderRadius: 10, background: C.card, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, color: C.ink, textAlign: "left" }}>
                <PenLine size={15} style={{ color: C.yellowDark, flexShrink: 0 }} /> {k.label}
              </button>
            ))}
          </div>
        </Modal>
      )}

      <div style={fullScreen
        ? { position: "fixed", inset: 0, zIndex: 300, background: C.paper, padding: "14px 16px", display: "flex", flexDirection: "column", overflow: "hidden" }
        : undefined}>
        {fullScreen && (
          <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 12, flexWrap: "wrap", flexShrink: 0 }}>
            <span style={{ fontWeight: 700, fontSize: 16 }}>Migratieplan — {s.name}</span>
            <span style={{ fontSize: 12.5, color: C.muted }}>{mapping.length} pagina's{selCount ? ` · ${selCount} geselecteerd` : ""}</span>
            <div style={{ marginLeft: "auto", display: "flex", gap: 8, flexWrap: "wrap" }}>
              {sortKey && <button onClick={() => setSortKey("")} style={{ ...miniBtn("transparent", C.muted) }}>sortering wissen</button>}
              <button onClick={resetCols} style={{ ...miniBtn("transparent", C.muted) }}>kolommen terugzetten</button>
              <Btn size="sm" variant="ghost" icon={X} onClick={() => setFullScreen(false)}>Sluiten</Btn>
            </div>
          </div>
        )}
      <Card style={{ padding: 0, overflow: "hidden", ...(fullScreen ? { flex: 1, display: "flex", flexDirection: "column", minHeight: 0 } : {}) }}>
        <div style={{ overflowX: "auto", ...(fullScreen ? { overflowY: "auto", flex: 1, minHeight: 0 } : {}) }}>
          <table style={{ width: totalW, minWidth: "100%", borderCollapse: "collapse", fontSize: 12.5, tableLayout: "fixed" }}>
            <colgroup>{COLS.map(c => <col key={c.key} style={{ width: (colW && colW[c.key]) || c.w }} />)}</colgroup>
            <thead><tr style={{ background: C.surface }}>
              {COLS.map(c => (
                <th key={c.key} style={{ ...cmpTh, textAlign: c.center ? "center" : "left", position: "relative", padding: c.key === "_check" ? "9px 4px" : cmpTh.padding, userSelect: "none" }}>
                  {c.sortable === false ? "" : (
                    <button onClick={() => toggleSort(c.key)} title="Klik om te sorteren"
                      style={{ display: "inline-flex", alignItems: "center", gap: 4, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: FONT_BODY, fontSize: cmpTh.fontSize, fontWeight: 700, color: sortKey === c.key ? C.yellow : "#fff" }}>
                      {c.label}
                      {sortKey === c.key && <span style={{ fontSize: 10 }}>{sortDir === "asc" ? "▲" : "▼"}</span>}
                    </button>
                  )}
                  {/* Sleepgreep om de kolom breder of smaller te maken */}
                  <span onMouseDown={(e) => startResize(c.key, e)} title="Versleep om de kolombreedte aan te passen"
                    style={{ position: "absolute", top: 0, right: 0, width: 7, height: "100%", cursor: "col-resize", background: "transparent" }} />
                </th>
              ))}
            </tr></thead>
            <tbody>{mapping.length === 0 ? <tr><td style={cmpTd} colSpan={COLS.length}>Geen resultaten. Draai het onderzoek opnieuw.</td></tr> : viewRows.map(({ m, i }) => (
              <tr key={i} style={{ background: sel[i] ? C.yellow + "12" : i % 2 ? C.paper : C.card }}>
                <td style={{ ...cmpTd, textAlign: "center" }}>
                  <input type="checkbox" checked={!!sel[i]} onChange={() => setSel(p => ({ ...p, [i]: !p[i] }))} />
                </td>
                <td style={{ ...cmpTd, fontFamily: "monospace", fontSize: 11, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={m.from}>{m.from}{statusBadge(m.from)}</td>
                <td style={{ ...cmpTd, fontFamily: "monospace", fontSize: 11 }} title={m.to}>
                  {(() => {
                    // De staat van de doel-URL: voorstel, bekend, of nog te bepalen.
                    const st = m.toEdited ? { c: C.blue, t: "zelf ingevuld" }
                      : m.toUnknown ? { c: C.bad, t: "nog te bepalen" }
                      : m.toProposed ? { c: C.yellowDark, t: "voorstel uit de slug" }
                      : m.to ? { c: C.good, t: "uit de match" }
                      : { c: C.muted, t: "" };
                    // De oorspronkelijke match bewaren zodra je zelf gaat typen, zodat je
                    // altijd terug kunt naar wat het onderzoek voorstelde.
                    const onEdit = (val) => updateRow(i, {
                      to: val, toEdited: true, toProposed: false, toUnknown: false,
                      ...(m.toOriginal === undefined ? { toOriginal: m.to || "" } : {}),
                    });
                    const canRestore = (m.toEdited || m.toProposed || m.toUnknown) && m.toOriginal !== undefined && m.toOriginal !== "";
                    return <>
                      <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
                        <input value={m.to || ""} onChange={e => onEdit(e.target.value)}
                          placeholder={m.toUnknown ? "bestemming nog bepalen…" : "doel-URL"}
                          style={{ flex: 1, minWidth: 0, boxSizing: "border-box", padding: "5px 7px", border: `1px solid ${st.c}55`, borderRadius: 6, fontFamily: "monospace", fontSize: 11, background: st.c + "0E", color: C.ink, outline: "none" }} />
                        {canRestore && (
                          <button onClick={() => updateRow(i, { to: m.toOriginal, toEdited: false, toProposed: false, toUnknown: false })}
                            title={`Terugzetten naar de match uit het onderzoek: ${m.toOriginal}`}
                            style={{ background: "none", border: "none", padding: 2, cursor: "pointer", color: C.good, flexShrink: 0, display: "grid", placeItems: "center" }}>
                            <RefreshCw size={13} />
                          </button>
                        )}
                      </div>
                      <div style={{ display: "flex", alignItems: "center", gap: 5, flexWrap: "wrap" }}>
                        {st.t && <span style={{ fontSize: 10, color: st.c, fontWeight: 700 }}>{st.t}</span>}
                        {statusBadge(m.to)}
                        {statusMap[m.to]?.redirect && (
                          <span title={`Verwijst door naar ${statusMap[m.to].redirect}`}
                            style={{ fontSize: 9.5, color: C.yellowDark, cursor: "help" }}>→ {String(statusMap[m.to].redirect).replace(/^https?:\/\/[^/]+/, "")}</span>
                        )}
                      </div>
                    </>;
                  })()}
                </td>
                <td style={{ ...cmpTd, textAlign: "center", whiteSpace: "nowrap" }}>
                  <span style={{ fontWeight: 700, fontSize: 11.5, color: C.muted }}>{m.lang || ""}</span>
                  {m.langWarning && <span title={m.langNote || "Vertaling ontbreekt"} style={{ marginLeft: 4, color: C.bad, cursor: "help", fontWeight: 700 }}>⚠</span>}
                </td>
                {wpOn && wpTypes.length > 0 && (
                  <td style={cmpTd}>
                    <select value={m.pageType || ""} onChange={e => {
                        const v = e.target.value;
                        const patch = { pageType: v };
                        if (v && !m.toEdited) {
                          const pt = wpTypes.find(t => t.id === v);
                          const slug = slugOf(m);
                          if (slug) {
                            const pre = (pt?.pathPrefix || "").replace(/^\/*/, "/").replace(/\/+$/, "");
                            patch.to = `https://www.whitevision.nl${pre}/${slug}`;
                            patch.toProposed = true;
                          }
                        }
                        updateRow(i, patch);
                      }}
                      style={{ padding: "4px 7px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 11.5, outline: "none", background: C.card, color: m.pageType ? C.ink : C.muted, maxWidth: 140 }}>
                      <option value="">—</option>
                      {wpTypes.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
                    </select>
                  </td>
                )}
                <td style={cmpTd}>
                  <select value={normAction(m.action)} onChange={e => updateRow(i, applyAction(m, e.target.value))}
                    title={actionMeta(normAction(m.action))?.hint || "Kies wat er met deze pagina gebeurt"}
                    style={{ fontSize: 11.5, fontWeight: 700, color: actionColor(normAction(m.action)), background: normAction(m.action) ? actionColor(normAction(m.action)) + "18" : "transparent", padding: "3px 7px", borderRadius: 7, border: "none", fontFamily: FONT_BODY, cursor: "pointer", outline: "none", maxWidth: 165 }}>
                    <option value="">—</option>
                    {ACTIONS.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
                  </select>
                </td>
              </tr>
            ))}</tbody>
          </table>
        </div>
      </Card>
      </div>
      {!fullScreen && (
        <Card style={{ marginTop: 12, background: C.paper, border: "none" }}>
          <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 7 }}>Wat de acties betekenen</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
            {ACTIONS.map(a => (
              <div key={a.id} style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>
                <b style={{ color: C.ink }}>{a.label}</b> — {a.hint}
              </div>
            ))}
          </div>
          <div style={{ fontSize: 11.5, color: C.muted, marginTop: 9, lineHeight: 1.5 }}>
            De kleur van de doel-URL laat zien waar hij vandaan komt: groen is de match uit het onderzoek, geel een voorstel op basis van de oude slug, blauw door jou ingevuld, rood nog te bepalen.
          </div>
        </Card>
      )}
    </div>
  );
}

function AgentSchedule({ goal }) {
  const [cfg, setCfg] = useState(null);
  const [saved, setSaved] = useState(false);
  const [ranNow, setRanNow] = useState(false);

  useEffect(() => {
    fetch("/api/agent/brand/config", { credentials: "include" }).then(r => r.ok ? r.json() : null).then(d => { if (d) setCfg(d); }).catch(() => {});
  }, []);
  if (!cfg) return null;
  const save = (patch) => {
    const next = { ...cfg, ...patch };
    setCfg(next);
    fetch("/api/agent/brand/config", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(next) })
      .then(r => r.ok ? r.json() : null).then(d => { if (d) { setCfg(d); setSaved(true); setTimeout(() => setSaved(false), 1500); } }).catch(() => {});
  };
  const runNow = () => {
    fetch("/api/agent/brand/run-now", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ goal }) })
      .then(r => r.ok ? r.json() : null).then(() => { setRanNow(true); setTimeout(() => setRanNow(false), 6000); }).catch(() => {});
  };

  return (
    <Card style={{ marginTop: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Workflow size={17} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15 }}>Automatisch laten draaien</span>
      </div>
      <div style={{ fontSize: 13, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Laat deze agent op een vast ritme draaien op de achtergrond — ook als je niet ingelogd bent. De voorstellen verschijnen in je Postvak; jij keurt goed. Het doel hierboven wordt gebruikt als vaste opdracht.</div>
      <label style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 13px", background: C.paper, borderRadius: 10, marginBottom: 12, cursor: "pointer" }}>
        <input type="checkbox" checked={!!cfg.enabled} onChange={e => save({ enabled: e.target.checked, goal })} />
        <span style={{ fontSize: 14, fontWeight: 600 }}>Geplande runs inschakelen</span>
      </label>
      {cfg.enabled && (
        <div style={{ display: "flex", gap: 14, flexWrap: "wrap", alignItems: "flex-end" }}>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Frequentie</label>
            <select value={cfg.freq} onChange={e => save({ freq: e.target.value, goal })} style={{ padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
              <option value="daily">Dagelijks</option><option value="weekly">Wekelijks</option><option value="monthly">Maandelijks</option>
            </select>
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Concepten per run</label>
            <select value={cfg.maxConcepts} onChange={e => save({ maxConcepts: parseInt(e.target.value), goal })} style={{ padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
              {[1, 2, 3, 4, 5].map(n => <option key={n} value={n}>{n}</option>)}
            </select>
          </div>
          {cfg.lastRun && <div style={{ fontSize: 12.5, color: C.muted }}>Laatste run: {new Date(cfg.lastRun).toLocaleString("nl-NL")}</div>}
        </div>
      )}
      <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 14 }}>
        <Btn variant="ghost" size="sm" icon={Workflow} onClick={runNow}>Nu op de achtergrond draaien</Btn>
        {saved && <span style={{ fontSize: 12.5, color: C.good, fontWeight: 600 }}>Opgeslagen</span>}
        {ranNow && <span style={{ fontSize: 12.5, color: C.yellowDark, fontWeight: 600 }}>Gestart — verschijnt straks in je Postvak</span>}
      </div>
    </Card>
  );
}

// Postvak: voorstellen van geplande/handmatige agent-runs, klaar voor goedkeuring.
function AgentInbox({ ctx, onBack }) {
  const { content, saveContent, confirm } = ctx;
  const [runs, setRuns] = useState(null);
  const [openRun, setOpenRun] = useState(null);

  const load = () => fetch("/api/agent/inbox", { credentials: "include" }).then(r => r.ok ? r.json() : null).then(d => { if (d) setRuns(d.runs || []); }).catch(() => setRuns([]));
  useEffect(() => { load(); }, []);

  const dismiss = (id) => confirm("Dit voorstel uit het postvak verwijderen?", () => {
    fetch(`/api/agent/inbox/${id}/dismiss`, { method: "POST", credentials: "include" }).then(() => { setOpenRun(null); load(); }).catch(() => {});
  });
  const approve = (run, picked) => {
    const entries = picked.map((c, i) => ({
      id: Date.now() + i, tile: "thought", phase: "done", kind: c.kind === "whitepaper" ? "whitepaper" : c.kind === "social" ? "social" : "blog",
      title: c.title, body: c.body, sources: c.sources || [], date: new Date().toISOString().slice(0, 10), fromAgent: "Content marketeer (gepland)",
    }));
    saveContent([...entries, ...(content || [])]);
    fetch(`/api/agent/inbox/${run.id}/dismiss`, { method: "POST", credentials: "include" }).then(() => { setOpenRun(null); load(); }).catch(() => {});
  };

  if (openRun) {
    const run = (runs || []).find(r => String(r.id) === String(openRun.id)) || openRun;
    return <InboxRunDetail run={run} onBack={() => setOpenRun(null)} onApprove={approve} onDismiss={() => dismiss(run.id)} />;
  }

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar agents</button>}
      <PageHead title="Postvak" sub="Voorstellen die agents op de achtergrond hebben klaargezet. Bekijk, keur goed of verwijder." />
      {runs == null && <Spinner label="Postvak laden…" />}
      {runs && runs.length === 0 && <Empty icon={Workflow} title="Postvak is leeg" sub="Zodra een geplande agent draait, verschijnen de voorstellen hier." />}
      <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        {(runs || []).map(run => (
          <Card key={run.id} hover style={{ padding: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
              <div onClick={() => setOpenRun(run)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}>
                  <Sparkles size={15} style={{ color: C.yellowDark }} />
                  <span style={{ fontWeight: 700, fontSize: 15 }}>Content marketeer</span>
                  <Tag color={C.ink} bg={C.yellow}>{(run.concepts || []).length} concepten</Tag>
                </div>
                <span style={{ fontSize: 12.5, color: C.muted }}>{run.goal ? `"${run.goal}" · ` : ""}{new Date(run.date).toLocaleString("nl-NL")}</span>
              </div>
              <button onClick={() => dismiss(run.id)} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
              <button onClick={() => setOpenRun(run)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

function InboxRunDetail({ run, onBack, onApprove, onDismiss }) {
  const safeRun = run || { concepts: [], themes: [] };
  const [picks, setPicks] = useState(() => Object.fromEntries((safeRun.concepts || []).map(c => [c.id, true])));
  const kindLabel = (k) => k === "whitepaper" ? "whitepaper" : k === "social" ? "social post" : "blog";
  const picked = (safeRun.concepts || []).filter(c => picks[c.id]);
  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar postvak</button>
      <PageHead title="Voorstel van de Content marketeer" sub={`${safeRun.goal ? '"' + safeRun.goal + '" · ' : ""}${safeRun.date ? new Date(safeRun.date).toLocaleString("nl-NL") : ""}`} />
      {safeRun.summary && <Card style={{ marginBottom: 16 }}>
        <div style={sectionLabel}>Stand van zaken</div>
        <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.6, marginTop: 6 }}>{safeRun.summary}</div>
        {safeRun.themes?.length > 0 && <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12 }}>
          {safeRun.themes.map((t, i) => <span key={i} title={t.why} style={{ fontSize: 12.5, fontWeight: 600, padding: "5px 11px", borderRadius: 8, background: C.yellow + "22", border: `1px solid ${C.yellow}` }}>{t.theme}</span>)}
        </div>}
      </Card>}
      {(safeRun.concepts || []).map(c => (
        <Card key={c.id} style={{ marginBottom: 12, border: `1.5px solid ${picks[c.id] ? C.good : C.line}` }}>
          <div style={{ display: "flex", gap: 9, alignItems: "flex-start", marginBottom: 8 }}>
            <input type="checkbox" checked={!!picks[c.id]} onChange={() => setPicks(p => ({ ...p, [c.id]: !p[c.id] }))} style={{ marginTop: 5 }} />
            <div><div style={{ fontWeight: 700, fontSize: 15 }}>{c.title}</div>
              <div style={{ display: "flex", gap: 7, marginTop: 3 }}><Tag color={C.yellowDark}>{kindLabel(c.kind)}</Tag>{c.theme && <Tag color={C.muted}>{c.theme}</Tag>}</div></div>
          </div>
          <details><summary style={{ cursor: "pointer", fontSize: 13, color: C.blue, fontWeight: 600 }}>Bekijk concept</summary>
            <div style={{ fontSize: 14, lineHeight: 1.6, color: C.ink, marginTop: 10, paddingTop: 10, borderTop: `1px solid ${C.line}` }} dangerouslySetInnerHTML={{ __html: contentToHtml(c.body) }} />
          </details>
        </Card>
      ))}
      <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 8 }}>
        <Btn variant="yellow" icon={Check} onClick={() => onApprove(safeRun, picked)} disabled={!picked.length}>Keur {picked.length} goed → naar Content</Btn>
        <Btn variant="danger" icon={Trash2} onClick={onDismiss}>Verwijder dit voorstel</Btn>
      </div>
    </div>
  );
}

/* ============================== TARGETING ============================== */
/* ============================== DATA ============================== */
const DATA_TILES = [
  { id: "targeting", label: "Targetinglijsten", icon: Users, desc: "Doelaccounts en -contacten uploaden en TAM/SAM/SOM bijhouden per product." },
  { id: "partners", label: "Partnerlijst", icon: Briefcase, desc: "Partners bijhouden: relatie, contactstatus en of er een link is." },
  { id: "events", label: "Events", icon: CalendarDays, desc: "Beurzen en events bijhouden, je ervaring noteren en suggesties krijgen." },
];

function DataSection({ ctx }) {
  const [tile, setTile] = useState(null);
  if (tile === "targeting") return <Targeting ctx={ctx} onBack={() => setTile(null)} />;
  if (tile === "partners") return <Partners ctx={ctx} onBack={() => setTile(null)} />;
  if (tile === "events") return <Events ctx={ctx} onBack={() => setTile(null)} />;
  return (
    <div>
      <PageHead title="Lijsten" sub="Je lijsten en marktdata op één plek: targeting, partners en events." />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }}>
        {DATA_TILES.map(t => (
          <Card key={t.id} hover onClick={() => setTile(t.id)} style={{ cursor: "pointer" }}>
            <div style={{ width: 46, height: 46, borderRadius: 13, background: C.yellow + "22", display: "grid", placeItems: "center", marginBottom: 12 }}>
              <t.icon size={23} style={{ color: C.ink }} />
            </div>
            <div style={{ fontWeight: 700, fontSize: 16.5 }}>{t.label}</div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{t.desc}</div>
            <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>Openen <ArrowUpRight size={15} /></div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ============================== PARTNERS ============================== */
function Partners({ ctx, onBack }) {
  const { profile, confirm } = ctx;
  const [partners, setPartners] = useState(null);
  const [phase, setPhase] = useState("backlog"); // backlog | benaderen | actief
  const [sel, setSel] = useState({});
  const [detail, setDetail] = useState(null);
  const [detailBusy, setDetailBusy] = useState(false);
  const [busy, setBusy] = useState(false);
  const [bulk, setBulk] = useState(false);
  const [bulkText, setBulkText] = useState("");
  const fileRef = useRef(null);

  useEffect(() => { (async () => { try { setPartners(await store.get("wv_partners", [])); } catch { setPartners([]); } })(); }, []);
  const save = (v) => { setPartners(v); store.set("wv_partners", v); };

  const TYPES = ["Strategisch", "Reseller", "Technologie", "Referral", "Integratie", "Overig"];
  const PHASES = [["backlog", "Backlog"], ["benaderen", "Benaderen"], ["actief", "Actief partnership"]];
  const SENTIMENT = ["Positief", "Neutraal", "Negatief"];

  const blank = (patch = {}) => { const base = { id: Date.now() + Math.floor(Math.random() * 1000), name: "", type: "Overig", phase: "backlog", sentiment: "Neutraal", contactName: "", contactEmail: "", contactPhone: "", theirUrl: "", ourUrl: "", description: "", notes: "" }; const m = { ...base, ...patch }; if (m.id == null) m.id = base.id; return m; };
  const addPartner = () => { const p = blank({ phase }); save([p, ...(partners || [])]); setDetail(p); };
  const upd = (id, patch) => save((partners || []).map(p => p.id === id ? { ...p, ...patch } : p));
  const updDetail = (patch) => { setDetail(d => ({ ...d, ...patch })); upd(detail.id, patch); };

  const inPhase = (partners || []).filter(p => (p.phase || "backlog") === phase);
  const count = (ph) => (partners || []).filter(p => (p.phase || "backlog") === ph).length;
  const selCount = Object.values(sel).filter(Boolean).length;
  const toggleSel = (id) => setSel(s => ({ ...s, [id]: !s[id] }));

  // AI verrijkt partners (type, contactgegevens, hun partnerpagina-URL, omschrijving). Geen verzinsels.
  const enrichPartners = async (rows) => {
    const ourSite = profile?.website ? profile.website.trim() : "";
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Voor elk onderstaand partnerbedrijf, vul met web search aan: type partner (kies uit: ${TYPES.join(", ")}), algemene contactgegevens (e-mail en telefoon van het bedrijf, indien openbaar), de URL van hun eigen website (bij voorkeur hun partner- of over-onspagina), en een korte omschrijving (1-2 zinnen) van wat ze doen en hoe ze bij whitevision passen. Verzin niets; laat leeg als je het niet betrouwbaar weet.\n\nPARTNERS:\n${rows.map((r, i) => `${i + 1}. ${r.name}`).join("\n")}\n\nJSON: {"partners":[{"name":"exact","type":"","contactEmail":"","contactPhone":"","theirUrl":"","description":""}]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: true, model: "light" });
    const out = Array.isArray(r?.partners) ? r.partners : [];
    return rows.map((row, i) => {
      const m = out.find(o => (o.name || "").trim().toLowerCase() === row.name.trim().toLowerCase()) || out[i] || {};
      return blank({
        id: row.id, name: row.name, phase: row.phase || "backlog", sentiment: row.sentiment || "Neutraal",
        type: row.type || (TYPES.includes(m.type) ? m.type : "Overig"),
        contactName: row.contactName || "", contactEmail: row.contactEmail || m.contactEmail || "", contactPhone: row.contactPhone || m.contactPhone || "",
        theirUrl: row.theirUrl || m.theirUrl || "", ourUrl: row.ourUrl || "",
        description: row.description || m.description || "", notes: row.notes || "",
      });
    });
  };

  const enrichSelected = async () => {
    const chosen = (partners || []).filter(p => sel[p.id]);
    if (!chosen.length) return;
    setBusy(true);
    try { const en = await enrichPartners(chosen); const byId = {}; en.forEach(p => byId[p.id] = p); save((partners || []).map(p => byId[p.id] || p)); setSel({}); } catch {}
    setBusy(false);
  };
  const moveSelected = (toPhase) => {
    if (!toPhase) return;
    const ids = new Set(Object.keys(sel).filter(k => sel[k]).map(Number));
    if (!ids.size) return;
    save((partners || []).map(p => ids.has(p.id) ? { ...p, phase: toPhase } : p));
    setSel({});
  };
  const enrichOne = async () => {
    if (!detail) return; setDetailBusy(true);
    try { const [p] = await enrichPartners([detail]); if (p) { setDetail(p); upd(detail.id, p); } } catch {}
    setDetailBusy(false);
  };

  // AI stelt mogelijke partners voor; die komen in de backlog.
  const suggest = async () => {
    setBusy(true);
    try {
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel met web search 6-8 concrete bedrijven voor die interessante partners voor whitevision kunnen zijn (resellers, technologiepartners, integraties, referral) binnen onze markt (AI-driven IDP, purchase-to-pay, ERP-integraties). Per partner: naam, type, en kort waarom relevant.\n\nJSON: {"partners":[{"name":"...","type":"...","why":"..."}]}`;
      const { data: r } = await callAIFull(prompt, { json: true, useSearch: true, model: "light" });
      const added = (r?.partners || []).map(s => blank({ name: s.name, type: TYPES.includes(s.type) ? s.type : "Overig", phase: "backlog", description: s.why || "" }));
      if (added.length) save([...added, ...(partners || [])]);
    } catch {}
    setBusy(false);
  };

  const parseBulkLines = (text) => String(text || "").split(/\r?\n/).map(l => l.trim()).filter(Boolean).map(line => {
    const p = line.split("|").map(x => x.trim());
    return { name: p[0] || "", type: p[1] || "Overig", contactEmail: p[2] || "", theirUrl: p[3] || "" };
  }).filter(r => r.name);
  const runBulk = async (rows, withAi) => {
    if (!rows.length) return;
    setBusy(true);
    try {
      const prepared = rows.map(r => ({ ...r, phase: "backlog" }));
      const added = withAi ? await enrichPartners(prepared) : prepared.map(r => blank({ ...r, phase: "backlog" }));
      save([...added, ...(partners || [])]); setBulk(false); setBulkText("");
    } catch { save([...rows.map(r => blank({ ...r, phase: "backlog" })), ...(partners || [])]); setBulk(false); setBulkText(""); }
    setBusy(false);
  };
  const onUpload = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      const csv = parseCSVRows(String(reader.result || ""));
      let rows = [];
      if (csv.length) {
        const header = csv[0].map(h => (h || "").toLowerCase());
        const looks = header.some(h => /naam|name|partner|bedrijf|company/.test(h));
        const idx = (ns) => header.findIndex(h => ns.some(n => h.includes(n)));
        const iName = looks ? idx(["naam", "name", "partner", "bedrijf", "company"]) : 0;
        const iType = looks ? idx(["type", "soort"]) : -1;
        const iMail = looks ? idx(["mail", "email", "e-mail"]) : -1;
        const iUrl = looks ? idx(["url", "website", "link"]) : -1;
        for (let r = looks ? 1 : 0; r < csv.length; r++) {
          const row = csv[r]; if (!row) continue;
          const cell = (i) => (i >= 0 && i < row.length ? (row[i] || "").trim() : "");
          const name = (iName >= 0 ? cell(iName) : row[0] || "").trim(); if (!name) continue;
          rows.push({ name, type: cell(iType) || "Overig", contactEmail: cell(iMail), theirUrl: cell(iUrl) });
        }
      }
      runBulk(rows, false);
    };
    reader.readAsText(f); e.target.value = "";
  };

  const sentColor = (s) => s === "Positief" ? C.good : s === "Negatief" ? C.bad : C.muted;
  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff", whiteSpace: "nowrap" };
  const cellInput = { width: "100%", boxSizing: "border-box", padding: "6px 7px", border: "1px solid transparent", borderRadius: 6, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: "transparent" };

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Lijsten</button>
      <PageHead title="Partnerlijst" sub="Beheer partners per fase: van backlog naar benaderen naar actief partnership. Klik op een naam voor alle details."
        right={<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn variant="ghost" size="sm" icon={Sparkles} onClick={suggest} disabled={busy}>{busy ? "Bezig…" : "AI-suggesties"}</Btn>
          <Btn variant="ghost" size="sm" icon={Plus} onClick={() => setBulk(b => !b)}>Bulk</Btn>
          <Btn variant="yellow" size="sm" icon={Plus} onClick={addPartner}>Partner</Btn>
        </div>} />

      {bulk && (
        <Card style={{ marginBottom: 18, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 6 }}>Meerdere partners toevoegen (in backlog)</div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Eén per regel. Alleen naam volstaat, of: <span style={{ fontFamily: "monospace" }}>naam | type | e-mail | url</span>. Of upload een CSV.</div>
          <textarea value={bulkText} onChange={e => setBulkText(e.target.value)} rows={5} placeholder={"Bijv.:\nAFAS Software\nExact | Technologie\nVisma"}
            style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: "monospace", fontSize: 12.5, resize: "vertical", outline: "none", marginBottom: 12 }} />
          {busy ? <Spinner label="Bezig…" /> : (
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
              <Btn variant="yellow" icon={Plus} onClick={() => runBulk(parseBulkLines(bulkText), true)} disabled={!bulkText.trim()}>Toevoegen + AI-aanvullen</Btn>
              <Btn variant="ghost" icon={Plus} onClick={() => runBulk(parseBulkLines(bulkText), false)} disabled={!bulkText.trim()}>Alleen toevoegen</Btn>
              <Btn variant="ghost" icon={Upload} onClick={() => fileRef.current?.click()}>CSV uploaden</Btn>
              <input ref={fileRef} type="file" accept=".csv,.txt,.tsv,text/csv,text/plain" onChange={onUpload} style={{ display: "none" }} />
              <Btn variant="ghost" onClick={() => { setBulk(false); setBulkText(""); }}>Annuleren</Btn>
            </div>
          )}
        </Card>
      )}

      {/* Fase-tabbladen */}
      <div style={{ display: "flex", gap: 6, marginBottom: 16, borderBottom: `1.5px solid ${C.line}` }}>
        {PHASES.map(([id, label]) => (
          <button key={id} onClick={() => { setPhase(id); setSel({}); }}
            style={{ padding: "9px 16px", fontSize: 13, fontWeight: 700, cursor: "pointer", background: "none", border: "none", borderBottom: `2.5px solid ${phase === id ? C.yellowDark : "transparent"}`, color: phase === id ? C.ink : C.muted, marginBottom: -1.5 }}>
            {label} <span style={{ color: C.muted, fontWeight: 600 }}>({count(id)})</span>
          </button>
        ))}
      </div>

      {selCount > 0 && (
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", background: C.yellow + "18", border: `1.5px solid ${C.yellow}`, borderRadius: 10, marginBottom: 14, flexWrap: "wrap" }}>
          <span style={{ fontSize: 13, fontWeight: 700 }}>{selCount} geselecteerd</span>
          <Btn size="sm" variant="yellow" icon={Sparkles} onClick={enrichSelected} disabled={busy}>{busy ? "Bezig…" : "Verrijk met AI"}</Btn>
          <span style={{ fontSize: 12.5, color: C.muted }}>Verplaats naar:</span>
          <select value="" onChange={e => moveSelected(e.target.value)} style={{ padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, background: C.card, outline: "none", cursor: "pointer" }}>
            <option value="">— kies fase —</option>
            {PHASES.filter(([id]) => id !== phase).map(([id, l]) => <option key={id} value={id}>{l}</option>)}
          </select>
          <button onClick={() => setSel({})} style={{ fontSize: 12.5, fontWeight: 600, color: C.muted, background: "none", border: "none", cursor: "pointer" }}>Selectie wissen</button>
        </div>
      )}

      {partners == null ? <Spinner label="Laden…" /> : inPhase.length === 0 ? <Empty icon={Briefcase} title="Nog niets in deze fase" sub={phase === "backlog" ? "Voeg partners toe, plak er meerdere in bulk, of vraag AI om suggesties." : "Verplaats partners hierheen via de detailkaart."} /> : (
        <Card style={{ padding: 0, overflow: "hidden" }}>
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={{ ...th, width: 34 }}></th>
                <th style={{ ...th, minWidth: 160 }}>Naam</th>
                <th style={{ ...th, minWidth: 110 }}>Type</th>
                <th style={{ ...th, minWidth: 160 }}>Contact</th>
                <th style={{ ...th, minWidth: 60 }}>Hun site</th>
                <th style={{ ...th, minWidth: 60 }}>Onze pagina</th>
                <th style={{ ...th, minWidth: 110 }}>Sentiment</th>
                <th style={{ ...th, width: 70 }}></th>
              </tr></thead>
              <tbody>
                {inPhase.map((p, i) => {
                  const tip = [p.type, p.description].filter(Boolean).join(" — ") || "Geen omschrijving. Open de kaart om die toe te voegen.";
                  return (
                    <tr key={p.id} style={{ background: sel[p.id] ? C.yellow + "12" : i % 2 ? C.paper : C.card, borderBottom: `1px solid ${C.line}` }}>
                      <td style={{ padding: "4px 6px", textAlign: "center" }}><input type="checkbox" checked={!!sel[p.id]} onChange={() => toggleSel(p.id)} /></td>
                      <td style={{ padding: "4px 6px" }}><span title={tip} onClick={() => setDetail(p)} style={{ fontWeight: 700, cursor: "pointer", borderBottom: `1px dotted ${C.muted}` }}>{p.name || "(naamloos)"}</span></td>
                      <td style={{ padding: "4px 6px", color: C.ink2 }}>{p.type || "—"}</td>
                      <td style={{ padding: "4px 6px", color: C.ink2, fontSize: 11.5 }}>{p.contactEmail || p.contactName || p.contactPhone || "—"}</td>
                      <td style={{ padding: "4px 6px", textAlign: "center" }}>{p.theirUrl ? <a href={p.theirUrl.startsWith("http") ? p.theirUrl : "https://" + p.theirUrl} target="_blank" rel="noopener noreferrer" title={p.theirUrl} style={{ color: C.blue }}><Globe size={15} /></a> : <span style={{ color: C.line }}>—</span>}</td>
                      <td style={{ padding: "4px 6px", textAlign: "center" }}>{p.ourUrl ? <a href={p.ourUrl.startsWith("http") ? p.ourUrl : "https://" + p.ourUrl} target="_blank" rel="noopener noreferrer" title={p.ourUrl} style={{ color: C.good }}><Check size={15} /></a> : <span style={{ color: C.line }}>—</span>}</td>
                      <td style={{ padding: "4px 6px" }}><span style={{ fontWeight: 700, color: sentColor(p.sentiment) }}>{p.sentiment || "Neutraal"}</span></td>
                      <td style={{ padding: "4px 6px", textAlign: "center", whiteSpace: "nowrap" }}>
                        <button onClick={() => setDetail(p)} title="Bewerken" style={{ background: "none", border: "none", cursor: "pointer", color: C.ink2 }}><Edit3 size={15} /></button>
                        <button onClick={() => confirm(`Partner "${p.name || ""}" verwijderen?`, () => save(partners.filter(x => x.id !== p.id)))} title="Verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </Card>
      )}
      {partners && inPhase.length > 0 && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Beweeg over een naam voor het type en de omschrijving. Klik op de naam of het potloodje voor alle details en om de fase te wijzigen. Vink rijen aan om in bulk te verrijken.</div>}

      {detail && (
        <Modal onClose={() => setDetail(null)} title="Partner bewerken">
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <Field label="Naam" value={detail.name || ""} onChange={v => updDetail({ name: v })} />
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
              <div>
                <label style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, display: "block", marginBottom: 5 }}>Type</label>
                <select value={detail.type || "Overig"} onChange={e => updDetail({ type: e.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>{TYPES.map(t => <option key={t}>{t}</option>)}</select>
              </div>
              <div>
                <label style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, display: "block", marginBottom: 5 }}>Fase</label>
                <select value={detail.phase || "backlog"} onChange={e => updDetail({ phase: e.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>{PHASES.map(([id, l]) => <option key={id} value={id}>{l}</option>)}</select>
              </div>
              <div>
                <label style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, display: "block", marginBottom: 5 }}>Sentiment</label>
                <select value={detail.sentiment || "Neutraal"} onChange={e => updDetail({ sentiment: e.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>{SENTIMENT.map(s => <option key={s}>{s}</option>)}</select>
              </div>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
              <Field label="Contactpersoon" value={detail.contactName || ""} onChange={v => updDetail({ contactName: v })} />
              <Field label="E-mail" value={detail.contactEmail || ""} onChange={v => updDetail({ contactEmail: v })} />
              <Field label="Telefoon" value={detail.contactPhone || ""} onChange={v => updDetail({ contactPhone: v })} />
            </div>
            <Field label="Partnerpagina op hún website" value={detail.theirUrl || ""} onChange={v => updDetail({ theirUrl: v })} placeholder="https://…" />
            <Field label="Partnerpagina op ónze website (indien aanwezig)" value={detail.ourUrl || ""} onChange={v => updDetail({ ourUrl: v })} placeholder="https://whitevision.com/partners/…" />
            <Field label="Omschrijving (type & waarom relevant)" value={detail.description || ""} onChange={v => updDetail({ description: v })} multiline rows={3} />
            <Field label="Eigen notities" value={detail.notes || ""} onChange={v => updDetail({ notes: v })} multiline rows={3} placeholder="Afspraken, contactmomenten, status…" />
            <div style={{ display: "flex", gap: 10, justifyContent: "space-between", alignItems: "center", flexWrap: "wrap" }}>
              {detailBusy ? <Spinner label="AI vult deze partner aan…" /> : <Btn variant="ghost" icon={Sparkles} onClick={enrichOne}>Aanvullen met AI</Btn>}
              <Btn variant="yellow" icon={Check} onClick={() => setDetail(null)}>Klaar</Btn>
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

/* ============================== EVENTS ============================== */
function Events({ ctx, onBack }) {
  const { profile, confirm } = ctx;
  const [events, setEvents] = useState(null);
  const [busy, setBusy] = useState(false);
  const [suggestions, setSuggestions] = useState(null);
  const [bulk, setBulk] = useState(false);       // bulk-invoer paneel open
  const [bulkText, setBulkText] = useState("");
  const [enrich, setEnrich] = useState(true);     // AI optioneel laten aanvullen
  const [bulkBusy, setBulkBusy] = useState(false);
  const [bulkMsg, setBulkMsg] = useState("");
  const fileRef = useRef(null);

  useEffect(() => { (async () => { try { setEvents(await store.get("wv_events", [])); } catch { setEvents([]); } })(); }, []);
  const save = (v) => { setEvents(v); store.set("wv_events", v); };

  const blankEvent = (patch = {}) => { const base = { id: Date.now() + Math.floor(Math.random() * 1000), name: "", date: "", location: "", audience: "", url: "", cost: "", phase: "backlog", experience: "Neutraal", type: "", description: "", notes: "" }; const merged = { ...base, ...patch }; if (merged.id == null) merged.id = base.id; return merged; };
  const addEvent = () => { const e = blankEvent({ phase }); save([e, ...(events || [])]); setDetail(e); };
  const upd = (id, patch) => save((events || []).map(e => e.id === id ? { ...e, ...patch } : e));

  const [sel, setSel] = useState({});            // bulk-selectie {id:true}
  const [detail, setDetail] = useState(null);    // event dat in de detailkaart open staat
  const [detailBusy, setDetailBusy] = useState(false);
  const [phase, setPhase] = useState("backlog"); // backlog | heen | deelgenomen
  const EV_PHASES = [["backlog", "Backlog"], ["heen", "Willen we heen"], ["deelgenomen", "Deelgenomen"]];
  const EXP = ["Positief", "Neutraal", "Negatief"];
  const inPhase = (events || []).filter(e => (e.phase || "backlog") === phase);
  const phaseCount = (ph) => (events || []).filter(e => (e.phase || "backlog") === ph).length;
  const selCount = Object.values(sel).filter(Boolean).length;
  const toggleSel = (id) => setSel(s => ({ ...s, [id]: !s[id] }));

  // Geselecteerde events alsnog door AI laten verrijken.
  const enrichSelected = async () => {
    const chosen = (events || []).filter(e => sel[e.id]);
    if (!chosen.length) return;
    setBusy(true);
    try {
      const enriched = await enrichEvents(chosen);
      const byId = {}; enriched.forEach(e => { byId[e.id] = e; });
      save((events || []).map(e => byId[e.id] ? byId[e.id] : e));
      setSel({});
    } catch { /* laat bestaande events ongemoeid bij fout */ }
    setBusy(false);
  };
  const moveSelected = (toPhase) => {
    if (!toPhase) return;
    const ids = new Set(Object.keys(sel).filter(k => sel[k]).map(Number));
    if (!ids.size) return;
    save((events || []).map(e => ids.has(e.id) ? { ...e, phase: toPhase } : e));
    setSel({});
  };

  // Eén event in de detailkaart door AI laten verrijken.
  const enrichOne = async () => {
    if (!detail) return;
    setDetailBusy(true);
    try {
      const [e] = await enrichEvents([detail]);
      if (e) { setDetail(e); upd(detail.id, e); }
    } catch { /* niets */ }
    setDetailBusy(false);
  };
  const updDetail = (patch) => { setDetail(d => ({ ...d, ...patch })); upd(detail.id, patch); };

  // AI vult ontbrekende velden aan (behalve kosten) op basis van de eventnaam.
  const enrichEvents = async (rows) => {
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${new Date().toLocaleDateString("nl-NL")}. OPDRACHT: Voor elk onderstaand event, vul met web search de ontbrekende gegevens aan: datum (van de eerstvolgende of meest recente editie; geef aan als onzeker met "±"), locatie (stad/land), doelgroep (kort), de offici\u00eble website-URL, het type event (bijv. beurs, congres, seminar, webinar, netwerkevent), en een korte omschrijving (1-2 zinnen) inclusief waarom het relevant kan zijn voor whitevision. Vul GEEN kosten in. Behoud de naam exact. Verzin niets; laat een veld leeg als je het niet betrouwbaar weet.\n\nEVENTS:\n${rows.map((r, i) => `${i + 1}. ${r.name}${r.date ? " | datum: " + r.date : ""}${r.location ? " | locatie: " + r.location : ""}`).join("\n")}\n\nJSON: {"events":[{"name":"exact zoals gegeven","date":"","location":"","audience":"","url":"","type":"","description":""}]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: true, model: "light" });
    const out = Array.isArray(r?.events) ? r.events : [];
    // Koppel terug op naam (volgorde-onafhankelijk), val terug op index.
    return rows.map((row, i) => {
      const m = out.find(o => (o.name || "").trim().toLowerCase() === row.name.trim().toLowerCase()) || out[i] || {};
      return blankEvent({
        id: row.id,
        name: row.name,
        date: row.date || m.date || "",
        location: row.location || m.location || "",
        audience: row.audience || m.audience || "",
        url: row.url || m.url || "",
        type: row.type || m.type || "",
        description: row.description || m.description || "",
        cost: row.cost || "",
        phase: row.phase || "backlog",
        experience: row.experience || "Neutraal",
        notes: row.notes || "",
      });
    });
  };

  // Parse bulk-tekst: per regel een event. Ondersteunt "naam | datum | locatie | doelgroep | url" of alleen een naam.
  const parseBulkLines = (text) => {
    return String(text || "").split(/\r?\n/).map(l => l.trim()).filter(Boolean).map(line => {
      const parts = line.split("|").map(p => p.trim());
      return { name: parts[0] || "", date: parts[1] || "", location: parts[2] || "", audience: parts[3] || "", url: parts[4] || "", cost: "", phase: "backlog", experience: "Neutraal" };
    }).filter(r => r.name);
  };

  const runBulk = async (rows) => {
    if (!rows.length) { setBulkMsg("Geen events gevonden om toe te voegen."); return; }
    setBulkBusy(true); setBulkMsg("");
    try {
      let newOnes;
      if (enrich) newOnes = await enrichEvents(rows);
      else newOnes = rows.map(r => blankEvent(r));
      save([...newOnes, ...(events || [])]);
      setBulk(false); setBulkText(""); setBulkMsg("");
    } catch {
      // Bij een fout alsnog zonder verrijking toevoegen, zodat je werk niet verloren gaat.
      save([...rows.map(r => blankEvent(r)), ...(events || [])]);
      setBulk(false); setBulkText("");
    }
    setBulkBusy(false);
  };

  const onUpload = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      const text = String(reader.result || "");
      const csvRows = parseCSVRows(text);
      let rows = [];
      if (csvRows.length) {
        const header = csvRows[0].map(h => (h || "").toLowerCase());
        const looksHeader = header.some(h => /naam|name|event|datum|date|locatie|location/.test(h));
        const idx = (names) => header.findIndex(h => names.some(n => h.includes(n)));
        const iName = looksHeader ? idx(["naam", "name", "event"]) : 0;
        const iDate = looksHeader ? idx(["datum", "date", "wanneer"]) : -1;
        const iLoc = looksHeader ? idx(["locatie", "location", "plaats", "stad"]) : -1;
        const iAud = looksHeader ? idx(["doelgroep", "audience", "publiek"]) : -1;
        const iUrl = looksHeader ? idx(["url", "website", "link"]) : -1;
        const iCost = looksHeader ? idx(["kosten", "cost", "prijs"]) : -1;
        const start = looksHeader ? 1 : 0;
        for (let r = start; r < csvRows.length; r++) {
          const row = csvRows[r]; if (!row) continue;
          const cell = (i) => (i >= 0 && i < row.length ? (row[i] || "").trim() : "");
          const name = (iName >= 0 ? cell(iName) : row[0] || "").trim();
          if (!name) continue;
          rows.push({ name, date: cell(iDate), location: cell(iLoc), audience: cell(iAud), url: cell(iUrl), cost: cell(iCost), phase: "backlog", experience: "Neutraal" });
        }
      }
      runBulk(rows);
    };
    reader.readAsText(f); e.target.value = "";
  };

  const suggest = async () => {
    setBusy(true); setSuggestions(null);
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${new Date().toLocaleDateString("nl-NL")}. OPDRACHT: Stel met web search 6-8 relevante beurzen, congressen en events voor waar whitevision aanwezig zou kunnen zijn, gezien onze markt (AI-driven IDP, purchase-to-pay, spend, contractmanagement) en doelgroep. Per event: naam, waar/wanneer (zo goed mogelijk; geef aan als onzeker) en waarom het relevant is.\n\nJSON: {"events":[{"name":"...","when":"...","why":"..."}]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: true, model: "light" });
    setSuggestions(r?.events || []);
    setBusy(false);
  };
  const addFromSuggestion = (s) => save([blankEvent({ name: s.name, date: s.when || "", description: s.why || "", phase: "backlog" }), ...(events || [])]);

  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff", whiteSpace: "nowrap" };
  const cellInput = { width: "100%", boxSizing: "border-box", padding: "6px 7px", border: `1px solid transparent`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: "transparent" };

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Lijsten</button>
      <PageHead title="Events" sub="Een overzichtelijke lijst van beurzen en events. Voeg ze los of in bulk toe en bewerk alles in de tabel."
        right={<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn variant="ghost" size="sm" icon={Sparkles} onClick={suggest} disabled={busy}>{busy ? "Bezig…" : "AI-suggesties"}</Btn>
          <Btn variant="ghost" size="sm" icon={Plus} onClick={() => setBulk(b => !b)}>Bulk toevoegen</Btn>
          <Btn variant="yellow" size="sm" icon={Plus} onClick={addEvent}>Event</Btn>
        </div>} />

      {bulk && (
        <Card style={{ marginBottom: 18, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 6 }}>Meerdere events tegelijk toevoegen</div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Typ of plak één event per regel. Alleen de naam volstaat; eventueel met streepjes ertussen in de volgorde: <span style={{ fontFamily: "monospace" }}>naam | datum | locatie | doelgroep | url</span>. Of upload een Excel/CSV. Kosten vul je later zelf in.</div>
          <textarea value={bulkText} onChange={e => setBulkText(e.target.value)} rows={6} placeholder={"Bijv.:\nDSAG Jahreskongress\nProcure-Tech Summit | mei 2026 | Amsterdam\nFinance Transformation Expo"}
            style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: "monospace", fontSize: 12.5, resize: "vertical", outline: "none", lineHeight: 1.5, marginBottom: 12 }} />
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, cursor: "pointer", marginBottom: 12 }}>
            <input type="checkbox" checked={enrich} onChange={e => setEnrich(e.target.checked)} /> Laat AI ontbrekende info aanvullen (datum, locatie, doelgroep, website) via web search
          </label>
          {bulkMsg && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 10 }}>{bulkMsg}</div>}
          {bulkBusy ? <Spinner label={enrich ? "AI vult de events aan…" : "Events toevoegen…"} /> : (
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
              <Btn variant="yellow" icon={Plus} onClick={() => runBulk(parseBulkLines(bulkText))} disabled={!bulkText.trim()}>Toevoegen{enrich ? " + aanvullen" : ""}</Btn>
              <Btn variant="ghost" icon={Upload} onClick={() => fileRef.current?.click()}>Excel/CSV uploaden</Btn>
              <input ref={fileRef} type="file" accept=".csv,.txt,.tsv,text/csv,text/plain" onChange={onUpload} style={{ display: "none" }} />
              <Btn variant="ghost" onClick={() => { setBulk(false); setBulkText(""); setBulkMsg(""); }}>Annuleren</Btn>
            </div>
          )}
        </Card>
      )}

      {busy && <Spinner label="AI zoekt relevante events…" />}
      {suggestions && (
        <Card style={{ marginBottom: 18, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}><AIBadge /><span style={{ fontWeight: 700 }}>Voorgestelde events</span></div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Een startlijst op basis van web search. Geen sluitende agenda; controleer data van toekomstige edities altijd zelf.</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {suggestions.map((s, i) => (
              <div key={i} style={{ display: "flex", gap: 10, alignItems: "flex-start", padding: "10px 12px", background: C.paper, borderRadius: 10 }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 700, fontSize: 13.5 }}>{s.name} {s.when && <span style={{ fontWeight: 400, color: C.muted }}>· {s.when}</span>}</div>
                  {s.why && <div style={{ fontSize: 12.5, color: C.ink2, marginTop: 2, lineHeight: 1.45 }}>{s.why}</div>}
                </div>
                <Btn size="sm" variant="ghost" icon={Plus} onClick={() => addFromSuggestion(s)}>Toevoegen</Btn>
              </div>
            ))}
          </div>
        </Card>
      )}

      <div style={{ display: "flex", gap: 6, marginBottom: 16, borderBottom: `1.5px solid ${C.line}` }}>
        {EV_PHASES.map(([id, label]) => (
          <button key={id} onClick={() => { setPhase(id); setSel({}); }}
            style={{ padding: "9px 16px", fontSize: 13, fontWeight: 700, cursor: "pointer", background: "none", border: "none", borderBottom: `2.5px solid ${phase === id ? C.yellowDark : "transparent"}`, color: phase === id ? C.ink : C.muted, marginBottom: -1.5 }}>
            {label} <span style={{ color: C.muted, fontWeight: 600 }}>({phaseCount(id)})</span>
          </button>
        ))}
      </div>
      {events == null ? <Spinner label="Laden…" /> : inPhase.length === 0 ? <Empty icon={CalendarDays} title="Nog niets in deze fase" sub={phase === "backlog" ? "Voeg een event toe, plak er meerdere in bulk, of vraag AI om suggesties." : "Verplaats events hierheen via de detailkaart."} /> : (
        <>
          {selCount > 0 && (
            <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", background: C.yellow + "18", border: `1.5px solid ${C.yellow}`, borderRadius: 10, marginBottom: 14, flexWrap: "wrap" }}>
              <span style={{ fontSize: 13, fontWeight: 700 }}>{selCount} geselecteerd</span>
              <Btn size="sm" variant="yellow" icon={Sparkles} onClick={enrichSelected} disabled={busy}>{busy ? "Bezig…" : "Verrijk met AI"}</Btn>
              <span style={{ fontSize: 12.5, color: C.muted }}>Verplaats naar:</span>
              <select value="" onChange={e => moveSelected(e.target.value)} style={{ padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, background: C.card, outline: "none", cursor: "pointer" }}>
                <option value="">— kies fase —</option>
                {EV_PHASES.filter(([id]) => id !== phase).map(([id, l]) => <option key={id} value={id}>{l}</option>)}
              </select>
              <button onClick={() => setSel({})} style={{ fontSize: 12.5, fontWeight: 600, color: C.muted, background: "none", border: "none", cursor: "pointer" }}>Selectie wissen</button>
            </div>
          )}
          <Card style={{ padding: 0, overflow: "hidden" }}>
            <div style={{ overflowX: "auto" }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
                <thead><tr style={{ background: C.surface }}>
                  <th style={{ ...th, width: 34 }}></th>
                  <th style={{ ...th, minWidth: 170 }}>Naam</th>
                  <th style={{ ...th, minWidth: 110 }}>Datum</th>
                  <th style={{ ...th, minWidth: 130 }}>Locatie</th>
                  <th style={{ ...th, minWidth: 140 }}>Doelgroep</th>
                  <th style={{ ...th, minWidth: 70 }}>Web</th>
                  <th style={{ ...th, minWidth: 90 }}>Kosten</th>
                  <th style={{ ...th, minWidth: 110 }}>Ervaring</th>
                  <th style={{ ...th, width: 70 }}></th>
                </tr></thead>
                <tbody>
                  {inPhase.map((e, i) => {
                    const tip = [e.type, e.description].filter(Boolean).join(" — ") || "Geen omschrijving. Open de kaart om die toe te voegen.";
                    const expColor = e.experience === "Positief" ? C.good : e.experience === "Negatief" ? C.bad : C.muted;
                    return (
                      <tr key={e.id} style={{ background: sel[e.id] ? C.yellow + "12" : i % 2 ? C.paper : C.card, borderBottom: `1px solid ${C.line}` }}>
                        <td style={{ padding: "4px 6px", textAlign: "center" }}><input type="checkbox" checked={!!sel[e.id]} onChange={() => toggleSel(e.id)} /></td>
                        <td style={{ padding: "4px 6px" }}>
                          <span title={tip} onClick={() => setDetail(e)} style={{ fontWeight: 700, cursor: "pointer", borderBottom: `1px dotted ${C.muted}` }}>{e.name || "(naamloos)"}</span>
                        </td>
                        <td style={{ padding: "4px 6px" }}><input value={e.date || ""} onChange={ev => upd(e.id, { date: ev.target.value })} placeholder="—" style={cellInput} /></td>
                        <td style={{ padding: "4px 6px" }}><input value={e.location || ""} onChange={ev => upd(e.id, { location: ev.target.value })} placeholder="—" style={cellInput} /></td>
                        <td style={{ padding: "4px 6px" }}><input value={e.audience || ""} onChange={ev => upd(e.id, { audience: ev.target.value })} placeholder="—" style={cellInput} /></td>
                        <td style={{ padding: "4px 6px", textAlign: "center" }}>
                          {e.url ? <a href={e.url.startsWith("http") ? e.url : "https://" + e.url} target="_blank" rel="noopener noreferrer" title={e.url} style={{ color: C.blue }}><Globe size={15} /></a> : <span style={{ color: C.line }}>—</span>}
                        </td>
                        <td style={{ padding: "4px 6px" }}><input value={e.cost || ""} onChange={ev => upd(e.id, { cost: ev.target.value })} placeholder="—" style={cellInput} /></td>
                        <td style={{ padding: "4px 6px" }}>
                          <select value={e.experience || "Neutraal"} onChange={ev => upd(e.id, { experience: ev.target.value })} style={{ ...cellInput, fontWeight: 700, color: expColor }}>
                            {EXP.map(x => <option key={x} style={{ color: C.ink }}>{x}</option>)}
                          </select>
                        </td>
                        <td style={{ padding: "4px 6px", textAlign: "center", whiteSpace: "nowrap" }}>
                          <button onClick={() => setDetail(e)} title="Bewerken" style={{ background: "none", border: "none", cursor: "pointer", color: C.ink2 }}><Edit3 size={15} /></button>
                          <button onClick={() => confirm(`Event "${e.name || ""}" verwijderen?`, () => save(events.filter(x => x.id !== e.id)))} title="Verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </Card>
        </>
      )}
      {events && inPhase.length > 0 && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Beweeg over een naam voor het type en de omschrijving. Klik op de naam of het potloodje voor alle details en om de fase te wijzigen. Vink rijen aan om in bulk te verrijken. Kosten vul je zelf in.</div>}

      {detail && (
        <Modal onClose={() => setDetail(null)} title="Event bewerken">
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <Field label="Naam" value={detail.name || ""} onChange={v => updDetail({ name: v })} />
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
              <Field label="Datum" value={detail.date || ""} onChange={v => updDetail({ date: v })} placeholder="bijv. mei 2026" />
              <Field label="Locatie" value={detail.location || ""} onChange={v => updDetail({ location: v })} />
              <Field label="Type" value={detail.type || ""} onChange={v => updDetail({ type: v })} placeholder="beurs, congres, webinar…" />
              <Field label="Doelgroep" value={detail.audience || ""} onChange={v => updDetail({ audience: v })} />
              <Field label="Website" value={detail.url || ""} onChange={v => updDetail({ url: v })} placeholder="https://…" />
              <Field label="Kosten" value={detail.cost || ""} onChange={v => updDetail({ cost: v })} placeholder="zelf invullen" />
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
              <div>
                <label style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, display: "block", marginBottom: 5 }}>Fase</label>
                <select value={detail.phase || "backlog"} onChange={ev => updDetail({ phase: ev.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
                  {EV_PHASES.map(([id, l]) => <option key={id} value={id}>{l}</option>)}
                </select>
              </div>
              <div>
                <label style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, display: "block", marginBottom: 5 }}>Ervaring</label>
                <select value={detail.experience || "Neutraal"} onChange={ev => updDetail({ experience: ev.target.value })} style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
                  {EXP.map(x => <option key={x}>{x}</option>)}
                </select>
              </div>
            </div>
            <Field label="Omschrijving (wat voor event & waarom relevant)" value={detail.description || ""} onChange={v => updDetail({ description: v })} multiline rows={3} />
            <Field label="Eigen notities" value={detail.notes || ""} onChange={v => updDetail({ notes: v })} multiline rows={3} placeholder="Bijv. ervaring vorige editie, contactpersoon, standkosten…" />
            <div style={{ display: "flex", gap: 10, justifyContent: "space-between", alignItems: "center", flexWrap: "wrap" }}>
              {detailBusy ? <Spinner label="AI vult dit event aan…" /> : <Btn variant="ghost" icon={Sparkles} onClick={enrichOne}>Aanvullen met AI</Btn>}
              <Btn variant="yellow" icon={Check} onClick={() => setDetail(null)}>Klaar</Btn>
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

function Targeting({ ctx, onBack }) {
  const { confirm } = ctx;
  const [lists, setLists] = useState(() => null);
  const [openListId, setOpenListId] = useState(null);
  const fileRef = useRef(null);

  useEffect(() => {
    (async () => {
      try { setLists(await store.get("wv_targeting_lists", [])); }
      catch { setLists([]); }
    })();
  }, []);
  const saveLists = (v) => { setLists(v); store.set("wv_targeting_lists", v); };

  // Parse een CSV naar contactrijen met herkende kolommen.
  const parseContacts = (text) => {
    const csv = parseCSVRows(text);
    if (!csv.length) return [];
    const header = csv[0].map(h => (h || "").toLowerCase());
    const looks = header.some(h => /bedrijf|company|naam|name|organisatie/.test(h));
    const idx = (ns) => header.findIndex(h => ns.some(n => h.includes(n)));
    const iComp = looks ? idx(["bedrijf", "company", "organisatie", "account"]) : 0;
    const iName = looks ? idx(["contact", "naam", "name", "persoon"]) : -1;
    const iMail = looks ? idx(["mail", "email", "e-mail"]) : -1;
    const iPhone = looks ? idx(["tel", "phone", "telefoon"]) : -1;
    const iBranch = looks ? idx(["branche", "sector", "industry"]) : -1;
    const iCity = looks ? idx(["plaats", "stad", "city", "locatie"]) : -1;
    const iUrl = looks ? idx(["url", "website", "site", "web"]) : -1;
    const out = [];
    for (let r = looks ? 1 : 0; r < csv.length; r++) {
      const row = csv[r]; if (!row) continue;
      const cell = (i) => (i >= 0 && i < row.length ? (row[i] || "").trim() : "");
      const company = (iComp >= 0 ? cell(iComp) : row[0] || "").trim(); if (!company) continue;
      out.push({ id: Date.now() + r + Math.floor(Math.random() * 1000), company, contactName: cell(iName), email: cell(iMail), phone: cell(iPhone), branch: cell(iBranch), city: cell(iCity), url: cell(iUrl), visited: "", notes: "" });
    }
    return out;
  };

  const onUpload = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => {
      const text = String(reader.result || "");
      const contacts = parseContacts(text);
      const list = { id: Date.now(), name: f.name.replace(/\.[^.]+$/, ""), contacts, date: new Date().toISOString().slice(0, 10) };
      saveLists([list, ...(lists || [])]);
    };
    reader.readAsText(f); e.target.value = "";
  };
  const addEmptyList = () => {
    const name = (prompt && typeof window !== "undefined") ? window.prompt("Naam van de lijst (bijv. 'Alle AFAS-gebruikers')") : "Nieuwe lijst";
    if (name === null) return;
    saveLists([{ id: Date.now(), name: name || "Nieuwe lijst", contacts: [], date: new Date().toISOString().slice(0, 10) }, ...(lists || [])]);
  };
  const updList = (id, patch) => saveLists((lists || []).map(l => l.id === id ? { ...l, ...patch } : l));

  // ---- Detailpagina van één lijst (contacttabel) ----
  if (openListId) {
    const list = (lists || []).find(l => l.id === openListId);
    if (!list) { setOpenListId(null); return null; }
    return <TargetingListDetail list={list} onBack={() => setOpenListId(null)} onSave={(patch) => updList(list.id, patch)} confirm={confirm} />;
  }

  return (
    <div>
      {onBack && <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Lijsten</button>}
      <PageHead title="Targetinglijsten" sub="Beheer je doelgroeplijsten. Klik op een lijst om de bedrijven en contacten te bekijken en bewerken."
        right={<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn variant="ghost" size="sm" icon={Plus} onClick={addEmptyList}>Lege lijst</Btn>
          <Btn variant="yellow" size="sm" icon={Upload} onClick={() => fileRef.current?.click()}>Lijst uploaden</Btn>
          <input ref={fileRef} type="file" accept=".csv,.txt,.tsv,text/csv,text/plain" onChange={onUpload} style={{ display: "none" }} />
        </div>} />
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Upload een CSV met je doelaccounts (kolommen als bedrijf, contactpersoon, e-mail, branche, plaats, website worden herkend) of maak een lege lijst. Website-bezoek koppelen we later aan Leadinfo.</div>

      {lists == null ? <Spinner label="Laden…" /> : lists.length === 0 ? <Empty icon={Users} title="Nog geen lijsten" sub="Upload een targetinglijst of maak een lege lijst aan." /> : (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 14, marginBottom: 26 }}>
          {lists.map(l => (
            <Card key={l.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "15px 16px" }}>
                <div onClick={() => setOpenListId(l.id)} style={{ flex: 1, minWidth: 0, cursor: "pointer" }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 3 }}><Users size={17} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15 }}>{l.name}</span></div>
                  <span style={{ fontSize: 12.5, color: C.muted }}>{(l.contacts || []).length} bedrijven · {l.date}</span>
                </div>
                <ChevronRight size={18} style={{ color: C.muted, cursor: "pointer" }} onClick={() => setOpenListId(l.id)} />
                <button onClick={() => confirm(`Lijst "${l.name}" verwijderen?`, () => saveLists(lists.filter(x => x.id !== l.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
              </div>
            </Card>
          ))}
        </div>
      )}
    </div>
  );
}

// Detailpagina van één targetinglijst: tabel met bedrijven/contacten.
function TargetingListDetail({ list, onBack, onSave, confirm }) {
  const [detail, setDetail] = useState(null);
  const [bulk, setBulk] = useState(false);
  const [bulkText, setBulkText] = useState("");
  const fileRef = useRef(null);
  const contacts = list.contacts || [];
  const save = (v) => onSave({ contacts: v });
  const blank = () => ({ id: Date.now() + Math.floor(Math.random() * 1000), company: "", contactName: "", email: "", phone: "", branch: "", city: "", url: "", visited: "", notes: "" });
  const add = () => { const c = blank(); save([c, ...contacts]); setDetail(c); };
  const upd = (id, patch) => save(contacts.map(c => c.id === id ? { ...c, ...patch } : c));
  const updDetail = (patch) => { setDetail(d => ({ ...d, ...patch })); upd(detail.id, patch); };

  const parseLines = (text) => String(text || "").split(/\r?\n/).map(l => l.trim()).filter(Boolean).map(line => {
    const p = line.split("|").map(x => x.trim());
    return { ...blank(), company: p[0] || "", contactName: p[1] || "", email: p[2] || "", branch: p[3] || "", city: p[4] || "", url: p[5] || "" };
  }).filter(r => r.company);
  const addBulk = (rows) => { if (!rows.length) return; save([...rows, ...contacts]); setBulk(false); setBulkText(""); };
  const parseCsv = (text) => {
    const csv = parseCSVRows(text); if (!csv.length) return [];
    const header = csv[0].map(h => (h || "").toLowerCase());
    const looks = header.some(h => /bedrijf|company|naam|name|organisatie/.test(h));
    const idx = (ns) => header.findIndex(h => ns.some(n => h.includes(n)));
    const iComp = looks ? idx(["bedrijf", "company", "organisatie", "account"]) : 0;
    const iName = looks ? idx(["contact", "persoon"]) : -1, iMail = looks ? idx(["mail", "email", "e-mail"]) : -1;
    const iPhone = looks ? idx(["tel", "phone", "telefoon"]) : -1, iBranch = looks ? idx(["branche", "sector", "industry"]) : -1;
    const iCity = looks ? idx(["plaats", "stad", "city", "locatie"]) : -1, iUrl = looks ? idx(["url", "website", "site", "web"]) : -1;
    const out = [];
    for (let r = looks ? 1 : 0; r < csv.length; r++) {
      const row = csv[r]; if (!row) continue;
      const cell = (i) => (i >= 0 && i < row.length ? (row[i] || "").trim() : "");
      const company = (iComp >= 0 ? cell(iComp) : row[0] || "").trim(); if (!company) continue;
      out.push({ ...blank(), company, contactName: cell(iName), email: cell(iMail), phone: cell(iPhone), branch: cell(iBranch), city: cell(iCity), url: cell(iUrl) });
    }
    return out;
  };
  const onUpload = (e) => { const f = e.target.files?.[0]; if (!f) return; const reader = new FileReader(); reader.onload = () => addBulk(parseCsv(String(reader.result || ""))); reader.readAsText(f); e.target.value = ""; };

  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff", whiteSpace: "nowrap" };
  const cellInput = { width: "100%", boxSizing: "border-box", padding: "6px 7px", border: "1px solid transparent", borderRadius: 6, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: "transparent" };

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Targetinglijsten</button>
      <PageHead title={list.name} sub={`${contacts.length} bedrijven in deze lijst. Klik op een bedrijfsnaam voor alle details.`}
        right={<div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <Btn variant="ghost" size="sm" icon={Plus} onClick={() => setBulk(b => !b)}>Bulk</Btn>
          <Btn variant="yellow" size="sm" icon={Plus} onClick={add}>Bedrijf</Btn>
        </div>} />

      {bulk && (
        <Card style={{ marginBottom: 18, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 6 }}>Meerdere bedrijven toevoegen</div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Eén per regel. Alleen de bedrijfsnaam volstaat, of: <span style={{ fontFamily: "monospace" }}>bedrijf | contact | e-mail | branche | plaats | url</span>. Of upload een CSV.</div>
          <textarea value={bulkText} onChange={e => setBulkText(e.target.value)} rows={5} placeholder={"Bijv.:\nAcme BV\nVoorbeeld NV | Jan Jansen | jan@voorbeeld.nl | Logistiek | Utrecht"}
            style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: "monospace", fontSize: 12.5, resize: "vertical", outline: "none", marginBottom: 12 }} />
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            <Btn variant="yellow" icon={Plus} onClick={() => addBulk(parseLines(bulkText))} disabled={!bulkText.trim()}>Toevoegen</Btn>
            <Btn variant="ghost" icon={Upload} onClick={() => fileRef.current?.click()}>CSV uploaden</Btn>
            <input ref={fileRef} type="file" accept=".csv,.txt,.tsv,text/csv,text/plain" onChange={onUpload} style={{ display: "none" }} />
            <Btn variant="ghost" onClick={() => { setBulk(false); setBulkText(""); }}>Annuleren</Btn>
          </div>
        </Card>
      )}

      {contacts.length === 0 ? <Empty icon={Users} title="Nog geen bedrijven" sub="Voeg een bedrijf toe; of upload bij Targetinglijsten een CSV om de lijst te vullen." /> : (
        <Card style={{ padding: 0, overflow: "hidden" }}>
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={{ ...th, minWidth: 160 }}>Bedrijf</th>
                <th style={{ ...th, minWidth: 140 }}>Contact</th>
                <th style={{ ...th, minWidth: 130 }}>Branche</th>
                <th style={{ ...th, minWidth: 110 }}>Plaats</th>
                <th style={{ ...th, minWidth: 60 }}>Web</th>
                <th style={{ ...th, minWidth: 110 }}>Site bezocht</th>
                <th style={{ ...th, width: 70 }}></th>
              </tr></thead>
              <tbody>
                {contacts.map((c, i) => {
                  const tip = [c.contactName, c.email, c.phone].filter(Boolean).join(" · ") || "Open de kaart voor contactgegevens.";
                  return (
                    <tr key={c.id} style={{ background: i % 2 ? C.paper : C.card, borderBottom: `1px solid ${C.line}` }}>
                      <td style={{ padding: "4px 6px" }}><span title={tip} onClick={() => setDetail(c)} style={{ fontWeight: 700, cursor: "pointer", borderBottom: `1px dotted ${C.muted}` }}>{c.company || "(naamloos)"}</span></td>
                      <td style={{ padding: "4px 6px", color: C.ink2, fontSize: 11.5 }}>{c.contactName || c.email || c.phone || "—"}</td>
                      <td style={{ padding: "4px 6px" }}><input value={c.branch || ""} onChange={e => upd(c.id, { branch: e.target.value })} placeholder="—" style={cellInput} /></td>
                      <td style={{ padding: "4px 6px" }}><input value={c.city || ""} onChange={e => upd(c.id, { city: e.target.value })} placeholder="—" style={cellInput} /></td>
                      <td style={{ padding: "4px 6px", textAlign: "center" }}>{c.url ? <a href={c.url.startsWith("http") ? c.url : "https://" + c.url} target="_blank" rel="noopener noreferrer" title={c.url} style={{ color: C.blue }}><Globe size={15} /></a> : <span style={{ color: C.line }}>—</span>}</td>
                      <td style={{ padding: "4px 6px", color: C.muted, fontSize: 11.5 }}>{c.visited || <span title="Wordt later gevuld via Leadinfo-koppeling" style={{ color: C.line }}>—</span>}</td>
                      <td style={{ padding: "4px 6px", textAlign: "center", whiteSpace: "nowrap" }}>
                        <button onClick={() => setDetail(c)} title="Bewerken" style={{ background: "none", border: "none", cursor: "pointer", color: C.ink2 }}><Edit3 size={15} /></button>
                        <button onClick={() => confirm(`"${c.company || ""}" verwijderen?`, () => save(contacts.filter(x => x.id !== c.id)))} title="Verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </Card>
      )}
      <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Klik op een bedrijfsnaam voor alle contactgegevens. "Site bezocht" wordt later automatisch gevuld via de Leadinfo-koppeling.</div>

      {detail && (
        <Modal onClose={() => setDetail(null)} title="Bedrijf bewerken">
          <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <Field label="Bedrijfsnaam" value={detail.company || ""} onChange={v => updDetail({ company: v })} />
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
              <Field label="Contactpersoon" value={detail.contactName || ""} onChange={v => updDetail({ contactName: v })} />
              <Field label="E-mail" value={detail.email || ""} onChange={v => updDetail({ email: v })} />
              <Field label="Telefoon" value={detail.phone || ""} onChange={v => updDetail({ phone: v })} />
              <Field label="Branche" value={detail.branch || ""} onChange={v => updDetail({ branch: v })} />
              <Field label="Plaats" value={detail.city || ""} onChange={v => updDetail({ city: v })} />
              <Field label="Website" value={detail.url || ""} onChange={v => updDetail({ url: v })} placeholder="https://…" />
            </div>
            <Field label="Website bezocht (later via Leadinfo)" value={detail.visited || ""} onChange={v => updDetail({ visited: v })} placeholder="Wordt later automatisch gevuld" />
            <Field label="Notities" value={detail.notes || ""} onChange={v => updDetail({ notes: v })} multiline rows={3} />
            <div style={{ display: "flex", justifyContent: "flex-end" }}>
              <Btn variant="yellow" icon={Check} onClick={() => setDetail(null)}>Klaar</Btn>
            </div>
          </div>
        </Modal>
      )}
    </div>
  );
}

/* ============================== ANALYTICS ============================== */
function Analytics({ ctx }) {
  const { profile } = ctx;
  const [subtab, setSubtab] = useState("overzicht"); // overzicht | content
  const [data, setData] = useState("");
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState(null);
  const fileRef = useRef(null);

  // Google Analytics (GA4) koppeling
  const [ga, setGa] = useState({ checked: false, connected: false, connectedAt: null, propertyId: "" });
  const [gaRange, setGaRange] = useState("28daysAgo");
  const [gaData, setGaData] = useState(null);
  const [gaBusy, setGaBusy] = useState(false);
  const [gaMsg, setGaMsg] = useState("");
  const [props, setProps] = useState(null); // null=niet geladen, []=geen, [...]=lijst
  const [propMsg, setPropMsg] = useState("");
  const [manualProp, setManualProp] = useState("");

  // Google Search Console
  const [gscSites, setGscSites] = useState(null); // null=niet geladen, []=geen, [...]=lijst
  const [gscSite, setGscSite] = useState("");
  const [gscRange, setGscRange] = useState(28);
  const [gscRows, setGscRows] = useState(null);
  const [gscBusy, setGscBusy] = useState(false);
  const [gscMsg, setGscMsg] = useState("");
  const [gscPage, setGscPage] = useState(0);

  const loadGscSites = async () => {
    try {
      const r = await fetch("/api/google/gsc/sites", { credentials: "include" });
      const d = await r.json();
      if (!r.ok) { setGscSites([]); setGscMsg(d.error || "Kon Search Console-sites niet ophalen. Staat de Search Console API aan en ben je opnieuw verbonden?"); }
      else { setGscSites(d.sites || []); if (d.selected) setGscSite(d.selected); setGscMsg((d.sites || []).length ? "" : "Geen sites gevonden waar je toegang tot hebt."); }
    } catch { setGscSites([]); setGscMsg("Kon Search Console-sites niet ophalen."); }
  };
  const chooseGscSite = async (site) => {
    if (!site) return;
    setGscSite(site); setGscMsg("");
    try { await fetch("/api/google/gsc/site", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ site }) }); loadGscQueries(site, gscRange); } catch {}
  };
  const loadGscQueries = async (site, days) => {
    if (!site) return;
    setGscBusy(true); setGscMsg("");
    try {
      const today = new Date();
      const end = today.toISOString().slice(0, 10);
      const start = new Date(today.getTime() - days * 86400000).toISOString().slice(0, 10);
      const r = await fetch("/api/google/gsc/queries", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ startDate: start, endDate: end, rowLimit: 100 }) });
      const d = await r.json();
      if (!r.ok) { setGscMsg(d.error || "Kon de zoekopdrachten niet ophalen."); setGscRows(null); }
      else { setGscRows(d); setGscPage(0); }
    } catch { setGscMsg("Kon de zoekopdrachten niet ophalen."); }
    setGscBusy(false);
  };

  const loadProps = async () => {
    setPropMsg("Properties ophalen…");
    try {
      const r = await fetch("/api/google/properties", { credentials: "include" });
      const d = await r.json();
      if (!r.ok) { setProps([]); setPropMsg(d.error || "Kon properties niet ophalen. Vul hieronder handmatig een property-ID in."); }
      else { setProps(d.properties || []); setPropMsg((d.properties || []).length ? "" : "Geen properties gevonden. Vul handmatig een property-ID in."); }
    } catch { setProps([]); setPropMsg("Kon properties niet ophalen. Vul handmatig een property-ID in."); }
  };
  const chooseProperty = async (id) => {
    if (!id) return;
    setGaMsg("");
    try {
      const r = await fetch("/api/google/property", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ propertyId: id }) });
      const d = await r.json();
      if (r.ok) { setGa(g => ({ ...g, propertyId: d.selected })); loadGaData(gaRange); }
    } catch {}
  };

  const loadGaData = async (range) => {
    setGaBusy(true); setGaMsg("");
    try {
      const r = await fetch("/api/google/analytics", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ startDate: range, endDate: "today" }) });
      const d = await r.json();
      if (!r.ok) { setGaMsg(d.error || "Kon de data niet ophalen."); setGaData(null); }
      else setGaData(d);
    } catch { setGaMsg("Kon de data niet ophalen."); }
    setGaBusy(false);
  };

  useEffect(() => {
    (async () => {
      try {
        const r = await fetch("/api/google/status", { credentials: "include" });
        const d = await r.json();
        setGa({ checked: true, connected: !!d.connected, connectedAt: d.connectedAt, propertyId: d.propertyId || "" });
        if (d.connected) { loadGaData(gaRange); loadProps(); if (d.gscSite) setGscSite(d.gscSite); loadGscSites(); if (d.gscSite) loadGscQueries(d.gscSite, gscRange); }
      } catch { setGa({ checked: true, connected: false, connectedAt: null }); }
      // melding na terugkeer van Google
      const q = new URLSearchParams(window.location.search).get("google");
      if (q === "connected") setGaMsg("Verbonden met Google Analytics.");
      else if (q === "error") setGaMsg("Verbinden met Google is niet gelukt. Probeer het opnieuw.");
      if (q) window.history.replaceState({}, "", window.location.pathname);
    })();
  }, []);

  const gaDisconnect = async () => {
    try { await fetch("/api/google/disconnect", { method: "POST", credentials: "include" }); } catch {}
    setGa(g => ({ ...g, connected: false })); setGaData(null);
  };
  const rangeLabel = (r) => r === "7daysAgo" ? "7 dagen" : r === "90daysAgo" ? "90 dagen" : "28 dagen";

  const onUpload = (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => setData(String(reader.result || "").slice(0, 20000));
    reader.readAsText(f); e.target.value = "";
  };

  const analyse = async () => {
    if (!data.trim()) return;
    setBusy(true); setResult(null);
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Analyseer de volgende data voor whitevision. Geef de belangrijkste inzichten, opvallende patronen en concrete aanbevelingen. Stel ook 1-3 eenvoudige grafieken voor die deze data het beste visualiseren (kies een passend type: bar, line of pie) met de bijbehorende datapunten.\n\nDATA:\n${data.slice(0, 12000)}\n\nJSON: {"summary":"korte samenvatting","insights":["inzicht..."],"recommendations":["aanbeveling..."],"charts":[{"title":"...","type":"bar|line|pie","labels":["..."],"values":[getal,...]}]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: false });
    setResult(r || { summary: "Geen analyse beschikbaar.", insights: [], recommendations: [], charts: [] });
    setBusy(false);
  };

  return (
    <div>
      <PageHead title="Analytics" sub="Live websitedata uit Google Analytics en Search Console, plus prestaties per pagina." />

      <div style={{ display: "flex", gap: 6, marginBottom: 18, borderBottom: `1.5px solid ${C.line}` }}>
        {[["overzicht", "Overzicht"], ["content", "Content"], ["ads", "Ads"]].map(([id, label]) => (
          <button key={id} onClick={() => setSubtab(id)}
            style={{ padding: "9px 16px", fontSize: 13.5, fontWeight: 700, cursor: "pointer", background: "none", border: "none", borderBottom: `2.5px solid ${subtab === id ? C.yellowDark : "transparent"}`, color: subtab === id ? C.ink : C.muted, marginBottom: -1.5 }}>
            {label}
          </button>
        ))}
      </div>

      {subtab === "content" && <ContentPerformance ctx={ctx} embedded />}
      {subtab === "ads" && <AdsPerformance ctx={ctx} />}

      {subtab === "overzicht" && <>

      <Card style={{ marginBottom: 16, border: `1.5px solid ${ga.connected ? C.good + "66" : C.line}` }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: ga.connected ? 14 : 6 }}>
          <BarChart3 size={18} style={{ color: C.yellowDark }} />
          <span style={{ fontWeight: 700, fontSize: 15 }}>Google Analytics</span>
          {ga.connected && <span style={{ fontSize: 11.5, fontWeight: 700, color: C.good, background: C.good + "18", padding: "2px 9px", borderRadius: 20 }}>Verbonden</span>}
          <div style={{ marginLeft: "auto" }}>
            {ga.connected
              ? <Btn size="sm" variant="ghost" onClick={gaDisconnect}>Ontkoppelen</Btn>
              : <Btn size="sm" variant="yellow" icon={ArrowUpRight} onClick={() => { window.location.href = "/api/google/auth"; }} disabled={!ga.checked}>Verbind met Google</Btn>}
          </div>
        </div>
        {gaMsg && <div style={{ fontSize: 12.5, color: gaMsg.includes("Verbonden") ? C.good : C.bad, marginBottom: 10 }}>{gaMsg}</div>}
        {!ga.connected && <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>Koppel je Google Analytics-account om live bezoekersaantallen, sessies en paginaweergaven op te halen. Je logt eenmalig in bij Google en geeft leestoegang.</div>}
        {ga.connected && <>
          <div style={{ marginBottom: 14 }}>
            <div style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6 }}>Property</div>
            {props && props.length > 0
              ? <select value={ga.propertyId} onChange={e => chooseProperty(e.target.value)}
                  style={{ width: "100%", maxWidth: 460, padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, background: C.card, outline: "none" }}>
                  {props.map(p => <option key={p.id} value={p.id}>{p.name}{p.account ? ` — ${p.account}` : ""} ({p.id})</option>)}
                </select>
              : <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                  <span style={{ fontSize: 12.5, color: C.ink2 }}>Huidige property-ID: <b>{ga.propertyId || "—"}</b></span>
                  <input value={manualProp} onChange={e => setManualProp(e.target.value)} placeholder="Andere property-ID"
                    style={{ width: 150, padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none" }} />
                  <Btn size="sm" variant="ghost" onClick={() => { chooseProperty(manualProp.trim()); setManualProp(""); }} disabled={!manualProp.trim()}>Instellen</Btn>
                </div>}
            {propMsg && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 5, lineHeight: 1.4 }}>{propMsg}</div>}
          </div>
          <div style={{ display: "flex", gap: 8, marginBottom: 14, flexWrap: "wrap", alignItems: "center" }}>
            <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Periode:</span>
            {["7daysAgo", "28daysAgo", "90daysAgo"].map(r => (
              <button key={r} onClick={() => { setGaRange(r); loadGaData(r); }}
                style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${gaRange === r ? C.yellow : C.line}`, background: gaRange === r ? C.yellow + "22" : C.card, color: C.ink }}>{rangeLabel(r)}</button>
            ))}
          </div>
          {gaBusy ? <Spinner label="Live data ophalen uit Google Analytics…" />
            : gaData ? (
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12 }}>
                {[
                  ["Bezoekers", Number(gaData.activeUsers).toLocaleString("nl-NL")],
                  ["Sessies", Number(gaData.sessions).toLocaleString("nl-NL")],
                  ["Paginaweergaven", Number(gaData.pageViews).toLocaleString("nl-NL")],
                  ["Belangrijke gebeurtenissen", Number(gaData.keyEvents || 0).toLocaleString("nl-NL")],
                  ["Betrokkenheidspercentage", `${(Number(gaData.engagementRate || 0) * 100).toFixed(1)}%`],
                  ["Terugkerende gebruikers", Number(gaData.returningUsers || 0).toLocaleString("nl-NL")],
                ].map(([lbl, val]) => (
                  <div key={lbl} style={{ padding: "16px 18px", border: `1.5px solid ${C.line}`, borderRadius: 12, background: C.paper }}>
                    <div style={{ fontSize: 12.5, color: C.muted, fontWeight: 600, marginBottom: 4 }}>{lbl}</div>
                    <div style={{ fontSize: 26, fontWeight: 800, color: C.ink }}>{val}</div>
                  </div>
                ))}
              </div>
            ) : <div style={{ fontSize: 13, color: C.muted }}>Nog geen data geladen.</div>}
          <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10 }}>Laatste {rangeLabel(gaRange)}, t/m vandaag. Bron: Google Analytics (GA4){ga.propertyId ? `, property ${ga.propertyId}` : ""}.</div>
        </>}
      </Card>

      {ga.connected && (
        <Card style={{ marginBottom: 16 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
            <Search size={17} style={{ color: C.yellowDark }} />
            <span style={{ fontWeight: 700, fontSize: 14.5 }}>Zoekopdrachten (Search Console)</span>
          </div>
          {gscSites == null ? <Spinner label="Search Console laden…" /> : (
            <>
              <div style={{ display: "flex", gap: 10, marginBottom: 12, flexWrap: "wrap", alignItems: "center" }}>
                <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Site:</span>
                {gscSites.length > 0 ? (
                  <select value={gscSite} onChange={e => chooseGscSite(e.target.value)} style={{ padding: "7px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, background: C.card, outline: "none", maxWidth: 360 }}>
                    <option value="">— kies een site —</option>
                    {gscSites.map(s => <option key={s.url} value={s.url}>{s.url}</option>)}
                  </select>
                ) : <span style={{ fontSize: 12.5, color: C.muted }}>Geen sites beschikbaar.</span>}
                {gscSite && <>
                  <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, marginLeft: 8 }}>Periode:</span>
                  {[[7, "7 d"], [28, "28 d"], [90, "90 d"]].map(([d, lbl]) => (
                    <button key={d} onClick={() => { setGscRange(d); loadGscQueries(gscSite, d); }}
                      style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 11px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${gscRange === d ? C.yellow : C.line}`, background: gscRange === d ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
                  ))}
                </>}
              </div>
              {gscMsg && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 10, lineHeight: 1.4 }}>{gscMsg}</div>}
              {gscBusy ? <Spinner label="Zoekopdrachten ophalen…" />
                : gscRows ? (
                  gscRows.rows.length === 0 ? <div style={{ fontSize: 13, color: C.muted }}>Geen zoekopdrachten gevonden in deze periode.</div> : (() => {
                    const sorted = [...gscRows.rows].sort((a, b) => b.impressions - a.impressions);
                    const perPage = 10;
                    const pages = Math.ceil(sorted.length / perPage);
                    const page = Math.min(gscPage, pages - 1);
                    const slice = sorted.slice(page * perPage, page * perPage + perPage);
                    return (
                    <>
                      <div style={{ display: "flex", gap: 24, marginBottom: 12, fontSize: 13 }}>
                        <span>Totaal klikken: <b>{Number(gscRows.totals.clicks).toLocaleString("nl-NL")}</b></span>
                        <span>Totaal vertoningen: <b>{Number(gscRows.totals.impressions).toLocaleString("nl-NL")}</b></span>
                      </div>
                      <div style={{ overflowX: "auto" }}>
                        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
                          <thead><tr style={{ background: C.surface }}>
                            <th style={{ textAlign: "left", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff" }}>Zoekopdracht</th>
                            <th style={{ textAlign: "right", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff" }}>Klikken</th>
                            <th style={{ textAlign: "right", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff" }}>Vertoningen</th>
                            <th style={{ textAlign: "right", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff" }}>CTR</th>
                            <th style={{ textAlign: "right", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff" }}>Positie</th>
                          </tr></thead>
                          <tbody>
                            {slice.map((r, i) => (
                              <tr key={i} style={{ background: i % 2 ? C.paper : C.card, borderBottom: `1px solid ${C.line}` }}>
                                <td style={{ padding: "7px 10px", fontWeight: 600 }}>{r.query}</td>
                                <td style={{ padding: "7px 10px", textAlign: "right" }}>{Number(r.clicks).toLocaleString("nl-NL")}</td>
                                <td style={{ padding: "7px 10px", textAlign: "right" }}>{Number(r.impressions).toLocaleString("nl-NL")}</td>
                                <td style={{ padding: "7px 10px", textAlign: "right" }}>{(r.ctr * 100).toFixed(1)}%</td>
                                <td style={{ padding: "7px 10px", textAlign: "right" }}>{r.position.toFixed(1)}</td>
                              </tr>
                            ))}
                          </tbody>
                        </table>
                      </div>
                      {pages > 1 && (
                        <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 12, marginTop: 12 }}>
                          <Btn size="sm" variant="ghost" onClick={() => setGscPage(p => Math.max(0, p - 1))} disabled={page === 0}>Vorige</Btn>
                          <span style={{ fontSize: 12.5, color: C.muted }}>Pagina {page + 1} van {pages}</span>
                          <Btn size="sm" variant="ghost" onClick={() => setGscPage(p => Math.min(pages - 1, p + 1))} disabled={page >= pages - 1}>Volgende</Btn>
                        </div>
                      )}
                      <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10 }}>{sorted.length} zoekopdrachten, gesorteerd op vertoningen, laatste {gscRange} dagen. Bron: Google Search Console ({gscRows.site}).</div>
                    </>
                    );
                  })()
                ) : gscSite ? <div style={{ fontSize: 13, color: C.muted }}>Nog niets geladen.</div> : <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>Kies een site om de zoekopdrachten te zien waarop whitevision in Google verschijnt.</div>}
            </>
          )}
        </Card>
      )}

      <Card style={{ marginBottom: 16 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 }}>
          <span style={{ fontWeight: 700, fontSize: 14.5 }}>Eigen data analyseren</span>
          <Btn variant="ghost" size="sm" icon={Upload} onClick={() => fileRef.current?.click()}>Bestand uploaden</Btn>
          <input ref={fileRef} type="file" accept=".csv,.txt,text/csv,text/plain" onChange={onUpload} style={{ display: "none" }} />
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>Plak hier cijfers of een tabel (bijv. uit Excel), of upload een CSV, en laat de AI het visualiseren en analyseren.</div>
        <textarea value={data} onChange={e => setData(e.target.value)} rows={8} placeholder={"Bijv.:\nMaand, Bezoekers, Leads\njan, 1200, 34\nfeb, 1450, 41\n..."}
          style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: "monospace", fontSize: 12.5, resize: "vertical", outline: "none", lineHeight: 1.5 }} />
        <div style={{ marginTop: 12 }}><Btn variant="yellow" icon={Sparkles} onClick={analyse} disabled={busy || !data.trim()}>{busy ? "Bezig…" : "Visualiseer & analyseer"}</Btn></div>
      </Card>
      {busy && <Spinner label="AI analyseert je data…" />}
      {result && <>
        {result.summary && <Card style={{ marginBottom: 14 }}><div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink }}>{result.summary}</div></Card>}
        {(result.charts || []).map((ch, i) => <Card key={i} style={{ marginBottom: 14 }}><div style={{ fontWeight: 700, fontSize: 14, marginBottom: 10 }}>{ch.title}</div><SimpleChart chart={ch} /></Card>)}
        {result.insights?.length > 0 && <Card style={{ marginBottom: 14 }}>
          <div style={sectionLabel}>Inzichten</div>
          {result.insights.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, marginBottom: 6, display: "flex", gap: 7 }}><Eye size={14} style={{ color: C.blue, flexShrink: 0, marginTop: 2 }} /> {x}</div>)}
        </Card>}
        {result.recommendations?.length > 0 && <Card>
          <div style={sectionLabel}>Aanbevelingen</div>
          {result.recommendations.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, marginBottom: 6, display: "flex", gap: 7 }}><CheckCircle2 size={14} style={{ color: C.good, flexShrink: 0, marginTop: 2 }} /> {x}</div>)}
        </Card>}
      </>}

      </>}
    </div>
  );
}

// Eenvoudige SVG-grafiek (bar/line/pie) zonder externe libraries.
function SimpleChart({ chart }) {
  const c = chart || {};
  const labels = c.labels || [];
  const values = (c.values || []).map(Number).filter(n => !isNaN(n));
  if (!values.length) return <div style={{ fontSize: 13, color: C.muted }}>Geen geldige data voor deze grafiek.</div>;
  const max = Math.max(...values, 1);
  const W = 520, H = 220, pad = 30;
  const palette = ["#8B5CF6", "#60A5FA", "#4ADE80", "#F87171", "#9B6DD8", "#7C3AED", "#9CA3AF", "#E5E7EB"];
  if (c.type === "pie") {
    const total = values.reduce((a, b) => a + b, 0) || 1;
    let acc = 0; const cx = 110, cy = 110, r = 90;
    return (
      <div style={{ display: "flex", gap: 24, alignItems: "center", flexWrap: "wrap" }}>
        <svg viewBox="0 0 220 220" style={{ width: 200, height: 200 }}>
          {values.map((v, i) => {
            const a0 = (acc / total) * 2 * Math.PI; acc += v; const a1 = (acc / total) * 2 * Math.PI;
            const x0 = cx + r * Math.sin(a0), y0 = cy - r * Math.cos(a0), x1 = cx + r * Math.sin(a1), y1 = cy - r * Math.cos(a1);
            const large = a1 - a0 > Math.PI ? 1 : 0;
            return <path key={i} d={`M${cx},${cy} L${x0},${y0} A${r},${r} 0 ${large} 1 ${x1},${y1} Z`} fill={palette[i % palette.length]} />;
          })}
        </svg>
        <div>{labels.map((l, i) => <div key={i} style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 12.5, marginBottom: 4 }}><span style={{ width: 12, height: 12, borderRadius: 3, background: palette[i % palette.length] }} /> {l}: <b>{values[i]}</b></div>)}</div>
      </div>
    );
  }
  const n = values.length;
  const bw = (W - pad * 2) / n;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: "100%", maxWidth: W }}>
      <line x1={pad} y1={H - pad} x2={W - pad} y2={H - pad} stroke={C.line} />
      <line x1={pad} y1={pad} x2={pad} y2={H - pad} stroke={C.line} />
      {c.type === "line" ? (
        <polyline fill="none" stroke={C.yellowDark} strokeWidth="2.5"
          points={values.map((v, i) => `${pad + bw * i + bw / 2},${H - pad - (v / max) * (H - pad * 2)}`).join(" ")} />
      ) : values.map((v, i) => {
        const h = (v / max) * (H - pad * 2);
        return <rect key={i} x={pad + bw * i + bw * 0.15} y={H - pad - h} width={bw * 0.7} height={h} rx="3" fill={palette[i % palette.length]} />;
      })}
      {labels.map((l, i) => {
        const step = Math.ceil(n / 8);
        if (n > 12 && i % step !== 0 && i !== n - 1) return null;
        return <text key={i} x={pad + bw * i + bw / 2} y={H - pad + 14} fontSize="10" fill={C.muted} textAnchor="middle">{String(l).slice(5, 10) || String(l).slice(0, 8)}</text>;
      })}
    </svg>
  );
}

/* ============================== AI VISIBILITY ============================== */
function AdsPerformance({ ctx }) {
  const [status, setStatus] = useState(null); // {connected, tokenConfigured, customerId}
  const [customers, setCustomers] = useState(null);
  const [customerId, setCustomerId] = useState("");
  const [loginCustomer, setLoginCustomer] = useState("");
  const [showLogin, setShowLogin] = useState(false);
  const [days, setDays] = useState(30);
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState("");
  const [open, setOpen] = useState({}); // uitgeklapte campagnes/groepen

  useEffect(() => {
    (async () => {
      try {
        const r = await fetch("/api/google/ads/status", { credentials: "include" });
        const d = await r.json();
        setStatus(d);
        if (d.customerId) setCustomerId(d.customerId);
        if (d.loginCustomerId) { setLoginCustomer(d.loginCustomerId); setShowLogin(true); }
        if (d.connected && d.tokenConfigured) {
          try { const c = await fetch("/api/google/ads/customers", { credentials: "include" }); const cd = await c.json(); if (c.ok) { setCustomers(cd.customers || []); if (cd.selected) setCustomerId(cd.selected); } else { setMsg(cd.error || ""); setCustomers([]); } } catch { setCustomers([]); }
        }
      } catch { setStatus({ connected: false }); }
    })();
  }, []);

  const saveLogin = async (id) => {
    try { await fetch("/api/google/ads/login-customer", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ loginCustomerId: id }) }); } catch {}
  };
  const chooseCustomer = async (id) => {
    setCustomerId(id);
    if (!id) return;
    try { await fetch("/api/google/ads/customer", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ customerId: id }) }); } catch {}
    load(id, days);
  };
  const load = async (id, d) => {
    if (!id) return;
    setBusy(true); setMsg("");
    try {
      const r = await fetch("/api/google/ads/structure", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ customerId: id, days: d, loginCustomerId: loginCustomer }) });
      const dd = await r.json();
      if (!r.ok) { setMsg(dd.error || "Kon de Ads-structuur niet ophalen."); setData(null); }
      else setData(dd);
    } catch { setMsg("Kon de Ads-structuur niet ophalen."); }
    setBusy(false);
  };

  const eur = (n) => "€ " + Number(n || 0).toLocaleString("nl-NL", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  const num = (n) => Number(n || 0).toLocaleString("nl-NL");

  if (status && status.connected === false) {
    return <Card style={{ background: C.paper, border: "none" }}><div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5 }}>Koppel eerst Google onder Instellingen → Koppelingen.</div></Card>;
  }
  if (status && !status.tokenConfigured) {
    return <Card style={{ background: C.paper, border: "none" }}><div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.6 }}>Google Ads vraagt eenmalig een developer token die door Google goedgekeurd moet worden. Zodra die is aangevraagd en op de server is ingesteld, verschijnen hier je campagnes, advertentiegroepen en zoekwoorden met vertoningen, klikken en kosten.</div></Card>;
  }

  return (
    <div>
      <div style={{ display: "flex", gap: 10, marginBottom: 6, flexWrap: "wrap", alignItems: "center" }}>
        <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Account:</span>
        {customers && customers.length > 0
          ? <select value={customerId} onChange={e => chooseCustomer(e.target.value)} style={{ padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, background: C.card, outline: "none" }}>
              <option value="">— kies een account —</option>
              {customers.map(c => <option key={c.id} value={c.id}>{c.id}</option>)}
            </select>
          : <span style={{ fontSize: 12.5, color: C.muted }}>{customers == null ? "Accounts laden…" : "Geen accounts beschikbaar."}</span>}
        {customerId && <>
          <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted, marginLeft: 8 }}>Periode:</span>
          {[[7, "7 d"], [30, "30 d"], [90, "90 d"]].map(([d, lbl]) => (
            <button key={d} onClick={() => { setDays(d); load(customerId, d); }} style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 11px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${days === d ? C.yellow : C.line}`, background: days === d ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
          ))}
        </>}
      </div>
      <div style={{ marginBottom: 14 }}>
        {!showLogin
          ? <button onClick={() => setShowLogin(true)} style={{ fontSize: 11.5, color: C.muted, background: "none", border: "none", cursor: "pointer", textDecoration: "underline" }}>Werk je via een beheerdersaccount? Beheerders-ID instellen</button>
          : <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
              <span style={{ fontSize: 11.5, color: C.muted }}>Beheerdersaccount (optioneel):</span>
              <input value={loginCustomer} onChange={e => setLoginCustomer(e.target.value)} placeholder="bijv. 4216144484" style={{ padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12, outline: "none", width: 150 }} />
              <Btn size="sm" variant="ghost" onClick={() => { saveLogin(loginCustomer.replace(/[^0-9]/g, "")); if (customerId) load(customerId, days); }}>Toepassen</Btn>
              <span style={{ fontSize: 11, color: C.muted }}>Alleen nodig als je account onder een beheerdersaccount valt.</span>
            </div>}
      </div>
      {msg && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 12, lineHeight: 1.4 }}>{msg}</div>}
      {busy ? <Spinner label="Campagnes ophalen uit Google Ads…" />
        : !data ? <div style={{ fontSize: 13, color: C.muted }}>{customerId ? "Nog niets geladen." : "Kies een account om je campagnes te zien."}</div>
        : data.campaigns.length === 0 ? <Empty icon={Megaphone} title="Geen campagnes" sub="Geen actieve campagnes met data in deze periode." />
        : (
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {data.campaigns.map(c => (
              <Card key={c.id} style={{ padding: 0, overflow: "hidden" }}>
                <div onClick={() => setOpen(o => ({ ...o, [c.id]: !o[c.id] }))} style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 16px", cursor: "pointer" }}>
                  <ChevronRight size={16} style={{ color: C.muted, transform: open[c.id] ? "rotate(90deg)" : "none", transition: "transform .15s", flexShrink: 0 }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 700, fontSize: 14 }}>{c.name}</div>
                    <div style={{ fontSize: 11.5, color: C.muted }}>{c.status} · {c.adGroups.length} advertentiegroep{c.adGroups.length === 1 ? "" : "en"}</div>
                  </div>
                  <div style={{ display: "flex", gap: 18, fontSize: 12.5, flexShrink: 0 }}>
                    <span title="Vertoningen">{num(c.impressions)} <span style={{ color: C.muted }}>vert.</span></span>
                    <span title="Klikken">{num(c.clicks)} <span style={{ color: C.muted }}>klik</span></span>
                    <span title="Kosten" style={{ fontWeight: 700 }}>{eur(c.cost)}</span>
                  </div>
                </div>
                {open[c.id] && (
                  <div style={{ borderTop: `1px solid ${C.line}`, background: C.paper, padding: "8px 16px 14px" }}>
                    {c.adGroups.length === 0 ? <div style={{ fontSize: 12.5, color: C.muted, padding: "8px 0" }}>Geen advertentiegroepen met data.</div> : c.adGroups.map(ag => (
                      <div key={ag.id} style={{ marginTop: 10 }}>
                        <div onClick={() => setOpen(o => ({ ...o, [ag.id]: !o[ag.id] }))} style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer", padding: "6px 0" }}>
                          <ChevronRight size={14} style={{ color: C.muted, transform: open[ag.id] ? "rotate(90deg)" : "none", transition: "transform .15s" }} />
                          <span style={{ fontWeight: 700, fontSize: 13, flex: 1 }}>{ag.name}</span>
                          <span style={{ fontSize: 12, color: C.ink2 }}>{num(ag.impressions)} vert. · {num(ag.clicks)} klik · <b>{eur(ag.cost)}</b></span>
                        </div>
                        {open[ag.id] && (
                          <div style={{ paddingLeft: 24, marginTop: 4 }}>
                            {ag.keywords.length === 0 ? <div style={{ fontSize: 12, color: C.muted, padding: "4px 0" }}>Geen zoekwoorden met data.</div> : (
                              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}>
                                <thead><tr style={{ borderBottom: `1px solid ${C.line}` }}>
                                  <th style={{ textAlign: "left", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Zoekwoord</th>
                                  <th style={{ textAlign: "left", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Type</th>
                                  <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Vert.</th>
                                  <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Klik</th>
                                  <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Kosten</th>
                                </tr></thead>
                                <tbody>
                                  {ag.keywords.map((k, ki) => (
                                    <tr key={ki}>
                                      <td style={{ padding: "5px 8px" }}>{k.text}</td>
                                      <td style={{ padding: "5px 8px", color: C.muted }}>{k.matchType}</td>
                                      <td style={{ padding: "5px 8px", textAlign: "right" }}>{num(k.impressions)}</td>
                                      <td style={{ padding: "5px 8px", textAlign: "right" }}>{num(k.clicks)}</td>
                                      <td style={{ padding: "5px 8px", textAlign: "right" }}>{eur(k.cost)}</td>
                                    </tr>
                                  ))}
                                </tbody>
                              </table>
                            )}
                          </div>
                        )}
                      </div>
                    ))}
                  </div>
                )}
              </Card>
            ))}
            <div style={{ fontSize: 11.5, color: C.muted, marginTop: 4, lineHeight: 1.5 }}>Klik op een campagne en daarna op een advertentiegroep om de zoekwoorden te zien. Cijfers over de laatste {days} dagen uit Google Ads.</div>
          </div>
        )}
    </div>
  );
}

function ContentPerformance({ ctx, embedded }) {
  const [urls, setUrls] = useState(null);     // [{id, url, sessions, gsc, lastFetch}]
  const [days, setDays] = useState(28);
  const [newUrl, setNewUrl] = useState("");
  const [busyId, setBusyId] = useState(null); // id van URL die laadt, of "all"
  const [expanded, setExpanded] = useState({});
  const [connected, setConnected] = useState(null);

  useEffect(() => {
    (async () => {
      try { setUrls(await store.get("wv_content_urls", [])); } catch { setUrls([]); }
      try { const r = await fetch("/api/google/status", { credentials: "include" }); const d = await r.json(); setConnected(!!d.connected); } catch { setConnected(false); }
    })();
  }, []);
  const save = (v) => { setUrls(v); store.set("wv_content_urls", v); };

  const addUrl = () => {
    const u = newUrl.trim(); if (!u) return;
    const full = u.startsWith("http") ? u : "https://" + u;
    save([{ id: Date.now(), url: full, sessions: null, gsc: null, lastFetch: null }, ...(urls || [])]);
    setNewUrl("");
  };
  const removeUrl = (id) => save((urls || []).filter(x => x.id !== id));
  const setItemUrl = (id, val) => {
    const u = (val || "").trim(); if (!u) return;
    const full = u.startsWith("http") ? u : "https://" + u;
    save((urls || []).map(x => x.id === id ? { ...x, url: full } : x));
  };

  const fetchOne = async (item) => {
    if (!item.url) return;
    setBusyId(item.id);
    try {
      const r = await fetch("/api/google/content/url", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: item.url, days }) });
      const d = await r.json();
      if (r.ok) save((urls || []).map(x => x.id === item.id ? { ...x, sessions: d.sessions, gsc: d.gsc, errors: d.errors || [], lastFetch: new Date().toISOString().slice(0, 10) } : x));
    } catch {}
    setBusyId(null);
  };
  const fetchAll = async () => {
    setBusyId("all");
    let list = [...(urls || [])];
    for (const item of list) {
      if (!item.url) continue;
      try {
        const r = await fetch("/api/google/content/url", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: item.url, days }) });
        const d = await r.json();
        if (r.ok) { list = list.map(x => x.id === item.id ? { ...x, sessions: d.sessions, gsc: d.gsc, errors: d.errors || [], lastFetch: new Date().toISOString().slice(0, 10) } : x); save(list); }
      } catch {}
    }
    setBusyId(null);
  };

  const shortUrl = (u) => { try { const x = new URL(u); return x.pathname === "/" ? x.hostname : x.pathname; } catch { return u; } };
  const th = { textAlign: "left", padding: "9px 10px", fontSize: 11.5, fontWeight: 700, color: "#fff", whiteSpace: "nowrap" };
  const thR = { ...th, textAlign: "right" };

  return (
    <div>
      {!embedded && <PageHead title="Content performance" sub="Volg per URL hoe een pagina presteert: vertoningen, klikken, positie en zoekwoorden uit Search Console, plus sessies uit Analytics." />}

      {connected === false && (
        <Card style={{ marginBottom: 16, background: C.paper, border: "none" }}>
          <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5 }}>Koppel eerst Google (Instellingen → Koppelingen) en kies een Search Console-site en Analytics-property onder Analytics. Daarna kun je hier per URL de cijfers ophalen.</div>
        </Card>
      )}

      <Card style={{ marginBottom: 16 }}>
        <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
          <input value={newUrl} onChange={e => setNewUrl(e.target.value)} onKeyDown={e => e.key === "Enter" && addUrl()} placeholder="https://whitevision.com/pagina"
            style={{ flex: 1, minWidth: 220, padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
          <Btn variant="yellow" icon={Plus} onClick={addUrl}>URL toevoegen</Btn>
        </div>
      </Card>

      <div style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap", alignItems: "center" }}>
        <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Periode:</span>
        {[[7, "7 dagen"], [28, "28 dagen"], [90, "90 dagen"]].map(([d, lbl]) => (
          <button key={d} onClick={() => setDays(d)} style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${days === d ? C.yellow : C.line}`, background: days === d ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
        ))}
        <div style={{ marginLeft: "auto" }}>
          <Btn size="sm" variant="ghost" icon={Sparkles} onClick={fetchAll} disabled={busyId !== null || !(urls && urls.length)}>{busyId === "all" ? "Bezig…" : "Alles verversen"}</Btn>
        </div>
      </div>

      {urls == null ? <Spinner label="Laden…" /> : urls.length === 0 ? <Empty icon={FileText} title="Nog geen URL's" sub="Voeg een URL toe om het verkeer en de zoekwoorden van die pagina te volgen." /> : (
        <Card style={{ padding: 0, overflow: "hidden" }}>
          <div style={{ overflowX: "auto" }}>
            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5 }}>
              <thead><tr style={{ background: C.surface }}>
                <th style={{ ...th, width: 28 }}></th>
                <th style={{ ...th, minWidth: 200 }}>Pagina</th>
                <th style={thR}>Sessies</th>
                <th style={thR}>Vertoningen</th>
                <th style={thR}>Klikken</th>
                <th style={thR}>CTR</th>
                <th style={thR}>Positie</th>
                <th style={{ ...th, width: 90 }}></th>
              </tr></thead>
              <tbody>
                {urls.map((item, i) => {
                  const g = item.gsc;
                  const isOpen = !!expanded[item.id];
                  const hasKw = g && g.keywords && g.keywords.length > 0;
                  return (
                    <React.Fragment key={item.id}>
                      <tr style={{ background: i % 2 ? C.paper : C.card, borderBottom: isOpen ? "none" : `1px solid ${C.line}` }}>
                        <td style={{ padding: "6px 6px", textAlign: "center" }}>
                          {hasKw ? <button onClick={() => setExpanded(e => ({ ...e, [item.id]: !e[item.id] }))} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted }}><ChevronRight size={15} style={{ transform: isOpen ? "rotate(90deg)" : "none", transition: "transform .15s" }} /></button> : null}
                        </td>
                        <td style={{ padding: "7px 10px" }}>
                          {item.url
                            ? <><a href={item.url} target="_blank" rel="noopener noreferrer" title={item.url} style={{ color: C.ink, fontWeight: 600, textDecoration: "none" }}>{shortUrl(item.url)}</a>
                                {item.lastFetch && <div style={{ fontSize: 10.5, color: C.muted }}>bijgewerkt {item.lastFetch}</div>}</>
                            : <div>
                                <div style={{ fontWeight: 600, fontSize: 12.5 }}>{item.title || "(zonder titel)"}</div>
                                <div style={{ display: "flex", gap: 6, alignItems: "center", marginTop: 4 }}>
                                  <input defaultValue="" placeholder="plak hier de live URL" onKeyDown={e => { if (e.key === "Enter") setItemUrl(item.id, e.target.value); }} onBlur={e => { if (e.target.value.trim()) setItemUrl(item.id, e.target.value); }}
                                    style={{ padding: "5px 8px", border: `1.5px solid ${C.line}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 11.5, outline: "none", width: 180 }} />
                                  <span style={{ fontSize: 10.5, color: C.yellowDark, fontWeight: 700 }}>gepubliceerd</span>
                                </div>
                              </div>}
                        </td>
                        <td style={{ padding: "7px 10px", textAlign: "right" }}>{item.sessions == null ? "—" : Number(item.sessions).toLocaleString("nl-NL")}</td>
                        <td style={{ padding: "7px 10px", textAlign: "right" }}>{g ? Number(g.impressions).toLocaleString("nl-NL") : "—"}</td>
                        <td style={{ padding: "7px 10px", textAlign: "right" }}>{g ? Number(g.clicks).toLocaleString("nl-NL") : "—"}</td>
                        <td style={{ padding: "7px 10px", textAlign: "right" }}>{g ? (g.ctr * 100).toFixed(1) + "%" : "—"}</td>
                        <td style={{ padding: "7px 10px", textAlign: "right" }}>{g ? g.position.toFixed(1) : "—"}</td>
                        <td style={{ padding: "7px 10px", textAlign: "right", whiteSpace: "nowrap" }}>
                          <button onClick={() => fetchOne(item)} title={item.url ? "Verversen" : "Koppel eerst een URL"} disabled={busyId !== null || !item.url} style={{ background: "none", border: "none", cursor: (busyId !== null || !item.url) ? "default" : "pointer", color: item.url ? C.ink2 : C.line }}>{busyId === item.id ? "…" : <RefreshCw size={14} />}</button>
                          <button onClick={() => removeUrl(item.id)} title="Verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.bad, marginLeft: 6 }}><Trash2 size={14} /></button>
                        </td>
                      </tr>
                      {isOpen && hasKw && (
                        <tr style={{ background: C.paper, borderBottom: `1px solid ${C.line}` }}>
                          <td></td>
                          <td colSpan={7} style={{ padding: "4px 10px 12px" }}>
                            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, margin: "6px 0", textTransform: "uppercase" }}>Zoekwoorden voor deze pagina</div>
                            <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}>
                              <thead><tr style={{ borderBottom: `1px solid ${C.line}` }}>
                                <th style={{ textAlign: "left", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Zoekwoord</th>
                                <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Vertoningen</th>
                                <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Klikken</th>
                                <th style={{ textAlign: "right", padding: "5px 8px", fontWeight: 700, color: C.muted }}>Positie</th>
                              </tr></thead>
                              <tbody>
                                {g.keywords.map((k, ki) => (
                                  <tr key={ki}>
                                    <td style={{ padding: "5px 8px" }}>{k.query}</td>
                                    <td style={{ padding: "5px 8px", textAlign: "right" }}>{Number(k.impressions).toLocaleString("nl-NL")}</td>
                                    <td style={{ padding: "5px 8px", textAlign: "right" }}>{Number(k.clicks).toLocaleString("nl-NL")}</td>
                                    <td style={{ padding: "5px 8px", textAlign: "right" }}>{k.position.toFixed(1)}</td>
                                  </tr>
                                ))}
                              </tbody>
                            </table>
                          </td>
                        </tr>
                      )}
                    </React.Fragment>
                  );
                })}
              </tbody>
            </table>
          </div>
        </Card>
      )}
      <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10, lineHeight: 1.5 }}>Vertoningen, klikken, positie en zoekwoorden komen uit Search Console; sessies uit Analytics. Klik op het pijltje om de zoekwoorden per pagina te zien, en op het ververs-icoon om één pagina bij te werken. Sessies tellen al het verkeer naar de pagina, inclusief eventueel betaald verkeer; losse advertentiecijfers vragen een aparte Google Ads-koppeling.</div>
    </div>
  );
}

function AIVisibility({ ctx }) {
  const [ga, setGa] = useState({ checked: false, connected: false });
  const [range, setRange] = useState("90daysAgo");
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState("");

  const rangeLabel = (r) => r === "7daysAgo" ? "7 dagen" : r === "30daysAgo" ? "30 dagen" : "90 dagen";

  const load = async (r) => {
    setBusy(true); setMsg("");
    try {
      const res = await fetch("/api/google/ai-traffic", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ startDate: r, endDate: "today" }) });
      const d = await res.json();
      if (!res.ok) { setMsg(d.error || "Kon het AI-verkeer niet ophalen."); setData(null); }
      else setData(d);
    } catch { setMsg("Kon het AI-verkeer niet ophalen."); }
    setBusy(false);
  };

  useEffect(() => {
    (async () => {
      try {
        const res = await fetch("/api/google/status", { credentials: "include" });
        const d = await res.json();
        setGa({ checked: true, connected: !!d.connected });
        if (d.connected) load(range);
      } catch { setGa({ checked: true, connected: false }); }
    })();
  }, []);

  const engineColor = (label) => ({ ChatGPT: "#34D399", Gemini: "#60A5FA", Claude: "#F87171", Perplexity: "#9B6DD8", NotebookLM: "#7C3AED", Copilot: "#9CA3AF" }[label] || "#E5E7EB");

  return (
    <div>
      <PageHead title="AI visibility" sub="Verkeer naar whitevision vanuit AI-engines (ChatGPT, Gemini, Claude, Perplexity en meer), op basis van de sessiebron in Google Analytics." />

      {!ga.connected ? (
        <Card style={{ background: C.paper, border: "none" }}>
          <div style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
            <Bot size={18} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
            <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.55 }}>
              {ga.checked ? <>Koppel eerst Google Analytics (onder Performance → Analytics) om het verkeer uit AI-engines te zien.</> : "Laden…"}
            </div>
          </div>
        </Card>
      ) : (
        <>
          <Card style={{ marginBottom: 16, background: C.paper, border: "none" }}>
            <div style={{ display: "flex", gap: 10, alignItems: "flex-start" }}>
              <Bot size={16} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
              <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>Dit toont het verkeer waarvan de sessiebron herkenbaar bij een AI-engine hoort. Niet al het AI-verkeer is in GA4 herkenbaar gelabeld (soms komt het binnen als 'direct'), dus zie dit als een betrouwbare indicatie van de trend, niet als een volledige meting.</div>
            </div>
          </Card>

          <div style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap", alignItems: "center" }}>
            <span style={{ fontSize: 12.5, fontWeight: 700, color: C.muted }}>Periode:</span>
            {["7daysAgo", "30daysAgo", "90daysAgo"].map(r => (
              <button key={r} onClick={() => { setRange(r); load(r); }}
                style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${range === r ? C.yellow : C.line}`, background: range === r ? C.yellow + "22" : C.card, color: C.ink }}>{rangeLabel(r)}</button>
            ))}
          </div>

          {msg && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 12 }}>{msg}</div>}
          {busy ? <Spinner label="AI-verkeer ophalen uit Google Analytics…" />
            : data ? (
              <>
                <Card style={{ marginBottom: 16 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 12 }}>
                    <span style={{ fontWeight: 700, fontSize: 14.5 }}>AI-verkeer over tijd (sessies)</span>
                    <span style={{ fontSize: 13, color: C.muted }}>Totaal: <b style={{ color: C.ink }}>{Number(data.total).toLocaleString("nl-NL")}</b></span>
                  </div>
                  {data.total > 0
                    ? <HoverLineChart labels={data.series.map(p => p.date)} series={[{ name: "AI-sessies", color: C.yellowDark, values: data.series.map(p => p.sessions) }]} fmtVal={(v) => Number(v).toLocaleString("nl-NL") + " sessies"} />
                    : <div style={{ fontSize: 13, color: C.muted, padding: "20px 0" }}>Geen herkenbaar AI-verkeer gevonden in deze periode.</div>}
                </Card>

                {data.engines.length > 0 && (
                  <Card style={{ padding: 0, overflow: "hidden" }}>
                    <div style={{ padding: "13px 16px", borderBottom: `1px solid ${C.line}`, fontWeight: 700, fontSize: 14 }}>Per engine</div>
                    {data.engines.map((e, i) => {
                      const pct = data.total > 0 ? (e.sessions / data.total) * 100 : 0;
                      return (
                        <div key={e.label} style={{ padding: "12px 16px", borderBottom: i < data.engines.length - 1 ? `1px solid ${C.line}` : "none" }}>
                          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }}>
                            <span style={{ display: "flex", alignItems: "center", gap: 8, fontWeight: 700, fontSize: 13.5 }}>
                              <span style={{ width: 11, height: 11, borderRadius: 3, background: engineColor(e.label) }} /> {e.label}
                            </span>
                            <span style={{ fontSize: 13 }}><b>{Number(e.sessions).toLocaleString("nl-NL")}</b> <span style={{ color: C.muted }}>({pct.toFixed(1)}%)</span></span>
                          </div>
                          <div style={{ height: 7, background: C.paper, borderRadius: 5, overflow: "hidden" }}>
                            <div style={{ width: `${pct}%`, height: "100%", background: engineColor(e.label) }} />
                          </div>
                        </div>
                      );
                    })}
                  </Card>
                )}
                <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10 }}>Laatste {rangeLabel(range)}, t/m vandaag. Bron: Google Analytics (GA4), sessiebron.</div>
              </>
            ) : <div style={{ fontSize: 13, color: C.muted }}>Nog geen data geladen.</div>}
        </>
      )}
    </div>
  );
}

/* ============================== CONTENT ============================== */
// Eenvoudige, betrouwbare Word-export (HTML-gebaseerd .doc dat Word netjes opent).
// Excel-export met meerdere tabbladen (SpreadsheetML 2003 — opent in Excel, geen library nodig).
function exportToExcel(filename, sheets) {
  const xmlEsc = (v) => String(v == null ? "" : v).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  const cell = (v) => {
    const num = typeof v === "number" || (/^-?\d+(\.\d+)?$/.test(String(v)) && String(v).trim() !== "");
    return `<Cell><Data ss:Type="${num ? "Number" : "String"}">${xmlEsc(v)}</Data></Cell>`;
  };
  const sheetXml = (s) => {
    const rows = (s.rows || []).map(r => `<Row>${r.map(cell).join("")}</Row>`).join("");
    const name = xmlEsc((s.name || "Blad").slice(0, 31).replace(/[\\\/\?\*\[\]:]/g, " "));
    return `<Worksheet ss:Name="${name}"><Table>${rows}</Table></Worksheet>`;
  };
  const xml = `<?xml version="1.0"?>\n<?mso-application progid="Excel.Sheet"?>\n<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">${sheets.map(sheetXml).join("")}</Workbook>`;
  const blob = new Blob(["\ufeff", xml], { type: "application/vnd.ms-excel" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = `${(filename || "export").replace(/[^\w\s-]/g, "").trim().slice(0, 60) || "export"}.xls`;
  document.body.appendChild(a); a.click(); document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}
function exportToWord(title, bodyHtml) {
  const html = `<!DOCTYPE html><html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'>
<head><meta charset='utf-8'><title>${title}</title>
<style>body{font-family:Calibri,Arial,sans-serif;font-size:11pt;line-height:1.5;color:#1C1C1A;} h1{font-size:20pt;color:#1C1C1A;} h2{font-size:14pt;color:#1C1C1A;border-bottom:2px solid #F9C112;padding-bottom:3px;} h3{font-size:12pt;} p{margin:0 0 10pt;}</style></head>
<body>${bodyHtml}</body></html>`;
  const blob = new Blob(["\ufeff", html], { type: "application/msword" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = `${title.replace(/[^\w\s-]/g, "").trim().slice(0, 60) || "content"}.doc`;
  document.body.appendChild(a); a.click(); document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}
// Markdown-achtige tekst -> nette HTML voor weergave en export.
function contentToHtml(text) {
  if (!text) return "";
  const lines = text.split("\n");
  let html = "", inList = false;
  for (let raw of lines) {
    const line = raw.trimEnd();
    const closeList = () => { if (inList) { html += "</ul>"; inList = false; } };
    if (/^#{3}\s/.test(line)) { closeList(); html += `<h3>${esc(line.replace(/^#{3}\s/, ""))}</h3>`; }
    else if (/^#{2}\s/.test(line)) { closeList(); html += `<h2>${esc(line.replace(/^#{2}\s/, ""))}</h2>`; }
    else if (/^#\s/.test(line)) { closeList(); html += `<h1>${esc(line.replace(/^#\s/, ""))}</h1>`; }
    else if (/^[-*]\s/.test(line)) { if (!inList) { html += "<ul>"; inList = true; } html += `<li>${esc(line.replace(/^[-*]\s/, ""))}</li>`; }
    else if (line.trim() === "") { closeList(); }
    else { closeList(); html += `<p>${esc(line)}</p>`; }
  }
  if (inList) html += "</ul>";
  return html.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
}
function esc(s) { return (s || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); }
const KIND_LABEL = (k) => k === "whitepaper" ? "Whitepaper" : k === "ebook" ? "E-book" : k === "landingspagina" ? "Landingspagina" : k === "nieuwsbericht" ? "Nieuwsbericht" : k === "persbericht" ? "Persbericht" : k === "social" ? "Social post" : "Blog";

const CONTENT_TILES = [
  { id: "blog", label: "Blogs", icon: PenLine, desc: "Bondige, pakkende blogs die je merk als autoriteit positioneren.", active: true, kind: "blog" },
  { id: "whitepaper", label: "Whitepapers", icon: FileText, desc: "Grondige, gestructureerde whitepapers met diepgang.", active: true, kind: "whitepaper" },
  { id: "ebook", label: "E-books", icon: BookOpen, desc: "Uitgebreide e-books die een onderwerp volledig behandelen.", active: true, kind: "ebook" },
  { id: "landingspagina", label: "Landingspagina's", icon: Globe, desc: "Wervende pagina's gericht op conversie.", active: true, kind: "landingspagina" },
  { id: "nieuwsbericht", label: "Nieuwsberichten", icon: Megaphone, desc: "Korte, actuele berichten en updates.", active: true, kind: "nieuwsbericht" },
  { id: "persbericht", label: "Persberichten", icon: Radar, desc: "Formele persberichten voor media.", active: true, kind: "persbericht" },
];
const COPY_KINDS = [
  { kind: "blog", label: "Blog" },
  { kind: "whitepaper", label: "Whitepaper" },
  { kind: "ebook", label: "E-book" },
  { kind: "landingspagina", label: "Landingspagina" },
  { kind: "nieuwsbericht", label: "Nieuwsbericht" },
  { kind: "persbericht", label: "Persbericht" },
];
// Standaardformaten die Google Ads voor display accepteert.
const BANNER_FORMATS = [
  { w: 300, h: 250, label: "Medium rectangle", on: true },
  { w: 336, h: 280, label: "Large rectangle", on: true },
  { w: 728, h: 90, label: "Leaderboard", on: true },
  { w: 300, h: 600, label: "Half page", on: true },
  { w: 160, h: 600, label: "Wide skyscraper", on: false },
  { w: 970, h: 250, label: "Billboard", on: false },
  { w: 320, h: 50, label: "Mobiele banner", on: true },
  { w: 320, h: 100, label: "Grote mobiele banner", on: false },
  { w: 250, h: 250, label: "Vierkant", on: false },
  { w: 468, h: 60, label: "Banner", on: false },
];

const DESIGN_TILES = [
  { id: "presentation", label: "Presentaties", icon: Presentation, desc: "Slidedecks in je huisstijl, automatisch opgesteld uit je bedrijfsprofiel.", active: true },
  { id: "banner", label: "Bannersets", icon: LayoutTemplate, desc: "HTML5-banners in je huisstijl, klaar voor Google Ads.", active: true },
  { id: "infographic", label: "Infographics", icon: Image, desc: "Visuele infographics in je huisstijl.", active: false },
  { id: "social_visual", label: "Social posts", icon: Share2, desc: "Posts voor LinkedIn, Meta en Instagram in de juiste formaten.", active: true },
  { id: "illustration", label: "Illustraties", icon: Palette, desc: "Illustraties en grafische elementen in je huisstijl.", active: false },
];

// ---- Contentagenda ----
// Onderwerpen inplannen met een datum. Staat er al tekst bij en is WordPress gekoppeld,
// dan kan een item op de geplande datum automatisch als concept naar je blog. Dat
// publiceren zelf gebruikt geen AI; alleen het schrijven van tekst doet dat.
function ContentAgenda({ ctx }) {
  const { content, saveContent, confirm, goTo } = ctx;
  const [wp, setWp] = useState({ on: false, types: [] });
  const [autoOn, setAutoOn] = useState(false);
  const [form, setForm] = useState({ title: "", kind: "blog", date: "", pageType: "" });
  const [msg, setMsg] = useState("");

  useEffect(() => {
    fetch("/api/wordpress/status", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setWp({ on: !!d.configured, types: d.pageTypes || [] }); })
      .catch(() => {});
    store.get("wv_agenda_auto", false).then(v => setAutoOn(v === true)).catch(() => {});
  }, []);

  const planned = (content || [])
    .filter(c => c.plannedAt)
    .sort((a, b) => String(a.plannedAt).localeCompare(String(b.plannedAt)));
  const unplanned = (content || []).filter(c => !c.plannedAt && c.phase !== "published");

  const flash = (t) => { setMsg(t); setTimeout(() => setMsg(""), 2500); };
  const upd = (id, patch) => saveContent((content || []).map(c => c.id === id ? { ...c, ...patch } : c));

  const addTopic = () => {
    if (!form.title.trim() || !form.date) return;
    saveContent([{
      id: Date.now(), kind: form.kind, title: form.title.trim(), phase: "todo",
      date: new Date().toISOString().slice(0, 10), plannedAt: form.date,
      pageType: form.pageType || "", autoPush: false, body: "",
    }, ...(content || [])]);
    setForm({ title: "", kind: form.kind, date: "", pageType: form.pageType });
    flash("Onderwerp ingepland.");
  };

  const toggleAuto = async () => {
    const next = !autoOn;
    setAutoOn(next);
    await store.set("wv_agenda_auto", next);
  };

  const today = new Date().toISOString().slice(0, 10);
  const statusOf = (c) => {
    if (c.wpId) return { label: "in WordPress", color: C.good };
    if (c.phase === "published") return { label: "gepubliceerd", color: C.good };
    if (c.body) return { label: "tekst klaar", color: C.blue };
    return { label: "nog te schrijven", color: C.muted };
  };
  const kindLabel = (k) => (CONTENT_TILES.find(t => t.kind === k) || {}).label || k;

  return (
    <div>
      {msg && <Card style={{ marginBottom: 14, border: `1.5px solid ${C.good}`, background: C.good + "0E" }}><div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 13.5, color: C.good, fontWeight: 600 }}><Check size={15} /> {msg}</div></Card>}

      {/* Automatisch publiceren */}
      <Card style={{ marginBottom: 18 }}>
        <div style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>
          <Toggle on={autoOn} onChange={toggleAuto} />
          <div style={{ flex: 1 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
              <span style={{ fontSize: 13.5, fontWeight: 600, color: C.ink }}>Automatisch naar je blog op de geplande datum</span>
              <Tag color={autoOn ? C.good : C.muted}>{autoOn ? "aan" : "uit"}</Tag>
            </div>
            <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.55, marginTop: 3 }}>
              {wp.on
                ? "Items waarbij je dit per stuk aanzet én waar al tekst bij staat, worden op de geplande datum als concept in WordPress aangemaakt. Publiceren kost geen AI-tegoed; je kijkt er in WordPress altijd nog naar voordat het live gaat."
                : "WordPress is nog niet gekoppeld. Zonder koppeling kan er niets automatisch worden aangemaakt."}
            </div>
            {!wp.on && <div style={{ marginTop: 8 }}><Btn size="sm" variant="ghost" icon={ArrowUpRight} onClick={() => { if (typeof window !== "undefined") window.__settingsTab = "wordpress"; goTo && goTo("settings"); }}>WordPress koppelen</Btn></div>}
          </div>
        </div>
      </Card>

      {/* Onderwerp inplannen */}
      <Card style={{ marginBottom: 18 }}>
        <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 12 }}>Onderwerp inplannen</div>
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr auto", gap: 10, alignItems: "end" }}>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Onderwerp</label>
            <input value={form.title} onChange={e => setForm(f => ({ ...f, title: e.target.value }))} onKeyDown={e => e.key === "Enter" && addTopic()}
              placeholder="bijv. 'Zo kies je een factuurverwerker'"
              style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Soort</label>
            <select value={form.kind} onChange={e => setForm(f => ({ ...f, kind: e.target.value }))}
              style={{ width: "100%", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", background: C.card }}>
              {CONTENT_TILES.map(t => <option key={t.kind} value={t.kind}>{t.label}</option>)}
            </select>
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Datum</label>
            <input type="date" value={form.date} onChange={e => setForm(f => ({ ...f, date: e.target.value }))}
              style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none" }} />
          </div>
          <Btn variant="yellow" icon={Plus} onClick={addTopic} disabled={!form.title.trim() || !form.date}>Inplannen</Btn>
        </div>
      </Card>

      {/* De agenda */}
      <div style={sectionLabel}>Ingeplande onderwerpen</div>
      {planned.length === 0
        ? <Empty icon={CalendarDays} title="Nog niets ingepland" sub="Plan een onderwerp in met een datum, of geef bestaande content een datum." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 9, marginTop: 10 }}>
            {planned.map(c => {
              const st = statusOf(c);
              const late = c.plannedAt < today && !c.wpId && c.phase !== "published";
              return (
                <Card key={c.id} style={{ padding: "12px 14px", borderLeft: `3px solid ${late ? C.bad : st.color}` }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
                    <input type="date" value={c.plannedAt || ""} onChange={e => upd(c.id, { plannedAt: e.target.value })}
                      style={{ padding: "5px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card, flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 160 }}>
                      <div style={{ fontWeight: 700, fontSize: 14 }}>{c.title}</div>
                      <div style={{ display: "flex", alignItems: "center", gap: 7, marginTop: 3, flexWrap: "wrap" }}>
                        <Tag color={C.muted}>{kindLabel(c.kind)}</Tag>
                        <Tag color={st.color}>{st.label}</Tag>
                        {late && <Tag color={C.bad}>datum verstreken</Tag>}
                      </div>
                    </div>
                    {wp.on && wp.types.length > 0 && (
                      <select value={c.pageType || ""} onChange={e => upd(c.id, { pageType: e.target.value })} title="Paginatype in WordPress"
                        style={{ padding: "5px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12, outline: "none", background: C.card, color: c.pageType ? C.ink : C.muted, maxWidth: 150 }}>
                        <option value="">paginatype…</option>
                        {wp.types.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
                      </select>
                    )}
                    {wp.on && (
                      <label title={c.body ? "Automatisch aanmaken op de geplande datum" : "Er moet eerst tekst zijn"} style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: c.body ? C.ink2 : C.muted, cursor: c.body ? "pointer" : "default", flexShrink: 0 }}>
                        <input type="checkbox" checked={!!c.autoPush} disabled={!c.body} onChange={e => upd(c.id, { autoPush: e.target.checked })} />
                        automatisch
                      </label>
                    )}
                    <button onClick={() => confirm(`"${c.title}" van de agenda halen? De content blijft bestaan.`, () => upd(c.id, { plannedAt: "", autoPush: false }))}
                      title="Van de agenda halen" style={iconBtn(C.muted)}><X size={15} /></button>
                  </div>
                  {!c.body && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 7, lineHeight: 1.5 }}>Er staat nog geen tekst bij dit onderwerp. Werk het uit op het tabblad Productie; daarna kun je het automatisch laten aanmaken.</div>}
                </Card>
              );
            })}
          </div>}

      {unplanned.length > 0 && <>
        <div style={sectionLabel}>Nog niet ingepland</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 7, marginTop: 10 }}>
          {unplanned.slice(0, 20).map(c => (
            <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 13px", border: `1px solid ${C.line}`, borderRadius: 10 }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <span style={{ fontSize: 13.5, fontWeight: 600 }}>{c.title}</span>
                <span style={{ fontSize: 12, color: C.muted, marginLeft: 8 }}>{kindLabel(c.kind)}</span>
              </div>
              <input type="date" onChange={e => e.target.value && upd(c.id, { plannedAt: e.target.value })}
                style={{ padding: "5px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card }} />
            </div>
          ))}
        </div>
      </>}
    </div>
  );
}

function Content({ ctx }) {
  const { content, saveContent, profile, confirm, goTo } = ctx;
  const [copyTab, setCopyTab] = useState("productie");
  const [tile, setTile] = useState(null);
  const [openId, setOpenId] = useState(null);
  const [mode, setMode] = useState(null); // "suggest" | "own"
  const [busy, setBusy] = useState(false);
  const [steer, setSteer] = useState(""); // sturing voor AI-suggesties
  const [genId, setGenId] = useState(null);
  const [editKey, setEditKey] = useState(null);
  const [form, setForm] = useState({ kind: "blog", topic: "", brief: "" });
  const [regenFb, setRegenFb] = useState("");
  const [regenBusy, setRegenBusy] = useState(false);
  const [outline, setOutline] = useState(null); // { item, subs:[{id,title}], words, busy, dragId }
  const [genErr, setGenErr] = useState(""); // foutmelding bij uitwerken
  const [delSel, setDelSel] = useState({}); // bulk-verwijder selectie {id:true}
  const toggleDel = (id) => setDelSel(s => ({ ...s, [id]: !s[id] }));
  const delSelCount = Object.values(delSel).filter(Boolean).length;
  const bulkDelete = () => {
    const ids = new Set(Object.keys(delSel).filter(k => delSel[k]).map(Number));
    if (!ids.size) return;
    confirm(`${ids.size} item(s) definitief verwijderen?`, () => { saveContent((content || []).filter(c => !ids.has(c.id))); setDelSel({}); });
  };

  // Genereer een bestaand stuk opnieuw, met optionele feedback/sturing.
  const regenerate = async (c) => {
    setRegenBusy(true);
    const today = new Date();
    const dateNL = today.toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" });
    const isWp = c.kind === "whitepaper";
    const lengte = isWp ? "grondig en gestructureerd (1500-2500 woorden)" : c.kind === "social" ? "kort en pakkend (LinkedIn-post, 80-150 woorden)" : "bondig en pakkend (600-900 woorden)";
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nVandaag is het ${dateNL}; schrijf vanuit ${today.getFullYear()}.\n\nOPDRACHT: Herschrijf het volgende ${KIND_LABEL(c.kind).toLowerCase()} voor whitevision. ${regenFb.trim() ? "Verwerk deze feedback/sturing nadrukkelijk: " + regenFb.trim() : "Maak het sterker en scherper."} Behoud het onderwerp, schrijf ${lengte}, in onze tone of voice. Markdown: # titel, ## koppen, - opsommingen. Geen <cite>-tags.\n\nHUIDIGE VERSIE:\n${(c.body || "").slice(0, 8000)}\n\nGeef ALLEEN de nieuwe tekst in markdown, geen uitleg eromheen.`;
    const { data: text, sources } = await callAIFull(prompt, {});
    const title = (text.match(/^#\s+(.+)$/m)?.[1] || c.title).trim();
    saveContent((content || []).map(x => x.id === c.id ? { ...x, title, body: text, sources: sources || [], date: new Date().toISOString().slice(0, 10) } : x));
    setRegenBusy(false); setRegenFb("");
  };

  const items = (content || []).filter(c => (c.tile === "thought" || c.tile === "copy") && (c.kind === tile || (tile === "blog" && !["whitepaper", "ebook", "social", "landingspagina", "nieuwsbericht", "persbericht"].includes(c.kind))));
  const backlog = items.filter(c => c.phase === "backlog");
  const todo = items.filter(c => c.phase === "todo");
  const done = items.filter(c => c.phase === "done" || (!c.phase && c.body)); // oude items zonder fase = uitgewerkt

  // Publiceren met menselijke controle: markeer als gepubliceerd (verdwijnt uit Uitgewerkt) en
  // zet een regel klaar in Content performance (Analytics) om de resultaten te volgen.
  const publishContent = (c) => {
    confirm(`"${c.title}" publiceren? Het stuk verdwijnt uit Uitgewerkt en komt als regel in Content performance (Analytics) te staan. Daar koppel je later zelf de live URL om de cijfers te volgen. Het zet het stuk niet automatisch live op je website.`, async () => {
      // status bijwerken
      saveContent((content || []).map(x => x.id === c.id ? { ...x, phase: "published", publishedAt: new Date().toISOString().slice(0, 10) } : x));
      // toevoegen aan Content performance (wv_content_urls) zonder bestaande te overschrijven
      try {
        const cur = await store.get("wv_content_urls", []);
        const list = Array.isArray(cur) ? cur : [];
        list.unshift({ id: Date.now(), url: "", title: c.title, fromContentId: c.id, sessions: null, gsc: null, lastFetch: null });
        await store.set("wv_content_urls", list);
      } catch {}
      setOpenId(null);
    });
  };

  // ---- Naar WordPress vanuit een uitgewerkt stuk ----
  // Je kiest het paginatype; de tool weet dan waar het heen gaat. Wil je de tekst laten
  // aansluiten op dat template, dan kan hij eerst herschreven worden (dat gebruikt AI).
  const [wpCfg, setWpCfg] = useState({ on: false, types: [] });
  const [wpModal, setWpModal] = useState(null);   // het stuk dat je doorzet
  const [wpType, setWpType] = useState("");
  const [wpRewrite, setWpRewrite] = useState(false);
  const [wpBusy, setWpBusy] = useState("");
  const [wpErr, setWpErr] = useState("");
  const [wpDone, setWpDone] = useState(null);
  useEffect(() => {
    fetch("/api/wordpress/status", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setWpCfg({ on: !!d.configured, types: d.pageTypes || [] }); })
      .catch(() => {});
  }, []);

  const openWp = (c) => { setWpModal(c); setWpType(c.pageType || ""); setWpRewrite(false); setWpErr(""); setWpDone(null); };

  const sendToWordPress = async () => {
    const c = wpModal;
    if (!c) return;
    const pt = wpCfg.types.find(t => t.id === wpType);
    setWpErr(""); setWpDone(null);
    let body = c.body || "";
    try {
      if (wpRewrite) {
        setWpBusy("rewrite");
        const structure = pt?.structure ? `\n\nDit paginatype is zo opgebouwd: ${pt.structure}` : "";
        const prompt = `${profileContext(profile)}\n\nOPDRACHT: Herschrijf de onderstaande tekst naar onze tone of voice, zodat hij past op een pagina van het type "${pt?.name || "pagina"}".${structure}\n\nBehoud de feitelijke inhoud; verzin niets nieuws. Lever markdown: # titel, ## koppen, - opsommingen.\n\nTEKST:\n${body.slice(0, 12000)}\n\nGeef alleen de nieuwe tekst.`;
        const { data: text, error: aiErr, cancelled } = await callAIFull(prompt, {});
        if (cancelled) { setWpBusy(""); return; }
        if (aiErr) { setWpErr(aiErr); setWpBusy(""); return; }
        if (text && text.trim()) body = text;
      }
      // Heeft dit paginatype een ontlede opbouw met eigen velden? Dan de tekst daarover
      // verdelen, zodat elk veld krijgt wat erin hoort.
      let fieldValues = null;
      const bp = (pt?.blueprint || []).filter(f => f.use);
      if (bp.length) {
        setWpBusy("fit");
        const spec = bp.map(f => `- ${f.name} — "${f.label}" (${f.type}, ongeveer ${f.chars} tekens). Voorbeeld van hoe dit veld normaal gevuld is: ${String(f.sample || "").slice(0, 240)}`).join("\n");
        const fitPrompt = `${profileContext(profile)}\n\nOPDRACHT: Verdeel de onderstaande tekst over de velden van een paginasjabloon. Elk veld heeft een eigen rol en lengte; houd je daaraan. Gebruik uitsluitend informatie uit de brontekst — verzin niets bij. Past er ergens niets zinnigs in, laat dat veld dan leeg.\n\nVELDEN:\n${spec}\n\nBRONTEKST:\n${String(body).slice(0, 12000)}\n\nGeef JSON met per veldnaam de tekst. Voor velden van het type wysiwyg mag je eenvoudige HTML gebruiken (<p>, <strong>, <ul><li>); voor type text alleen platte tekst zonder opmaak.\n\nJSON: {${bp.map(f => `"${f.name}":"…"`).join(",")}}`;
        const { data: fit, error: fitErr, cancelled: fitCancel } = await callAIFull(fitPrompt, { json: true });
        if (fitCancel) { setWpBusy(""); return; }
        if (fitErr) { setWpErr(fitErr); setWpBusy(""); return; }
        if (fit && typeof fit === "object") {
          fieldValues = {};
          bp.forEach(f => {
            const v = fit[f.name];
            if (typeof v === "string" && v.trim()) fieldValues[f.key || f.name] = v;
          });
          if (!Object.keys(fieldValues).length) fieldValues = null;
        }
      }

      setWpBusy("publish");
      const html = mdToHtml(body);
      const slug = (c.title || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
      const r = await fetch("/api/wordpress/publish", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          title: c.title, content: html, restBase: pt?.restBase || "posts",
          categoryId: pt?.categoryId || null, slug,
          contentField: pt?.contentField || "",
          fields: fieldValues,
        }),
      });
      const d = await r.json();
      if (!r.ok) { setWpErr(d.error || "Publiceren mislukte."); setWpBusy(""); return; }
      setWpDone(d);
      // Onthouden welk paginatype gekozen is, en de herschreven tekst bewaren.
      saveContent((content || []).map(x => x.id === c.id ? { ...x, pageType: wpType, body, wpId: d.id, wpLink: d.link } : x));
    } catch { setWpErr("Er ging iets mis bij het doorzetten."); }
    setWpBusy("");
  };

  const suggestIdeas = async () => {
    setBusy(true); setGenErr("");
    try {
      const site = profile?.website ? profile.website.trim() : "";
      const sitePart = site
        ? `Doorzoek eerst met web search de website ${site} om te zien welke content/onderwerpen daar AL bestaan. Stel vervolgens ALLEEN onderwerpen voor die nog NIET op die site behandeld zijn — vul aan, overlap niet. Noem in 'angle' kort waarom het aanvult.`
        : `Er is geen website ingevuld in het merkprofiel, dus baseer je op het merkprofiel en algemene marktinzichten.`;
      const steerPart = steer.trim() ? `Richt de onderwerpen specifiek op: ${steer.trim()}.` : "";
      const typeLabel = tile === "whitepaper" ? "whitepapers" : tile === "ebook" ? "e-books" : "blogs";
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel 6 sterke onderwerpen voor ${typeLabel} voor whitevision voor die het merk als autoriteit in AI-driven IDP / P2P positioneren. ${steerPart} Sluit aan op de doelstellingen en doelgroepen. ${sitePart}\n\nJSON: {"ideas":[{"title":"pakkende titel","angle":"korte invalshoek/waarom relevant"}]}`;
      const { data: r } = await callAIFull(prompt, { json: true, useSearch: true });
      const now = Date.now();
      const newItems = (r?.ideas || []).map((idea, i) => ({
        id: now + i, tile: "copy", phase: "backlog", kind: tile,
        title: idea.title, angle: idea.angle || "", date: new Date().toISOString().slice(0, 10),
      }));
      if (newItems.length) saveContent([...newItems, ...(content || [])]);
      else setGenErr("Geen suggesties teruggekregen. Probeer het zo nog eens.");
    } catch (e) {
      setGenErr("Het ophalen van suggesties lukte niet. Probeer het opnieuw.");
    } finally {
      setBusy(false); setMode(null); setSteer("");
    }
  };

  const pickUp = (id) => saveContent((content || []).map(c => c.id === id ? { ...c, phase: "todo" } : c));
  const updateItem = (id, patch) => saveContent((content || []).map(c => c.id === id ? { ...c, ...patch } : c));
  const removeItem = (id) => saveContent((content || []).filter(c => c.id !== id));
  const addOwn = () => {
    if (!form.topic.trim()) return;
    const entry = { id: Date.now(), tile: "copy", phase: "todo", kind: tile, title: form.topic.trim(), angle: form.brief.trim(), date: new Date().toISOString().slice(0, 10) };
    saveContent([entry, ...(content || [])]);
    setMode(null); setForm({ kind: "blog", topic: "", brief: "" });
  };

  // Werk een todo-item uit tot volledige content (fase -> done).
  const generate = async (item) => {
    setBusy(true); setGenId(item.id); setGenErr("");
    try {
      const isWp = item.kind === "whitepaper";
      const isEbook = item.kind === "ebook";
      const today = new Date();
      const dateNL = today.toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" });
      const tpl = (profile?.brandTemplates || []).find(t => t.kind === item.kind && t.structure?.trim())
        || (profile?.brandTemplates || []).find(t => t.kind === "algemeen" && t.structure?.trim());
      const tplPart = tpl ? `\n\nVolg deze vaste brand template voor structuur en stijl (van de brand manager):\n${tpl.structure}\n` : "";
      const lengte = isEbook ? "een uitgebreid e-book (3000-5000 woorden) met duidelijke hoofdstukken" : isWp ? "een whitepaper, grondig en gestructureerd (1500-2500 woorden) met duidelijke hoofdstukken" : "een blog, bondig en pakkend (600-900 woorden)";
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nVANDAAG IS HET ${dateNL} (jaar ${today.getFullYear()}). Schrijf vanuit het heden: ga uit van ${today.getFullYear()} als huidig jaar. Verwijs niet naar eerdere jaren als "nu" of "de realiteit van [ouder jaar]"; gebruik web search om actuele cijfers, trends en ontwikkelingen van ${today.getFullYear()} (en zo nodig ${today.getFullYear() - 1}) op te halen en daarnaar te verwijzen.${tplPart}\n\nOPDRACHT: Schrijf ${lengte} voor whitevision over: "${item.title}". ${item.angle ? "Aanvullende richting: " + item.angle : ""} Schrijf in onze tone of voice, voor onze doelgroep, met een sterke, autoritaire maar toegankelijke toon. ${tpl ? "Houd je aan de structuur en stijl van de brand template hierboven." : ""} Gebruik web search voor feiten en actualiteit. Gebruik markdown: # voor de titel, ## voor koppen, - voor opsommingen. Geen <cite>-tags.\n\nGeef ALLEEN de tekst in markdown, geen JSON, geen inleidende opmerkingen.`;
      const { data: text, sources } = await callAIFull(prompt, { useSearch: true });
      if (!text || !text.trim()) throw new Error("leeg antwoord");
      const title = (text.match(/^#\s+(.+)$/m)?.[1] || item.title).trim();
      saveContent((content || []).map(c => c.id === item.id
        ? { ...c, phase: "done", title, body: text, sources: sources || [], date: new Date().toISOString().slice(0, 10) }
        : c));
      setOpenId(item.id);
    } catch (e) {
      setGenErr("Het uitwerken lukte niet. Het onderwerp blijft staan; probeer het zo nog eens.");
    } finally {
      setBusy(false); setGenId(null);
    }
  };

  // Outline-builder: AI stelt subonderwerpen voor; gebruiker sleept volgorde en kiest totaal aantal woorden.
  const openOutline = async (item) => {
    const defWords = item.kind === "whitepaper" ? 2000 : item.kind === "ebook" ? 4000 : item.kind === "social" ? 120 : item.kind === "nieuwsbericht" ? 300 : 800;
    const defMode = item.mode === "translate" || item.action === "vertalen" ? "translate" : "rewrite";
    setGenErr("");
    setOutline({ item, subs: null, busy: true, dragId: null, words: defWords, lang: item.lang === "EN" ? "en" : "nl", mode: defMode, templateUrl: item.templateUrl || "" });
    try {
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel een logische inhoudsopgave voor een ${KIND_LABEL(item.kind).toLowerCase()} over "${item.title}"${item.angle ? " (invalshoek: " + item.angle + ")" : ""} voor whitevision. Geef 4-7 subonderwerpen/secties in een logische volgorde. Schrijf de subonderwerpen in het ${item.lang === "EN" ? "Engels" : "Nederlands"}.\n\nJSON: {"subs":["subonderwerp 1","subonderwerp 2","..."]}`;
      const { data: r } = await callAIFull(prompt, { json: true, useSearch: false });
      const subs = (r?.subs || []).map((t, i) => ({ id: Date.now() + i, title: t }));
      if (!subs.length) {
        // Geen voorstel gekregen: laat de gebruiker zelf subonderwerpen toevoegen i.p.v. vastlopen.
        setOutline(o => o ? { ...o, subs: [{ id: Date.now(), title: "Inleiding" }], busy: false } : o);
      } else {
        setOutline(o => o ? { ...o, subs, busy: false } : o);
      }
    } catch (e) {
      setOutline(null);
      setGenErr("Het voorstellen van de indeling lukte niet. Controleer je verbinding en probeer het opnieuw.");
    }
  };
  const outlineMove = (dragId, overId) => {
    setOutline(o => {
      if (!o || dragId === overId) return o;
      const arr = [...o.subs];
      const from = arr.findIndex(s => s.id === dragId), to = arr.findIndex(s => s.id === overId);
      if (from < 0 || to < 0) return o;
      const [m] = arr.splice(from, 1); arr.splice(to, 0, m);
      return { ...o, subs: arr };
    });
  };
  const outlineUpd = (id, title) => setOutline(o => o ? { ...o, subs: o.subs.map(s => s.id === id ? { ...s, title } : s) } : o);
  const outlineRemove = (id) => setOutline(o => o ? { ...o, subs: o.subs.filter(s => s.id !== id) } : o);
  const outlineAdd = () => setOutline(o => o ? { ...o, subs: [...o.subs, { id: Date.now(), title: "Nieuw subonderwerp" }] } : o);

  const generateWithOutline = async () => {
    const o = outline; if (!o) return;
    const item = o.item;
    setBusy(true); setGenId(item.id); setOutline(null); setGenErr("");
    try {
      const today = new Date();
      const dateNL = today.toLocaleDateString("nl-NL", { day: "numeric", month: "long", year: "numeric" });
      const taal = o.lang === "en" ? "Engels" : "Nederlands";
      // Optioneel: voorbeeldpagina ophalen als losse richtlijn voor de opzet.
      let tplPart = "";
      const tplUrl = (o.templateUrl || "").trim();
      if (tplUrl) {
        try {
          const r = await fetch("/api/page", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: tplUrl }) });
          const d = await r.json();
          if (d.text) tplPart = `\n\nHieronder de tekst van een VOORBEELDPAGINA (${tplUrl}). Gebruik de OPZET, opbouw en het soort secties hiervan als losse richtlijn voor je tekst (niet letterlijk overnemen, het hoeft niet exact, maar houd er wel rekening mee):\n"""\n${d.text.slice(0, 4000)}\n"""\n`;
        } catch { /* richtlijn overslaan als ophalen mislukt */ }
      }
      const outlinePart = `\n\nGebruik exact deze indeling en volgorde van secties:\n${o.subs.map((s, i) => `${i + 1}. ${s.title}`).join("\n")}\n\nSchrijf in totaal ongeveer ${o.words} woorden, verdeeld over deze secties.`;
      const srcPart = item.sourceUrl ? `\n\nDit komt voort uit een websitemigratie. De oorspronkelijke (over te nemen) pagina is: ${item.sourceUrl}.` : "";
      const modePart = o.mode === "translate"
        ? `Dit is een VERTAALOPDRACHT: neem de oorspronkelijke inhoud van het onderwerp en vertaal die redelijk letterlijk naar het ${taal}, met behoud van betekenis en structuur.`
        : `Herschrijf het onderwerp volledig in onze eigen tone of voice (niet letterlijk vertalen of overnemen), als nieuwe, originele content voor whitevision.`;
      const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nVANDAAG IS HET ${dateNL} (jaar ${today.getFullYear()}). Schrijf vanuit het heden.${tplPart}${outlinePart}${srcPart}\n\nOPDRACHT: Schrijf een ${KIND_LABEL(item.kind).toLowerCase()} voor whitevision over: "${item.title}". ${item.angle ? "Invalshoek: " + item.angle : ""} ${modePart} SCHRIJF VOLLEDIG IN HET ${taal.toUpperCase()}. Gebruik web search voor actuele feiten. Markdown: # titel, ## per sectie, - opsommingen. Geen <cite>-tags.\n\nGeef ALLEEN de tekst in markdown.`;
      const { data: text, sources } = await callAIFull(prompt, { useSearch: true });
      if (!text || !text.trim()) throw new Error("leeg antwoord");
      const title = (text.match(/^#\s+(.+)$/m)?.[1] || item.title).trim();
      saveContent((content || []).map(c => c.id === item.id ? { ...c, phase: "done", title, body: text, sources: sources || [], lang: o.lang === "en" ? "EN" : "NL", date: new Date().toISOString().slice(0, 10) } : c));
      setOpenId(item.id);
    } catch (e) {
      setGenErr("Het uitwerken lukte niet. Het onderwerp blijft staan; probeer het zo nog eens.");
    } finally {
      setBusy(false); setGenId(null);
    }
  };
  // Eén modal-definitie, gebruikt in zowel de flow- als de overzichtsweergave,
  // zodat hij altijd zichtbaar is ongeacht in welke weergave je op Uitwerken klikt.
  const outlineModal = outline ? (
        <Modal onClose={() => !outline.busy && setOutline(null)} title="Indeling bepalen">
          <div style={{ fontSize: 13.5, color: C.ink2, marginBottom: 6, lineHeight: 1.5 }}><b>{outline.item.title}</b></div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Sleep de subonderwerpen in de volgorde die je wilt, pas ze aan of voeg toe, en kies het totaal aantal woorden. De AI werkt het daarna uit volgens deze indeling.</div>
          {outline.busy && !outline.subs && <Spinner label="AI stelt subonderwerpen voor…" />}
          {outline.subs && <>
            <div style={{ display: "flex", flexDirection: "column", gap: 7, marginBottom: 16 }}>
              {outline.subs.map((s, i) => (
                <div key={s.id} draggable onDragStart={() => setOutline(o => ({ ...o, dragId: s.id }))}
                  onDragOver={e => { e.preventDefault(); if (outline.dragId) outlineMove(outline.dragId, s.id); }}
                  onDragEnd={() => setOutline(o => ({ ...o, dragId: null }))}
                  style={{ display: "flex", alignItems: "center", gap: 9, padding: "9px 11px", background: outline.dragId === s.id ? C.yellow + "22" : C.paper, border: `1.5px solid ${outline.dragId === s.id ? C.yellow : C.line}`, borderRadius: 9, cursor: "grab" }}>
                  <GripVertical size={15} style={{ color: C.muted, flexShrink: 0 }} />
                  <span style={{ fontSize: 12, color: C.muted, fontWeight: 700, flexShrink: 0 }}>{i + 1}</span>
                  <input value={s.title} onChange={e => outlineUpd(s.id, e.target.value)}
                    style={{ flex: 1, padding: "5px 7px", border: "none", background: "transparent", fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
                  <button onClick={() => outlineRemove(s.id)} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad, flexShrink: 0 }}><X size={14} /></button>
                </div>
              ))}
            </div>
            <button onClick={outlineAdd} style={{ ...miniBtn(C.paper, C.ink), marginBottom: 16 }}><Plus size={13} /> Subonderwerp toevoegen</button>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14, flexWrap: "wrap" }}>
              <label style={{ fontSize: 13, fontWeight: 700 }}>Taal:</label>
              {[["nl", "Nederlands"], ["en", "Engels"]].map(([v, lbl]) => (
                <button key={v} onClick={() => setOutline(o => ({ ...o, lang: v }))}
                  style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${outline.lang === v ? C.yellow : C.line}`, background: outline.lang === v ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
              ))}
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14, flexWrap: "wrap" }}>
              <label style={{ fontSize: 13, fontWeight: 700 }}>Aanpak:</label>
              {[["rewrite", "Herschrijven naar tone-of-voice"], ["translate", "Vertalen"]].map(([v, lbl]) => (
                <button key={v} onClick={() => setOutline(o => ({ ...o, mode: v }))}
                  style={{ fontSize: 12.5, fontWeight: 700, padding: "6px 12px", borderRadius: 8, cursor: "pointer", border: `1.5px solid ${outline.mode === v ? C.yellow : C.line}`, background: outline.mode === v ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
              ))}
            </div>
            <div style={{ marginBottom: 14 }}>
              <label style={{ fontSize: 13, fontWeight: 700, display: "block", marginBottom: 5 }}>Template: voorbeeld-URL <span style={{ fontWeight: 400, color: C.muted }}>(optioneel)</span></label>
              <input value={outline.templateUrl || ""} onChange={e => setOutline(o => ({ ...o, templateUrl: e.target.value }))} placeholder="https://… een pagina waarvan je de opzet wilt aanhouden"
                style={{ width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none" }} />
              <div style={{ fontSize: 11.5, color: C.muted, marginTop: 4, lineHeight: 1.4 }}>De tool leest die pagina en houdt de opzet als losse richtlijn aan. Niet verplicht en niet exact; laat leeg om zonder voorbeeld te werken.</div>
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 18 }}>
              <label style={{ fontSize: 13, fontWeight: 700 }}>Totaal aantal woorden:</label>
              <input type="number" min="50" step="50" value={outline.words} onChange={e => setOutline(o => ({ ...o, words: parseInt(e.target.value) || 0 }))}
                style={{ width: 100, padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
            </div>
            <div style={{ display: "flex", gap: 10 }}>
              <Btn variant="yellow" icon={Sparkles} onClick={generateWithOutline} disabled={!outline.subs.length}>Uitwerken met deze indeling</Btn>
              <Btn variant="ghost" onClick={() => setOutline(null)}>Annuleren</Btn>
            </div>
          </>}
        </Modal>
  ) : null;

  if (openId) {
    const c = items.find(x => x.id === openId);
    if (!c) { setOpenId(null); return null; }
    return (
      <div>
        {outlineModal}
        <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar content</button>
        <PageHead title={c.title} sub={`${KIND_LABEL(c.kind)} · ${c.date}`}
          right={<div style={{ display: "flex", gap: 8 }}>
            <Btn variant="yellow" size="sm" icon={Download} onClick={() => exportToWord(c.title, contentToHtml(c.body))}>Exporteer naar Word</Btn>
      {wpModal && (
        <Modal onClose={() => { setWpModal(null); setWpDone(null); }} title="Naar WordPress">
          {wpDone ? (
            <div>
              <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}>
                <CheckCircle2 size={20} style={{ color: C.good }} />
                <span style={{ fontWeight: 700, fontSize: 15 }}>Als {wpDone.status === "publish" ? "gepubliceerd" : "concept"} aangemaakt</span>
              </div>
              <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.55, marginBottom: 14 }}>
                "{wpModal.title}" staat nu in WordPress. Loop hem daar na en publiceer zelf wanneer je tevreden bent.
              </div>
              {wpDone.fieldWritten === false && (
                <div style={{ display: "flex", gap: 9, alignItems: "flex-start", padding: "11px 13px", background: C.yellow + "12", borderRadius: 10, marginBottom: 14 }}>
                  <AlertTriangle size={15} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} />
                  <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>
                    Het veld "{wpDone.fieldName}" kon niet gevuld worden. Controleer bij Instellingen → WordPress of dit paginatype naar het juiste veld verwijst.
                  </div>
                </div>
              )}
              <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
                {wpDone.editLink && <a href={wpDone.editLink} target="_blank" rel="noopener noreferrer" style={{ textDecoration: "none" }}><Btn variant="yellow" icon={ArrowUpRight}>Openen in WordPress</Btn></a>}
                <Btn variant="ghost" onClick={() => { setWpModal(null); setWpDone(null); }}>Sluiten</Btn>
              </div>
            </div>
          ) : !wpCfg.on ? (
            <div>
              <div style={{ display: "flex", alignItems: "flex-start", gap: 10, padding: "12px 14px", background: C.blue + "12", border: `1px solid ${C.blue}33`, borderRadius: 10, marginBottom: 14 }}>
                <AlertTriangle size={16} style={{ color: C.blue, flexShrink: 0, marginTop: 2 }} />
                <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.55 }}>
                  WordPress is nog niet gekoppeld. Zodra je je site koppelt, kun je content vanuit hier rechtstreeks als concept in WordPress zetten, in de juiste categorie.
                </div>
              </div>
              <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.6, marginBottom: 16 }}>
                Je hebt hiervoor drie dingen nodig: het adres van je site, je WordPress-gebruikersnaam, en een applicatiewachtwoord. Dat laatste maak je aan in WordPress onder Gebruikers → je profiel → Applicatiewachtwoorden. Je gewone wachtwoord heb je er niet voor nodig.
              </div>
              <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
                <Btn variant="yellow" icon={ArrowUpRight} onClick={() => { if (typeof window !== "undefined") window.__settingsTab = "wordpress"; setWpModal(null); goTo("settings"); }}>WordPress koppelen</Btn>
                <Btn variant="ghost" onClick={() => setWpModal(null)}>Later</Btn>
              </div>
            </div>
          ) : (
            <div>
              <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.55, marginBottom: 14 }}>
                "{wpModal.title}" wordt als concept in WordPress aangemaakt. Kies het paginatype, dan weet de tool in welk soort pagina en welke categorie het hoort.
              </div>

              <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Paginatype</label>
              {wpCfg.types.length === 0
                ? <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Er zijn nog geen paginatypen ingericht. Doe dat eerst bij Instellingen → WordPress; dan kun je hier kiezen waar het stuk terechtkomt.</div>
                : <select value={wpType} onChange={e => setWpType(e.target.value)}
                    style={{ width: "100%", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", background: C.card, marginBottom: 8 }}>
                    <option value="">— kies een type —</option>
                    {wpCfg.types.map(t => <option key={t.id} value={t.id}>{t.name}{t.pathPrefix ? ` (${t.pathPrefix})` : ""}</option>)}
                  </select>}

              {wpType && (() => {
                const pt = wpCfg.types.find(t => t.id === wpType);
                if (!pt) return null;
                const bp = (pt.blueprint || []).filter(f => f.use);
                return <>
                  {bp.length > 0 && (
                    <div style={{ fontSize: 12, color: C.ink2, lineHeight: 1.55, marginBottom: 12, padding: "10px 12px", background: C.yellow + "10", borderRadius: 8 }}>
                      <b>Dit paginatype heeft eigen velden.</b> De tekst wordt verdeeld over: {bp.map(f => f.label).join(", ")}. Dat gebruikt AI-tegoed, maar zorgt dat de pagina niet leeg blijft.
                    </div>
                  )}
                  {pt.structure ? <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5, marginBottom: 12, padding: "9px 11px", background: C.paper, borderRadius: 8 }}><b style={{ color: C.ink2 }}>Opbouw van dit type: </b>{pt.structure}</div> : null}
                </>;
              })()}

              <label style={{ display: "flex", alignItems: "flex-start", gap: 9, marginBottom: 14, cursor: "pointer" }}>
                <input type="checkbox" checked={wpRewrite} onChange={e => setWpRewrite(e.target.checked)} style={{ marginTop: 3 }} />
                <span>
                  <span style={{ fontSize: 13.5, fontWeight: 600, color: C.ink }}>Eerst herschrijven naar dit paginatype</span>
                  <span style={{ display: "block", fontSize: 12, color: C.muted, lineHeight: 1.5 }}>
                    Past de tekst aan op onze tone of voice en op de opbouw van dit type. Handig als de oorspronkelijke pagina anders is ingestoken dan jouw template. Dit gebruikt AI-tegoed; laat het uit om de tekst één op één over te nemen.
                  </span>
                </span>
              </label>

              <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
                <Btn variant="yellow" icon={wpBusy ? Loader2 : Globe} onClick={sendToWordPress} disabled={!!wpBusy || (wpCfg.types.length > 0 && !wpType)}>
                  {wpBusy === "rewrite" ? "Herschrijven…" : wpBusy === "fit" ? "Tekst over de velden verdelen…" : wpBusy === "publish" ? "Aanmaken in WordPress…" : "Doorzetten"}
                </Btn>
                <Btn variant="ghost" onClick={() => setWpModal(null)}>Annuleren</Btn>
                {wpRewrite && <CostHint kind="light" />}
              </div>
              {wpErr && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{wpErr}</div>}
            </div>
          )}
        </Modal>
      )}
            {(c.phase === "done" || (!c.phase && c.body)) && <Btn variant="yellow" size="sm" icon={Globe} onClick={() => openWp(c)}>Naar WordPress</Btn>}
            {(c.phase === "done" || (!c.phase && c.body)) && <Btn variant="primary" size="sm" icon={Send} onClick={() => publishContent(c)}>Publiceren</Btn>}
            <Btn variant="danger" size="sm" icon={Trash2} onClick={() => confirm("Deze content wordt definitief verwijderd.", () => { saveContent(content.filter(x => x.id !== c.id)); setOpenId(null); })}>Verwijderen</Btn>
          </div>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.65, color: C.ink }} dangerouslySetInnerHTML={{ __html: contentToHtml(c.body) }} />
          <SourceLinks sources={c.sources} />
        </Card>
        <Card style={{ marginTop: 16, border: `1.5px solid ${C.line}` }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
            <Sparkles size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 14.5 }}>Opnieuw genereren</span>
          </div>
          <div style={{ fontSize: 13, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>Niet helemaal goed? Geef sturing mee (bijv. "korter", "zakelijker", "meer focus op compliance") en laat de AI een nieuwe versie maken. De huidige versie wordt vervangen.</div>
          <Field label="Feedback / sturing (optioneel)" value={regenFb} onChange={setRegenFb} multiline rows={2} placeholder="Bijv. 'maak het korter en concreter, met een sterkere openingszin'" />
          <Btn variant="yellow" icon={regenBusy ? Loader2 : Sparkles} onClick={() => regenerate(c)} disabled={regenBusy}>{regenBusy ? "Bezig met herschrijven…" : "Genereer opnieuw"}</Btn>
          {regenBusy && <div style={{ marginTop: 12 }}><Spinner label="AI herschrijft de content…" /></div>}
        </Card>
      </div>
    );
  }

  // ---- thought leadership tegel ----
  if (CONTENT_TILES.some(t => t.id === tile)) {
    const phaseCard = (item, opts) => (
      <Card key={item.id} style={{ padding: 0, border: delSel[item.id] ? `1.5px solid ${C.yellow}` : undefined }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 15px" }}>
          <input type="checkbox" checked={!!delSel[item.id]} onChange={() => toggleDel(item.id)} style={{ flexShrink: 0 }} title="Selecteer voor verwijderen" />
          <div style={{ flex: 1, minWidth: 0, cursor: opts.openable ? "pointer" : "default" }} onClick={opts.openable ? () => setOpenId(item.id) : undefined}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 3 }}>
              <Tag color={C.yellowDark}>{KIND_LABEL(item.kind).toLowerCase()}</Tag>
              {item.lang && <Tag color={C.muted}>{item.lang}</Tag>}
              <span style={{ fontSize: 12.5, color: C.muted }}>{item.date}</span>
            </div>
            <div style={{ fontWeight: 700, fontSize: 14.5 }}>{item.title}</div>
            {item.angle && opts.showAngle && <div style={{ fontSize: 12.5, color: C.ink2, marginTop: 3, lineHeight: 1.45 }}>{item.angle}</div>}
          </div>
          <div style={{ display: "flex", gap: 6, alignItems: "center", flexShrink: 0 }}>{opts.actions}</div>
        </div>
      </Card>
    );

    return (
      <div>
        {outlineModal}
        <button onClick={() => { setTile(null); setMode(null); }} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar content</button>
        <PageHead title={(CONTENT_TILES.find(t => t.id === tile) || {}).label || "Content"} sub="Van suggestie naar uitgewerkte content. Onderwerpen schuiven van backlog naar uitwerken naar uitgewerkt." />
        {genErr && (
          <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", background: C.bad + "12", border: `1.5px solid ${C.bad}55`, borderRadius: 10, marginBottom: 16 }}>
            <AlertTriangle size={16} style={{ color: C.bad, flexShrink: 0 }} />
            <span style={{ fontSize: 13, color: C.ink, flex: 1 }}>{genErr}</span>
            <button onClick={() => setGenErr("")} style={{ fontSize: 12.5, fontWeight: 600, color: C.muted, background: "none", border: "none", cursor: "pointer" }}>Sluiten</button>
          </div>
        )}
        {delSelCount > 0 && (
          <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 14px", background: C.yellow + "18", border: `1.5px solid ${C.yellow}`, borderRadius: 10, marginBottom: 16 }}>
            <span style={{ fontSize: 13, fontWeight: 700 }}>{delSelCount} geselecteerd</span>
            <Btn size="sm" variant="danger" icon={Trash2} onClick={bulkDelete}>Verwijderen</Btn>
            <button onClick={() => setDelSel({})} style={{ fontSize: 12.5, fontWeight: 600, color: C.muted, background: "none", border: "none", cursor: "pointer" }}>Selectie wissen</button>
          </div>
        )}

        {!mode && <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 22 }}>
          <Card hover onClick={() => setMode("suggest")}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}><Lightbulb size={20} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15.5 }}>Laat AI onderwerpen voorstellen</span></div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 6 }}>Geef eventueel sturing en krijg 6 ideeën in je backlog.</div>
          </Card>
          <Card hover onClick={() => setMode("own")}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}><PenLine size={20} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15.5 }}>Eigen onderwerp</span></div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 6 }}>Zet zelf een onderwerp klaar bij Uitwerken.</div>
          </Card>
        </div>}

        {mode === "suggest" && <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, marginBottom: 6 }}>Onderwerpen laten voorstellen</div>
          <div style={{ fontSize: 13, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Wil je sturing meegeven? Geef bijvoorbeeld een thema of doelgroep op. Laat leeg voor brede suggesties.</div>
          <Field label="Sturing (optioneel)" value={steer} onChange={setSteer} placeholder="Bijv. 'rondom procurement' of 'voor de zorgsector'" />
          <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
            <Btn variant="yellow" icon={Sparkles} onClick={suggestIdeas} disabled={busy}>{busy ? "Bezig…" : "Onderwerpen voorstellen"}</Btn>
            <Btn variant="ghost" onClick={() => { setMode(null); setSteer(""); }}>Annuleren</Btn>
            <CostHint kind="desk" />
          </div>
          {busy && <div style={{ marginTop: 12 }}><Spinner label="AI bedenkt onderwerpen…" /></div>}
        </Card>}

        {mode === "own" && <Card style={{ marginBottom: 22, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, marginBottom: 12 }}>Eigen onderwerp klaarzetten</div>
          <Field label="Onderwerp" value={form.topic} onChange={v => setForm({ ...form, topic: v })} placeholder="Bijv. 'Waarom AI-gedreven factuurverwerking de nieuwe standaard wordt'" />
          <Field label="Richting / focus (optioneel)" value={form.brief} onChange={v => setForm({ ...form, brief: v })} multiline rows={2} placeholder="Bijv. 'richt op de zorgsector' of 'benadruk compliance'" />
          <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
            <Btn variant="yellow" icon={Plus} onClick={addOwn} disabled={!form.topic.trim()}>Zet bij Uitwerken</Btn>
            <Btn variant="ghost" onClick={() => setMode(null)}>Annuleren</Btn>
          </div>
        </Card>}

        {/* UITGEWERKT */}
        <div style={{ marginBottom: 24 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
            <CheckCircle2 size={17} style={{ color: C.good }} />
            <span style={{ fontWeight: 700, fontSize: 15.5, fontFamily: FONT_DISPLAY }}>Uitgewerkt</span>
            <Tag color={C.good}>{done.length}</Tag>
          </div>
          {done.length === 0 ? <div style={{ fontSize: 13, color: C.muted }}>Nog niets uitgewerkt.</div>
            : <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
              {done.map(item => phaseCard(item, {
                openable: true,
                actions: <>
                  <button onClick={() => exportToWord(item.title, contentToHtml(item.body))} style={iconBtn(C.yellowDark)} title="Exporteer naar Word"><Download size={17} /></button>
                  <button onClick={() => confirm("Deze content wordt definitief verwijderd.", () => removeItem(item.id))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  <button onClick={() => setOpenId(item.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                </>,
              }))}
            </div>}
        </div>

        {/* UITWERKEN */}
        <div style={{ marginBottom: 24 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
            <PenLine size={17} style={{ color: C.yellowDark }} />
            <span style={{ fontWeight: 700, fontSize: 15.5, fontFamily: FONT_DISPLAY }}>Uitwerken</span>
            <Tag color={C.yellowDark}>{todo.length}</Tag>
          </div>
          {todo.length === 0 ? <div style={{ fontSize: 13, color: C.muted }}>Niets in bewerking. Pak iets op uit je backlog of voeg een eigen onderwerp toe.</div>
            : <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
              {todo.map(item => {
                const working = genId === item.id;
                if (editKey === item.id) return (
                  <Card key={item.id} style={{ border: `1.5px solid ${C.yellow}` }}>
                    <input value={item.title} onChange={e => updateItem(item.id, { title: e.target.value })} placeholder="Titel"
                      style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 14, fontWeight: 700, marginBottom: 8, outline: "none" }} />
                    <textarea value={item.angle || ""} onChange={e => updateItem(item.id, { angle: e.target.value })} rows={2} placeholder="Invalshoek / richting"
                      style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, resize: "vertical", outline: "none", marginBottom: 10 }} />
                    <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }}>
                      <span style={{ fontSize: 12, fontWeight: 700, color: C.muted }}>Taal:</span>
                      {[["NL", "Nederlands"], ["EN", "Engels"]].map(([v, lbl]) => (
                        <button key={v} onClick={() => updateItem(item.id, { lang: v })}
                          style={{ fontSize: 12, fontWeight: 700, padding: "5px 10px", borderRadius: 7, cursor: "pointer", border: `1.5px solid ${(item.lang || "NL") === v ? C.yellow : C.line}`, background: (item.lang || "NL") === v ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
                      ))}
                    </div>
                    <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8, flexWrap: "wrap" }}>
                      <span style={{ fontSize: 12, fontWeight: 700, color: C.muted }}>Aanpak:</span>
                      {[["rewrite", "Tone-of-voice"], ["translate", "Vertalen"]].map(([v, lbl]) => (
                        <button key={v} onClick={() => updateItem(item.id, { mode: v })}
                          style={{ fontSize: 12, fontWeight: 700, padding: "5px 10px", borderRadius: 7, cursor: "pointer", border: `1.5px solid ${(item.mode || "rewrite") === v ? C.yellow : C.line}`, background: (item.mode || "rewrite") === v ? C.yellow + "22" : C.card, color: C.ink }}>{lbl}</button>
                      ))}
                    </div>
                    <input value={item.templateUrl || ""} onChange={e => updateItem(item.id, { templateUrl: e.target.value })} placeholder="Template: voorbeeld-URL (optioneel)"
                      style={{ width: "100%", boxSizing: "border-box", padding: "7px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", marginBottom: 10 }} />
                    <Btn variant="ghost" size="sm" icon={Check} onClick={() => setEditKey(null)}>Klaar met aanpassen</Btn>
                  </Card>
                );
                return phaseCard(item, {
                  openable: false, showAngle: true,
                  actions: <>
                    <button onClick={() => setEditKey(item.id)} disabled={busy} style={{ ...miniBtn(C.paper, C.ink), opacity: busy ? .5 : 1 }}><Edit3 size={13} /> Tweaken</button>
                    <button onClick={() => openOutline(item)} disabled={busy}
                      style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12.5, fontWeight: 700, padding: "7px 12px", borderRadius: 8, cursor: busy ? "default" : "pointer", border: "none", background: working ? C.good : C.yellow, color: working ? "#fff" : C.ink, opacity: busy && !working ? .5 : 1 }}>
                      {working ? <><Loader2 size={13} className="spin" /> {KIND_LABEL(item.kind)} wordt uitgewerkt…</> : <><Sparkles size={13} /> Uitwerken</>}
                    </button>
                    <button onClick={() => confirm("Dit onderwerp wordt verwijderd.", () => removeItem(item.id))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  </>,
                });
              })}
            </div>}
        </div>

        {/* BACKLOG */}
        <div>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
            <Lightbulb size={17} style={{ color: C.muted }} />
            <span style={{ fontWeight: 700, fontSize: 15.5, fontFamily: FONT_DISPLAY }}>Backlog</span>
            <Tag color={C.muted}>{backlog.length}</Tag>
            {backlog.length > 0 && <button onClick={() => { const ids = new Set(backlog.map(b => b.id)); confirm(`Alle ${backlog.length} onderwerpen in de backlog verwijderen?`, () => saveContent((content || []).filter(c => !ids.has(c.id)))); }}
              style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 5, fontSize: 12.5, fontWeight: 600, color: C.bad, background: "none", border: "none", cursor: "pointer" }}><Trash2 size={14} /> Backlog leegmaken</button>}
          </div>
          {backlog.length === 0 ? <div style={{ fontSize: 13, color: C.muted }}>Geen voorgestelde onderwerpen. Laat AI onderwerpen voorstellen.</div>
            : <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
              {backlog.map(item => {
                if (editKey === item.id) return (
                  <Card key={item.id} style={{ border: `1.5px solid ${C.yellow}` }}>
                    <input value={item.title} onChange={e => updateItem(item.id, { title: e.target.value })} placeholder="Titel"
                      style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 14, fontWeight: 700, marginBottom: 8, outline: "none" }} />
                    <textarea value={item.angle || ""} onChange={e => updateItem(item.id, { angle: e.target.value })} rows={2} placeholder="Invalshoek / richting"
                      style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, resize: "vertical", outline: "none", marginBottom: 10 }} />
                    <Btn variant="ghost" size="sm" icon={Check} onClick={() => setEditKey(null)}>Klaar met aanpassen</Btn>
                  </Card>
                );
                return phaseCard(item, {
                  openable: false, showAngle: true,
                  actions: <>
                    <button onClick={() => setEditKey(item.id)} style={miniBtn(C.paper, C.ink)}><Edit3 size={13} /> Tweaken</button>
                    <button onClick={() => pickUp(item.id)} style={miniBtn(C.yellow, C.ink)}><ArrowUpRight size={13} /> Oppakken</button>
                    <button onClick={() => confirm("Dit onderwerp wordt verwijderd.", () => removeItem(item.id))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  </>,
                });
              })}
            </div>}
        </div>
      </div>
    );
  }

  // ---- overview ----
  return (
    <div>
      {outlineModal}
      <PageHead title="Copy agent" sub="Tekstuele content die voortbouwt op je merk: thought leadership zoals blogs en whitepapers." />

      <div style={{ display: "flex", gap: 8, marginBottom: 22, borderBottom: `1.5px solid ${C.line}` }}>
        {[["productie", "Productie"], ["agenda", "Contentagenda"]].map(([id, lbl]) => (
          <button key={id} onClick={() => setCopyTab(id)} style={{
            padding: "10px 16px", border: "none", background: "none", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5,
            fontWeight: copyTab === id ? 700 : 500, color: copyTab === id ? C.ink : C.muted,
            borderBottom: `2.5px solid ${copyTab === id ? C.yellow : "transparent"}`, marginBottom: -1.5,
          }}>{lbl}</button>
        ))}
      </div>

      {copyTab === "agenda" && <ContentAgenda ctx={ctx} />}
      {copyTab === "productie" && <>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }}>
        {CONTENT_TILES.map(t => {
          const doneCount = (content || []).filter(c => c.kind === t.kind && (c.phase === "done" || c.phase === "published" || (!c.phase && c.body))).length;
          return (
          <Card key={t.id} hover={t.active} onClick={t.active ? () => setTile(t.id) : undefined}
            style={{ opacity: t.active ? 1 : .6, cursor: t.active ? "pointer" : "default", position: "relative" }}>
            <div style={{ width: 44, height: 44, borderRadius: 12, background: C.yellow + "22", display: "grid", placeItems: "center", marginBottom: 12 }}>
              <t.icon size={22} style={{ color: C.ink }} />
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <span style={{ fontWeight: 700, fontSize: 16.5 }}>{t.label}</span>
              {doneCount > 0 && <Tag color={C.good}>{doneCount} uitgewerkt</Tag>}
            </div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{t.desc}</div>
            {t.active
              ? <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>Openen <ArrowUpRight size={15} /></div>
              : <div style={{ marginTop: 14 }}><Tag color={C.muted}>binnenkort</Tag></div>}
          </Card>
          );
        })}
      </div>
      </>}
    </div>
  );
}

/* ============================== DESIGN ============================== */
// Toont de opgeslagen presentatie-templates die voor deze plek (copy/studio) bedoeld zijn.
// Het daadwerkelijk vullen tot een afgewerkt deck gebeurt samen per stuk; dit is de verwijzing.
function TemplatePicker({ where }) {
  const [tpls, setTpls] = useState(null);
  useEffect(() => {
    let on = true;
    fetch("/api/templates", { credentials: "include" }).then(r => r.ok ? r.json() : null).then(d => { if (on && d) setTpls(d.templates || []); }).catch(() => { if (on) setTpls([]); });
    return () => { on = false; };
  }, []);
  const list = (tpls || []).filter(t => t.use === where || t.use === "both");
  if (tpls === null || list.length === 0) return null;
  return (
    <Card style={{ marginBottom: 18, border: `1.5px solid ${C.line}` }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 8 }}>
        <LayoutTemplate size={17} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 14.5 }}>Beschikbare templates</span>
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Deze templates heb je bij het merkprofiel klaargezet voor gebruik hier. Download de juiste als basis; het afgewerkte deck maken we samen per stuk.</div>
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {list.map(t => (
          <div key={t.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 12px", border: `1px solid ${C.line}`, borderRadius: 10 }}>
            <LayoutTemplate size={15} style={{ color: C.yellowDark, flexShrink: 0 }} />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontWeight: 700, fontSize: 13.5 }}>{t.name}</div>
              {t.purpose && <div style={{ fontSize: 12, color: C.muted, marginTop: 1 }}>{t.purpose}</div>}
            </div>
            <a href={`/api/templates/${t.id}/download`} title="Downloaden" style={{ color: C.ink2, display: "grid", placeItems: "center" }}><Download size={15} /></a>
          </div>
        ))}
      </div>
    </Card>
  );
}

// ---- Banner-editor ----
// Hier verfijn je het gekozen ontwerp: teksten, kleuren, middelen, vormgeving en
// beweging, met links een voorbeeld dat direct meeverandert. In de vrije stand sleep
// je de elementen naar de plek waar je ze wilt hebben.
const BAN_LAYOUTS = [["stacked", "Onder elkaar"], ["centered", "Gecentreerd"], ["corner", "Knop onderaan"], ["free", "Vrij slepen"]];
const BAN_BGS = [["solid", "Effen"], ["gradient", "Verloop"], ["image", "Foto"], ["pattern", "Patroon"]];
const BAN_SHAPES = [["none", "Geen"], ["bar", "Accentbalk"], ["circle", "Cirkel"], ["diagonal", "Schuin vlak"], ["corner", "Hoek"]];
const BAN_INTROS = [["fade", "Vervagen"], ["slide", "Omhoog schuiven"], ["zoom", "Inzoomen"], ["none", "Geen"]];
const BAN_CTAS = [["pulse", "Pulseren"], ["glow", "Gloed"], ["arrow", "Pijltje"], ["none", "Stil"]];

function BannerEditor({ concept, onChange, colors, setColors, assets, logoId, setLogoId, imageId, setImageId, patternId, setPatternId, bgId, setBgId, clickUrl, profile, brandColors, Picker }) {
  const [fmt, setFmt] = useState("300x250");
  const [html, setHtml] = useState("");
  const [busy, setBusy] = useState(false);
  const [drag, setDrag] = useState(null);
  const [fout, setFout] = useState("");
  const boxRef = useRef();
  const timer = useRef();
  const lopend = useRef(null);   // om een achterhaald verzoek af te breken

  const [fw, fh] = fmt.split("x").map(Number);
  const d = concept.design || {};
  const setD = (patch) => onChange({ ...concept, design: { ...d, ...patch } });
  const setC = (patch) => onChange({ ...concept, ...patch });

  // Het voorbeeld opnieuw maken, maar niet bij elke toetsaanslag.
  useEffect(() => {
    clearTimeout(timer.current);
    timer.current = setTimeout(async () => {
      // Een eerder verzoek dat nog loopt is achterhaald; die breken we af.
      if (lopend.current) { try { lopend.current.abort(); } catch {} }
      const ctrl = new AbortController();
      lopend.current = ctrl;
      setBusy(true);
      try {
        const r = await fetch("/api/studio/banner-preview", {
          signal: ctrl.signal,
          method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            name: concept.name, headline: concept.headline, subline: concept.subline, cta: concept.cta,
            clickUrl: clickUrl || profile?.website || "",
            colors, logoId, imageId, patternId, bgId,
            design: { ...d, sequence: d.sequence !== false },
            formats: [{ w: fw, h: fh }],
          }),
        });
        const j = await r.json().catch(() => ({}));
        if (r.ok && j.html) { setHtml(j.html); setFout(""); }
        else setFout(j.error || `Het voorbeeld kon niet worden gemaakt (status ${r.status}).`);
      } catch (e) {
        // Afgebroken omdat je alweer verder schoof: dat is geen fout.
        if (!(e && e.name === "AbortError")) setFout("Het voorbeeld kon niet worden gemaakt.");
      }
      setBusy(false);
    }, 500);
    return () => clearTimeout(timer.current);
  }, [JSON.stringify(concept), JSON.stringify(colors), logoId, imageId, patternId, fmt, clickUrl]);

  // Slepen in de vrije stand: we rekenen de muispositie om naar procenten.
  const startDrag = (key, e) => {
    if (d.layout !== "free") return;
    e.preventDefault();
    const box = boxRef.current?.getBoundingClientRect();
    if (!box) return;
    const move = (ev) => {
      const x = Math.max(0, Math.min(100, ((ev.clientX - box.left) / box.width) * 100));
      const y = Math.max(0, Math.min(100, ((ev.clientY - box.top) / box.height) * 100));
      setD({ pos: { ...(d.pos || {}), [key]: { x: Math.round(x), y: Math.round(y) } } });
    };
    const up = () => { document.removeEventListener("mousemove", move); document.removeEventListener("mouseup", up); setDrag(null); };
    document.addEventListener("mousemove", move);
    document.addEventListener("mouseup", up);
    setDrag(key);
  };

  const posOf = (key, dx, dy) => {
    const p = (d.pos || {})[key] || {};
    return { x: p.x === undefined ? dx : p.x, y: p.y === undefined ? dy : p.y };
  };

  const lbl = { display: "block", fontSize: 11, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" };
  const sel = { width: "100%", padding: "7px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card };
  const inp = { ...sel, background: "transparent" };
  const slider = { width: "100%", accentColor: C.yellow };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "minmax(320px, 1fr) minmax(280px, 340px)", gap: 20, alignItems: "start" }}>
      {/* Voorbeeld met sleepgrepen */}
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10, flexWrap: "wrap" }}>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Bekijken als</span>
          {["300x250", "728x90", "160x600", "320x50"].map(f => (
            <button key={f} onClick={() => setFmt(f)} style={{
              padding: "5px 10px", borderRadius: 7, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 11.5, fontWeight: 700,
              border: `1px solid ${fmt === f ? "transparent" : C.line}`,
              background: fmt === f ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: fmt === f ? "#fff" : C.muted,
            }}>{f}</button>
          ))}
          {busy && <Loader2 size={13} className="spin" style={{ color: C.muted }} />}
        </div>

        <div ref={boxRef} style={{ position: "relative", width: fw, height: fh, maxWidth: "100%", border: `1px solid ${C.line}`, borderRadius: 8, overflow: "hidden", background: "#000" }}>
          {html
            ? <iframe title="voorbeeld" srcDoc={html} sandbox="allow-scripts" style={{ width: fw, height: fh, border: "none", display: "block" }} />
            : <div style={{ width: "100%", height: "100%", display: "grid", placeItems: "center", color: fout ? C.bad : C.muted, fontSize: 12.5, padding: 14, textAlign: "center", lineHeight: 1.5 }}>{fout || "voorbeeld maken…"}</div>}

          {/* Grepen om te slepen, alleen in de vrije stand */}
          {d.layout === "free" && [
            ["logo", "Logo", 22, 18, !!logoId],
            ["head", "Kop", 50, 45, !!concept.headline],
            ["sub", "Ondertitel", 50, 66, !!concept.subline],
            ["cta", "Knop", 50, 85, true],
          ].filter(([, , , , tonen]) => tonen).map(([key, naam, dx, dy]) => {
            const p = posOf(key, dx, dy);
            return (
              <div key={key} onMouseDown={(e) => startDrag(key, e)} title={`${naam} verslepen`}
                style={{
                  position: "absolute", left: `${p.x}%`, top: `${p.y}%`, transform: "translate(-50%,-50%)",
                  padding: "2px 7px", borderRadius: 20, fontSize: 10, fontWeight: 700, whiteSpace: "nowrap",
                  background: drag === key ? C.yellow : C.yellow + "CC", color: "#fff",
                  cursor: "grab", userSelect: "none", boxShadow: "0 1px 6px rgba(0,0,0,.4)", zIndex: 5,
                }}>{naam}</div>
            );
          })}
        </div>
        {d.layout === "free"
          ? <div style={{ fontSize: 11.5, color: C.good, marginTop: 7, lineHeight: 1.5, fontWeight: 600 }}>Sleep de gekleurde labels naar de plek waar je het element wilt hebben. De posities gelden in procenten, dus ze blijven kloppen bij elk formaat.</div>
          : <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 8, flexWrap: "wrap" }}>
              <span style={{ fontSize: 11.5, color: C.muted, lineHeight: 1.5 }}>De tool bepaalt nu zelf waar alles staat.</span>
              <button onClick={() => setD({ layout: "free" })} style={{ ...miniBtn(C.yellow, "#fff") }}>zelf slepen</button>
            </div>}
      </div>

      {/* Regelpaneel */}
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div>
          <label style={lbl}>Kop</label>
          <input value={concept.headline || ""} onChange={e => setC({ headline: e.target.value })} style={inp} />
        </div>
        <div>
          <label style={lbl}>Ondertitel</label>
          <input value={concept.subline || ""} onChange={e => setC({ subline: e.target.value })} style={inp} />
        </div>
        <div>
          <label style={lbl}>Knoptekst</label>
          <input value={concept.cta || ""} onChange={e => setC({ cta: e.target.value })} style={inp} />
        </div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Vormgeving</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 9 }}>
            <div><label style={lbl}>Indeling</label>
              <select value={d.layout || "stacked"} onChange={e => setD({ layout: e.target.value })} style={sel}>
                {BAN_LAYOUTS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
              </select></div>
            <div><label style={lbl}>Achtergrond</label>
              <select value={d.bg || "solid"} onChange={e => setD({ bg: e.target.value })} style={sel}>
                {BAN_BGS.map(([v, l]) => <option key={v} value={v} disabled={(v === "image" && !bgId && !imageId) || (v === "pattern" && !patternId)}>{l}{v === "image" && !imageId ? " (geen foto)" : v === "pattern" && !patternId ? " (geen patroon)" : ""}</option>)}
              </select></div>
            <div><label style={lbl}>Vorm</label>
              <select value={d.shape || "none"} onChange={e => setD({ shape: e.target.value })} style={sel}>
                {BAN_SHAPES.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
              </select></div>
            {d.bg === "image" && (
              <div><label style={lbl}>Sluier over de foto</label>
                <select value={d.scrim || "normaal"} onChange={e => setD({ scrim: e.target.value })} style={sel}>
                  <option value="geen">Geen, foto vol in beeld</option>
                  <option value="licht">Licht</option>
                  <option value="normaal">Normaal</option>
                  <option value="sterk">Sterk</option>
                </select></div>
            )}
            <div><label style={lbl}>Uitlijning</label>
              <select value={d.align || "left"} onChange={e => setD({ align: e.target.value })} style={sel}>
                <option value="left">Links</option><option value="center">Gecentreerd</option>
              </select></div>
          </div>
        </div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Beweging</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 9, marginBottom: 10 }}>
            <div><label style={lbl}>Binnenkomen</label>
              <select value={d.intro || "fade"} onChange={e => setD({ intro: e.target.value })} style={sel}>
                {BAN_INTROS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
              </select></div>
            <div><label style={lbl}>Knop</label>
              <select value={d.cta || "pulse"} onChange={e => setD({ cta: e.target.value })} style={sel}>
                {BAN_CTAS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
              </select></div>
          </div>
          <label style={{ ...lbl, marginTop: 2 }}>Breedte tekstvak ({d.textWidth || 100}%)</label>
          <input type="range" min="30" max="100" step="5" value={d.textWidth || 100} onChange={e => setD({ textWidth: Number(e.target.value) })} style={slider} />
          <div style={{ fontSize: 11, color: C.muted, marginTop: 2, marginBottom: 8, lineHeight: 1.45 }}>Smaller maken geeft ruimte aan de achtergrondfoto.</div>
          <label style={lbl}>Snelheid ({d.speed || 100}%)</label>
          <input type="range" min="50" max="200" step="10" value={d.speed || 100} onChange={e => setD({ speed: Number(e.target.value) })} style={slider} />
          <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 12.5, color: C.ink2, marginTop: 8, cursor: "pointer" }}>
            <input type="checkbox" checked={d.sequence !== false} onChange={e => setD({ sequence: e.target.checked })} />
            Elementen na elkaar laten verschijnen
          </label>
        </div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Middelen</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
            {Picker
              ? <>
                  <Picker label="Logo" value={logoId} onPick={setLogoId} veld="logo" />
                  <Picker label="Achtergrondbeeld" value={bgId} onPick={setBgId} veld="achtergrond" />
                  <Picker label="Foto (los element)" value={imageId} onPick={setImageId} veld="foto" />
                  <Picker label="Patroon" value={patternId} onPick={setPatternId} veld="patroon" />
                </>
              : [["Logo", logoId, setLogoId], ["Foto", imageId, setImageId], ["Patroon", patternId, setPatternId]].map(([l, v, setr]) => (
                  <div key={l}><label style={lbl}>{l}</label>
                    <select value={v} onChange={e => setr(e.target.value)} style={sel}>
                      <option value="">Geen</option>
                      {assets.map(a => <option key={a.id} value={a.id}>{a.name}</option>)}
                    </select></div>
                ))}
          </div>
          {logoId && <>
            <label style={{ ...lbl, marginTop: 10 }}>Logogrootte ({d.logoScale || 100}%)</label>
            <input type="range" min="50" max="200" step="5" value={d.logoScale || 100} onChange={e => setD({ logoScale: Number(e.target.value) })} style={slider} />
          </>}
          <label style={{ ...lbl, marginTop: 10 }}>Lettertype</label>
          <select value={d.font || ""} onChange={e => setD({ font: e.target.value })} style={sel}>
            <option value="">Huisletter uit je bedrijfsprofiel</option>
            <option value="poppins">Poppins</option>
            <option value="montserrat">Montserrat</option>
            <option value="verdana">Verdana</option>
            <option value="arial">Arial</option>
          </select>
          <div style={{ fontSize: 11, color: C.muted, marginTop: 3, lineHeight: 1.45 }}>
            Laat dit op je huisletter staan tenzij je bewust iets anders wilt. Staat het bestand van je huisletter nog niet in je bedrijfsprofiel, dan valt de tool terug op een standaardletter.
          </div>

          <label style={{ ...lbl, marginTop: 10 }}>Tekstgrootte kop ({d.headScale || 100}%)</label>
          <input type="range" min="60" max="180" step="5" value={d.headScale || 100} onChange={e => setD({ headScale: Number(e.target.value) })} style={slider} />
        </div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 4, textTransform: "uppercase" }}>Kleuren</div>
          <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 9, lineHeight: 1.5 }}>
            {brandColors.length ? "Uit je bedrijfsprofiel. Klik een kleur om hem toe te wijzen." : "Er staan nog geen kleuren in je bedrijfsprofiel; vul ze daar in om ze hier te kunnen kiezen."}
          </div>
          {brandColors.length > 0 && [["accent", "Accent"], ["bg", "Achtergrond"], ["text", "Tekst"], ["ctaText", "Knoptekst"]].map(([k, l]) => (
            <div key={k} style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 7, flexWrap: "wrap" }}>
              <span style={{ fontSize: 12, color: C.ink2, width: 78, flexShrink: 0 }}>{l}</span>
              {brandColors.map(hex => (
                <button key={hex} onClick={() => setColors(c => ({ ...c, [k]: hex }))} title={hex}
                  style={{
                    width: 24, height: 24, borderRadius: 6, cursor: "pointer", flexShrink: 0, background: hex,
                    border: `2px solid ${String(colors[k]).toLowerCase() === hex.toLowerCase() ? C.yellow : C.line}`,
                  }} />
              ))}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ---- Social posts ----
// De standaardformaten per platform. Zo hoef je niet te onthouden wat waar past.
const SOCIAL_PLATFORMS = [
  { id: "linkedin", label: "LinkedIn", formats: [
    { w: 1200, h: 1200, label: "Vierkant", on: true },
    { w: 1200, h: 627, label: "Liggend", on: true },
    { w: 1080, h: 1350, label: "Staand", on: false },
  ]},
  { id: "linkedin-ads", label: "LinkedIn ads", formats: [
    { w: 1200, h: 627, label: "Single image ad", on: true },
    { w: 1080, h: 1080, label: "Vierkant ad", on: true },
    { w: 300, h: 250, label: "Spotlight", on: false },
  ]},
  { id: "meta", label: "Meta / Facebook", formats: [
    { w: 1200, h: 630, label: "Linkbericht", on: true },
    { w: 1080, h: 1080, label: "Vierkant", on: true },
    { w: 1080, h: 1920, label: "Story", on: false },
  ]},
  { id: "instagram", label: "Instagram", formats: [
    { w: 1080, h: 1080, label: "Vierkant", on: true },
    { w: 1080, h: 1350, label: "Staand", on: true },
    { w: 1080, h: 1920, label: "Story of reel", on: false },
  ]},
];

// De editor voor social: zelfde opzet als bij banners, maar met een afbeelding als
// voorbeeld in plaats van een werkende banner.
function SocialEditor({ concept, onChange, colors, setColors, assets, logoId, setLogoId, imageId, setImageId, patternId, setPatternId, bgId, setBgId, brandColors, formats, Picker }) {
  const [fmtIdx, setFmtIdx] = useState(0);
  const [png, setPng] = useState("");
  const [busy, setBusy] = useState(false);
  const [drag, setDrag] = useState(null);
  const [fout, setFout] = useState("");
  const boxRef = useRef();
  const timer = useRef();
  const lopend = useRef(null);   // om een achterhaald verzoek af te breken

  const f = formats[fmtIdx] || formats[0] || { w: 1080, h: 1080, label: "Vierkant" };
  const d = concept.design || {};
  const setD = (patch) => onChange({ ...concept, design: { ...d, ...patch } });
  const setC = (patch) => onChange({ ...concept, ...patch });

  // Weergavegrootte: passend in het scherm, met de juiste verhouding.
  const maxZij = 420;
  const schaal = Math.min(maxZij / f.w, maxZij / f.h, 1);
  const vw = Math.round(f.w * schaal), vh = Math.round(f.h * schaal);

  useEffect(() => {
    clearTimeout(timer.current);
    timer.current = setTimeout(async () => {
      if (lopend.current) { try { lopend.current.abort(); } catch {} }
      const ctrl = new AbortController();
      lopend.current = ctrl;
      setBusy(true);
      try {
        const r = await fetch("/api/studio/social-preview", {
          signal: ctrl.signal,
          method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            name: concept.name, headline: concept.headline, subline: concept.subline, cta: concept.cta,
            colors, logoId, imageId, patternId, bgId,
            design: d, formats: [{ w: f.w, h: f.h }],
          }),
        });
        const j = await r.json().catch(() => ({}));
        if (r.ok && j.png) { setPng(j.png); setFout(""); }
        else setFout(j.error || `Het voorbeeld kon niet worden gemaakt (status ${r.status}).`);
      } catch (e) {
        // Afgebroken omdat je alweer verder schoof: dat is geen fout.
        if (!(e && e.name === "AbortError")) setFout("Het voorbeeld kon niet worden gemaakt.");
      }
      setBusy(false);
    }, 500);
    return () => clearTimeout(timer.current);
  }, [JSON.stringify(concept), JSON.stringify(colors), logoId, imageId, patternId, fmtIdx]);

  const startDrag = (key, e) => {
    if (d.layout !== "free") return;
    e.preventDefault();
    const box = boxRef.current?.getBoundingClientRect();
    if (!box) return;
    const move = (ev) => {
      const x = Math.max(0, Math.min(100, ((ev.clientX - box.left) / box.width) * 100));
      const y = Math.max(0, Math.min(100, ((ev.clientY - box.top) / box.height) * 100));
      setD({ pos: { ...(d.pos || {}), [key]: { x: Math.round(x), y: Math.round(y) } } });
    };
    const up = () => { document.removeEventListener("mousemove", move); document.removeEventListener("mouseup", up); setDrag(null); };
    document.addEventListener("mousemove", move);
    document.addEventListener("mouseup", up);
    setDrag(key);
  };
  const posOf = (key, dx, dy) => {
    const p = (d.pos || {})[key] || {};
    return { x: p.x === undefined ? dx : p.x, y: p.y === undefined ? dy : p.y };
  };

  const lbl = { display: "block", fontSize: 11, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" };
  const sel = { width: "100%", padding: "7px 9px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card };
  const inp = { ...sel, background: "transparent" };

  return (
    <div style={{ display: "grid", gridTemplateColumns: "minmax(300px, 1fr) minmax(280px, 330px)", gap: 20, alignItems: "start" }}>
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10, flexWrap: "wrap" }}>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Bekijken als</span>
          {formats.map((x, i) => (
            <button key={i} onClick={() => setFmtIdx(i)} style={{
              padding: "5px 10px", borderRadius: 7, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 11.5, fontWeight: 700,
              border: `1px solid ${fmtIdx === i ? "transparent" : C.line}`,
              background: fmtIdx === i ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: fmtIdx === i ? "#fff" : C.muted,
            }}>{x.label}</button>
          ))}
          {busy && <Loader2 size={13} className="spin" style={{ color: C.muted }} />}
        </div>

        <div ref={boxRef} style={{ position: "relative", width: vw, height: vh, border: `1px solid ${C.line}`, borderRadius: 8, overflow: "hidden", background: "#111" }}>
          {png
            ? <img src={`data:image/png;base64,${png}`} alt="voorbeeld" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
            : <div style={{ width: "100%", height: "100%", display: "grid", placeItems: "center", color: fout ? C.bad : C.muted, fontSize: 12.5, padding: 14, textAlign: "center", lineHeight: 1.5 }}>{fout || "voorbeeld maken…"}</div>}

          {d.layout === "free" && [
            ["photo", "Foto", 72, 50, !!imageId && d.photoMode === "element"],
            ["logo", "Logo", 22, 16, !!logoId],
            ["head", "Kop", 50, 44, !!concept.headline],
            ["sub", "Ondertitel", 50, 66, !!concept.subline],
            ["cta", "Knop", 50, 86, !!concept.cta],
          ].filter(([, , , , t]) => t).map(([key, naam, dx, dy]) => {
            const p = posOf(key, dx, dy);
            return (
              <div key={key} onMouseDown={(e) => startDrag(key, e)} title={`${naam} verslepen`}
                style={{
                  position: "absolute", left: `${p.x}%`, top: `${p.y}%`, transform: "translate(-50%,-50%)",
                  padding: "2px 7px", borderRadius: 20, fontSize: 10, fontWeight: 700, whiteSpace: "nowrap",
                  background: drag === key ? C.yellow : C.yellow + "CC", color: "#fff",
                  cursor: "grab", userSelect: "none", boxShadow: "0 1px 6px rgba(0,0,0,.4)", zIndex: 5,
                }}>{naam}</div>
            );
          })}
        </div>
        {d.layout === "free"
          ? <div style={{ fontSize: 11.5, color: C.good, marginTop: 7, lineHeight: 1.5, fontWeight: 600 }}>Sleep de labels naar de gewenste plek. De posities gelden in procenten, dus ze kloppen in elk formaat.</div>
          : <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 8, flexWrap: "wrap" }}>
              <span style={{ fontSize: 11.5, color: C.muted }}>De tool bepaalt nu zelf waar alles staat.</span>
              <button onClick={() => setD({ layout: "free" })} style={{ ...miniBtn(C.yellow, "#fff") }}>zelf slepen</button>
            </div>}
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 13 }}>
        <div><label style={lbl}>Kop</label>
          <textarea value={concept.headline || ""} onChange={e => setC({ headline: e.target.value })} rows={2} style={{ ...inp, resize: "vertical", lineHeight: 1.4 }} /></div>
        <div><label style={lbl}>Ondertitel</label>
          <textarea value={concept.subline || ""} onChange={e => setC({ subline: e.target.value })} rows={2} style={{ ...inp, resize: "vertical", lineHeight: 1.4 }} /></div>
        <div><label style={lbl}>Knoptekst (leeg = geen knop)</label>
          <input value={concept.cta || ""} onChange={e => setC({ cta: e.target.value })} style={inp} /></div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Vormgeving</div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 9 }}>
            <div><label style={lbl}>Indeling</label>
              <select value={d.layout || "stacked"} onChange={e => setD({ layout: e.target.value })} style={sel}>
                <option value="stacked">Onder elkaar</option><option value="centered">Gecentreerd</option><option value="free">Vrij slepen</option>
              </select></div>
            <div><label style={lbl}>Achtergrond</label>
              <select value={d.bg || "solid"} onChange={e => setD({ bg: e.target.value })} style={sel}>
                {BAN_BGS.map(([v, l]) => <option key={v} value={v} disabled={(v === "image" && !bgId && !imageId) || (v === "pattern" && !patternId)}>{l}</option>)}
              </select></div>
            <div><label style={lbl}>Vorm</label>
              <select value={d.shape || "none"} onChange={e => setD({ shape: e.target.value })} style={sel}>
                {BAN_SHAPES.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
              </select></div>
            {d.bg === "image" && (
              <div><label style={lbl}>Sluier over de foto</label>
                <select value={d.scrim || "normaal"} onChange={e => setD({ scrim: e.target.value })} style={sel}>
                  <option value="geen">Geen, foto vol in beeld</option>
                  <option value="licht">Licht</option>
                  <option value="normaal">Normaal</option>
                  <option value="sterk">Sterk</option>
                </select></div>
            )}
            <div><label style={lbl}>Uitlijning</label>
              <select value={d.align || "left"} onChange={e => setD({ align: e.target.value })} style={sel}>
                <option value="left">Links</option><option value="center">Gecentreerd</option>
              </select></div>
          </div>
          {d.bg === "image" && <div style={{ fontSize: 11, color: C.muted, marginTop: 5, lineHeight: 1.45 }}>
            De sluier maakt de foto donkerder achter je tekst, zodat die leesbaar blijft. Staat je tekst op een rustig deel van de foto, dan kun je hem uitzetten.
          </div>}
          <label style={{ ...lbl, marginTop: 10 }}>Lettertype</label>
          <select value={d.font || ""} onChange={e => setD({ font: e.target.value })} style={sel}>
            <option value="">Huisletter uit je bedrijfsprofiel</option>
            <option value="poppins">Poppins</option>
            <option value="montserrat">Montserrat</option>
            <option value="verdana">Verdana</option>
            <option value="arial">Arial</option>
          </select>
          <div style={{ fontSize: 11, color: C.muted, marginTop: 3, lineHeight: 1.45 }}>
            Laat dit op je huisletter staan tenzij je bewust iets anders wilt. Staat het bestand van je huisletter nog niet in je bedrijfsprofiel, dan valt de tool terug op een standaardletter.
          </div>

          <label style={{ ...lbl, marginTop: 10 }}>Tekstgrootte kop ({d.headScale || 100}%)</label>
          <input type="range" min="60" max="180" step="5" value={d.headScale || 100} onChange={e => setD({ headScale: Number(e.target.value) })} style={{ width: "100%", accentColor: C.yellow }} />
          <label style={{ ...lbl, marginTop: 8 }}>Breedte tekstvak ({d.textWidth || 100}%)</label>
          <input type="range" min="30" max="100" step="5" value={d.textWidth || 100} onChange={e => setD({ textWidth: Number(e.target.value) })} style={{ width: "100%", accentColor: C.yellow }} />
          <div style={{ fontSize: 11, color: C.muted, marginTop: 2, lineHeight: 1.45 }}>Maak het tekstvak smaller om ruimte te maken voor een foto ernaast.</div>
          {logoId && <>
            <label style={{ ...lbl, marginTop: 8 }}>Logogrootte ({d.logoScale || 100}%)</label>
            <input type="range" min="50" max="200" step="5" value={d.logoScale || 100} onChange={e => setD({ logoScale: Number(e.target.value) })} style={{ width: "100%", accentColor: C.yellow }} />
          </>}
        </div>

        {/* De foto: als achtergrond of als los element dat je zelf neerzet */}
        {imageId && (
          <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Foto</div>
            <label style={lbl}>Hoe wordt de foto gebruikt?</label>
            <select value={d.photoMode || "bg"} onChange={e => {
                const v = e.target.value;
                // De achtergrond staat hier los van; die laten we ongemoeid.
                setD({ photoMode: v });
              }} style={sel}>
              <option value="bg">Als achtergrond, vult het hele vlak</option>
              <option value="element">Als los element, zelf neerzetten</option>
            </select>

            {d.photoMode === "element" && <>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 9, marginTop: 9 }}>
                <div><label style={lbl}>Vorm</label>
                  <select value={d.photoShape || "rect"} onChange={e => setD({ photoShape: e.target.value })} style={sel}>
                    <option value="rect">Rechthoek</option><option value="rounded">Afgeronde hoeken</option><option value="circle">Rond</option>
                  </select></div>
                <div><label style={lbl}>Laag</label>
                  <select value={d.photoFront ? "front" : "back"} onChange={e => setD({ photoFront: e.target.value === "front" })} style={sel}>
                    <option value="front">Voor de tekst</option><option value="back">Achter de tekst</option>
                  </select></div>
              </div>
              <label style={{ ...lbl, marginTop: 9 }}>Grootte ({d.photoScale || 45}% van de breedte)</label>
              <input type="range" min="15" max="110" step="5" value={d.photoScale || 45} onChange={e => setD({ photoScale: Number(e.target.value) })} style={{ width: "100%", accentColor: C.yellow }} />
              <div style={{ fontSize: 11, color: C.muted, marginTop: 4, lineHeight: 1.45 }}>
                {d.layout === "free" ? "Sleep het label \"Foto\" op het voorbeeld om hem te verplaatsen." : "Zet de indeling op \"Vrij slepen\" om de foto te verplaatsen."}
              </div>
            </>}
          </div>
        )}

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 9, textTransform: "uppercase" }}>Middelen</div>
          <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
            {Picker
              ? <>
                  <Picker label="Logo" value={logoId} onPick={setLogoId} veld="logo" />
                  <Picker label="Achtergrondbeeld" value={bgId} onPick={setBgId} veld="achtergrond" />
                  <Picker label="Foto (los element)" value={imageId} onPick={setImageId} veld="foto" />
                  <Picker label="Patroon" value={patternId} onPick={setPatternId} veld="patroon" />
                </>
              : [["Logo", logoId, setLogoId], ["Foto", imageId, setImageId], ["Patroon", patternId, setPatternId]].map(([l, v, setr]) => (
                  <div key={l}><label style={lbl}>{l}</label>
                    <select value={v} onChange={e => setr(e.target.value)} style={sel}>
                      <option value="">Geen</option>
                      {assets.map(a => <option key={a.id} value={a.id}>{a.name}</option>)}
                    </select></div>
                ))}
          </div>
        </div>

        <div style={{ borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <div style={{ fontSize: 11.5, fontWeight: 700, color: C.ink, marginBottom: 4, textTransform: "uppercase" }}>Kleuren</div>
          <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 9, lineHeight: 1.5 }}>
            {brandColors.length ? "Uit je bedrijfsprofiel." : "Vul kleuren in bij je bedrijfsprofiel om ze hier te kunnen kiezen."}
          </div>
          {brandColors.length > 0 && [["accent", "Accent"], ["bg", "Achtergrond"], ["text", "Tekst"], ["ctaText", "Knoptekst"]].map(([k, l]) => (
            <div key={k} style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 7, flexWrap: "wrap" }}>
              <span style={{ fontSize: 12, color: C.ink2, width: 78, flexShrink: 0 }}>{l}</span>
              {brandColors.map(hex => (
                <button key={hex} onClick={() => setColors(c => ({ ...c, [k]: hex }))} title={hex}
                  style={{ width: 24, height: 24, borderRadius: 6, cursor: "pointer", flexShrink: 0, background: hex,
                           border: `2px solid ${String(colors[k]).toLowerCase() === hex.toLowerCase() ? C.yellow : C.line}` }} />
              ))}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function Design({ ctx }) {
  const { companies, profile, confirm } = ctx;
  const [tile, setTile] = useState(null);
  const [company, setCompany] = useState("whitevision");
  // Voor welk bedrijf maak je het? Het hoofdmerk plus de bedrijven uit je lijst.
  const companyOptions = [
    { id: "whitevision", name: "whitevision" },
    ...((companies || []).filter(c => c && c.name).map(c => ({ id: String(c.id), name: c.name }))),
  ];
  const [assets, setAssets] = useState({}); // { "tileId::company": [ {id,name,...} ] }
  const [topic, setTopic] = useState("");
  const [genBusy, setGenBusy] = useState("");
  const [genErr, setGenErr] = useState("");
  const [outline, setOutline] = useState(null);
  const [presMode, setPresMode] = useState("scratch"); // "scratch" | "convert"
  const [convFile, setConvFile] = useState(null);
  const convFileRef = useRef();
  const [templateId, setTemplateId] = useState("");   // gekozen huisstijl-template
  const [tplList, setTplList] = useState([]);          // beschikbare .pptx-templates
  const [done, setDone] = useState("");
  const [layouts, setLayouts] = useState(null);        // indelingen uit de gekozen template
  const [layoutMap, setLayoutMap] = useState({});      // {slidetype: layout-index}
  const [layoutErr, setLayoutErr] = useState("");
  const [showLayouts, setShowLayouts] = useState(false);

  // De slidetypen waaraan je een indeling uit je template koppelt.
  const SLIDE_TYPES = [
    ["title", "Openingsslide"],
    ["section", "Hoofdstuk / tussenkop"],
    ["content", "Tekstslide"],
    ["picture", "Tekst met foto"],
  ];

  // Beschikbare templates ophalen voor de presentatiefunctie.
  useEffect(() => {
    let on = true;
    fetch("/api/templates", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => {
        if (!on || !d) return;
        const pptx = (d.templates || []).filter(t => /\.pptx$/i.test(t.filename || ""));
        setTplList(pptx);
        if (pptx.length && !templateId) setTemplateId(pptx[0].id);
      })
      .catch(() => {});
    return () => { on = false; };
  }, []);

  // Indelingen ophalen zodra er een template gekozen is, en een eerdere koppeling terughalen.
  useEffect(() => {
    if (!templateId) { setLayouts(null); setLayoutMap({}); return; }
    let on = true;
    setLayoutErr("");
    fetch(`/api/studio/template-layouts?templateId=${encodeURIComponent(templateId)}`, { credentials: "include" })
      .then(async r => { const d = await r.json(); if (!r.ok) throw new Error(d.error || "Kon de indelingen niet ophalen."); return d; })
      .then(d => { if (on) setLayouts(d.layouts || []); })
      .catch(e => { if (on) { setLayouts([]); setLayoutErr(e.message); } });
    store.get("wv_tpl_layoutmap", {}).then(all => {
      if (on && all && all[templateId]) setLayoutMap(all[templateId]);
      else if (on) setLayoutMap({});
    }).catch(() => {});
    return () => { on = false; };
  }, [templateId]);

  const setLayoutFor = (type, idx) => {
    const next = { ...layoutMap, [type]: idx === "" ? undefined : Number(idx) };
    if (idx === "") delete next[type];
    setLayoutMap(next);
    store.get("wv_tpl_layoutmap", {}).then(all => {
      store.set("wv_tpl_layoutmap", { ...(all || {}), [templateId]: next });
    }).catch(() => {});
  };

  // Het logo uit je assets, om in de presentatie te zetten.
  const brandLogo = async () => {
    try {
      const r = await fetch("/api/assets", { credentials: "include" });
      if (!r.ok) return null;
      const d = await r.json();
      const lg = (d.assets || []).find(a => (a.category || "") === "logo");
      if (!lg) return null;
      const fr = await fetch(`/api/assets/${lg.id}/file`, { credentials: "include" });
      if (!fr.ok) return null;
      const blob = await fr.blob();
      const dataUrl = await new Promise(res => { const f = new FileReader(); f.onload = () => res(f.result); f.onerror = () => res(null); f.readAsDataURL(blob); });
      return dataUrl || null;
    } catch { return null; }
  };

  const buildAndDownload = async (spec) => {
    const logo = await brandLogo();
    const res = await fetch("/api/studio/pptx", {
      method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ...spec, templateId: templateId || null, layoutMap, ink: C.ink, yellow: C.yellow, logo }),
    });
    if (!res.ok) { let m = "Kon de presentatie niet genereren."; try { m = (await res.json()).error || m; } catch {} throw new Error(m); }
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = ((spec.title || "presentatie").replace(/[^a-z0-9\- ]/gi, "").trim() || "presentatie") + ".pptx";
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(() => URL.revokeObjectURL(url), 4000);
  };

  // Stap 1: een voorstel voor de indeling, nog geen bestand.
  const proposeOutline = async () => {
    if (!topic.trim()) { setGenErr("Geef eerst een onderwerp op."); return; }
    setGenErr(""); setOutline(null); setDone("");
    const coName = (companyOptions.find(c => c.id === company) || {}).name || "whitevision";
    try {
      setGenBusy("outline");
      const prompt = `${profileContext(profile)}\n\nOPDRACHT: Bedenk de inhoud en indeling van een professionele presentatie voor ${coName} over: "${topic.trim()}". Bepaal zelf een logische opbouw met 6 tot 10 slides. Per slide een korte, krachtige titel en 3 tot 5 bondige bullets in gewone taal (geen buzzwords). Voeg per slide een korte spreeknotitie toe.\n\nGeef per slide ook een type: "section" voor een hoofdstuk- of tussenkopslide (korte titel, weinig of geen bullets), "picture" als beeld de hoofdrol speelt, anders "content".\n\nJSON: {"title":"titel van de presentatie","subtitle":"korte ondertitel","slides":[{"type":"content|section|picture","title":"...","bullets":["...","..."],"note":"spreeknotitie"}]}`;
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setGenBusy(""); return; }
      if (aiErr) { setGenErr(aiErr); setGenBusy(""); return; }
      if (!r || !Array.isArray(r.slides) || !r.slides.length) { setGenErr("De indeling kon niet worden opgesteld. Probeer opnieuw of pas het onderwerp aan."); setGenBusy(""); return; }
      setOutline(r);
    } catch (e) { setGenErr(e.message || "Er ging iets mis bij het opstellen."); }
    setGenBusy("");
  };

  // Stap 2: het goedgekeurde voorstel omzetten naar een bestand.
  const buildFromOutline = async () => {
    if (!outline) return;
    const coName = (companyOptions.find(c => c.id === company) || {}).name || "whitevision";
    setGenErr("");
    try {
      setGenBusy("pptx");
      await buildAndDownload({ title: outline.title || topic.trim(), subtitle: outline.subtitle || coName, slides: outline.slides });
      setDone(`Presentatie gedownload — ${outline.slides.length + 1} slides.`);
    } catch (e) { setGenErr(e.message || "Er ging iets mis bij het genereren."); }
    setGenBusy("");
  };

  const updateSlide = (i, patch) => setOutline(o => ({ ...o, slides: o.slides.map((sd, j) => j === i ? { ...sd, ...patch } : sd) }));
  const removeSlide = (i) => setOutline(o => ({ ...o, slides: o.slides.filter((_, j) => j !== i) }));

  // Een bestaande presentatie omzetten. De INHOUD blijft ongewijzigd; alleen de
  // vormgeving verandert doordat we de tekst in jouw template plaatsen. Geen AI nodig.
  const convertPptx = async () => {
    if (!convFile) { setGenErr("Kies eerst een bestand."); return; }
    setGenErr(""); setOutline(null); setDone("");
    const coName = (companyOptions.find(c => c.id === company) || {}).name || "whitevision";
    try {
      setGenBusy("extract");
      const buf = await convFile.arrayBuffer();
      const exRes = await fetch("/api/studio/extract-pptx", { method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf });
      const exData = await exRes.json();
      if (!exRes.ok || !Array.isArray(exData.slides) || !exData.slides.length) { setGenErr(exData.error || "Kon de presentatie niet uitlezen."); setGenBusy(""); return; }
      const slides = exData.slides.map(texts => ({
        title: texts[0] || "",
        bullets: texts.slice(1),
        type: texts.slice(1).length === 0 ? "section" : "content",
        note: "",
      }));
      const deckTitle = convFile.name.replace(/\.pptx?$/i, "");
      setGenBusy("pptx");
      await buildAndDownload({ title: deckTitle, subtitle: coName, slides });
      setDone(`Omgezet naar je huisstijl — ${slides.length + 1} slides, inhoud ongewijzigd.`);
      setConvFile(null); if (convFileRef.current) convFileRef.current.value = "";
    } catch (e) { setGenErr(e.message || "Er ging iets mis bij het omzetten."); }
    setGenBusy("");
  };

  // Een asset uploaden die niet in het bedrijfsprofiel hoort, bijvoorbeeld een foto van
  // een persoon voor één specifieke post. Die krijgt de categorie "eenmalig", zodat je
  // merkbibliotheek overzichtelijk blijft, maar hij wordt wel bewaard: een opgeslagen
  // ontwerp verwijst ernaar en moet hem later kunnen terugvinden.
  const [uploadBusy, setUploadBusy] = useState("");
  const [uploadErr, setUploadErr] = useState("");
  const uploadAsset = async (file, kies) => {
    if (!file) return;
    setUploadBusy(kies || "x"); setUploadErr("");
    try {
      const buf = await file.arrayBuffer();
      const naam = file.name.replace(/\.[^.]+$/, "").slice(0, 60) || "upload";
      const r = await fetch(`/api/assets?name=${encodeURIComponent(naam)}&category=eenmalig&filename=${encodeURIComponent(file.name)}`, {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf,
      });
      const d = await r.json();
      if (!r.ok) { setUploadErr(d.error || "Uploaden mislukte."); setUploadBusy(""); return; }
      // Lijst verversen en de nieuwe meteen kiezen.
      const lr = await fetch("/api/assets", { credentials: "include" });
      if (lr.ok) { const ld = await lr.json(); setBanAssets(ld.assets || []); }
      if (d.asset?.id && kies) kies(d.asset.id);
    } catch { setUploadErr("Uploaden mislukte."); }
    setUploadBusy("");
  };

  // Keuzelijst met uploadknop ernaast.
  const AssetPicker = ({ label, value, onPick, veld }) => (
    <div>
      <label style={{ display: "block", fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>{label}</label>
      <div style={{ display: "flex", gap: 6 }}>
        <select value={value} onChange={e => onPick(e.target.value)}
          style={{ flex: 1, minWidth: 0, padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card }}>
          <option value="">Geen</option>
          {banAssets.map(a => <option key={a.id} value={a.id}>{a.name}{a.category === "eenmalig" ? " (eigen upload)" : ""}</option>)}
        </select>
        <label title="Eigen bestand uploaden voor deze post"
          style={{ display: "grid", placeItems: "center", width: 36, flexShrink: 0, border: `1.5px solid ${C.line}`, borderRadius: 9, cursor: uploadBusy ? "default" : "pointer", background: C.card }}>
          {uploadBusy === veld ? <Loader2 size={15} className="spin" style={{ color: C.muted }} /> : <Upload size={15} style={{ color: C.ink2 }} />}
          <input type="file" accept="image/png,image/jpeg,image/gif,image/webp,image/svg+xml"
            onChange={e => { const f = e.target.files?.[0]; e.target.value = ""; uploadAsset(f, onPick); }}
            disabled={!!uploadBusy} style={{ display: "none" }} />
        </label>
      </div>
    </div>
  );

  // ---- Bewaarde creaties ----
  // Wat je maakt wordt opgeslagen, zodat je het later terug kunt halen en verder kunt
  // bewerken. Zonder dit ben je je werk kwijt zodra je wegklikt.
  const [saved, setSaved] = useState([]);
  const [currentId, setCurrentId] = useState(null);
  const [savedMsg, setSavedMsg] = useState("");

  useEffect(() => {
    store.get("wv_studio_items", []).then(v => setSaved(Array.isArray(v) ? v : [])).catch(() => {});
  }, []);
  const persistSaved = (next) => { setSaved(next); store.set("wv_studio_items", next); };

  // Het huidige werk bewaren. Bij een bestaand item werken we dat bij.
  const saveCreation = (kind, concept, extra = {}) => {
    if (!concept) return;
    const now = new Date().toISOString();
    const naam = (concept.name || concept.headline || "Zonder titel").slice(0, 80);
    if (currentId && saved.some(x => x.id === currentId)) {
      persistSaved(saved.map(x => x.id === currentId ? { ...x, name: naam, concept, ...extra, updatedAt: now } : x));
    } else {
      const id = "s" + Date.now().toString(36);
      persistSaved([{ id, kind, name: naam, concept, ...extra, createdAt: now, updatedAt: now }, ...saved]);
      setCurrentId(id);
    }
    setSavedMsg("Opgeslagen.");
    setTimeout(() => setSavedMsg(""), 2200);
  };

  const loadCreation = (item) => {
    if (!item) return;
    setCurrentId(item.id);
    if (item.colors) setBanColors(item.colors);
    if (item.logoId !== undefined) setBanLogoId(item.logoId || "");
    if (item.imageId !== undefined) setBanImageId(item.imageId || "");
    if (item.patternId !== undefined) setBanPatternId(item.patternId || "");
    if (item.bgId !== undefined) setBanBgId(item.bgId || "");
    if (item.kind === "banner") {
      setBanConcepts([item.concept]);
      setBanPicked(0);
      setBanEdit(true);
      if (Array.isArray(item.formats) && item.formats.length) setBanFormats(item.formats);
      if (item.clickUrl) setBanForm(f => ({ ...f, clickUrl: item.clickUrl }));
    } else {
      setSocConcepts([item.concept]);
      setSocPicked(0);
      setSocEdit(true);
      if (item.platformId) setSocPlatform(item.platformId);
      if (item.platformId && Array.isArray(item.formats)) setSocFormats(m => ({ ...m, [item.platformId]: item.formats }));
    }
    if (typeof window !== "undefined") window.scrollTo({ top: 0, behavior: "smooth" });
  };

  const deleteCreation = (item) => confirm(`"${item.name}" verwijderen?`, () => {
    persistSaved(saved.filter(x => x.id !== item.id));
    if (currentId === item.id) setCurrentId(null);
  });

  // ---- Social posts ----
  const [socPlatform, setSocPlatform] = useState("linkedin");
  const [socFormats, setSocFormats] = useState({});   // platform → gekozen formaten
  const [socForm, setSocForm] = useState({ goal: "", cta: "" });
  const [socConcepts, setSocConcepts] = useState([]);
  const [socPreviews, setSocPreviews] = useState({});
  const [socPicked, setSocPicked] = useState(null);
  const [socEdit, setSocEdit] = useState(false);
  const [socBusy, setSocBusy] = useState("");
  const [socErr, setSocErr] = useState("");
  const [socDone, setSocDone] = useState("");

  const platform = SOCIAL_PLATFORMS.find(p2 => p2.id === socPlatform) || SOCIAL_PLATFORMS[0];
  const gekozenFormaten = (socFormats[socPlatform] || platform.formats.filter(f => f.on).map(f => `${f.w}x${f.h}`));
  const activeSocFormats = platform.formats.filter(f => gekozenFormaten.includes(`${f.w}x${f.h}`));
  const toggleSocFormat = (key) => setSocFormats(m => {
    const huidig = m[socPlatform] || platform.formats.filter(f => f.on).map(f => `${f.w}x${f.h}`);
    return { ...m, [socPlatform]: huidig.includes(key) ? huidig.filter(x => x !== key) : [...huidig, key] };
  });

  // De server haalt de afbeeldingen zelf van schijf; wij sturen alleen welke.
  const socAssets = async () => ({ logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId });

  // Drie postontwerpen laten maken, afgestemd op het gekozen platform.
  const makeSocialConcepts = async () => {
    setSocBusy("ideas"); setSocErr(""); setSocDone(""); setSocPicked(null); setSocPreviews({}); setSocEdit(false);
    const heeftBeeld = !!banImageId, heeftPatroon = !!banPatternId;
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Ontwerp DRIE social posts voor ${platform.label}${socForm.goal.trim() ? ` met dit doel: ${socForm.goal.trim()}` : ""}.${socForm.cta.trim() ? ` De gewenste actie is: ${socForm.cta.trim()}.` : ""} Laat ze echt van elkaar verschillen in invalshoek en vormgeving.\n\nOp ${platform.label} scrollen mensen snel. De kop moet in één oogopslag te vatten zijn: maximaal 8 woorden. De ondertitel maximaal 14 woorden. De knoptekst 2 of 3 woorden, of leeg als een knop niet past bij dit platform. Gebruik onze tone of voice, geen buzzwords, geen uitroeptekens, geen hashtags in het beeld.\n\nBeschikbaar: ${heeftBeeld ? "een achtergrondfoto" : "geen foto"}${heeftPatroon ? ", een patroon" : ""}. Kies alleen een achtergrond die beschikbaar is.\n\nPer ontwerp bepaal je:\n- layout: "stacked" of "centered"\n- bg: "solid", "gradient"${heeftBeeld ? ', "image"' : ""}${heeftPatroon ? ', "pattern"' : ""}\n- shape: "none", "bar", "circle", "diagonal", "corner"\n- align: "left" of "center"\n\nGeef ook een begeleidende tekst voor bij de post (de caption), 2 tot 4 zinnen, die aanzet tot de gewenste actie.\n\nJSON: {"concepts":[{"name":"korte naam","why":"waarom dit werkt, één zin","headline":"...","subline":"...","cta":"...","caption":"begeleidende tekst","layout":"...","bg":"...","shape":"...","align":"..."}]}`;
    try {
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setSocBusy(""); return; }
      if (aiErr) { setSocErr(aiErr); setSocBusy(""); return; }
      const list = (r?.concepts || []).filter(c => c && c.headline).slice(0, 3).map(c => ({
        name: c.name, why: c.why, headline: c.headline, subline: c.subline, cta: c.cta, caption: c.caption || "",
        design: { layout: c.layout || "stacked", bg: c.bg || "solid", shape: c.shape || "none",
                  align: c.align || "left", logoScale: 100, headScale: 100 },
      }));
      if (!list.length) { setSocErr("Er konden geen ontwerpen worden gemaakt. Probeer het opnieuw."); setSocBusy(""); return; }
      setSocConcepts(list);
      setCurrentId(null);
      setSocBusy("preview");
      const assets = await socAssets();
      const f0 = activeSocFormats[0] || platform.formats[0];
      const out = {};
      const fouten = [];
      for (let i = 0; i < list.length; i++) {
        try {
          const rr = await fetch("/api/studio/social-preview", {
            method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ name: list[i].name, headline: list[i].headline, subline: list[i].subline, cta: list[i].cta,
              colors: banColors, ...assets, design: list[i].design, formats: [{ w: f0.w, h: f0.h }] }),
          });
          const dd = await rr.json().catch(() => ({}));
          if (rr.ok && dd.png) out[i] = dd.png;
          else fouten.push(dd.error || `De server gaf status ${rr.status}.`);
        } catch (e) { fouten.push("De aanvraag kwam niet aan."); }
      }
      setSocPreviews(out);
      if (!Object.keys(out).length && fouten.length) {
        // Bij een mislukking meteen nakijken of de server alles heeft.
        let extra = "";
        try {
          const cr = await fetch("/api/studio/social-check", { credentials: "include" });
          const cd = await cr.json();
          if (cd && !cd.ok && cd.advies) extra = ` ${cd.advies}`;
        } catch {}
        setSocErr(`De voorbeelden konden niet worden gemaakt: ${fouten[0]}${extra}`);
      }
    } catch { setSocErr("Er ging iets mis bij het ontwerpen."); }
    setSocBusy("");
  };

  const buildSocial = async () => {
    const c = socConcepts[socPicked];
    if (!c) { setSocErr("Kies eerst een ontwerp."); return; }
    if (!activeSocFormats.length) { setSocErr("Kies minstens één formaat."); return; }
    setSocBusy("build"); setSocErr(""); setSocDone("");
    try {
      const assets = await socAssets();
      const res = await fetch("/api/studio/social", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: `${platform.label} - ${c.name || c.headline}`,
          headline: c.headline, subline: c.subline, cta: c.cta,
          colors: banColors, ...assets, design: c.design,
          formats: activeSocFormats.map(f => ({ w: f.w, h: f.h, label: f.label })),
        }),
      });
      if (!res.ok) { let m = "Kon de posts niet maken."; try { m = (await res.json()).error || m; } catch {} setSocErr(m); setSocBusy(""); return; }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url; a.download = `${platform.id}-posts.zip`;
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 4000);
      setSocDone(`${activeSocFormats.length} afbeeldingen gedownload voor ${platform.label}.`);
      saveCreation("social", c, { colors: banColors, logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId,
        platformId: socPlatform, platform: platform.label, formats: gekozenFormaten });
    } catch { setSocErr("Er ging iets mis bij het maken."); }
    setSocBusy("");
  };

  // ---- Bannersets ----
  // De AI maakt drie ontwerpen (indeling, kleurinzet, vorm en beweging). Je bekijkt ze
  // naast elkaar, kiest er één en zet die door naar alle gewenste formaten.
  const [banForm, setBanForm] = useState({ goal: "", headline: "", subline: "", cta: "", clickUrl: "" });
  const [banFormats, setBanFormats] = useState(BANNER_FORMATS.filter(f => f.on).map(f => `${f.w}x${f.h}`));
  const [banBusy, setBanBusy] = useState("");
  const [banErr, setBanErr] = useState("");
  const [banDone, setBanDone] = useState("");
  const [banAssets, setBanAssets] = useState([]);
  const [banLogoId, setBanLogoId] = useState("");
  const [banImageId, setBanImageId] = useState("");
  const [banPatternId, setBanPatternId] = useState("");
  const [banBgId, setBanBgId] = useState("");   // beeld voor de achtergrond, los van de foto
  const [banColors, setBanColors] = useState({ bg: "#0A0A0A", text: "#FFFFFF", accent: "#8B5CF6", ctaText: "#FFFFFF" });
  const [banConcepts, setBanConcepts] = useState([]);   // de drie ontwerpen
  const [banPreviews, setBanPreviews] = useState({});   // ontwerp-index → HTML
  const [banPicked, setBanPicked] = useState(null);     // gekozen ontwerp
  const [banEdit, setBanEdit] = useState(false);       // bewerken van het gekozen ontwerp

  useEffect(() => {
    let on = true;
    fetch("/api/assets", { credentials: "include" })
      .then(r => r.ok ? r.json() : null)
      .then(d => {
        if (!on || !d) return;
        const list = d.assets || [];
        setBanAssets(list);
        const lg = list.find(a => (a.category || "") === "logo");
        if (lg) setBanLogoId(lg.id);
      })
      .catch(() => {});
    return () => { on = false; };
  }, []);

  // De kleuren uit het bedrijfsprofiel; daar kies je uit, zodat je in de huisstijl blijft.
  const brandColors = [...new Set(
    String(profile?.colors || "").split(/[,\s]+/).map(x => x.trim())
      .filter(x => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(x))
      .concat(["#FFFFFF", "#000000"])
  )];
  useEffect(() => {
    const raw = brandColors.filter(x => !["#FFFFFF", "#000000"].includes(x.toUpperCase()));
    if (raw.length) setBanColors(c => ({ ...c, accent: raw[0] || c.accent, bg: raw[1] || c.bg }));
  }, [profile?.colors]);

  // De server haalt de afbeeldingen zelf van schijf; wij sturen alleen welke.
  const banAssetBundle = async () => ({ logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId });

  // Drie ontwerpen laten maken: tekst én vormgeving én beweging.
  const makeConcepts = async () => {
    setBanBusy("ideas"); setBanErr(""); setBanDone(""); setBanPicked(null); setBanPreviews({});
    const heeftBeeld = !!banImageId, heeftPatroon = !!banPatternId;
    const prompt = `${profileContext(profile)}\n\nOPDRACHT: Ontwerp DRIE verschillende display-banners${banForm.goal.trim() ? ` met dit doel: ${banForm.goal.trim()}` : ""}. Ze moeten echt van elkaar verschillen in toon en vormgeving, zodat er iets te kiezen valt: bijvoorbeeld één ingetogen en zakelijk, één met een sterk visueel accent, en één die direct op de actie speelt.\n\n${banForm.cta.trim() ? `De gewenste actie is: ${banForm.cta.trim()}. Laat de knoptekst daarop aansluiten.\\n\\n` : ""}Een banner heeft weinig ruimte. Houd de kop maximaal 6 woorden, de ondertitel maximaal 10 woorden en de oproep 2 of 3 woorden. Gebruik onze tone of voice, geen buzzwords, geen uitroeptekens.\n\nBeschikbaar beeld: ${heeftBeeld ? "een achtergrondfoto" : "geen foto"}${heeftPatroon ? ", een patroon" : ""}. Kies alleen een achtergrond die ook echt beschikbaar is.\n\nPer ontwerp bepaal je:\n- layout: "stacked" (alles onder elkaar), "centered" (gecentreerd), "corner" (tekst boven, knop en logo onderaan)\n- bg: "solid", "gradient"${heeftBeeld ? ', "image"' : ""}${heeftPatroon ? ', "pattern"' : ""}\n- shape: "none", "bar" (accentbalk links), "circle" (grote cirkel), "diagonal" (schuin vlak), "corner" (hoekdriehoek)\n- intro: "fade", "slide" (van onder omhoog), "zoom"\n- cta: "pulse" (knop pulseert), "glow" (zachte gloed), "arrow" (bewegend pijltje), "none"\n- align: "left" of "center"\n\nJSON: {"concepts":[{"name":"korte naam van het ontwerp","why":"waarom dit werkt, één zin","headline":"...","subline":"...","cta":"...","layout":"...","bg":"...","shape":"...","intro":"...","cta_anim":"...","align":"..."}]}`;
    try {
      const { data: r, error: aiErr, cancelled } = await callAIFull(prompt, { json: true });
      if (cancelled) { setBanBusy(""); return; }
      if (aiErr) { setBanErr(aiErr); setBanBusy(""); return; }
      const list = (r?.concepts || []).filter(c => c && c.headline).slice(0, 3).map(c => ({
        name: c.name, why: c.why, headline: c.headline, subline: c.subline, cta: c.cta,
        design: { layout: c.layout || "stacked", bg: c.bg || "solid", shape: c.shape || "none",
                  intro: c.intro || "fade", cta: c.cta_anim || "pulse", align: c.align || "left",
                  sequence: true, speed: 100, logoScale: 100, headScale: 100 },
      }));
      if (!list.length) { setBanErr("Er konden geen ontwerpen worden gemaakt. Probeer het opnieuw."); setBanBusy(""); return; }
      setBanConcepts(list);
      setCurrentId(null);
      setBanBusy("preview");
      await renderPreviews(list);
    } catch { setBanErr("Er ging iets mis bij het ontwerpen."); }
    setBanBusy("");
  };

  // Van elk ontwerp een echt voorbeeld renderen, inclusief beweging.
  const renderPreviews = async (list) => {
    const assets = await banAssetBundle();
    const out = {};
    const fouten = [];
    for (let i = 0; i < list.length; i++) {
      const c = list[i];
      try {
        const r = await fetch("/api/studio/banner-preview", {
          method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            name: c.name || "ontwerp", headline: c.headline, subline: c.subline, cta: c.cta,
            clickUrl: banForm.clickUrl || profile?.website || "",
            colors: banColors, ...assets,
            design: c.design,
            formats: [{ w: 300, h: 250 }],
          }),
        });
        const d = await r.json().catch(() => ({}));
        if (r.ok && d.html) out[i] = d.html;
        else fouten.push(d.error || `De server gaf status ${r.status}.`);
      } catch { fouten.push("De aanvraag kwam niet aan."); }
    }
    setBanPreviews(out);
    if (!Object.keys(out).length && fouten.length) {
      setBanErr(`De voorbeelden konden niet worden gemaakt: ${fouten[0]}`);
    }
  };

  // Het gekozen ontwerp doorzetten naar alle gewenste formaten.
  const buildBanners = async () => {
    const c = banConcepts[banPicked];
    if (!c) { setBanErr("Kies eerst een ontwerp."); return; }
    if (!banFormats.length) { setBanErr("Kies minstens één formaat."); return; }
    setBanBusy("build"); setBanErr(""); setBanDone("");
    try {
      const assets = await banAssetBundle();
      const chosen = BANNER_FORMATS.filter(f => banFormats.includes(`${f.w}x${f.h}`)).map(f => ({ w: f.w, h: f.h, label: f.label }));
      const res = await fetch("/api/studio/banners", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: c.name || c.headline || "bannerset",
          headline: c.headline, subline: c.subline, cta: c.cta,
          clickUrl: banForm.clickUrl || profile?.website || "https://www.example.com",
          colors: banColors, ...assets,
          design: c.design,
          formats: chosen,
        }),
      });
      if (!res.ok) { let m = "Kon de banners niet bouwen."; try { m = (await res.json()).error || m; } catch {} setBanErr(m); setBanBusy(""); return; }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url; a.download = ((c.name || "bannerset").replace(/[^a-z0-9\- ]/gi, "").trim() || "bannerset") + ".zip";
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 4000);
      setBanDone(`${chosen.length} formaten gedownload, elk als aparte zip voor Google Ads.`);
      // Meteen bewaren, zodat je werk niet verloren gaat na het downloaden.
      saveCreation("banner", c, { colors: banColors, logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId, formats: banFormats, clickUrl: banForm.clickUrl });
    } catch { setBanErr("Er ging iets mis bij het bouwen."); }
    setBanBusy("");
  };

  const toggleFormat = (key) => setBanFormats(f => f.includes(key) ? f.filter(x => x !== key) : [...f, key]);

  if (tile) {
    const coName = (companyOptions.find(c => c.id === company) || {}).name || company;
    // De opgeslagen assets van dit soort voor het gekozen bedrijf.
    const list = (banAssets || []).filter(a => (a.company || "whitevision") === company && (!a.kind || a.kind === tile.id));
    return (
      <div>
        <button onClick={() => setTile(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar Studio</button>
        <PageHead title={tile.label} sub={tile.desc} />
        <Card style={{ marginBottom: 20 }}>
          <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Voor welk bedrijf?</label>
          <select value={company} onChange={e => setCompany(e.target.value)} style={{ width: "100%", maxWidth: 360, padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
            {companyOptions.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
          <div style={{ fontSize: 12.5, color: C.muted, marginTop: 8, lineHeight: 1.5 }}>Kies whitevision of een overgenomen bedrijf. De gemaakte assets voor {coName} verschijnen hieronder.</div>
        </Card>

        {tile.id === "social_visual" && (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Social post maken</span></div>
            <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 18, lineHeight: 1.55 }}>
              Kies een platform, dan weet de tool welke formaten nodig zijn. Je krijgt drie ontwerpen, kiest er één, bewerkt hem naar wens en downloadt de afbeeldingen. Het zijn echte PNG-bestanden die je rechtstreeks kunt uploaden.
            </div>

            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>1 · Platform</div>
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 14 }}>
              {SOCIAL_PLATFORMS.map(p2 => (
                <button key={p2.id} onClick={() => { setSocPlatform(p2.id); setSocPicked(null); setSocEdit(false); }} style={{
                  padding: "8px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, fontWeight: 700,
                  border: `1px solid ${socPlatform === p2.id ? "transparent" : C.line}`,
                  background: socPlatform === p2.id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                  color: socPlatform === p2.id ? "#fff" : C.muted,
                }}>{p2.label}</button>
              ))}
            </div>

            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>2 · Formaten ({activeSocFormats.length} gekozen)</div>
            <div style={{ display: "flex", gap: 7, flexWrap: "wrap", marginBottom: 16 }}>
              {platform.formats.map(f => {
                const key = `${f.w}x${f.h}`;
                const on = gekozenFormaten.includes(key);
                return (
                  <button key={key} onClick={() => toggleSocFormat(key)} style={{
                    padding: "7px 12px", borderRadius: 9, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 700,
                    border: `1px solid ${on ? "transparent" : C.line}`,
                    background: on ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                    color: on ? "#fff" : C.muted,
                  }}>{f.label} <span style={{ opacity: .75, fontWeight: 500 }}>{f.w}×{f.h}</span></button>
                );
              })}
            </div>

            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>3 · Waar gaat de post over</div>
            <div style={{ display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 10, marginBottom: 12 }}>
              <input value={socForm.goal} onChange={e => setSocForm(f => ({ ...f, goal: e.target.value }))}
                placeholder="bijv. 'aandacht voor onze gratis inkoopscan'"
                style={{ boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              <input value={socForm.cta} onChange={e => setSocForm(f => ({ ...f, cta: e.target.value }))}
                placeholder="wat moet men doen? bijv. scan aanvragen"
                style={{ boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 6 }}>
              <AssetPicker label="Logo" value={banLogoId} onPick={setBanLogoId} veld="logo" />
              <AssetPicker label="Achtergrondbeeld" value={banBgId} onPick={setBanBgId} veld="achtergrond" />
              <AssetPicker label="Foto (los element)" value={banImageId} onPick={setBanImageId} veld="foto" />
              <AssetPicker label="Patroon" value={banPatternId} onPick={setBanPatternId} veld="patroon" />
            </div>
            <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 14, lineHeight: 1.55 }}>
              Het <b>achtergrondbeeld</b> vult het hele vlak. De <b>foto</b> zet je als los element neer, bijvoorbeeld een portret naast de tekst; die kun je verplaatsen en schalen. Een <b>patroon</b> ligt als subtiele textuur over de achtergrond. Met het pictogram naast een lijst upload je een eigen bestand; dat komt niet in je merkbibliotheek maar wordt wel bewaard.
            </div>
            {uploadErr && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 10 }}>{uploadErr}</div>}

            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <Btn variant="yellow" icon={socBusy ? Loader2 : Sparkles} onClick={makeSocialConcepts} disabled={!!socBusy || !activeSocFormats.length}>
                {socBusy === "ideas" ? "Ontwerpen…" : socBusy === "preview" ? "Voorbeelden maken…" : socConcepts.length ? "Nieuwe ontwerpen" : "Maak 3 ontwerpen"}
              </Btn>
              <CostHint kind="light" />
            </div>
            {socErr && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{socErr}</div>}

            {socConcepts.length > 0 && (
              <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 12 }}>4 · Kies een ontwerp</div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))", gap: 16 }}>
                  {socConcepts.map((c, i) => (
                    <div key={i} onClick={() => setSocPicked(i)} style={{
                      border: `2px solid ${socPicked === i ? C.yellow : C.line}`, borderRadius: 12, padding: 12, cursor: "pointer",
                      background: socPicked === i ? C.yellow + "0A" : "transparent",
                    }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 9 }}>
                        <span style={{ width: 18, height: 18, borderRadius: 9, border: `2px solid ${socPicked === i ? C.yellow : C.line}`, background: socPicked === i ? C.yellow : "transparent", display: "grid", placeItems: "center", flexShrink: 0 }}>
                          {socPicked === i && <Check size={11} style={{ color: "#fff" }} />}
                        </span>
                        <span style={{ fontWeight: 700, fontSize: 14 }}>{c.name || `Ontwerp ${i + 1}`}</span>
                      </div>
                      {socPreviews[i]
                        ? <img src={`data:image/png;base64,${socPreviews[i]}`} alt="" style={{ width: "100%", borderRadius: 8, display: "block", border: `1px solid ${C.line}` }} />
                        : <div style={{ width: "100%", aspectRatio: "1", border: `1px dashed ${C.line}`, borderRadius: 8, display: "grid", placeItems: "center", color: C.muted, fontSize: 12.5 }}>voorbeeld maken…</div>}
                      {c.why && <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5, marginTop: 9 }}>{c.why}</div>}
                    </div>
                  ))}
                </div>
              </div>
            )}

            {socPicked !== null && (
              <div style={{ marginTop: 20, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, flexWrap: "wrap", marginBottom: socEdit ? 16 : 0 }}>
                  <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>5 · Bewerken en downloaden</div>
                  <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                    {savedMsg && <span style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 12.5, color: C.good, fontWeight: 700 }}><Check size={13} /> {savedMsg}</span>}
                    <Btn size="sm" variant="ghost" icon={Save} onClick={() => saveCreation("social", socConcepts[socPicked], {
                      colors: banColors, logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId,
                      platformId: socPlatform, platform: platform.label, formats: gekozenFormaten,
                    })}>Bewaren</Btn>
                    <Btn size="sm" variant={socEdit ? "ghost" : "yellow"} icon={socEdit ? X : PenLine} onClick={() => setSocEdit(o => !o)}>
                      {socEdit ? "Bewerken sluiten" : "Ontwerp bewerken"}
                    </Btn>
                    <Btn size="sm" variant="yellow" icon={socBusy === "build" ? Loader2 : Download} onClick={buildSocial} disabled={!!socBusy}>
                      {socBusy === "build" ? "Maken…" : `Download ${activeSocFormats.length} afbeeldingen`}
                    </Btn>
                  </div>
                </div>

                {socEdit && (
                  <SocialEditor
                    concept={socConcepts[socPicked]}
                    onChange={(next) => setSocConcepts(list => list.map((c, i) => i === socPicked ? next : c))}
                    colors={banColors} setColors={setBanColors} assets={banAssets}
                    logoId={banLogoId} setLogoId={setBanLogoId}
                    imageId={banImageId} setImageId={setBanImageId}
                    patternId={banPatternId} setPatternId={setBanPatternId}
                    bgId={banBgId} setBgId={setBanBgId}
                    brandColors={brandColors} formats={activeSocFormats} Picker={AssetPicker}
                  />
                )}

                {socConcepts[socPicked]?.caption && (
                  <div style={{ marginTop: 16, padding: "12px 14px", background: C.paper, borderRadius: 10 }}>
                    <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 6 }}>Begeleidende tekst bij de post</div>
                    <textarea value={socConcepts[socPicked].caption}
                      onChange={e => setSocConcepts(list => list.map((c, i) => i === socPicked ? { ...c, caption: e.target.value } : c))}
                      rows={3}
                      style={{ width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, resize: "vertical", outline: "none", lineHeight: 1.55, background: C.card }} />
                    <div style={{ fontSize: 11.5, color: C.muted, marginTop: 5 }}>Deze tekst wordt niet in de afbeelding gezet; je plakt hem bij de post.</div>
                  </div>
                )}

                {socDone && <div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 12.5, color: C.good, fontWeight: 600, marginTop: 12 }}><Check size={14} /> {socDone}</div>}
              </div>
            )}
          </Card>
        )}

        {tile.id === "banner" && (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Bannerset maken</span></div>
            <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 18, lineHeight: 1.55 }}>
              De AI maakt drie ontwerpen die van elkaar verschillen in vormgeving en beweging. Je bekijkt ze hieronder echt, kiest er één, en zet die door naar alle formaten die je nodig hebt.
            </div>

            {/* Stap 1: wat moet erin */}
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>1 · Wat moet de banner doen</div>
            <div style={{ display: "grid", gridTemplateColumns: "1.6fr 1fr 1.2fr", gap: 10, marginBottom: 14 }}>
              <div>
                <label style={{ display: "block", fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Doel van de banner</label>
                <input value={banForm.goal} onChange={e => setBanForm(f => ({ ...f, goal: e.target.value }))}
                  placeholder="bijv. 'demo-aanvragen bij finance managers'"
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              </div>
              <div>
                <label style={{ display: "block", fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Wat moet men doen?</label>
                <input value={banForm.cta} onChange={e => setBanForm(f => ({ ...f, cta: e.target.value }))}
                  placeholder="bijv. demo aanvragen"
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              </div>
              <div>
                <label style={{ display: "block", fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Klikbestemming</label>
                <input value={banForm.clickUrl} onChange={e => setBanForm(f => ({ ...f, clickUrl: e.target.value }))}
                  placeholder={profile?.website || "https://..."}
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none" }} />
              </div>
            </div>

            {/* Stap 2: middelen */}
            <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>2 · Welke middelen mag hij gebruiken</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10, marginBottom: 6 }}>
              <AssetPicker label="Logo" value={banLogoId} onPick={setBanLogoId} veld="logo" />
              <AssetPicker label="Achtergrondbeeld" value={banBgId} onPick={setBanBgId} veld="achtergrond" />
              <AssetPicker label="Patroon" value={banPatternId} onPick={setBanPatternId} veld="patroon" />
            </div>
            <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 12, lineHeight: 1.55 }}>
              Het achtergrondbeeld vult het hele vlak; kies daarvoor bij Achtergrond de optie "Foto". Met het pictogram naast een lijst upload je een eigen bestand voor deze banner; dat komt niet in je merkbibliotheek maar wordt wel bewaard.
            </div>
            {uploadErr && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 10 }}>{uploadErr}</div>}
            <div style={{ display: "flex", gap: 14, flexWrap: "wrap", marginBottom: 6 }}>
              {[["accent", "Accentkleur"], ["bg", "Achtergrond"], ["text", "Tekst"], ["ctaText", "Knoptekst"]].map(([k, lbl]) => (
                <label key={k} style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 12.5, color: C.ink2 }}>
                  <input type="color" value={banColors[k]} onChange={e => setBanColors(c => ({ ...c, [k]: e.target.value }))}
                    style={{ width: 30, height: 26, padding: 0, border: `1px solid ${C.line}`, borderRadius: 6, background: "none", cursor: "pointer" }} />
                  {lbl}
                </label>
              ))}
            </div>
            {banAssets.length === 0 && (
              <div style={{ fontSize: 12, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>
                Er staan nog geen assets in je bedrijfsprofiel. Upload je logo, beeld en patronen bij Bedrijfsprofiel → Brand assets; dan kun je ze hier kiezen.
              </div>
            )}

            <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 14, marginBottom: 4 }}>
              <Btn variant="yellow" icon={banBusy ? Loader2 : Sparkles} onClick={makeConcepts} disabled={!!banBusy}>
                {banBusy === "ideas" ? "Ontwerpen…" : banBusy === "preview" ? "Voorbeelden maken…" : banConcepts.length ? "Nieuwe ontwerpen" : "Maak 3 ontwerpen"}
              </Btn>
              <CostHint kind="light" />
            </div>
            {banErr && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{banErr}</div>}

            {/* Stap 3: kiezen */}
            {banConcepts.length > 0 && (
              <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 4 }}>3 · Kies een ontwerp</div>
                <div style={{ fontSize: 12, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Dit zijn echte banners, inclusief beweging. Herlaad de pagina om de animatie opnieuw te zien.</div>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: 16 }}>
                  {banConcepts.map((c, i) => (
                    <div key={i} onClick={() => setBanPicked(i)} style={{
                      border: `2px solid ${banPicked === i ? C.yellow : C.line}`, borderRadius: 12, padding: 12, cursor: "pointer",
                      background: banPicked === i ? C.yellow + "0A" : "transparent",
                    }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 9, flexWrap: "wrap" }}>
                        <span style={{ width: 18, height: 18, borderRadius: 9, border: `2px solid ${banPicked === i ? C.yellow : C.line}`, background: banPicked === i ? C.yellow : "transparent", display: "grid", placeItems: "center", flexShrink: 0 }}>
                          {banPicked === i && <Check size={11} style={{ color: "#fff" }} />}
                        </span>
                        <span style={{ fontWeight: 700, fontSize: 14 }}>{c.name || `Ontwerp ${i + 1}`}</span>
                      </div>
                      {banPreviews[i]
                        ? <iframe title={`ontwerp-${i}`} srcDoc={banPreviews[i]} sandbox="allow-scripts"
                            style={{ width: 300, height: 250, border: `1px solid ${C.line}`, borderRadius: 8, display: "block", background: "#000" }} />
                        : <div style={{ width: 300, height: 250, border: `1px dashed ${C.line}`, borderRadius: 8, display: "grid", placeItems: "center", color: C.muted, fontSize: 12.5 }}>voorbeeld maken…</div>}
                      {c.why && <div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5, marginTop: 9 }}>{c.why}</div>}
                      <div style={{ display: "flex", gap: 5, flexWrap: "wrap", marginTop: 8 }}>
                        {[c.design?.layout, c.design?.bg, c.design?.shape !== "none" && c.design?.shape,
                          c.design?.cta !== "none" && `knop: ${c.design?.cta}`].filter(Boolean).map((t, j) => (
                          <Tag key={j} color={C.muted}>{t}</Tag>
                        ))}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Stap 3b: bewerken */}
            {banPicked !== null && (
              <div style={{ marginTop: 20, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, flexWrap: "wrap", marginBottom: banEdit ? 16 : 0 }}>
                  <div>
                    <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase" }}>Bewerken</div>
                    <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, lineHeight: 1.5 }}>
                      Pas teksten, vormgeving, beweging en kleuren aan. In de stand "Vrij slepen" zet je elementen zelf op hun plek.
                    </div>
                  </div>
                  <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                    {savedMsg && <span style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 12.5, color: C.good, fontWeight: 700 }}><Check size={13} /> {savedMsg}</span>}
                    <Btn size="sm" variant="ghost" icon={Save} onClick={() => saveCreation("banner", banConcepts[banPicked], {
                      colors: banColors, logoId: banLogoId, imageId: banImageId, patternId: banPatternId, bgId: banBgId,
                      formats: banFormats, clickUrl: banForm.clickUrl,
                    })}>Bewaren</Btn>
                    <Btn size="sm" variant={banEdit ? "ghost" : "yellow"} icon={banEdit ? X : PenLine} onClick={() => setBanEdit(o => !o)}>
                      {banEdit ? "Bewerken sluiten" : "Ontwerp bewerken"}
                    </Btn>
                  </div>
                </div>

                {banEdit && (
                  <BannerEditor
                    concept={banConcepts[banPicked]}
                    onChange={(next) => setBanConcepts(list => list.map((c, i) => i === banPicked ? next : c))}
                    colors={banColors} setColors={setBanColors}
                    assets={banAssets}
                    logoId={banLogoId} setLogoId={setBanLogoId}
                    imageId={banImageId} setImageId={setBanImageId}
                    patternId={banPatternId} setPatternId={setBanPatternId}
                    bgId={banBgId} setBgId={setBanBgId}
                    clickUrl={banForm.clickUrl} profile={profile} brandColors={brandColors} Picker={AssetPicker}
                  />
                )}
              </div>
            )}

            {/* Stap 4: formaten en downloaden */}
            {banPicked !== null && (
              <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", marginBottom: 8 }}>4 · In welke formaten ({banFormats.length} gekozen)</div>
                <div style={{ display: "flex", gap: 7, flexWrap: "wrap", marginBottom: 14 }}>
                  {BANNER_FORMATS.map(f => {
                    const key = `${f.w}x${f.h}`;
                    const on = banFormats.includes(key);
                    return (
                      <button key={key} onClick={() => toggleFormat(key)} title={f.label} style={{
                        padding: "7px 12px", borderRadius: 9, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 700,
                        border: `1px solid ${on ? "transparent" : C.line}`,
                        background: on ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                        color: on ? "#fff" : C.muted,
                      }}>{key}</button>
                    );
                  })}
                </div>
                <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                  <Btn variant="yellow" icon={banBusy === "build" ? Loader2 : Download} onClick={buildBanners} disabled={!!banBusy || !banFormats.length}>
                    {banBusy === "build" ? "Banners bouwen…" : "Bouw en download"}
                  </Btn>
                  <span style={{ fontSize: 11.5, color: C.muted, fontWeight: 600 }}>bouwen kost geen tegoed</span>
                </div>
                {banDone && <div style={{ display: "flex", alignItems: "flex-start", gap: 7, fontSize: 12.5, color: C.good, fontWeight: 600, marginTop: 10, lineHeight: 1.5 }}><Check size={14} style={{ flexShrink: 0, marginTop: 2 }} /> {banDone}</div>}
              </div>
            )}

            <div style={{ fontSize: 11.5, color: C.muted, marginTop: 16, lineHeight: 1.55, paddingTop: 12, borderTop: `1px solid ${C.line}` }}>
              Bij Google Ads upload je per formaat de zip onder Advertenties → display-advertentie → HTML5. In de zip zit ook een map met losse HTML, zodat je de banners eerst in je browser kunt bekijken. Gebruik je een zware achtergrondfoto, controleer dan of elk formaat onder de 150 kB blijft.
            </div>
          </Card>
        )}

        {tile.id === "presentation" && (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Presentatie maken</span></div>

            <div style={{ display: "flex", gap: 8, marginBottom: 14 }}>
              {[["scratch", "Nieuw op basis van profiel"], ["convert", "Bestaande omzetten"]].map(([id, lbl]) => (
                <button key={id} onClick={() => { setPresMode(id); setGenErr(""); setOutline(null); setDone(""); }} style={{
                  padding: "8px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, fontWeight: 700,
                  border: `1px solid ${presMode === id ? "transparent" : C.line}`,
                  background: presMode === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                  color: presMode === id ? "#fff" : C.muted,
                }}>{lbl}</button>
              ))}
            </div>

            {/* Welke huisstijl-template moet als basis dienen */}
            <div style={{ marginBottom: 14 }}>
              <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Huisstijl-template</label>
              {tplList.length === 0
                ? <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>Nog geen PowerPoint-template geüpload. Upload er een bij Bedrijfsprofiel → Brand templates; dan wordt die als basis gebruikt en erft je presentatie de opmaak ervan.</div>
                : <>
                    <select value={templateId} onChange={e => setTemplateId(e.target.value)} style={{ width: "100%", maxWidth: 420, padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", background: C.card }}>
                      {tplList.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
                      <option value="">Geen template (basisopmaak)</option>
                    </select>
                    <div style={{ fontSize: 12, color: C.muted, marginTop: 6, lineHeight: 1.5 }}>De slides worden op deze template gebouwd, zodat ze de indelingen, lettertypen en kleuren ervan overnemen.</div>

                    {templateId && (
                      <div style={{ marginTop: 12, border: `1px solid ${C.line}`, borderRadius: 10, padding: "10px 12px" }}>
                        <button onClick={() => setShowLayouts(o => !o)} style={{ display: "flex", alignItems: "center", gap: 7, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, fontWeight: 700, color: C.ink }}>
                          <ChevronRight size={14} style={{ transform: showLayouts ? "rotate(90deg)" : "none", transition: "transform .15s" }} />
                          Welke indeling hoort bij welk soort slide?
                        </button>
                        {!showLayouts && <div style={{ fontSize: 12, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{Object.keys(layoutMap).length ? "Ingesteld — klik om aan te passen." : "Nog niet ingesteld; de tool kiest dan zelf, wat vaak niet klopt bij een eigen huisstijl."}</div>}
                        {showLayouts && (
                          <div style={{ marginTop: 10 }}>
                            {layoutErr && <div style={{ fontSize: 12.5, color: C.bad, marginBottom: 8 }}>{layoutErr}</div>}
                            {layouts === null
                              ? <div style={{ fontSize: 12.5, color: C.muted }}>Indelingen laden…</div>
                              : layouts.length === 0
                                ? <div style={{ fontSize: 12.5, color: C.muted }}>Geen indelingen gevonden in deze template.</div>
                                : <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                                    {SLIDE_TYPES.map(([type, lbl]) => (
                                      <div key={type} style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                                        <span style={{ fontSize: 12.5, fontWeight: 600, minWidth: 150, color: C.ink }}>{lbl}</span>
                                        <select value={layoutMap[type] === undefined ? "" : String(layoutMap[type])} onChange={e => setLayoutFor(type, e.target.value)}
                                          style={{ flex: 1, minWidth: 200, padding: "7px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card }}>
                                          <option value="">— tool kiest zelf —</option>
                                          {layouts.map(l => (
                                            <option key={l.index} value={l.index}>
                                              {l.index + 1}. {l.name}{l.pictures ? " (met beeld)" : ""}
                                            </option>
                                          ))}
                                        </select>
                                      </div>
                                    ))}
                                    <div style={{ fontSize: 11.5, color: C.muted, marginTop: 4, lineHeight: 1.5 }}>Deze koppeling wordt onthouden bij deze template. Weet je niet welke indeling wat is? Open je template in PowerPoint, bekijk Beeld → Diamodel, en vergelijk de namen.</div>
                                  </div>}
                          </div>
                        )}
                      </div>
                    )}
                  </>}
            </div>

            {presMode === "scratch" ? <>
              <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Geef je opdracht. Je krijgt eerst een voorstel voor de indeling; pas dat aan of keur het goed, en daarna wordt de presentatie gemaakt.</div>
              <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Opdracht</label>
              <textarea value={topic} onChange={e => setTopic(e.target.value)} rows={2} placeholder="bijv. 'Introductie voor nieuwe klanten in de UK' of 'Onze visie op agentic finance'"
                style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.5, marginBottom: 12 }} />
              <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                <Btn variant="yellow" icon={genBusy === "outline" ? Loader2 : Sparkles} onClick={proposeOutline} disabled={!!genBusy || !topic.trim()}>
                  {genBusy === "outline" ? "Indeling opstellen…" : outline ? "Nieuw voorstel" : "Stel indeling voor"}
                </Btn>
                <CostHint kind="desk" />
              </div>
            </> : <>
              <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Upload een bestaande presentatie (.pptx). De tekst wordt overgenomen en in je huisstijl-template geplaatst. De inhoud blijft ongewijzigd; alleen de vormgeving verandert. Beeld, grafieken en bijzondere opmaak uit het origineel komen niet mee.</div>
              <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", marginBottom: 4 }}>
                <input ref={convFileRef} type="file" accept=".pptx" onChange={e => { setConvFile(e.target.files?.[0] || null); setGenErr(""); setDone(""); }} style={{ fontSize: 13, fontFamily: FONT_BODY }} />
                <Btn variant="yellow" icon={genBusy ? Loader2 : RefreshCw} onClick={convertPptx} disabled={!!genBusy || !convFile}>
                  {genBusy === "extract" ? "Presentatie uitlezen…" : genBusy === "pptx" ? "Presentatie bouwen…" : "Omzetten naar huisstijl"}
                </Btn>
              </div>
            </>}

            {genErr && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{genErr}</div>}
            {done && !genErr && (
              <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: C.good, fontWeight: 700, marginTop: 10 }}><Check size={14} /> {done}</div>
            )}

            {/* Voorstel voor de indeling, ter goedkeuring */}
            {outline && presMode === "scratch" && (
              <div style={{ marginTop: 18, borderTop: `1px solid ${C.line}`, paddingTop: 16 }}>
                <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 4 }}>Voorstel voor de indeling</div>
                <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.5 }}>Pas titels aan of haal slides weg die je niet wilt. Ben je akkoord, dan maak ik de presentatie.</div>
                <input value={outline.title || ""} onChange={e => setOutline(o => ({ ...o, title: e.target.value }))} placeholder="Titel van de presentatie"
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 14, fontWeight: 700, outline: "none", marginBottom: 8, background: C.card }} />
                <input value={outline.subtitle || ""} onChange={e => setOutline(o => ({ ...o, subtitle: e.target.value }))} placeholder="Ondertitel"
                  style={{ width: "100%", boxSizing: "border-box", padding: "9px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13, outline: "none", marginBottom: 14, background: C.card }} />

                <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                  {outline.slides.map((sd, i) => (
                    <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: "10px 12px" }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                        <span style={{ fontSize: 12, fontWeight: 700, color: C.muted, minWidth: 20 }}>{i + 1}</span>
                        <input value={sd.title || ""} onChange={e => updateSlide(i, { title: e.target.value })}
                          style={{ flex: 1, minWidth: 120, padding: "6px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, outline: "none", background: C.card }} />
                        <select value={sd.type || "content"} onChange={e => updateSlide(i, { type: e.target.value })} title="Soort slide"
                          style={{ padding: "6px 8px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12, outline: "none", background: C.card, color: C.muted }}>
                          {SLIDE_TYPES.filter(([t]) => t !== "title").map(([t, l]) => <option key={t} value={t}>{l}</option>)}
                        </select>
                        <button onClick={() => removeSlide(i)} title="Slide weghalen" style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                      </div>
                      {(sd.bullets || []).length > 0 && (
                        <div style={{ marginTop: 6, paddingLeft: 30, fontSize: 12.5, color: C.muted, lineHeight: 1.55 }}>
                          {(sd.bullets || []).map((b, j) => <div key={j}>• {b}</div>)}
                        </div>
                      )}
                    </div>
                  ))}
                </div>

                <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 14 }}>
                  <Btn variant="yellow" icon={genBusy === "pptx" ? Loader2 : Check} onClick={buildFromOutline} disabled={!!genBusy || !outline.slides.length}>
                    {genBusy === "pptx" ? "Presentatie bouwen…" : "Akkoord, maak de presentatie"}
                  </Btn>
                  <Btn variant="ghost" size="sm" onClick={() => { setOutline(null); setDone(""); }}>Verwerpen</Btn>
                </div>
              </div>
            )}
          </Card>
        )}

        {/* Wat je eerder hebt gemaakt, om verder te bewerken of opnieuw te downloaden */}
        {(() => {
          const soort = tile.id === "banner" ? "banner" : tile.id === "social_visual" ? "social" : null;
          if (!soort) return null;
          const mijn = saved.filter(x => x.kind === soort);
          return <>
            <div style={sectionLabel}>Bewaarde {soort === "banner" ? "bannersets" : "posts"}</div>
            {mijn.length === 0
              ? <Card style={{ background: C.paper, border: "none" }}>
                  <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.55 }}>
                    Nog niets bewaard. Zodra je een ontwerp kiest en op "Bewaren" klikt, staat het hier en kun je er later mee verder.
                  </div>
                </Card>
              : <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
                  {mijn.map(item => (
                    <Card key={item.id} style={{ padding: "12px 14px", border: currentId === item.id ? `1.5px solid ${C.yellow}` : undefined }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
                        <div style={{ flex: 1, minWidth: 180 }}>
                          <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap" }}>
                            <span style={{ fontWeight: 700, fontSize: 14.5 }}>{item.name}</span>
                            {currentId === item.id && <Tag color={C.yellowDark}>nu geopend</Tag>}
                            {item.platform && <Tag color={C.muted}>{item.platform}</Tag>}
                          </div>
                          <div style={{ fontSize: 12, color: C.muted, marginTop: 3 }}>
                            Bijgewerkt {new Date(item.updatedAt || item.createdAt).toLocaleDateString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}
                            {item.concept?.headline ? ` · ${String(item.concept.headline).slice(0, 60)}` : ""}
                          </div>
                        </div>
                        <Btn size="sm" variant="ghost" icon={PenLine} onClick={() => loadCreation(item)}>Openen</Btn>
                        <button onClick={() => deleteCreation(item)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                      </div>
                    </Card>
                  ))}
                </div>}
          </>;
        })()}
      </div>
    );
  }

  return (
    <div>
      <PageHead title="Studio" sub="Visuele content in je huisstijl. Tekstuele content maak je onder Copy." />
      <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }}>
        {DESIGN_TILES.map(t => {
          // Hoeveel heb je van dit soort bewaard?
          const soort = t.id === "banner" ? "banner" : t.id === "social_visual" ? "social" : null;
          const cnt = soort ? saved.filter(x => x.kind === soort).length : 0;
          return (
          <Card key={t.id} hover onClick={() => { setTile(t); setCompany("whitevision"); }} style={{ cursor: "pointer" }}>
            <div style={{ width: 44, height: 44, borderRadius: 12, background: C.yellow + "22", display: "grid", placeItems: "center", marginBottom: 12 }}>
              <t.icon size={22} style={{ color: C.ink }} />
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <span style={{ fontWeight: 700, fontSize: 16.5 }}>{t.label}</span>
              {cnt > 0 && <Tag color={C.good}>{cnt} bewaard</Tag>}
            </div>
            <div style={{ fontSize: 13.5, color: C.muted, marginTop: 5, lineHeight: 1.5 }}>{t.desc}</div>
            <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 13, fontWeight: 700, color: C.ink, marginTop: 14 }}>Openen <ArrowUpRight size={15} /></div>
          </Card>
          );
        })}
      </div>
    </div>
  );
}

/* ============================== CAMPAGNES (CANVAS) ============================== */
const CAMPAIGN_PALETTE = ["#8B5CF6", "#60A5FA", "#4ADE80", "#F87171", "#9B6DD8", "#7C3AED", "#E5E7EB", "#9CA3AF"];
const BLOCK_TYPES = ["Product", "Paid", "Owned", "Earned", "Shared", "Inbound", "Outbound", "ATL", "BTL", "Organisch", "Events", "Overig"];
// Standaard activiteiten per type. Afvinkbaar: groen = doen we, leeg = kans (rood).
const ACTIVITY_CATALOG = {
  Product: ["Productlaunch-campagne", "Feature-aankondigingen", "Release notes / changelog", "Productdemo's", "Free trial / freemium", "In-app berichten"],
  Paid: ["Google Ads (search)", "LinkedIn Ads", "Meta Ads", "Microsoft Ads", "Retargeting", "ABM-campagnes voor grote prospects", "Paid sponsorships"],
  Owned: ["Website / landingspagina's", "Blog / kennisbank", "Nieuwsbrief", "Klantcases delen", "Whitepapers / e-books", "Webinars", "Eigen podcast"],
  Earned: ["PR / persberichten", "Analist-relaties (Gartner/Forrester)", "Reviews stimuleren (G2/Capterra)", "Gastartikelen / bijdragen", "Awards / nominaties", "Influencer/partner-vermeldingen"],
  Shared: ["Organische social posts", "Social community-beheer", "Employee advocacy", "Partner co-marketing", "User-generated content"],
  Inbound: ["SEO / contentmarketing", "Lead magnets", "Marketing automation / nurturing", "Demo-aanvragen", "Chat / conversational marketing"],
  Outbound: ["Cold outreach / SDR", "E-mailcampagnes", "Account-based outreach", "Direct mail", "Telemarketing"],
  ATL: ["Vakbladen / print", "Radio / podcast-advertenties", "Out-of-home", "Branche-brede campagnes"],
  BTL: ["Beurzen / events", "Direct mail", "Klantevents", "Account-based marketing", "Sales enablement"],
  Organisch: ["SEO", "Organische social", "Community / forum", "Mond-tot-mond / referrals", "Content/blog"],
  Events: ["Eigen events / klantendagen", "Beursdeelname", "Webinars", "Roadshows", "Spreekplekken / keynotes", "Netwerkborrels"],
  Overig: [],
};
// Assets/content en kanalen per blok. Elk item heeft een icoon (uit lucide).
const ASSET_CATALOG = [
  { id: "html5", label: "HTML5-banners", icon: "LayoutTemplate" },
  { id: "video", label: "Video", icon: "Video" },
  { id: "whitepaper", label: "Whitepaper / e-book", icon: "FileText" },
  { id: "case", label: "Klantcase", icon: "Award" },
  { id: "social", label: "Social posts", icon: "Share2" },
  { id: "blog", label: "Blog / artikel", icon: "FileText" },
  { id: "email", label: "E-mail / nieuwsbrief", icon: "Mail" },
  { id: "landing", label: "Landingspagina", icon: "Globe" },
  { id: "infographic", label: "Infographic", icon: "Image" },
  { id: "webinar", label: "Webinar", icon: "Presentation" },
  { id: "demo", label: "Demo / productvideo", icon: "PlayCircle" },
  { id: "ad", label: "Advertentie-creatives", icon: "Megaphone" },
];
const CHANNEL_CATALOG = [
  { id: "gsearch", label: "Google Search", icon: "Search" },
  { id: "gdisplay", label: "Google Display", icon: "Image" },
  { id: "msearch", label: "Microsoft Search", icon: "Search" },
  { id: "linkedin", label: "LinkedIn", icon: "Users" },
  { id: "meta", label: "Meta (FB/IG)", icon: "Share2" },
  { id: "youtube", label: "YouTube", icon: "Video" },
  { id: "email_ch", label: "E-mail", icon: "Mail" },
  { id: "organic", label: "Organisch / SEO", icon: "Globe" },
  { id: "events_ch", label: "Events / beurzen", icon: "CalendarDays" },
  { id: "pr", label: "PR / media", icon: "Radio" },
  { id: "direct", label: "Direct / outbound", icon: "Send" },
  { id: "partner", label: "Partnerkanaal", icon: "Briefcase" },
];
// Landen voor campagneblokken: emoji-vlag + naam. Eigen landen kunnen toegevoegd worden.
const COUNTRY_CATALOG = [
  { id: "nl", label: "Nederland", flag: "🇳🇱" },
  { id: "be", label: "België", flag: "🇧🇪" },
  { id: "lu", label: "Luxemburg", flag: "🇱🇺" },
  { id: "uk", label: "Verenigd Koninkrijk", flag: "🇬🇧" },
  { id: "de", label: "Duitsland", flag: "🇩🇪" },
  { id: "at", label: "Oostenrijk", flag: "🇦🇹" },
  { id: "ch", label: "Zwitserland", flag: "🇨🇭" },
  { id: "fr", label: "Frankrijk", flag: "🇫🇷" },
  { id: "es", label: "Spanje", flag: "🇪🇸" },
  { id: "it", label: "Italië", flag: "🇮🇹" },
  { id: "se", label: "Zweden", flag: "🇸🇪" },
  { id: "dk", label: "Denemarken", flag: "🇩🇰" },
  { id: "no", label: "Noorwegen", flag: "🇳🇴" },
  { id: "ie", label: "Ierland", flag: "🇮🇪" },
  { id: "pl", label: "Polen", flag: "🇵🇱" },
  { id: "us", label: "Verenigde Staten", flag: "🇺🇸" },
];
// Map icoonnaam -> component, voor de items hierboven.
const TAG_ICONS = {
  LayoutTemplate, Video, FileText, Award, Share2, Mail, Globe, Image, Presentation,
  PlayCircle, Megaphone, Search, Users, CalendarDays, Radio, Send, Briefcase, Tag: TagIcon,
};

function Campaigns({ ctx }) {
  const { campaigns, saveCampaigns, profile, addAction, confirm, appSettings } = ctx;
  const [openId, setOpenId] = useState(null);
  const [editName, setEditName] = useState(null);
  const [newBoard, setNewBoard] = useState(null); // { name, basis }

  const boards = campaigns || [];
  const createBoard = () => {
    if (!newBoard?.name) return;
    const b = { id: Date.now(), name: newBoard.name, basis: newBoard.basis || "vrij", date: new Date().toISOString().slice(0, 10), blocks: [], links: [] };
    saveCampaigns([b, ...boards]);
    setNewBoard(null); setOpenId(b.id);
  };
  const updateBoard = (id, patch) => saveCampaigns(boards.map(b => b.id === id ? { ...b, ...patch } : b));
  const deleteBoard = (id) => confirm("Dit campagne-canvas en alle blokken worden definitief verwijderd.", () => { saveCampaigns(boards.filter(b => b.id !== id)); setOpenId(null); });

  if (openId) {
    const board = boards.find(b => b.id === openId);
    if (!board) { setOpenId(null); return null; }
    return <CampaignBoard board={board} onChange={(patch) => updateBoard(board.id, patch)} onDelete={() => deleteBoard(board.id)} onBack={() => setOpenId(null)} profile={profile} addAction={addAction} confirm={confirm} channelLib={(appSettings && appSettings.channelLibrary) || []} />;
  }

  return (
    <div>
      <PageHead title="Campagnes" sub="Teken je campagnestructuur uit op een vrij canvas. Per product, markt of thema — jij bepaalt de indeling." />
      {newBoard ? (
        <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
          <div style={{ fontWeight: 700, marginBottom: 12 }}>Nieuw canvas</div>
          <Field label="Naam" value={newBoard.name} onChange={v => setNewBoard(n => ({ ...n, name: v }))} placeholder="Bijv. 'Document Logistix UK' of 'P2P-suite 2026'" />
          <div style={{ marginBottom: 14 }}>
            <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Indeling</label>
            <select value={newBoard.basis || "vrij"} onChange={e => setNewBoard(n => ({ ...n, basis: e.target.value }))} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
              <option value="vrij">Vrij (zelf bepalen)</option>
              <option value="product">Per product</option>
              <option value="markt">Per markt</option>
              <option value="thema">Per thema</option>
            </select>
            <div style={{ fontSize: 12, color: C.muted, marginTop: 6 }}>Dit is alleen een label dat helpt ordenen; het canvas blijft volledig vrij.</div>
          </div>
          <div style={{ display: "flex", gap: 10 }}>
            <Btn variant="yellow" icon={Plus} onClick={createBoard}>Canvas maken</Btn>
            <Btn variant="ghost" onClick={() => setNewBoard(null)}>Annuleren</Btn>
          </div>
        </Card>
      ) : <div style={{ marginBottom: 16 }}><Btn variant="yellow" icon={Plus} onClick={() => setNewBoard({ name: "", basis: "vrij" })}>Nieuw canvas</Btn></div>}

      {boards.length === 0 && !newBoard && <Empty icon={Presentation} title="Nog geen campagne-canvassen" sub="Maak een canvas en teken je campagnestructuur uit met blokken, kleuren en logo's." />}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 14 }}>
        {boards.map(b => (
          <Card key={b.id} hover={editName !== b.id} onClick={editName === b.id ? undefined : () => setOpenId(b.id)}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
              <Presentation size={17} style={{ color: C.yellowDark, flexShrink: 0 }} />
              {editName === b.id
                ? <input autoFocus value={b.name} onClick={e => e.stopPropagation()} onChange={e => updateBoard(b.id, { name: e.target.value })}
                    onBlur={() => setEditName(null)} onKeyDown={e => { if (e.key === "Enter") setEditName(null); }}
                    style={{ flex: 1, padding: "5px 8px", border: `1.5px solid ${C.yellow}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 15, fontWeight: 700, outline: "none" }} />
                : <span style={{ fontWeight: 700, fontSize: 15.5, flex: 1 }}>{b.name}</span>}
              <button onClick={e => { e.stopPropagation(); setEditName(editName === b.id ? null : b.id); }} title="Naam aanpassen" style={{ background: "none", border: "none", cursor: "pointer", color: C.muted, flexShrink: 0 }}><Edit3 size={15} /></button>
              <button onClick={e => { e.stopPropagation(); confirm(`Canvas "${b.name}" wordt verwijderd.`, () => saveCampaigns(boards.filter(x => x.id !== b.id))); }} title="Verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.bad, flexShrink: 0 }}><Trash2 size={15} /></button>
            </div>
            <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
              <Tag>{b.basis}</Tag>
              <span style={{ fontSize: 12.5, color: C.muted }}>{(b.blocks || []).length} blokken · {b.date}</span>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

function CampaignBoard({ board, onChange, onDelete, onBack, profile, addAction, confirm, channelLib }) {
  const blocks = (board && board.blocks) || [];
  const links = (board && board.links) || [];
  const [sel, setSel] = useState(null);
  const [selIds, setSelIds] = useState([]); // meervoudige selectie
  const [clip, setClip] = useState([]); // gekopieerde blokken
  const [linkFrom, setLinkFrom] = useState(null);
  const [aiBusy, setAiBusy] = useState(false);
  const [aiOut, setAiOut] = useState(null);
  const [full, setFull] = useState(false);
  const [newAct, setNewAct] = useState("");
  const [newAsset, setNewAsset] = useState("");
  const [newChannel, setNewChannel] = useState("");
  const [newCountry, setNewCountry] = useState("");
  const [editTitleId, setEditTitleId] = useState(null);
  const dragRef = useRef(null);
  const resizeRef = useRef(null);
  const fileRef = useRef(null);
  const canvasRef = useRef(null);

  const setBlocks = (next) => onChange({ blocks: next });
  const setLinks = (next) => onChange({ links: next });

  const addBlock = () => {
    const b = { id: Date.now(), title: "Nieuwe campagne", type: "Paid", color: C.yellow, x: 60 + (blocks.length % 5) * 30, y: 60 + (blocks.length % 5) * 30, w: 180, h: 96, notes: "", img: null };
    setBlocks([...blocks, b]); setSel(b.id);
  };
  const updateBlock = (id, patch) => setBlocks(blocks.map(b => b.id === id ? { ...b, ...patch } : b));
  const removeBlock = (id) => { setBlocks(blocks.filter(b => b.id !== id)); setLinks(links.filter(l => l.from !== id && l.to !== id)); if (sel === id) setSel(null); };

  // slepen
  const onPointerDown = (e, b) => {
    if (linkFrom) {
      if (linkFrom !== b.id) {
        const exists = links.some(l => (l.from === linkFrom && l.to === b.id) || (l.from === b.id && l.to === linkFrom));
        if (!exists) setLinks([...links, { from: linkFrom, to: b.id }]);
      }
      setLinkFrom(null);
      return;
    }
    const multi = e.shiftKey || e.metaKey || e.ctrlKey;
    if (multi) {
      setSelIds(ids => ids.includes(b.id) ? ids.filter(x => x !== b.id) : [...ids, b.id]);
      setSel(b.id);
      return;
    }
    // als dit blok deel is van een meervoudige selectie: hele groep verslepen
    const group = selIds.includes(b.id) && selIds.length > 1 ? selIds : [b.id];
    if (!selIds.includes(b.id)) setSelIds([]);
    setSel(b.id);
    const rect = canvasRef.current.getBoundingClientRect();
    const origins = {};
    group.forEach(id => { const bl = blocks.find(x => x.id === id); if (bl) origins[id] = { x: bl.x, y: bl.y }; });
    dragRef.current = { id: b.id, group, origins, startX: e.clientX, startY: e.clientY, rect };
    e.currentTarget.setPointerCapture(e.pointerId);
  };
  const onResizeDown = (e, b) => {
    e.stopPropagation();
    setSel(b.id);
    resizeRef.current = { id: b.id, startX: e.clientX, startY: e.clientY, w: b.w || 180, h: b.h || 96 };
    e.currentTarget.setPointerCapture(e.pointerId);
  };
  const onPointerMove = (e) => {
    if (resizeRef.current) {
      const w = Math.max(120, resizeRef.current.w + (e.clientX - resizeRef.current.startX));
      const h = Math.max(70, resizeRef.current.h + (e.clientY - resizeRef.current.startY));
      updateBlock(resizeRef.current.id, { w, h });
      return;
    }
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.startX;
    const dy = e.clientY - dragRef.current.startY;
    const { group, origins } = dragRef.current;
    setBlocks(blocks.map(b => group.includes(b.id) && origins[b.id]
      ? { ...b, x: Math.max(0, origins[b.id].x + dx), y: Math.max(0, origins[b.id].y + dy) }
      : b));
  };
  const onPointerUp = () => { dragRef.current = null; resizeRef.current = null; };

  const onImg = (e, id) => {
    const f = e.target.files?.[0]; if (!f) return;
    const reader = new FileReader();
    reader.onload = () => updateBlock(id, { img: reader.result });
    reader.readAsDataURL(f);
  };

  const activeSel = () => selIds.length ? selIds : (sel ? [sel] : []);
  const copySel = () => { const ids = activeSel(); setClip(blocks.filter(b => ids.includes(b.id)).map(b => ({ ...b }))); };
  const pasteClip = () => {
    if (!clip.length) return;
    const now = Date.now();
    const fresh = clip.map((b, i) => ({ ...b, id: now + i, x: (b.x || 0) + 24, y: (b.y || 0) + 24 }));
    setBlocks([...blocks, ...fresh]);
    setSelIds(fresh.map(b => b.id)); setSel(fresh[fresh.length - 1].id);
  };
  const selectAll = () => { setSelIds(blocks.map(b => b.id)); setSel(blocks[0]?.id || null); };
  const clearSel = () => { setSelIds([]); setSel(null); };
  const deleteSel = () => {
    const ids = activeSel(); if (!ids.length) return;
    confirm(`${ids.length} blok${ids.length > 1 ? "ken" : ""} worden verwijderd.`, () => {
      setBlocks(blocks.filter(b => !ids.includes(b.id)));
      setLinks(links.filter(l => !ids.includes(l.from) && !ids.includes(l.to)));
      clearSel();
    });
  };
  useEffect(() => {
    const onKey = (e) => {
      const tag = (e.target.tagName || "").toLowerCase();
      if (tag === "input" || tag === "textarea" || tag === "select") return;
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "c") { copySel(); }
      else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "v") { pasteClip(); }
      else if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "a") { e.preventDefault(); selectAll(); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  });

  const askAI = async () => {
    setAiBusy(true); setAiOut(null);
    const struct = blocks.map(b => {
      const acts = b.acts || {};
      const base = ACTIVITY_CATALOG[b.type] || [];
      const all = [...base, ...(b.customList || [])];
      const doing = all.filter(a => acts[a]);
      const gaps = all.filter(a => acts[a] === false || (acts[a] === undefined && base.includes(a)));
      let line = `- ${b.title} [${b.type}]${b.notes ? ": " + b.notes : ""}`;
      if (doing.length) line += `\n    Doen we al: ${doing.join(", ")}`;
      if (gaps.length) line += `\n    Nog niet (kansen): ${gaps.join(", ")}`;
      if (b.assets?.length) line += `\n    Assets/content: ${b.assets.map(a => a.label).join(", ")}`;
      if (b.channels?.length) line += `\n    Kanalen: ${b.channels.map(c => c.label).join(", ")}`;
      if (b.countries?.length) line += `\n    Landen: ${b.countries.map(c => c.label).join(", ")}`;
      return line;
    }).join("\n") || "(nog geen blokken)";
    const linkDesc = links.map(l => {
      const a = blocks.find(b => b.id === l.from)?.title, c = blocks.find(b => b.id === l.to)?.title;
      return a && c ? `- ${a} → ${c}` : null;
    }).filter(Boolean).join("\n");
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Dit is een campagnestructuur ("${board?.name||""}", indeling: ${board?.basis||""}) van de brand manager. Per blok is aangegeven wat al gedaan wordt en wat nog niet (kansen). Denk mee: focus je suggesties op de aangegeven kansen en op wat echt ontbreekt. Stel NIET iets voor dat al onder "Doen we al" staat. Geef concrete, bruikbare suggesties.\n\nHUIDIGE BLOKKEN:\n${struct}\n${linkDesc ? "\nVERBINDINGEN:\n" + linkDesc : ""}\n\nJSON: {"observations":["wat opvalt..."],"suggestions":[{"title":"voorgestelde campagne/aanpak","type":"Paid|Owned|Earned|Shared|Inbound|Outbound|Events|Organisch|Product","text":"waarom en hoe"}]}`;
    const { data: r } = await callAIFull(prompt, { json: true, useSearch: false });
    setAiOut(r || { observations: [], suggestions: [] });
    setAiBusy(false);
  };

  const selBlock = blocks.find(b => b.id === sel);

  return (
    <div>
      <button onClick={onBack} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar campagnes</button>
      <PageHead title={board?.name} sub={`Campagne-canvas · indeling: ${board?.basis || ""}`}
        right={<div style={{ display: "flex", gap: 8 }}>
          <Btn variant="yellow" size="sm" icon={Plus} onClick={addBlock}>Blok</Btn>
          <Btn variant={aiBusy ? "ghost" : "primary"} size="sm" icon={Sparkles} onClick={askAI} disabled={aiBusy}>{aiBusy ? "Bezig…" : "AI-suggesties"}</Btn>
          <Btn variant="ghost" size="sm" icon={Maximize2} onClick={() => setFull(true)}>Fullscreen</Btn>
          <Btn variant="danger" size="sm" icon={Trash2} onClick={onDelete}>Verwijderen</Btn>
        </div>} />

      <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 12, flexWrap: "wrap" }}>
        <span style={{ fontSize: 12.5, color: C.muted }}>
          {selIds.length > 1 ? `${selIds.length} blokken geselecteerd` : "Selecteer met Shift/Ctrl + klik, of sleep een groep samen"}
        </span>
        <div style={{ flex: 1 }} />
        <button onClick={selectAll} style={miniBtn(C.paper, C.ink)}>Alles</button>
        <button onClick={copySel} disabled={!activeSel().length} style={{ ...miniBtn(C.paper, C.ink), opacity: activeSel().length ? 1 : .5 }}><Copy size={13} /> Kopieer{activeSel().length > 1 ? ` (${activeSel().length})` : ""}</button>
        <button onClick={pasteClip} disabled={!clip.length} style={{ ...miniBtn(C.paper, C.ink), opacity: clip.length ? 1 : .5 }}><ClipboardPaste size={13} /> Plak{clip.length ? ` (${clip.length})` : ""}</button>
        <button onClick={deleteSel} disabled={!activeSel().length} style={{ ...miniBtn("transparent", C.bad), opacity: activeSel().length ? 1 : .5 }}><Trash2 size={13} /> Verwijder selectie</button>
        {selIds.length > 0 && <button onClick={clearSel} style={miniBtn(C.paper, C.muted)}>Deselecteer</button>}
      </div>

      {linkFrom && <div style={{ marginBottom: 10, fontSize: 13, color: C.yellowDark, fontWeight: 600 }}>Klik op een ander blok om te verbinden, of <button onClick={() => setLinkFrom(null)} style={{ background: "none", border: "none", color: C.blue, cursor: "pointer", textDecoration: "underline" }}>annuleer</button>.</div>}

      <div style={full
        ? { position: "fixed", inset: 0, zIndex: 1000, background: C.paper, padding: 16, display: "flex", flexDirection: "column", gap: 12 }
        : { display: "flex", gap: 16, alignItems: "flex-start" }}>
        {full && (
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
              <Btn variant="yellow" size="sm" icon={Plus} onClick={addBlock}>Blok</Btn>
              <Btn variant={aiBusy ? "ghost" : "primary"} size="sm" icon={Sparkles} onClick={askAI} disabled={aiBusy}>{aiBusy ? "Bezig…" : "AI-suggesties"}</Btn>
              <button onClick={selectAll} style={miniBtn(C.card, C.ink)}>Alles</button>
              <button onClick={copySel} disabled={!activeSel().length} style={{ ...miniBtn(C.card, C.ink), opacity: activeSel().length ? 1 : .5 }}><Copy size={13} /> Kopieer{activeSel().length > 1 ? ` (${activeSel().length})` : ""}</button>
              <button onClick={pasteClip} disabled={!clip.length} style={{ ...miniBtn(C.card, C.ink), opacity: clip.length ? 1 : .5 }}><ClipboardPaste size={13} /> Plak{clip.length ? ` (${clip.length})` : ""}</button>
              <button onClick={deleteSel} disabled={!activeSel().length} style={{ ...miniBtn(C.card, C.bad), opacity: activeSel().length ? 1 : .5 }}><Trash2 size={13} /> Verwijder selectie</button>
            </div>
            <Btn variant="ghost" size="sm" icon={Minimize2} onClick={() => setFull(false)}>Sluit fullscreen</Btn>
          </div>
        )}
        <div style={full ? { display: "flex", gap: 16, alignItems: "flex-start", flex: 1, minHeight: 0 } : { display: "contents" }}>
        {/* canvas */}
        <div ref={canvasRef} onPointerMove={onPointerMove} onPointerUp={onPointerUp}
          style={{ position: "relative", flex: 1, height: full ? "100%" : 560, alignSelf: "stretch", background: C.card, border: `1px solid ${C.line}`, borderRadius: 14, overflow: "auto", backgroundImage: `radial-gradient(${C.line} 1px, transparent 1px)`, backgroundSize: "22px 22px" }}>
          {/* lijnen */}
          <svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
            {links.map((l, i) => {
              const a = blocks.find(b => b.id === l.from), c = blocks.find(b => b.id === l.to);
              if (!a || !c) return null;
              return <line key={i} x1={a.x + (a.w || 180) / 2} y1={a.y + 28} x2={c.x + (c.w || 180) / 2} y2={c.y + 28} stroke={C.muted} strokeWidth="2" />;
            })}
          </svg>
          {blocks.length === 0 && <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", color: C.muted, fontSize: 14 }}>Klik op "Blok" om je eerste campagne toe te voegen.</div>}
          {blocks.map(b => (
            <div key={b.id} onPointerDown={(e) => onPointerDown(e, b)}
              style={{ position: "absolute", left: b.x, top: b.y, width: b.w || 180, height: b.h || 96, background: C.card, border: `2px solid ${b.color}`, borderRadius: 12, boxShadow: selIds.includes(b.id) ? `0 0 0 3px ${C.blue}` : sel === b.id ? `0 0 0 3px ${b.color}44` : "0 1px 4px rgba(0,0,0,.08)", cursor: "grab", touchAction: "none", userSelect: "none", overflow: "hidden", display: "flex", flexDirection: "column" }}>
              <div style={{ height: 6, background: b.color, borderRadius: "10px 10px 0 0", flexShrink: 0 }} />
              <div style={{ padding: "8px 10px", overflow: "auto", flex: 1 }}>
                {b.img && <img src={b.img} alt="" style={{ maxHeight: 40, maxWidth: "100%", marginBottom: 6, objectFit: "contain" }} />}
                <div style={{ fontWeight: 700, fontSize: 13.5, lineHeight: 1.3, display: "flex", alignItems: "center", gap: 5, flexWrap: "wrap" }}>
                  {editTitleId === b.id
                    ? <input autoFocus value={b.title}
                        onPointerDown={e => e.stopPropagation()}
                        onChange={e => updateBlock(b.id, { title: e.target.value })}
                        onBlur={() => setEditTitleId(null)}
                        onKeyDown={e => { if (e.key === "Enter") setEditTitleId(null); }}
                        style={{ flex: 1, minWidth: 80, padding: "2px 6px", border: `1.5px solid ${C.yellow}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, outline: "none" }} />
                    : <span onDoubleClick={e => { e.stopPropagation(); setEditTitleId(b.id); }} title="Dubbelklik om de naam aan te passen" style={{ cursor: "text" }}>{b.title}</span>}
                  {(b.countries || []).map((c, i) => <span key={i} title={c.label} style={{ fontSize: 14, cursor: "default" }}>{c.flag}</span>)}
                </div>
                <div style={{ marginTop: 4 }}><Tag color={b.color}>{b.type}</Tag></div>
                {((b.assets && b.assets.length) || (b.channels && b.channels.length)) ? (
                  <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginTop: 6 }}>
                    {(b.assets || []).map((x, i) => { const Ico = TAG_ICONS[x.icon] || TagIcon; return (
                      <span key={"a" + i} title={x.note ? `${x.label} — ${x.note}` : x.label} style={{ display: "inline-flex", width: 22, height: 22, borderRadius: 6, background: C.paper, border: `1px solid ${C.line}`, alignItems: "center", justifyContent: "center", color: C.ink2 }}><Ico size={12} /></span>
                    ); })}
                    {(b.channels || []).map((x, i) => { const Ico = TAG_ICONS[x.icon] || TagIcon; return (
                      <span key={"c" + i} title={x.note ? `${x.label} — ${x.note}` : x.label} style={{ display: "inline-flex", width: 22, height: 22, borderRadius: 6, background: x.img ? "#fff" : C.yellow + "1E", border: `1px solid ${x.img ? C.line : C.yellow + "55"}`, alignItems: "center", justifyContent: "center", color: C.yellowDark, overflow: "hidden" }}>{x.img ? <img src={x.img} alt="" style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} /> : <Ico size={12} />}</span>
                    ); })}
                  </div>
                ) : null}
                {b.notes && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 5, lineHeight: 1.4 }}>{b.notes}</div>}
                {(() => {
                  const acts = b.acts || {};
                  const base = ACTIVITY_CATALOG[b.type] || [];
                  const all = [...base, ...(b.customList || [])];
                  const doing = all.filter(a => acts[a]).length;
                  const gaps = all.filter(a => acts[a] === false || (acts[a] === undefined && base.includes(a))).length;
                  if (!doing && !gaps) return null;
                  return <div style={{ display: "flex", gap: 8, marginTop: 6, fontSize: 11, fontWeight: 700 }}>
                    {doing > 0 && <span style={{ color: C.good, display: "inline-flex", alignItems: "center", gap: 3 }}><Check size={11} /> {doing}</span>}
                    {gaps > 0 && <span style={{ color: C.bad, display: "inline-flex", alignItems: "center", gap: 3 }}><X size={11} /> {gaps}</span>}
                  </div>;
                })()}
              </div>
              {/* resize-hoek */}
              <div onPointerDown={(e) => onResizeDown(e, b)} title="Sleep om grootte aan te passen"
                style={{ position: "absolute", right: 0, bottom: 0, width: 16, height: 16, cursor: "nwse-resize", touchAction: "none",
                  background: `linear-gradient(135deg, transparent 50%, ${b.color} 50%)`, borderRadius: "0 0 10px 0" }} />
            </div>
          ))}
        </div>

        {/* zijpaneel */}
        <div style={{ width: 270, flexShrink: 0, ...(full ? { overflowY: "auto", maxHeight: "100%" } : {}) }}>
          {selBlock ? (
            <Card>
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }}>
                <span style={{ fontWeight: 700, fontSize: 14 }}>Blok bewerken</span>
                <button onClick={() => removeBlock(selBlock.id)} style={{ background: "none", border: "none", color: C.bad, cursor: "pointer" }}><Trash2 size={15} /></button>
              </div>
              <input value={selBlock.title} onChange={e => updateBlock(selBlock.id, { title: e.target.value })} placeholder="Titel"
                style={{ width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, marginBottom: 8, outline: "none" }} />
              <select value={selBlock.type} onChange={e => updateBlock(selBlock.id, { type: e.target.value })} style={{ width: "100%", padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, marginBottom: 8, background: C.card, outline: "none" }}>
                {BLOCK_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
              </select>
              <textarea value={selBlock.notes} onChange={e => updateBlock(selBlock.id, { notes: e.target.value })} rows={3} placeholder="Notities (kanaal, budget, timing…)"
                style={{ width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, marginBottom: 10, resize: "vertical", outline: "none" }} />
              <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Kleur</div>
              <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 10 }}>
                {CAMPAIGN_PALETTE.map(col => (
                  <button key={col} onClick={() => updateBlock(selBlock.id, { color: col })} style={{ width: 24, height: 24, borderRadius: 6, background: col, border: selBlock.color === col ? `2px solid ${C.ink}` : `1px solid ${C.line}`, cursor: "pointer" }} />
                ))}
                <input type="color" value={selBlock.color} onChange={e => updateBlock(selBlock.id, { color: e.target.value })} style={{ width: 24, height: 24, padding: 0, border: "none", cursor: "pointer", background: "none" }} title="Eigen kleur" />
              </div>
              <div style={{ display: "flex", gap: 8 }}>
                <button onClick={() => fileRef.current?.click()} style={{ ...miniBtn(C.paper, C.ink), flex: 1, justifyContent: "center" }}><Upload size={13} /> {selBlock.img ? "Logo wijzigen" : "Logo"}</button>
                {selBlock.img && <button onClick={() => updateBlock(selBlock.id, { img: null })} style={miniBtn("transparent", C.bad)}>Verwijder</button>}
                <input ref={fileRef} type="file" accept="image/*" onChange={e => onImg(e, selBlock.id)} style={{ display: "none" }} />
              </div>
              <button onClick={() => setLinkFrom(selBlock.id)} style={{ ...miniBtn(C.paper, C.ink), width: "100%", justifyContent: "center", marginTop: 8 }}>Verbind met ander blok</button>

              {/* Activiteiten: groen = doen we, rood = kans */}
              <div style={{ marginTop: 14, borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
                <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 4, textTransform: "uppercase" }}>Activiteiten</div>
                <div style={{ fontSize: 11.5, color: C.muted, marginBottom: 8, lineHeight: 1.4 }}>Groen = dit doen we al. Rood = nog niet (een kans). De AI gebruikt dit.</div>
                {(() => {
                  const acts = selBlock.acts || {};
                  const custom = selBlock.customList || [];
                  const base = ACTIVITY_CATALOG[selBlock.type] || [];
                  const all = [...base, ...custom.filter(c => !base.includes(c))];
                  const toggle = (name) => updateBlock(selBlock.id, { acts: { ...acts, [name]: !acts[name] } });
                  const addCustom = () => { const n = newAct.trim(); if (!n) return; updateBlock(selBlock.id, { customList: [...custom, n], acts: { ...acts, [n]: true } }); setNewAct(""); };
                  const removeCustom = (name) => { const a = { ...acts }; delete a[name]; updateBlock(selBlock.id, { customList: custom.filter(c => c !== name), acts: a }); };
                  return <>
                    {all.length === 0 && <div style={{ fontSize: 12, color: C.muted, marginBottom: 6 }}>Geen standaard activiteiten voor dit type. Voeg er zelf toe.</div>}
                    {all.map((name, i) => {
                      const on = !!acts[name];
                      const isCustom = custom.includes(name) && !base.includes(name);
                      return (
                        <div key={i} style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 5 }}>
                          <button onClick={() => toggle(name)} title={on ? "Doen we" : "Kans"} style={{ width: 20, height: 20, borderRadius: 6, border: "none", cursor: "pointer", flexShrink: 0, display: "grid", placeItems: "center", background: on ? C.good : C.bad, color: "#fff" }}>{on ? <Check size={13} /> : <X size={12} />}</button>
                          <span style={{ fontSize: 12.5, color: C.ink2, flex: 1, lineHeight: 1.3 }}>{name}</span>
                          {isCustom && <button onClick={() => removeCustom(name)} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted, padding: 0 }}><Trash2 size={12} /></button>}
                        </div>
                      );
                    })}
                    <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
                      <input value={newAct} onChange={e => setNewAct(e.target.value)} onKeyDown={e => { if (e.key === "Enter") addCustom(); }} placeholder="Eigen activiteit…"
                        style={{ flex: 1, padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none" }} />
                      <button onClick={addCustom} style={miniBtn(C.yellow, C.ink)}><Plus size={13} /></button>
                    </div>
                  </>;
                })()}
              </div>

              {/* Assets/content en kanalen */}
              {(() => {
                const renderPicker = (titleLabel, fieldKey, catalog, newVal, setNewVal, allowLogo) => {
                  const chosen = selBlock[fieldKey] || [];
                  const has = (id) => chosen.some(x => x.id === id);
                  const toggle = (item) => {
                    if (has(item.id)) updateBlock(selBlock.id, { [fieldKey]: chosen.filter(x => x.id !== item.id) });
                    else updateBlock(selBlock.id, { [fieldKey]: [...chosen, { id: item.id, label: item.label, icon: item.icon, note: "", img: item.img || null }] });
                  };
                  const setNote = (id, note) => updateBlock(selBlock.id, { [fieldKey]: chosen.map(x => x.id === id ? { ...x, note } : x) });
                  const setLogo = (id, img) => updateBlock(selBlock.id, { [fieldKey]: chosen.map(x => x.id === id ? { ...x, img } : x) });
                  const onLogo = (id, e) => { const f = e.target.files?.[0]; if (!f) return; const r = new FileReader(); r.onload = () => setLogo(id, r.result); r.readAsDataURL(f); e.target.value = ""; };
                  const addCustom = () => { const n = newVal.trim(); if (!n) return; const id = "c_" + Date.now(); updateBlock(selBlock.id, { [fieldKey]: [...chosen, { id, label: n, icon: "Tag", note: "" }] }); setNewVal(""); };
                  return (
                    <div style={{ marginTop: 14, borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
                      <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>{titleLabel}</div>
                      <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginBottom: 8 }}>
                        {catalog.map(item => {
                          const Ico = TAG_ICONS[item.icon] || TagIcon;
                          const on = has(item.id);
                          return (
                            <button key={item.id} onClick={() => toggle(item)} title={item.label}
                              style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11.5, fontWeight: 600, padding: "4px 8px", borderRadius: 7, cursor: "pointer",
                                border: `1.5px solid ${on ? C.yellow : C.line}`, background: on ? C.yellow + "22" : C.card, color: C.ink }}>
                              {item.img ? <img src={item.img} alt="" style={{ width: 13, height: 13, objectFit: "contain" }} /> : <Ico size={12} />} {item.label}
                            </button>
                          );
                        })}
                      </div>
                      {chosen.filter(x => !catalog.some(c => c.id === x.id)).map(x => (
                        <div key={x.id} style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11.5, fontWeight: 600, padding: "4px 8px", borderRadius: 7, border: `1.5px solid ${C.yellow}`, background: C.yellow + "22", marginRight: 5, marginBottom: 5 }}>
                          <TagIcon size={12} /> {x.label}
                          <button onClick={() => updateBlock(selBlock.id, { [fieldKey]: chosen.filter(y => y.id !== x.id) })} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted, padding: 0, marginLeft: 2 }}><X size={11} /></button>
                        </div>
                      ))}
                      {chosen.length > 0 && (
                        <div style={{ marginTop: 4 }}>
                          {chosen.map(x => {
                            const Ico = TAG_ICONS[x.icon] || TagIcon;
                            return (
                              <div key={x.id} style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }}>
                                {allowLogo && (
                                  <label title="Eigen logo uploaden" style={{ width: 26, height: 26, flexShrink: 0, border: `1px solid ${C.line}`, borderRadius: 6, display: "grid", placeItems: "center", cursor: "pointer", background: C.card, overflow: "hidden" }}>
                                    {x.img ? <img src={x.img} alt="" style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} /> : <Ico size={13} style={{ color: C.muted }} />}
                                    <input type="file" accept="image/*" onChange={e => onLogo(x.id, e)} style={{ display: "none" }} />
                                  </label>
                                )}
                                <span style={{ fontSize: 11.5, color: C.muted, minWidth: 84, flexShrink: 0 }}>{x.label}</span>
                                <input value={x.note || ""} onChange={e => setNote(x.id, e.target.value)} placeholder="toelichting (hover-tekst)…"
                                  style={{ flex: 1, padding: "5px 8px", border: `1px solid ${C.line}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 11.5, outline: "none" }} />
                                {allowLogo && x.img && <button onClick={() => setLogo(x.id, null)} title="Logo verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.muted, padding: 0 }}><X size={12} /></button>}
                              </div>
                            );
                          })}
                        </div>
                      )}
                      <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
                        <input value={newVal} onChange={e => setNewVal(e.target.value)} onKeyDown={e => { if (e.key === "Enter") addCustom(); }} placeholder={`Eigen ${titleLabel.toLowerCase()} toevoegen…`}
                          style={{ flex: 1, padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none" }} />
                        <button onClick={addCustom} style={miniBtn(C.yellow, C.ink)}><Plus size={13} /></button>
                      </div>
                    </div>
                  );
                };
                return <>
                  {renderPicker("Assets / content", "assets", ASSET_CATALOG, newAsset, setNewAsset, false)}
                  {renderPicker("Kanalen", "channels", [...CHANNEL_CATALOG, ...((channelLib || []).filter(c => c.label).map(c => ({ id: "lib_" + c.id, label: c.label, icon: "Tag", img: c.img })))], newChannel, setNewChannel, true)}
                </>;
              })()}

              {/* Landen */}
              {(() => {
                const chosen = selBlock.countries || [];
                const has = (id) => chosen.some(x => x.id === id);
                const toggle = (c) => has(c.id)
                  ? updateBlock(selBlock.id, { countries: chosen.filter(x => x.id !== c.id) })
                  : updateBlock(selBlock.id, { countries: [...chosen, { id: c.id, label: c.label, flag: c.flag }] });
                const addCustom = () => {
                  const n = newCountry.trim(); if (!n) return;
                  const code = n.replace(/[^A-Za-z]/g, "").slice(0, 2).toUpperCase();
                  const flag = code.length === 2 ? String.fromCodePoint(...[...code].map(ch => 0x1F1E6 + ch.charCodeAt(0) - 65)) : "🏳️";
                  updateBlock(selBlock.id, { countries: [...chosen, { id: "c_" + Date.now(), label: n, flag }] });
                  setNewCountry("");
                };
                return (
                  <div style={{ marginTop: 14, borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
                    <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>Landen</div>
                    <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginBottom: 8 }}>
                      {COUNTRY_CATALOG.map(c => {
                        const on = has(c.id);
                        return (
                          <button key={c.id} onClick={() => toggle(c)} title={c.label}
                            style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11.5, fontWeight: 600, padding: "4px 8px", borderRadius: 7, cursor: "pointer",
                              border: `1.5px solid ${on ? C.yellow : C.line}`, background: on ? C.yellow + "22" : C.card, color: C.ink }}>
                            <span style={{ fontSize: 13 }}>{c.flag}</span> {c.id.toUpperCase()}
                          </button>
                        );
                      })}
                    </div>
                    {chosen.filter(x => !COUNTRY_CATALOG.some(c => c.id === x.id)).map(x => (
                      <span key={x.id} style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11.5, fontWeight: 600, padding: "4px 8px", borderRadius: 7, border: `1.5px solid ${C.yellow}`, background: C.yellow + "22", marginRight: 5, marginBottom: 5 }}>
                        <span style={{ fontSize: 13 }}>{x.flag}</span> {x.label}
                        <button onClick={() => updateBlock(selBlock.id, { countries: chosen.filter(y => y.id !== x.id) })} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted, padding: 0, marginLeft: 2 }}><X size={11} /></button>
                      </span>
                    ))}
                    <div style={{ display: "flex", gap: 6, marginTop: 8 }}>
                      <input value={newCountry} onChange={e => setNewCountry(e.target.value)} onKeyDown={e => { if (e.key === "Enter") addCustom(); }} placeholder="Eigen land toevoegen…"
                        style={{ flex: 1, padding: "6px 9px", border: `1.5px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none" }} />
                      <button onClick={addCustom} style={miniBtn(C.yellow, C.ink)}><Plus size={13} /></button>
                    </div>
                  </div>
                );
              })()}
            </Card>
          ) : (
            <Card><div style={{ fontSize: 13, color: C.muted, lineHeight: 1.5 }}>Klik op een blok om het te bewerken: titel, type, kleur, logo en verbindingen. Sleep blokken om ze te verplaatsen.</div></Card>
          )}
        </div>
        </div>
      </div>

      {aiBusy && <div style={{ marginTop: 16 }}><Spinner label="AI bekijkt je campagnestructuur en denkt mee…" /></div>}
      {aiOut && <Card style={{ marginTop: 16 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}><AIBadge /><span style={{ fontWeight: 700 }}>AI-suggesties</span></div>
        {aiOut.observations?.length > 0 && <div style={{ marginBottom: 12 }}>{aiOut.observations.map((o, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, marginBottom: 4, display: "flex", gap: 7 }}><Eye size={14} style={{ color: C.blue, flexShrink: 0, marginTop: 2 }} /> {o}</div>)}</div>}
        {(aiOut.suggestions || []).map((s, i) => (
          <div key={i} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: 12, marginBottom: 8 }}>
            <div style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start" }}>
              <div><div style={{ fontWeight: 700, fontSize: 14 }}>{s.title} {s.type && <Tag color={C.yellowDark}>{s.type}</Tag>}</div><div style={{ fontSize: 13, color: C.ink2, marginTop: 3, lineHeight: 1.5 }}>{s.text}</div></div>
              <button onClick={() => { const b = { id: Date.now() + i, title: s.title, type: BLOCK_TYPES.includes(s.type) ? s.type : "Overig", color: C.yellow, x: 80 + (i % 4) * 40, y: 80 + (i % 4) * 40, w: 180, notes: s.text?.slice(0, 120) || "", img: null }; setBlocks([...blocks, b]); }}
                style={{ ...miniBtn(C.yellow, C.ink), flexShrink: 0 }}><Plus size={13} /> Op canvas</button>
            </div>
          </div>
        ))}
        {(!aiOut.suggestions || aiOut.suggestions.length === 0) && <div style={{ fontSize: 13, color: C.muted }}>Geen aanvullende suggesties.</div>}
      </Card>}
    </div>
  );
}

/* ============================== KADERS & PLANNEN ============================== */
// Modellenbibliotheek: elk model vult het canvas met vooraf geplaatste blokken.
const STRATEGY_MODELS = [
  { id: "blank", name: "Blanco canvas", desc: "Begin met een leeg vlak en teken je eigen proces of model.", blocks: [] },
  { id: "swot", name: "SWOT-analyse", desc: "Sterktes, zwaktes, kansen, bedreigingen.", blocks: [
    { title: "Sterktes", color: "#4ADE80", x: 60, y: 60 }, { title: "Zwaktes", color: "#F87171", x: 320, y: 60 },
    { title: "Kansen", color: "#60A5FA", x: 60, y: 240 }, { title: "Bedreigingen", color: "#7C3AED", x: 320, y: 240 },
  ] },
  { id: "funnel", name: "Marketingfunnel", desc: "Awareness tot loyaliteit.", blocks: [
    { title: "Awareness", color: "#8B5CF6", x: 120, y: 40 }, { title: "Consideration", color: "#7C3AED", x: 120, y: 150 },
    { title: "Conversion", color: "#4ADE80", x: 120, y: 260 }, { title: "Retention / loyaliteit", color: "#60A5FA", x: 120, y: 370 },
  ] },
  { id: "vpc", name: "Waardepropositie", desc: "Klanttaken, pijnpunten en je aanbod.", blocks: [
    { title: "Klanttaken (jobs)", color: "#60A5FA", x: 60, y: 60 }, { title: "Pijnpunten", color: "#F87171", x: 60, y: 230 },
    { title: "Gewenste resultaten", color: "#4ADE80", x: 60, y: 400 }, { title: "Producten & diensten", color: "#8B5CF6", x: 360, y: 60 },
    { title: "Pijnverzachters", color: "#9B6DD8", x: 360, y: 230 }, { title: "Voordeelversterkers", color: "#4ADE80", x: 360, y: 400 },
  ] },
  { id: "7s", name: "7S-model", desc: "Strategy, structure, systems, en meer.", blocks: [
    { title: "Strategy", color: "#8B5CF6", x: 220, y: 40 }, { title: "Structure", color: "#60A5FA", x: 60, y: 150 },
    { title: "Systems", color: "#60A5FA", x: 380, y: 150 }, { title: "Shared values", color: "#E5E7EB", x: 220, y: 200 },
    { title: "Skills", color: "#4ADE80", x: 60, y: 320 }, { title: "Style", color: "#9B6DD8", x: 220, y: 360 }, { title: "Staff", color: "#7C3AED", x: 380, y: 320 },
  ] },
  { id: "ansoff", name: "Ansoff-groeimatrix", desc: "Markt- en productgroei.", blocks: [
    { title: "Marktpenetratie", color: "#4ADE80", x: 60, y: 60 }, { title: "Productontwikkeling", color: "#60A5FA", x: 340, y: 60 },
    { title: "Marktontwikkeling", color: "#7C3AED", x: 60, y: 240 }, { title: "Diversificatie", color: "#F87171", x: 340, y: 240 },
  ] },
];

const PLAN_SECTIONS = [
  { id: "summary", label: "Managementsamenvatting", hint: "Kernpunten van het plan in het kort.", default: true },
  { id: "situation", label: "Situatieanalyse", hint: "Markt, trends, interne en externe context.", default: true },
  { id: "swot", label: "SWOT-analyse", hint: "Sterktes, zwaktes, kansen, bedreigingen.", default: true },
  { id: "audience", label: "Doelgroepen & ICP's", hint: "Voor wie, met welke behoeften.", default: true },
  { id: "positioning", label: "Positionering", hint: "Hoe whitevision zich onderscheidt.", default: true },
  { id: "goals", label: "Doelstellingen & KPI's", hint: "Meetbare doelen.", default: true },
  { id: "strategy", label: "Strategie", hint: "De kernkeuzes om de doelen te bereiken.", default: true },
  { id: "messaging", label: "Kernboodschappen", hint: "Wat we consistent willen uitdragen.", default: false },
  { id: "channels", label: "Kanalen & middelen", hint: "Via welke kanalen en assets.", default: false },
  { id: "roadmap", label: "Roadmap & planning", hint: "Fasering en tijdlijn.", default: false },
  { id: "budget", label: "Budget & resources", hint: "Indicatie van middelen en mensen.", default: false },
  { id: "risks", label: "Risico's & aannames", hint: "Wat kan misgaan en waar gaan we van uit.", default: false },
];

function Frameworks({ ctx, initialView }) {
  const { frameworks, saveFrameworks, integrations, saveIntegrations, companies, profile, saveProfile, addAction, confirm, plans, savePlans, strategyBoards, saveStrategyBoards } = ctx;
  const [view, setView] = useState(initialView || "plan");
  const [openFw, setOpenFw] = useState(null);
  const [openInt, setOpenInt] = useState(null);
  const [openPlan, setOpenPlan] = useState(null);
  const [busy, setBusy] = useState(false);
  const [form, setForm] = useState(null);
  const [intForm, setIntForm] = useState(null);
  const [planForm, setPlanForm] = useState(null); // { title, market, sections:{id:bool} }
  const [planKind, setPlanKind] = useState("total"); // total | integration
  const [openBoard, setOpenBoard] = useState(null);

  const genFramework = async () => {
    setBusy(true);
    const market = form?.market || "";
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel een positioneringskader op voor whitevision${market ? " voor: " + market : ""}. Dit kader is het fundament waarop de Marketing & Communicatie Manager voortbouwt. Wees scherp en concreet.\n\nJSON: {"target":"voor wie","frame":"in welke categorie/markt","benefit":"belangrijkste belofte","reasons":["reason to believe..."],"differentiator":"wat ons echt onderscheidt","statement":"één krachtig positioneringsstatement in 1-2 zinnen","messages":["kernboodschap..."],"tone":"gewenste toon"}`;
    const { data: r } = await callAIFull(prompt, { json: true });
    const entry = { id: Date.now(), market: market || "Algemeen", date: new Date().toISOString().slice(0, 10), ...(r || {}) };
    saveFrameworks([entry, ...(frameworks || [])]);
    setBusy(false); setForm(null); setOpenFw(entry.id);
  };

  const genIntegration = async () => {
    setBusy(true);
    const co = companies.find(c => String(c.id) === String(intForm?.companyId));
    if (!co) { setBusy(false); return; }
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel een merk-integratieplan ("One Company") op voor het overgenomen bedrijf "${co.name}"${co.segment ? " (" + co.segment + ")" : ""}${co.country ? ", " + co.country : ""}. ${co.notes ? "Context: " + co.notes : ""} Focus op het verankeren van het whitevision-merk en consistente identiteit.\n\nJSON: {"summary":"korte situatieschets","phases":[{"title":"fase","items":["concrete stap..."]}],"risks":["merkrisico..."],"quickwins":["snelle winst..."],"actions":[{"title":"...","text":"...","severity":"hoog|midden|laag"}]}`;
    const { data: r, sources } = await callAIFull(prompt, { json: true });
    const entry = { id: Date.now(), companyId: co.id, companyName: co.name, date: new Date().toISOString().slice(0, 10), sources: sources || [], ...(r || {}) };
    saveIntegrations([entry, ...(integrations || [])]);
    setBusy(false); setIntForm(null); setOpenInt(entry.id);
  };

  const genPlan = async () => {
    setBusy(true);
    const chosen = PLAN_SECTIONS.filter(s => planForm.sections[s.id]);
    const list = chosen.map(s => `- ${s.label}: ${s.hint}`).join("\n");
    const prompt = `${profileContext(profile)}\n\n${PLAIN}\n\nOPDRACHT: Stel een strategisch plan op voor whitevision${planForm.market ? " gericht op: " + planForm.market : ""}, met de titel "${planForm.title || "Strategisch plan"}". Werk UITSLUITEND de volgende onderdelen uit, in deze volgorde, elk als apart hoofdstuk:\n${list}\n\nSchrijf in het Nederlands, professioneel en concreet, klaar om te delen met directie. Gebruik markdown: # voor de titel van het plan, ## voor elk hoofdstuk, - voor opsommingen. Geen <cite>-tags.\n\nGeef ALLEEN de tekst in markdown, geen JSON.`;
    const { data: text, sources } = await callAIFull(prompt, {});
    const entry = {
      id: Date.now(), title: planForm.title || "Strategisch plan", market: planForm.market || "",
      sections: chosen.map(s => s.id), body: text, sources: sources || [], date: new Date().toISOString().slice(0, 10),
    };
    savePlans([entry, ...(plans || [])]);
    setBusy(false); setPlanForm(null); setOpenPlan(entry.id);
  };

  const tabBtn = (id, label) => (
    <button onClick={() => { setView(id); setOpenFw(null); setOpenInt(null); setOpenPlan(null); setOpenBoard(null); }}
      style={{ padding: "8px 16px", borderRadius: 9, border: "none", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, background: view === id ? C.surface : "transparent", color: view === id ? "#fff" : C.muted }}>{label}</button>
  );

  if (openFw) {
    const f = (frameworks || []).find(x => x.id === openFw);
    if (!f) { setOpenFw(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenFw(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug</button>
        <PageHead title={`Positioneringskader: ${f.market}`} sub={f.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => confirm("Dit positioneringskader wordt definitief verwijderd.", () => { saveFrameworks(frameworks.filter(x => x.id !== f.id)); setOpenFw(null); })}>Verwijderen</Btn>} />
        <Card>
          {f.statement && <div style={{ padding: "16px 18px", background: C.surface, borderRadius: 12, color: "#fff", fontSize: 16, lineHeight: 1.5, fontFamily: FONT_DISPLAY, marginBottom: 16 }}>"{f.statement}"</div>}
          <div style={{ border: `1px solid ${C.line}`, borderRadius: 12, overflow: "hidden" }}>
            {[["Doelgroep", f.target], ["Frame / categorie", f.frame], ["Belofte", f.benefit], ["Onderscheider", f.differentiator], ["Tone", f.tone]].filter(rw => rw[1]).map(([k, v], i) => (
              <div key={i} style={{ display: "flex", borderBottom: `1px solid ${C.line}`, background: i % 2 ? C.paper : C.card }}>
                <div style={{ width: 170, padding: "10px 13px", fontWeight: 700, fontSize: 13, background: C.surface, color: "#fff", flexShrink: 0 }}>{k}</div>
                <div style={{ padding: "10px 13px", fontSize: 13.5, color: C.ink2, lineHeight: 1.5 }}>{v}</div>
              </div>
            ))}
          </div>
          {f.reasons?.length > 0 && <><div style={sectionLabel}>Reasons to believe</div><div style={{ marginTop: 8 }}>{f.reasons.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, marginBottom: 5, display: "flex", gap: 7 }}><CheckCircle2 size={15} style={{ color: C.good, flexShrink: 0, marginTop: 1 }} /> {x}</div>)}</div></>}
          {f.messages?.length > 0 && <><div style={sectionLabel}>Kernboodschappen</div><div style={{ marginTop: 8 }}>{f.messages.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, marginBottom: 5, display: "flex", gap: 7 }}><Megaphone size={14} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} /> {x}</div>)}</div></>}
        </Card>
      </div>
    );
  }

  if (openInt) {
    const p = (integrations || []).find(x => x.id === openInt);
    if (!p) { setOpenInt(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenInt(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug</button>
        <PageHead title={`Integratieplan: ${p.companyName}`} sub={p.date}
          right={<Btn variant="danger" size="sm" icon={Trash2} onClick={() => confirm("Dit integratieplan wordt definitief verwijderd.", () => { saveIntegrations(integrations.filter(x => x.id !== p.id)); setOpenInt(null); })}>Verwijderen</Btn>} />
        <Card>
          {p.summary && <><div style={sectionLabel}>Situatie</div><div style={{ fontSize: 14.5, lineHeight: 1.6, color: C.ink2, marginTop: 6 }}>{p.summary}</div></>}
          {p.phases?.length > 0 && <>
            <div style={sectionLabel}>Stappenplan</div>
            <div style={{ marginTop: 10 }}>
              {p.phases.map((ph, i) => (
                <div key={i} style={{ display: "flex", gap: 12, marginBottom: 12 }}>
                  <div style={{ width: 28, height: 28, borderRadius: 28, background: C.yellow, display: "grid", placeItems: "center", fontWeight: 700, fontSize: 13, flexShrink: 0 }}>{i + 1}</div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontWeight: 700, fontSize: 14.5, marginBottom: 4 }}>{ph.title}</div>
                    {(ph.items || []).map((it, j) => (
                      <div key={j} style={{ display: "flex", justifyContent: "space-between", gap: 8, alignItems: "flex-start", marginBottom: 5 }}>
                        <div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.5, flex: 1 }}>• {it}</div>
                        <ActionButton small label="" onAdd={() => addAction({ title: it, detail: `Integratie ${p.companyName} — ${ph.title}`, source: "Integratieplan" })} />
                      </div>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </>}
          {p.quickwins?.length > 0 && <><div style={sectionLabel}>Quick wins</div><div style={{ marginTop: 8 }}>{p.quickwins.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, marginBottom: 5, display: "flex", gap: 7 }}><Zap size={14} style={{ color: C.yellowDark, flexShrink: 0, marginTop: 2 }} /> {x}</div>)}</div></>}
          {p.risks?.length > 0 && <><div style={sectionLabel}>Merkrisico's</div><div style={{ marginTop: 8 }}>{p.risks.map((x, i) => <div key={i} style={{ fontSize: 13.5, color: C.ink2, marginBottom: 5, display: "flex", gap: 7 }}><AlertTriangle size={14} style={{ color: C.bad, flexShrink: 0, marginTop: 2 }} /> {x}</div>)}</div></>}
          {p.actions?.length > 0 && <><div style={sectionLabel}>Actiepunten</div><Insights items={p.actions} onAction={addAction} prefix={"Integratie: " + p.companyName} profile={profile} /></>}
          <SourceLinks sources={p.sources} />
        </Card>
      </div>
    );
  }

  if (openPlan) {
    const pl = (plans || []).find(x => x.id === openPlan);
    if (!pl) { setOpenPlan(null); return null; }
    return (
      <div>
        <button onClick={() => setOpenPlan(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug</button>
        <PageHead title={pl.title} sub={`Strategisch plan${pl.market ? " · " + pl.market : ""} · ${pl.date}`}
          right={<div style={{ display: "flex", gap: 8 }}>
            <Btn variant="yellow" size="sm" icon={Download} onClick={() => exportToWord(pl.title, contentToHtml(pl.body))}>Exporteer naar Word</Btn>
            <Btn variant="danger" size="sm" icon={Trash2} onClick={() => confirm("Dit strategisch plan wordt definitief verwijderd.", () => { savePlans(plans.filter(x => x.id !== pl.id)); setOpenPlan(null); })}>Verwijderen</Btn>
          </div>} />
        <Card>
          <div style={{ fontSize: 14.5, lineHeight: 1.65, color: C.ink }} dangerouslySetInnerHTML={{ __html: contentToHtml(pl.body) }} />
          <SourceLinks sources={pl.sources} />
        </Card>
      </div>
    );
  }

  if (openBoard) {
    const board = (strategyBoards || []).find(b => b.id === openBoard);
    if (!board) { setOpenBoard(null); return null; }
    return <CampaignBoard board={board}
      onChange={(patch) => saveStrategyBoards((strategyBoards || []).map(b => b.id === board.id ? { ...b, ...patch } : b))}
      onDelete={() => confirm("Deze visualisatie wordt definitief verwijderd.", () => { saveStrategyBoards((strategyBoards || []).filter(b => b.id !== board.id)); setOpenBoard(null); })}
      onBack={() => setOpenBoard(null)} profile={profile} addAction={addAction} confirm={confirm} />;
  }

  return (
    <div>
      <PageHead title={view === "visual" ? "Modellen" : "Strategisch plan"} sub={view === "visual" ? "Teken processen en strategische modellen uit op een canvas, of start vanuit de modellenbibliotheek." : "Stel een strategisch plan of integratieplan samen en exporteer het naar Word."} />

      {view === "visual" && <>
        <div style={{ fontSize: 13.5, color: C.muted, marginBottom: 16, lineHeight: 1.5, maxWidth: 720 }}>Kies een model uit de bibliotheek als startpunt, of begin met een blanco canvas om je eigen proces of model uit te tekenen. Het canvas werkt net als bij Campagnes: blokken slepen, kleuren, verbinden en in grootte aanpassen.</div>
        <div style={sectionLabel}>Modellenbibliotheek</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 12, margin: "10px 0 24px" }}>
          {STRATEGY_MODELS.map(m => (
            <Card key={m.id} hover onClick={() => {
              const blocks = (m.blocks || []).map((b, i) => ({ id: Date.now() + i, title: b.title, type: "Overig", color: b.color, x: b.x, y: b.y, w: 180, h: 96, notes: "", img: null }));
              const board = { id: Date.now(), name: m.id === "blank" ? "Nieuw canvas" : m.name, basis: "strategie", model: m.id, date: new Date().toISOString().slice(0, 10), blocks, links: [] };
              saveStrategyBoards([board, ...(strategyBoards || [])]); setOpenBoard(board.id);
            }}>
              <div style={{ width: 40, height: 40, borderRadius: 11, background: C.yellow + "22", display: "grid", placeItems: "center", marginBottom: 10 }}>
                {m.id === "blank" ? <Plus size={20} style={{ color: C.ink }} /> : <Target size={20} style={{ color: C.ink }} />}
              </div>
              <div style={{ fontWeight: 700, fontSize: 15 }}>{m.name}</div>
              <div style={{ fontSize: 12.5, color: C.muted, marginTop: 4, lineHeight: 1.45 }}>{m.desc}</div>
            </Card>
          ))}
        </div>

        <div style={sectionLabel}>Mijn visualisaties</div>
        {(!strategyBoards || strategyBoards.length === 0)
          ? <div style={{ fontSize: 13, color: C.muted, marginTop: 8 }}>Nog geen visualisaties. Kies hierboven een model of een blanco canvas.</div>
          : <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 8 }}>
            {strategyBoards.map(b => (
              <Card key={b.id} hover style={{ padding: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                  <div onClick={() => setOpenBoard(b.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}><Target size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 15 }}>{b.name}</span></div>
                    <span style={{ fontSize: 12.5, color: C.muted }}>{(b.blocks || []).length} blokken · {b.date}</span>
                  </div>
                  <button onClick={() => confirm("Deze visualisatie wordt definitief verwijderd.", () => saveStrategyBoards(strategyBoards.filter(x => x.id !== b.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  <button onClick={() => setOpenBoard(b.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
                </div>
              </Card>
            ))}
          </div>}
      </>}

      {view === "positioning" && <>
        {form ? (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuw positioneringskader</span></div>
            <Field label="Markt of product-marktcombinatie (optioneel)" value={form.market} onChange={v => setForm({ market: v })} placeholder="Bijv. 'UK / Document Logistix' of 'zorgbranche DACH'" />
            <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
              <Btn variant="yellow" icon={Sparkles} onClick={genFramework} disabled={busy}>{busy ? "Bezig…" : "Genereer kader"}</Btn>
              <Btn variant="ghost" onClick={() => setForm(null)}>Annuleren</Btn>
              <CostHint kind="desk" />
            </div>
            {busy && <div style={{ marginTop: 12 }}><Spinner label="AI stelt het positioneringskader op…" /></div>}
          </Card>
        ) : <div style={{ marginBottom: 16 }}><Btn variant="yellow" icon={Plus} onClick={() => setForm({ market: "" })}>Nieuw positioneringskader</Btn></div>}
        {(!frameworks || frameworks.length === 0) && !form && <Empty icon={Target} title="Nog geen kaders" sub="Maak een positioneringskader dat dient als fundament voor marketing en communicatie." />}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(frameworks || []).map(f => (
            <Card key={f.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                <div onClick={() => setOpenFw(f.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}><Tag color={C.ink} bg={C.yellow}>{f.market}</Tag><span style={{ fontSize: 12.5, color: C.muted }}>{f.date}</span></div>
                  <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{f.statement}</div>
                </div>
                <button onClick={() => confirm("Dit positioneringskader wordt definitief verwijderd.", () => saveFrameworks(frameworks.filter(x => x.id !== f.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                <button onClick={() => setOpenFw(f.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              </div>
            </Card>
          ))}
        </div>
      </>}

      {view === "plan" && <>
        <div style={{ display: "flex", gap: 10, marginBottom: 18 }}>
          <button onClick={() => setPlanKind("total")} style={{ flex: 1, textAlign: "left", padding: "13px 15px", borderRadius: 12, cursor: "pointer", border: `1.5px solid ${planKind === "total" ? C.yellow : C.line}`, background: planKind === "total" ? C.yellow + "14" : C.card }}>
            <div style={{ fontWeight: 700, fontSize: 14.5 }}>Totaal strategisch plan</div>
            <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3 }}>Een compleet plan; jij kiest de onderdelen.</div>
          </button>
          <button onClick={() => setPlanKind("integration")} style={{ flex: 1, textAlign: "left", padding: "13px 15px", borderRadius: 12, cursor: "pointer", border: `1.5px solid ${planKind === "integration" ? C.yellow : C.line}`, background: planKind === "integration" ? C.yellow + "14" : C.card }}>
            <div style={{ fontWeight: 700, fontSize: 14.5 }}>Strategisch integratieplan</div>
            <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3 }}>One Company-plan per overgenomen bedrijf.</div>
          </button>
        </div>
      </>}

      {view === "plan" && planKind === "total" && <>
        {planForm ? (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuw strategisch plan</span></div>
            <Field label="Titel" value={planForm.title} onChange={v => setPlanForm({ ...planForm, title: v })} placeholder="Bijv. 'Strategisch marketingplan UK 2026'" />
            <Field label="Markt / focus (optioneel)" value={planForm.market} onChange={v => setPlanForm({ ...planForm, market: v })} placeholder="Bijv. 'UK / Document Logistix'" />
            <div style={sectionLabel}>Welke onderdelen moeten erin?</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, margin: "10px 0 16px" }}>
              {PLAN_SECTIONS.map(s => {
                const on = !!planForm.sections[s.id];
                return (
                  <label key={s.id} style={{ display: "flex", gap: 9, alignItems: "flex-start", padding: "9px 11px", border: `1.5px solid ${on ? C.yellow : C.line}`, background: on ? C.yellow + "14" : C.card, borderRadius: 10, cursor: "pointer" }}>
                    <input type="checkbox" checked={on} onChange={() => setPlanForm({ ...planForm, sections: { ...planForm.sections, [s.id]: !on } })} style={{ marginTop: 2 }} />
                    <span><span style={{ fontWeight: 700, fontSize: 13.5 }}>{s.label}</span><br /><span style={{ fontSize: 12, color: C.muted }}>{s.hint}</span></span>
                  </label>
                );
              })}
            </div>
            <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
              <Btn variant="yellow" icon={Sparkles} onClick={genPlan} disabled={busy || !Object.values(planForm.sections).some(Boolean)}>{busy ? "Bezig…" : "Genereer plan"}</Btn>
              <Btn variant="ghost" onClick={() => setPlanForm(null)}>Annuleren</Btn>
              <CostHint kind="desk" />
            </div>
            {busy && <div style={{ marginTop: 12 }}><Spinner label="AI stelt je strategisch plan op…" /></div>}
          </Card>
        ) : <div style={{ marginBottom: 16 }}><Btn variant="yellow" icon={Plus} onClick={() => setPlanForm({ title: "", market: "", sections: Object.fromEntries(PLAN_SECTIONS.map(s => [s.id, s.default])) })}>Nieuw strategisch plan</Btn></div>}
        {(!plans || plans.length === 0) && !planForm && <Empty icon={Target} title="Nog geen plannen" sub="Stel een strategisch plan samen door zelf de onderdelen te kiezen en het door AI te laten uitwerken." />}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(plans || []).map(pl => (
            <Card key={pl.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                <div onClick={() => setOpenPlan(pl.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}>{pl.market && <Tag color={C.ink} bg={C.yellow}>{pl.market}</Tag>}<span style={{ fontSize: 12.5, color: C.muted }}>{pl.date} · {(pl.sections || []).length} onderdelen</span></div>
                  <div style={{ fontWeight: 700, fontSize: 15 }}>{pl.title}</div>
                </div>
                <button onClick={() => exportToWord(pl.title, contentToHtml(pl.body))} style={iconBtn(C.yellowDark)} title="Exporteer naar Word"><Download size={17} /></button>
                <button onClick={() => confirm("Dit strategisch plan wordt definitief verwijderd.", () => savePlans(plans.filter(x => x.id !== pl.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                <button onClick={() => setOpenPlan(pl.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              </div>
            </Card>
          ))}
        </div>
      </>}

      {view === "plan" && planKind === "integration" && <>
        {intForm ? (
          <Card style={{ marginBottom: 20, border: `1.5px solid ${C.yellow}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 12 }}><AIBadge /><span style={{ fontWeight: 700 }}>Nieuw integratieplan</span></div>
            <div style={{ marginBottom: 16 }}>
              <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Overgenomen bedrijf</label>
              {(!companies || companies.length === 0)
                ? <div style={{ fontSize: 13.5, color: C.muted }}>Voeg eerst overgenomen bedrijven toe op het merkprofiel.</div>
                : <select value={intForm.companyId} onChange={e => setIntForm({ companyId: e.target.value })} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
                    <option value="">— Selecteer —</option>
                    {companies.map(c => <option key={c.id} value={c.id}>{c.name}{c.segment ? ` (${c.segment})` : ""}</option>)}
                  </select>}
            </div>
            <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
              <Btn variant="yellow" icon={Sparkles} onClick={genIntegration} disabled={busy || !intForm.companyId}>{busy ? "Bezig…" : "Genereer plan"}</Btn>
              <Btn variant="ghost" onClick={() => setIntForm(null)}>Annuleren</Btn>
              <CostHint kind="desk" />
            </div>
            {busy && <div style={{ marginTop: 12 }}><Spinner label="AI stelt het integratieplan op…" /></div>}
          </Card>
        ) : <div style={{ marginBottom: 16 }}><Btn variant="yellow" icon={Plus} onClick={() => setIntForm({ companyId: "" })}>Nieuw integratieplan</Btn></div>}
        {(!integrations || integrations.length === 0) && !intForm && <Empty icon={Briefcase} title="Nog geen integratieplannen" sub="Maak per overgenomen bedrijf een One Company merk-integratieplan." />}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(integrations || []).map(p => (
            <Card key={p.id} hover style={{ padding: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 16px" }}>
                <div onClick={() => setOpenInt(p.id)} style={{ flex: 1, cursor: "pointer", minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 3 }}><Tag color={C.ink} bg={C.yellow}>{p.companyName}</Tag><span style={{ fontSize: 12.5, color: C.muted }}>{p.date}</span>{p.phases?.length > 0 && <span style={{ fontSize: 12, color: C.muted }}>{p.phases.length} fasen</span>}</div>
                  <div style={{ fontSize: 14, color: C.ink2, lineHeight: 1.45, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{p.summary}</div>
                </div>
                <button onClick={() => confirm("Dit integratieplan wordt definitief verwijderd.", () => saveIntegrations(integrations.filter(x => x.id !== p.id)))} style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                <button onClick={() => setOpenInt(p.id)} style={iconBtn(C.muted)}><ChevronRight size={18} /></button>
              </div>
            </Card>
          ))}
        </div>
      </>}
    </div>
  );
}

/* ============================== 6. MERKPROFIEL (KERN) ============================== */
const PROFILE_FIELDS = [
  { key: "proposition", label: "Huidige propositie", icon: Megaphone, multiline: true },
  { key: "positioning", label: "Positioneringsstatement", icon: Target, multiline: true },
  { key: "mission", label: "Missie & visie", icon: Eye, multiline: true },
  { key: "goals", label: "Bedrijfsdoelstellingen", icon: Target, multiline: true },
  { key: "usps", label: "USP's", icon: Zap, multiline: true },
  { key: "products", label: "Producten & oplossingen", icon: Briefcase, multiline: true },
  { key: "website", label: "Website", icon: Globe, multiline: false },
  { key: "techstack", label: "Tech stack", icon: Wrench, multiline: true },
  { key: "tone", label: "Tone of voice", icon: FileText, multiline: true },
  { key: "icps", label: "ICP-profielen", icon: Users, multiline: true },
  { key: "competitors", label: "Concurrenten", icon: Building2, multiline: true },
  { key: "notes", label: "Brandbook-notities", icon: Palette, multiline: true },
];

// De overname-blauwdruk: vaste basis voor de rebrand-checklist. Per bedrijf aanpasbaar.
const ACQ_BLUEPRINT = [
  { phase: "1 · Bekendmaking", tasks: [
    ["Persbericht opstellen / nalezen", "Dag 0–1"],
    ["Nieuwsbericht op website", "Dag 0–2"],
    ["Persbericht uitsturen (via Main)", "Dag 0–2"],
    ["Bericht naar klanten overgenomen bedrijf", "Dag 0–2"],
    ["Bericht naar Whitevision klanten", "Dag 0–3"],
    ["Nieuwsbericht op socials", "Dag 0–3"],
    ["About us pagina aanpassen", "Week 1"],
    ["By Whitevision doorvoeren in logo", "Week 1–2"],
    ["Balk op website overgenomen partij", "Week 1–2"],
    ["Uitrollen by whitevision logo (software, website, handtekening)", "Week 1–2"],
  ] },
  { phase: "2 · Kennismaking & Inventarisatie", tasks: [
    ["Contact met marketingteam overgenomen bedrijf", "Week 1"],
    ["Goodiebag regelen", "Week 1"],
    ["Inventarisatie signing", "Week 1–2"],
    ["Inventarisatie marketing tooling", "Week 1–2"],
    ["Inventarisatie marketingbudget", "Week 1"],
    ["Inventarisatie marcom kanalen & socials", "Week 1"],
    ["Inventarisatie lopende contracten (bureaus etc.)", "Week 1"],
    ["Toegang regelen tot tools & socials", "Week 1"],
    ["Marketingdata opvragen (analytics, CRM)", "Week 1–2"],
    ["Verhaal Whitevision (presentatie voor overgenomen bedrijven)", "Week 1–2"],
    ["Brandbook delen en toelichten", "Week 1–2"],
  ] },
  { phase: "3 · Product & Marktkennis", tasks: [
    ["Productmarketeer in gesprek over product & propositie", "Week 2"],
    ["Inventarisatie release proces", "Week 2"],
    ["Klantflows analyseren", "Week 2–3"],
    ["Screenshots opvragen", "Week 2–3"],
    ["Verdieping concurrenten", "Week 2–3"],
    ["Verdieping producten, ICP, PMC", "Week 2–3"],
    ["Productdemo's inplannen", "Week 2"],
    ["Opleveren en presenteren battlesheet", "Week 3–4"],
  ] },
  { phase: "4 · Propositie & Strategie", tasks: [
    ["Positionering bepalen binnen Whitevision group", "Week 3"],
  ] },
  { phase: "5 · Migratie & content creatie", tasks: [
    ["Crawl maken", "Week 4"],
    ["Content gap inventarisatie", "Week 4–5"],
    ["Content creatie & publicatie (whitepapers, landingpages etc.)", "Week 5–9"],
    ["Migratieplan maken & doorzetten", "Week 9"],
    ["Communicatie over migratie en veranderingen", "Week 10"],
    ["Backlinks & listings laten aanpassen", "Week 10–12"],
  ] },
  { phase: "6 · Huisstijl & Logo Uitrol", tasks: [
    ["Nieuw logo laten maken in lijn met huisstijl", "Week 5"],
    ["Klantenportaal", "Week 5–7"],
    ["Salespitch", "Week 10–13"],
    ["Presentaties", "Week 10–13"],
    ["Whitepapers/Downloads", "Week 10–13"],
    ["Signing kantoor", "Week 10–13"],
    ["Website", "Week 10–13"],
    ["Kernwaarden", "Week 10–13"],
    ["Offertes", "Week 10–13"],
    ["Facturen", "Week 10–13"],
    ["Socials omzetten", "Week 10–13"],
    ["Partner logo's aanpassen", "Week 10–13"],
    ["Software logo", "Week 10–13"],
    ["Appstore updates", "Week 10–13"],
    ["Handleidingen", "Week 10–13"],
  ] },
  { phase: "7 · Overige zaken na migratie", tasks: [
    ["Overbodige tooling opzeggen", "Week 10–13"],
  ] },
  { phase: "8 · Promotie & campagne", tasks: [
    ["Listings aanpassen", "Week 10–30"],
    ["Nieuwsbrief delen", "Week 10–31"],
    ["Advertentiekanalen omzetten", "Week 13–16"],
    ["Promotie op whitevision.com", "Week 13–16"],
    ["Cross- en upsellkansen in kaart brengen", "Week 13–16"],
    ["Webinar organiseren", "Week 13–16"],
  ] },
];
function blueprintTaskList() {
  // Vlakke lijst met unieke id per taak, voor een nieuwe checklist.
  const out = [];
  ACQ_BLUEPRINT.forEach((ph, pi) => ph.tasks.forEach(([label, planning], ti) => {
    out.push({ id: `b${pi}_${ti}`, phase: ph.phase, label, planning, done: false });
  }));
  return out;
}
const EMPTY_ENTITY_PROFILE = { proposition: "", goals: "", usps: "", products: "", tone: "", icps: "", competitors: "" };
const ENTITY_FIELDS = [
  ["proposition", "Propositie"], ["goals", "Doelstellingen"], ["usps", "USP's"],
  ["products", "Producten"], ["tone", "Tone of voice"], ["icps", "ICP's"], ["competitors", "Concurrenten"],
];

// Acquisities-tabblad: per overgenomen bedrijf voortgang (rebrand) of een los profiel (zelfstandig).
function AcquisitionsPanel({ companies, confirm }) {
  const [progress, setProgress] = useState({});   // { [companyId]: [ {id,phase,label,planning,done} ] }
  const [entities, setEntities] = useState({});    // { [companyId]: {proposition,...} }
  const [openId, setOpenId] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [newTask, setNewTask] = useState("");
  const [entityDraft, setEntityDraft] = useState(null);
  const [entitySaved, setEntitySaved] = useState(false);

  useEffect(() => {
    let on = true;
    Promise.all([store.get("wv_acq_progress", {}), store.get("wv_entity_profiles", {})]).then(([pr, en]) => {
      if (!on) return;
      setProgress(pr && typeof pr === "object" ? pr : {});
      setEntities(en && typeof en === "object" ? en : {});
      setLoaded(true);
    }).catch(() => setLoaded(true));
    return () => { on = false; };
  }, []);

  const isRebrand = (co) => co.status === "rebranded" || co.status === "endorsed";
  const saveProgress = (next) => { setProgress(next); store.set("wv_acq_progress", next); };
  const saveEntities = (next) => { setEntities(next); store.set("wv_entity_profiles", next); };

  const listFor = (id) => progress[id] || blueprintTaskList();
  const pct = (id) => {
    const l = listFor(id);
    if (!l.length) return 0;
    return Math.round(l.filter(t => t.done).length / l.length * 100);
  };

  if (!loaded) return <Spinner label="Acquisities laden…" />;

  // ---- Detailweergave van één acquisitie ----
  if (openId) {
    const co = (companies || []).find(c => String(c.id) === String(openId));
    if (!co) { setOpenId(null); return null; }
    const rebrand = isRebrand(co);

    if (rebrand) {
      const list = listFor(co.id);
      const byPhase = {};
      list.forEach(t => { (byPhase[t.phase] = byPhase[t.phase] || []).push(t); });
      const toggle = (tid) => saveProgress({ ...progress, [co.id]: list.map(t => t.id === tid ? { ...t, done: !t.done } : t) });
      const removeTask = (tid) => saveProgress({ ...progress, [co.id]: list.filter(t => t.id !== tid) });
      const addTask = () => {
        const label = newTask.trim(); if (!label) return;
        saveProgress({ ...progress, [co.id]: [...list, { id: "u" + Date.now(), phase: "Eigen taken", label, planning: "", done: false }] });
        setNewTask("");
      };
      const p = pct(co.id);
      return (
        <div>
          <button onClick={() => setOpenId(null)} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar acquisities</button>
          <PageHead title={co.name} sub="Voortgang van de overname volgens de blauwdruk. Vink af wat klaar is; voeg eigen taken toe waar nodig." />
          <Card style={{ marginBottom: 20 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
              <div style={{ flex: 1 }}>
                <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
                  <span style={{ fontWeight: 700, fontSize: 14 }}>{p}% overgenomen</span>
                  <span style={{ fontSize: 12.5, color: C.muted }}>{list.filter(t => t.done).length} van {list.length} taken</span>
                </div>
                <div style={{ height: 10, background: C.line, borderRadius: 6, overflow: "hidden" }}>
                  <div style={{ width: `${p}%`, height: "100%", background: p === 100 ? C.good : C.yellow, transition: "width .3s" }} />
                </div>
              </div>
            </div>
          </Card>
          {Object.keys(byPhase).map(phase => {
            const tasks = byPhase[phase];
            const allDone = tasks.every(t => t.done);
            const toggleAll = () => {
              const ids = new Set(tasks.map(t => t.id));
              saveProgress({ ...progress, [co.id]: list.map(t => ids.has(t.id) ? { ...t, done: !allDone } : t) });
            };
            return (
            <Card key={phase} style={{ marginBottom: 14 }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
                <span style={{ fontWeight: 700, fontSize: 14.5 }}>{phase}</span>
                <button onClick={toggleAll} style={{ display: "flex", alignItems: "center", gap: 6, background: "none", border: `1.5px solid ${C.line}`, borderRadius: 8, padding: "5px 10px", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 700, color: C.ink2 }}>
                  {allDone ? <><Circle size={13} /> Alles uitvinken</> : <><CheckCircle2 size={13} /> Alles afvinken</>}
                </button>
              </div>
              <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
                {tasks.map(t => (
                  <div key={t.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 4px", borderBottom: `1px solid ${C.line}` }}>
                    <button onClick={() => toggle(t.id)} style={{ width: 20, height: 20, borderRadius: 6, border: `1.5px solid ${t.done ? C.good : C.line}`, background: t.done ? C.good : C.card, cursor: "pointer", display: "grid", placeItems: "center", flexShrink: 0 }}>
                      {t.done && <Check size={13} style={{ color: "#fff" }} />}
                    </button>
                    <span style={{ flex: 1, fontSize: 13.5, color: t.done ? C.muted : C.ink, textDecoration: t.done ? "line-through" : "none" }}>{t.label}</span>
                    {t.planning && <Tag color={C.muted}>{t.planning}</Tag>}
                    <button onClick={() => removeTask(t.id)} title="Taak verwijderen" style={{ background: "none", border: "none", cursor: "pointer", color: C.line }}><Trash2 size={14} /></button>
                  </div>
                ))}
              </div>
            </Card>
            );
          })}
          <Card>
            <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 8, textTransform: "uppercase" }}>Eigen taak toevoegen</div>
            <div style={{ display: "flex", gap: 8 }}>
              <input value={newTask} onChange={e => setNewTask(e.target.value)} onKeyDown={e => e.key === "Enter" && addTask()} placeholder="bijv. 'domeinnaam doorlinken'" style={{ flex: 1, padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              <Btn variant="yellow" icon={Plus} onClick={addTask} disabled={!newTask.trim()}>Toevoegen</Btn>
            </div>
          </Card>
        </div>
      );
    }

    // Zelfstandige entiteit: los profiel, met dezelfde velden als de kennisbasis, maar NIET als kennisbasis.
    const ent = entityDraft !== null ? entityDraft : (entities[co.id] || EMPTY_ENTITY_PROFILE);
    const setField = (k, v) => setEntityDraft({ ...(entityDraft !== null ? entityDraft : (entities[co.id] || EMPTY_ENTITY_PROFILE)), [k]: v });
    const saveEntity = () => { saveEntities({ ...entities, [co.id]: ent }); setEntityDraft(null); setEntitySaved(true); setTimeout(() => setEntitySaved(false), 1800); };
    return (
      <div>
        <button onClick={() => { setOpenId(null); setEntityDraft(null); }} style={backLink}><ChevronRight size={15} style={{ transform: "rotate(180deg)" }} /> Terug naar acquisities</button>
        <PageHead title={co.name} sub="Zelfstandige entiteit — een eigen profiel, los van whitevision."
          right={<Btn variant={entityDraft !== null ? "yellow" : "ghost"} icon={entitySaved ? Check : Save} onClick={saveEntity} disabled={entityDraft === null && !entitySaved}>{entitySaved ? "Opgeslagen" : "Opslaan"}</Btn>} />
        <div style={{ display: "flex", alignItems: "flex-start", gap: 9, padding: "12px 14px", background: C.blue + "10", border: `1px solid ${C.blue}33`, borderRadius: 10, marginBottom: 20 }}>
          <AlertTriangle size={15} style={{ color: C.blue, flexShrink: 0, marginTop: 2 }} />
          <div style={{ fontSize: 12.5, color: C.ink2, lineHeight: 1.5 }}>Dit profiel hoort bij een zelfstandige entiteit en wordt <b>niet</b> gebruikt als kennisbasis van whitevision. De tool spiegelt content en analyses nooit tegen dit profiel; het staat hier puur als eigen naslag.</div>
        </div>
        <Card>
          {ENTITY_FIELDS.map(([k, label]) => (
            <div key={k} style={{ marginBottom: 16 }}>
              <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>{label}</label>
              <textarea value={ent[k] || ""} onChange={e => setField(k, e.target.value)} rows={2}
                style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.5 }} />
            </div>
          ))}
        </Card>
      </div>
    );
  }

  // ---- Tegeloverzicht ----
  return (
    <div>
      {(!companies || companies.length === 0)
        ? <Empty icon={Building2} title="Nog geen overgenomen bedrijven" sub="Voeg ze toe op het tabblad Kennisbasis, onderaan bij 'Overgenomen bedrijven'." />
        : <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 16 }}>
            {companies.map(co => {
              const rebrand = isRebrand(co);
              const p = pct(co.id);
              const hasEntity = !!entities[co.id];
              return (
                <Card key={co.id} hover onClick={() => setOpenId(co.id)} style={{ cursor: "pointer" }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
                    <Building2 size={18} style={{ color: C.yellowDark }} />
                    <span style={{ fontWeight: 700, fontSize: 16 }}>{co.name}</span>
                  </div>
                  <div style={{ marginBottom: 12 }}>
                    <Tag color={rebrand ? C.yellowDark : C.blue}>{co.status === "rebranded" ? "Rebrand naar whitevision" : co.status === "endorsed" ? "Endorsed (overgang)" : "Zelfstandig merk"}</Tag>
                  </div>
                  {rebrand ? (
                    <div>
                      <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
                        <span style={{ fontSize: 13, fontWeight: 700 }}>{p}% overgenomen</span>
                      </div>
                      <div style={{ height: 8, background: C.line, borderRadius: 5, overflow: "hidden" }}>
                        <div style={{ width: `${p}%`, height: "100%", background: p === 100 ? C.good : C.yellow }} />
                      </div>
                    </div>
                  ) : (
                    <div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>{hasEntity ? "Eigen profiel ingevuld — open om te bewerken." : "Nog geen eigen profiel — open om er een te maken."}</div>
                  )}
                </Card>
              );
            })}
          </div>}
    </div>
  );
}

// Brand assets: beeldbestanden per omgeving, server-kant bewaard (niet in de profiel-JSON,
// want base64-afbeeldingen zijn te groot en gingen daardoor verloren bij verversen).
const ASSET_CATS = [["logo", "Logo"], ["foto", "Foto"], ["achtergrond", "Achtergrond"], ["icoon", "Icoon"], ["patroon", "Patroon"], ["eenmalig", "Eenmalig gebruik"], ["overig", "Overig"]];
// ---- Producten ----
// Meer dan een opsomming: per product de propositie, voor wie het is, waarin het zich
// onderscheidt en hoe het geprijsd is. De agents gebruiken dit bij content, benchmarks
// en het budget, dus hoe scherper dit staat, hoe bruikbaarder de uitkomsten.
const PRODUCT_STATUS = [["actief", "Actief"], ["nieuw", "In ontwikkeling"], ["uitfaseren", "Wordt uitgefaseerd"]];
const PRICING_MODELS = [["abonnement", "Abonnement"], ["staffel", "Staffel op volume"], ["licentie", "Licentie"], ["eenmalig", "Eenmalig"], ["maatwerk", "Maatwerk"], ["anders", "Anders"]];

function ProductsPanel({ p, set, confirm }) {
  const list = p.productList || [];
  const [openId, setOpenId] = useState(null);
  const [newName, setNewName] = useState("");

  const add = () => {
    if (!newName.trim()) return;
    const id = "p" + Date.now() + Math.random().toString(36).slice(2, 5);
    set("productList", [...list, {
      id, name: newName.trim(), status: "actief", proposition: "", audience: "",
      usps: "", pricing: "abonnement", price: "", competitors: "", note: "",
    }]);
    setNewName(""); setOpenId(id);
  };
  const upd = (id, patch) => set("productList", list.map(x => x.id === id ? { ...x, ...patch } : x));
  const del = (x) => confirm(`"${x.name}" verwijderen uit je producten?`, () => { set("productList", list.filter(y => y.id !== x.id)); setOpenId(null); });

  const stColor = (st) => st === "actief" ? C.good : st === "nieuw" ? C.blue : C.muted;
  const stLabel = (st) => (PRODUCT_STATUS.find(x => x[0] === st) || [null, "Actief"])[1];
  const field = { width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", lineHeight: 1.55 };

  return (
    <div>
      <Card style={{ marginBottom: 18 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
          <Briefcase size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Producten en diensten</span>
          {list.length > 0 && <Tag color={C.muted}>{list.filter(x => x.status === "actief").length} actief van {list.length}</Tag>}
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.55 }}>
          Beschrijf per product wat het is, voor wie, waarin het zich onderscheidt en hoe het geprijsd is. Dit gebruiken de agents bij content, benchmarks en het budget; hoe scherper het hier staat, hoe bruikbaarder de uitkomsten.
        </div>
        <div style={{ display: "flex", gap: 10 }}>
          <input value={newName} onChange={e => setNewName(e.target.value)} onKeyDown={e => e.key === "Enter" && add()}
            placeholder="Naam van het product, bijv. Factuurverwerking"
            style={{ ...field, flex: 1 }} />
          <Btn variant="yellow" icon={Plus} onClick={add} disabled={!newName.trim()}>Toevoegen</Btn>
        </div>
      </Card>

      {list.length === 0
        ? <Empty icon={Briefcase} title="Nog geen producten vastgelegd" sub="Voeg je producten toe; klik er daarna op om ze uit te werken." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {list.map(x => {
              const isOpen = openId === x.id;
              const filled = [x.proposition, x.audience, x.usps].filter(v => String(v || "").trim()).length;
              return (
                <Card key={x.id} style={{ padding: 0, overflow: "hidden" }}>
                  <div onClick={() => setOpenId(isOpen ? null : x.id)}
                    style={{ display: "flex", alignItems: "center", gap: 12, padding: "14px 16px", cursor: "pointer" }}>
                    <ChevronRight size={16} style={{ color: C.muted, transform: isOpen ? "rotate(90deg)" : "none", transition: "transform .15s", flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap" }}>
                        <span style={{ fontWeight: 700, fontSize: 15.5 }}>{x.name}</span>
                        <Tag color={stColor(x.status)}>{stLabel(x.status)}</Tag>
                        {filled < 3 && <Tag color={C.yellowDark}>{filled}/3 ingevuld</Tag>}
                      </div>
                      {x.proposition && <div style={{ fontSize: 13, color: C.muted, marginTop: 3, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{x.proposition}</div>}
                    </div>
                    <button onClick={(e) => { e.stopPropagation(); del(x); }} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                  </div>

                  {isOpen && (
                    <div style={{ borderTop: `1px solid ${C.line}`, padding: 16, background: C.paper }}>
                      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Naam</label>
                          <input value={x.name} onChange={e => upd(x.id, { name: e.target.value })} style={{ ...field, fontWeight: 700 }} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Status</label>
                          <select value={x.status} onChange={e => upd(x.id, { status: e.target.value })} style={{ ...field, background: C.card }}>
                            {PRODUCT_STATUS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                          </select>
                        </div>
                      </div>

                      <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Propositie</label>
                      <textarea value={x.proposition} onChange={e => upd(x.id, { proposition: e.target.value })} rows={2}
                        placeholder="Wat doet het en welk probleem lost het op? In gewone taal, zoals je het aan een klant uitlegt."
                        style={{ ...field, resize: "vertical", marginBottom: 12 }} />

                      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Voor wie</label>
                          <textarea value={x.audience} onChange={e => upd(x.id, { audience: e.target.value })} rows={3}
                            placeholder="Welke organisaties en welke rol daarbinnen? Wanneer past het juist wel of niet?"
                            style={{ ...field, resize: "vertical" }} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Onderscheidend</label>
                          <textarea value={x.usps} onChange={e => upd(x.id, { usps: e.target.value })} rows={3}
                            placeholder="Waarin verschilt dit van alternatieven? Eén punt per regel."
                            style={{ ...field, resize: "vertical" }} />
                        </div>
                      </div>

                      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Prijsmodel</label>
                          <select value={x.pricing} onChange={e => upd(x.id, { pricing: e.target.value })} style={{ ...field, background: C.card }}>
                            {PRICING_MODELS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                          </select>
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Prijsindicatie</label>
                          <input value={x.price} onChange={e => upd(x.id, { price: e.target.value })} placeholder="bijv. vanaf € 500 p/m" style={field} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Concurrenten hierop</label>
                          <input value={x.competitors} onChange={e => upd(x.id, { competitors: e.target.value })} placeholder="komma-gescheiden" style={field} />
                        </div>
                      </div>

                      <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Notities</label>
                      <textarea value={x.note} onChange={e => upd(x.id, { note: e.target.value })} rows={2}
                        placeholder="Wat je verder wilt onthouden: veelgestelde vragen, bezwaren, plannen."
                        style={{ ...field, resize: "vertical" }} />
                    </div>
                  )}
                </Card>
              );
            })}
          </div>}

      {/* De oude vrije tekst blijft bestaan, zodat niets verdwijnt. */}
      <Card style={{ marginTop: 18 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Vrije omschrijving (oud veld)</div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>
          Dit veld gebruikten we eerder voor je productenlijst. Het blijft meegaan naar de agents; je kunt het leeg laten zodra je je producten hierboven hebt uitgewerkt.
        </div>
        <textarea value={p.products || ""} onChange={e => set("products", e.target.value)} rows={3}
          style={{ ...field, resize: "vertical" }} />
      </Card>
    </div>
  );
}

// ---- Cijfers per jaar ----
// Op hoofdlijnen per jaar, en optioneel uitgesplitst per product of markt. De cijfers
// van een jaar worden gebruikt als doelstelling bij het opstellen van een budget.
// ---- Typografie ----
// Welke letters horen bij het merk. De tool gebruikt dit bij het maken van visuals:
// voor social posts moet het bestand er echt zijn, voor banners werkt een terugvalreeks.
function TypographyBlock({ p, set, confirm }) {
  const [fonts, setFonts] = useState([]);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");

  const laden = async () => {
    try {
      const r = await fetch("/api/assets", { credentials: "include" });
      if (!r.ok) return;
      const d = await r.json();
      setFonts((d.assets || []).filter(a => /\.(ttf|otf|woff2?)$/i.test(a.filename || "")));
    } catch {}
  };
  useEffect(() => { laden(); }, []);

  const upload = async (file) => {
    if (!file) return;
    setBusy(true); setErr("");
    try {
      const buf = await file.arrayBuffer();
      const naam = file.name.replace(/\.[^.]+$/, "").slice(0, 60);
      const r = await fetch(`/api/assets?name=${encodeURIComponent(naam)}&category=lettertype&filename=${encodeURIComponent(file.name)}`, {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf,
      });
      const d = await r.json();
      if (!r.ok) setErr(d.error || "Uploaden mislukte.");
      else await laden();
    } catch { setErr("Uploaden mislukte."); }
    setBusy(false);
  };

  const verwijder = (f) => confirm(`"${f.name}" verwijderen?`, async () => {
    try { await fetch(`/api/assets/${f.id}/delete`, { method: "POST", credentials: "include" }); await laden(); } catch {}
  });

  const veld = { width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" };

  return (
    <Card style={{ marginTop: 22 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Type size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Typografie</span>
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>
        Leg vast welke letters bij je merk horen. De tool gebruikt dit bij het maken van visuals. Let op het verschil: voor social posts wordt het lettertype echt gebruikt, dus daarvoor moet het bestand hier staan. Voor banners werkt een terugvalreeks, omdat die op het apparaat van de lezer draaien.
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1.4fr", gap: 12, marginBottom: 12 }}>
        <div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Huisletter</label>
          <input value={p.fontPrimary || ""} onChange={e => set("fontPrimary", e.target.value)} placeholder="bijv. Fieldwork" style={veld} />
        </div>
        <div>
          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Terugval, op volgorde</label>
          <input value={p.fontFallbacks || ""} onChange={e => set("fontFallbacks", e.target.value)} placeholder="bijv. Montserrat, Verdana" style={veld} />
        </div>
      </div>
      <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Toelichting</label>
      <input value={p.fontNotes || ""} onChange={e => set("fontNotes", e.target.value)} placeholder="bijv. koppen in Bold, lopende tekst in Regular" style={{ ...veld, marginBottom: 18 }} />

      <div style={{ paddingTop: 14, borderTop: `1px solid ${C.line}` }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, flexWrap: "wrap", marginBottom: 8 }}>
          <span style={{ fontSize: 13.5, fontWeight: 700 }}>Lettertypebestanden</span>
          <label style={{ ...miniBtn(C.paper, C.ink), cursor: busy ? "default" : "pointer" }}>
            {busy ? <Loader2 size={13} className="spin" /> : <Upload size={13} />} {busy ? "Uploaden…" : "Bestand toevoegen"}
            <input type="file" accept=".ttf,.otf,.woff,.woff2" onChange={e => { const f = e.target.files?.[0]; e.target.value = ""; upload(f); }} disabled={busy} style={{ display: "none" }} />
          </label>
        </div>
        <div style={{ fontSize: 12, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>
          Upload het bestand van je huisletter, bij voorkeur als TTF of OTF. Zonder dit bestand kan de tool je huisletter niet in social posts zetten en valt hij terug op wat er beschikbaar is.
        </div>

        {fonts.length === 0
          ? <div style={{ fontSize: 13, color: C.muted }}>Nog geen lettertypen geüpload.</div>
          : <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
              {fonts.map(f => (
                <div key={f.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", border: `1px solid ${C.line}`, borderRadius: 9 }}>
                  <Type size={15} style={{ color: C.muted, flexShrink: 0 }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600 }}>{f.name}</div>
                    <div style={{ fontSize: 11.5, color: C.muted }}>{f.filename} · {Math.round((f.size || 0) / 1024)} kB</div>
                  </div>
                  <button onClick={() => verwijder(f)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                </div>
              ))}
            </div>}
        {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 9 }}>{err}</div>}
      </div>
    </Card>
  );
}

function FiguresBlock({ p, set, confirm }) {
  const thisYear = new Date().getFullYear();
  const years = [thisYear, thisYear + 1];
  const figures = p.figures || {};
  const detail = p.figureDetail || [];
  const [open, setOpen] = useState({});   // per jaar uitgeklapt

  const setYear = (y, field, val) => set("figures", { ...figures, [y]: { ...(figures[y] || {}), [field]: val } });
  const forYear = (y) => detail.filter(d => String(d.year) === String(y));
  const addDetail = (y) => {
    set("figureDetail", [...detail, { id: "d" + Date.now() + Math.random().toString(36).slice(2, 5), year: String(y), dimension: "product", name: "", revenue: "", result: "" }]);
    setOpen(o => ({ ...o, [y]: true }));
  };
  const updDetail = (id, patch) => set("figureDetail", detail.map(d => d.id === id ? { ...d, ...patch } : d));
  const delDetail = (id) => set("figureDetail", detail.filter(d => d.id !== id));

  const num = (v) => Number(String(v || "").replace(/[^\d,.-]/g, "").replace(/\.(?=\d{3}\b)/g, "").replace(",", ".")) || 0;
  const sumFor = (y, field) => forYear(y).reduce((s2, d) => s2 + num(d[field]), 0);

  const cell = { width: "100%", boxSizing: "border-box", padding: "8px 10px", border: `1px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13, outline: "none", background: C.card, textAlign: "right" };
  const cellL = { ...cell, textAlign: "left" };

  return (
    <Card style={{ marginTop: 22 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <BarChart3 size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Cijfers en doelstellingen</span>
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>
        Vul per jaar je netto omzet, netto resultaat en vast personeel in fte's in. Het lopende jaar is je vertrekpunt, het volgende jaar je doelstelling. Klap een jaar open om het per product of markt uit te splitsen. Bij het opstellen van een budget worden de cijfers van dat jaar als uitgangspunt gebruikt.
      </div>

      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {years.map((y, i) => {
          const f = figures[y] || {};
          const kids = forYear(y);
          const isOpen = !!open[y];
          return (
            <div key={y} style={{ border: `1px solid ${C.line}`, borderRadius: 12, overflow: "hidden" }}>
              {/* Hoofdregel van het jaar */}
              <div style={{ display: "grid", gridTemplateColumns: "150px 1fr 1fr 110px 1.3fr", gap: 10, alignItems: "center", padding: "12px 14px", background: i === 0 ? "transparent" : C.yellow + "0A" }}>
                <button onClick={() => setOpen(o => ({ ...o, [y]: !isOpen }))} style={{ display: "flex", alignItems: "center", gap: 7, background: "none", border: "none", padding: 0, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 15, fontWeight: 700, color: C.ink, textAlign: "left" }}>
                  <ChevronRight size={15} style={{ transform: isOpen ? "rotate(90deg)" : "none", transition: "transform .15s", color: C.muted }} />
                  {y}
                  {i === 0 ? <Tag color={C.muted}>lopend</Tag> : <Tag color={C.yellowDark}>doel</Tag>}
                </button>
                <div>
                  <div style={{ fontSize: 11, color: C.muted, marginBottom: 3, textTransform: "uppercase", fontWeight: 700 }}>Netto omzet</div>
                  <input value={f.revenue || ""} onChange={e => setYear(y, "revenue", e.target.value)} placeholder="€ 0" style={cell} />
                </div>
                <div>
                  <div style={{ fontSize: 11, color: C.muted, marginBottom: 3, textTransform: "uppercase", fontWeight: 700 }}>Netto resultaat</div>
                  <input value={f.result || ""} onChange={e => setYear(y, "result", e.target.value)} placeholder="€ 0" style={cell} />
                </div>
                <div>
                  <div style={{ fontSize: 11, color: C.muted, marginBottom: 3, textTransform: "uppercase", fontWeight: 700 }}>Fte</div>
                  <input value={f.fte || ""} onChange={e => setYear(y, "fte", e.target.value)} placeholder="0" style={cell} />
                </div>
                <div>
                  <div style={{ fontSize: 11, color: C.muted, marginBottom: 3, textTransform: "uppercase", fontWeight: 700 }}>Toelichting</div>
                  <input value={f.note || ""} onChange={e => setYear(y, "note", e.target.value)} placeholder="bijv. groei uit overnames" style={cellL} />
                </div>
              </div>

              {/* Producten en markten onder dit jaar */}
              {isOpen && (
                <div style={{ borderTop: `1px solid ${C.line}`, padding: "12px 14px", background: C.paper }}>
                  <div style={{ fontSize: 12, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>
                    Optioneel: hang hier producten of markten onder {y}. Handig als je op dat niveau wilt sturen; de agents gebruiken deze uitsplitsing waar die er is.
                  </div>
                  {kids.length > 0 && (
                    <div style={{ overflowX: "auto", border: `1px solid ${C.line}`, borderRadius: 10, marginBottom: 10, background: C.card }}>
                      <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
                        <thead><tr style={{ background: C.surface }}>
                          <th style={{ ...cmpTh, width: 110 }}>Soort</th><th style={cmpTh}>Naam</th>
                          <th style={{ ...cmpTh, width: 150 }}>Netto omzet</th><th style={{ ...cmpTh, width: 150 }}>Netto resultaat</th><th style={{ ...cmpTh, width: 40 }}></th>
                        </tr></thead>
                        <tbody>
                          {kids.map((d, j) => (
                            <tr key={d.id} style={{ background: j % 2 ? C.paper : C.card }}>
                              <td style={cmpTd}>
                                <select value={d.dimension} onChange={e => updDetail(d.id, { dimension: e.target.value })} style={cellL}>
                                  <option value="product">Product</option><option value="markt">Markt</option>
                                </select>
                              </td>
                              <td style={cmpTd}><input value={d.name} onChange={e => updDetail(d.id, { name: e.target.value })} placeholder="bijv. Factuurverwerking" style={cellL} /></td>
                              <td style={cmpTd}><input value={d.revenue} onChange={e => updDetail(d.id, { revenue: e.target.value })} placeholder="€ 0" style={cell} /></td>
                              <td style={cmpTd}><input value={d.result} onChange={e => updDetail(d.id, { result: e.target.value })} placeholder="€ 0" style={cell} /></td>
                              <td style={{ ...cmpTd, textAlign: "center" }}><button onClick={() => delDetail(d.id)} style={iconBtn(C.bad)}><Trash2 size={14} /></button></td>
                            </tr>
                          ))}
                          <tr style={{ background: C.surface }}>
                            <td style={{ ...cmpTd, color: "#fff", fontWeight: 700 }} colSpan={2}>Som van de regels</td>
                            <td style={{ ...cmpTd, color: C.yellow, fontWeight: 700, textAlign: "right" }}>{eurFmt(sumFor(y, "revenue"))}</td>
                            <td style={{ ...cmpTd, color: C.yellow, fontWeight: 700, textAlign: "right" }}>{eurFmt(sumFor(y, "result"))}</td>
                            <td style={cmpTd}></td>
                          </tr>
                        </tbody>
                      </table>
                    </div>
                  )}
                  {kids.length === 0 && <div style={{ fontSize: 13, color: C.muted, marginBottom: 10 }}>Nog niets uitgesplitst voor {y}.</div>}
                  <Btn size="sm" variant="ghost" icon={Plus} onClick={() => addDetail(y)}>Product of markt toevoegen</Btn>
                  {kids.length > 0 && num(f.revenue) > 0 && Math.abs(sumFor(y, "revenue") - num(f.revenue)) > 1 && (
                    <div style={{ fontSize: 12, color: C.yellowDark, marginTop: 9, lineHeight: 1.5 }}>
                      De som van de regels ({eurFmt(sumFor(y, "revenue"))}) wijkt af van de omzet op de hoofdregel ({eurFmt(num(f.revenue))}). Dat mag, maar kijk even of het klopt.
                    </div>
                  )}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </Card>
  );
}

// ---- Kanalen, platformen en events ----
// Wat je inzet en wat je interessant vindt, met genoeg detail om er beslissingen op te
// nemen: doel, doelgroep, kosten, looptijd en wat je ervaring ermee is.
const CH_TYPES = [["kanaal", "Advertentiekanaal"], ["platform", "Platform / vakmedia"], ["event", "Event"], ["organisch", "Organisch / eigen kanaal"], ["partner", "Partnerkanaal"]];
const CH_STATUS = [["actief", "Zet ik in"], ["interessant", "Interessant"], ["overwogen", "Overwogen"], ["gestopt", "Gestopt"], ["afgewezen", "Niet passend"]];
const CH_GOALS = [["leadgeneratie", "Leadgeneratie"], ["merkbekendheid", "Merkbekendheid"], ["retentie", "Retentie"], ["recruitment", "Recruitment"], ["partners", "Partners"]];

function ChannelsPanel({ p, set, confirm }) {
  const list = p.channels || [];
  const [form, setForm] = useState({ name: "", type: "kanaal", status: "actief" });
  const [openId, setOpenId] = useState(null);
  const [filter, setFilter] = useState("alle");

  const add = () => {
    if (!form.name.trim()) return;
    const id = "c" + Date.now() + Math.random().toString(36).slice(2, 5);
    set("channels", [...list, { id, name: form.name.trim(), type: form.type, status: form.status, goal: "leadgeneratie", audience: "", cost: "", period: "", experience: "", note: "" }]);
    setForm({ name: "", type: form.type, status: form.status }); setOpenId(id);
  };
  const upd = (id, patch) => set("channels", list.map(c => c.id === id ? { ...c, ...patch } : c));
  const del = (c) => confirm(`"${c.name}" verwijderen?`, () => { set("channels", list.filter(x => x.id !== c.id)); setOpenId(null); });

  const stColor = (st) => st === "actief" ? C.good : st === "interessant" ? C.yellowDark : st === "gestopt" ? C.blue : st === "afgewezen" ? C.bad : C.muted;
  const stLabel = (st) => (CH_STATUS.find(x => x[0] === st) || [null, st])[1];
  const tpLabel = (t) => (CH_TYPES.find(x => x[0] === t) || [null, t])[1];
  const field = { width: "100%", boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", lineHeight: 1.55 };

  const shown = filter === "alle" ? list : list.filter(c => (filter === "actief" ? c.status === "actief" : filter === "kans" ? c.status === "interessant" : c.type === filter));

  return (
    <div>
      <Card style={{ marginBottom: 18 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
          <Share2 size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Kanalen, platformen en events</span>
          {list.length > 0 && <Tag color={C.muted}>{list.filter(c => c.status === "actief").length} actief van {list.length}</Tag>}
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.55 }}>
          Leg vast wat je inzet en wat je interessant vindt. Klik een regel open om doel, doelgroep, kosten en je ervaring erbij te zetten. De Opportunity agent vult deze lijst aan met kansen; het budget gebruikt de actieve kanalen als vertrekpunt.
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "2fr 1.2fr 1fr auto", gap: 9 }}>
          <input value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))} onKeyDown={e => e.key === "Enter" && add()}
            placeholder="Naam, bijv. Google Ads of Vakbeurs Finance" style={field} />
          <select value={form.type} onChange={e => setForm(f => ({ ...f, type: e.target.value }))} style={{ ...field, background: C.card }}>
            {CH_TYPES.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
          </select>
          <select value={form.status} onChange={e => setForm(f => ({ ...f, status: e.target.value }))} style={{ ...field, background: C.card }}>
            {CH_STATUS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
          </select>
          <Btn variant="yellow" icon={Plus} onClick={add} disabled={!form.name.trim()}>Toevoegen</Btn>
        </div>
      </Card>

      {list.length > 3 && (
        <div style={{ display: "flex", gap: 7, marginBottom: 14, flexWrap: "wrap" }}>
          {[["alle", "Alles"], ["actief", "Zet ik in"], ["kans", "Interessant"], ...CH_TYPES.map(([v, l]) => [v, l])].map(([id, lbl]) => (
            <button key={id} onClick={() => setFilter(id)} style={{
              padding: "6px 12px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 700,
              border: `1px solid ${filter === id ? "transparent" : C.line}`,
              background: filter === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: filter === id ? "#fff" : C.muted,
            }}>{lbl}</button>
          ))}
        </div>
      )}

      {shown.length === 0
        ? <Empty icon={Share2} title={list.length ? "Niets in deze selectie" : "Nog geen kanalen vastgelegd"} sub={list.length ? "" : "Voeg je kanalen en events toe; klik ze daarna open om ze uit te werken."} />
        : <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
            {shown.map(c => {
              const isOpen = openId === c.id;
              return (
                <Card key={c.id} style={{ padding: 0, overflow: "hidden", borderLeft: `3px solid ${stColor(c.status)}` }}>
                  <div onClick={() => setOpenId(isOpen ? null : c.id)} style={{ display: "flex", alignItems: "center", gap: 12, padding: "13px 15px", cursor: "pointer" }}>
                    <ChevronRight size={16} style={{ color: C.muted, transform: isOpen ? "rotate(90deg)" : "none", transition: "transform .15s", flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 9, flexWrap: "wrap" }}>
                        <span style={{ fontWeight: 700, fontSize: 15 }}>{c.name}</span>
                        <Tag color={stColor(c.status)}>{stLabel(c.status)}</Tag>
                        <Tag color={C.muted}>{tpLabel(c.type)}</Tag>
                        {c.cost && <Tag color={C.blue}>{c.cost} p/j</Tag>}
                      </div>
                      {c.note && <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 1, WebkitBoxOrient: "vertical" }}>{c.note}</div>}
                    </div>
                    <button onClick={(e) => { e.stopPropagation(); del(c); }} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                  </div>

                  {isOpen && (
                    <div style={{ borderTop: `1px solid ${C.line}`, padding: 16, background: C.paper }}>
                      <div style={{ display: "grid", gridTemplateColumns: "1.5fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Naam</label>
                          <input value={c.name} onChange={e => upd(c.id, { name: e.target.value })} style={{ ...field, fontWeight: 700 }} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Soort</label>
                          <select value={c.type} onChange={e => upd(c.id, { type: e.target.value })} style={{ ...field, background: C.card }}>
                            {CH_TYPES.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                          </select>
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Status</label>
                          <select value={c.status} onChange={e => upd(c.id, { status: e.target.value })} style={{ ...field, background: C.card, color: stColor(c.status), fontWeight: 700 }}>
                            {CH_STATUS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                          </select>
                        </div>
                      </div>

                      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Doel</label>
                          <select value={c.goal || "leadgeneratie"} onChange={e => upd(c.id, { goal: e.target.value })} style={{ ...field, background: C.card }}>
                            {CH_GOALS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                          </select>
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Kosten per jaar</label>
                          <input value={c.cost || ""} onChange={e => upd(c.id, { cost: e.target.value })} placeholder="€ 0" style={field} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Looptijd of periode</label>
                          <input value={c.period || ""} onChange={e => upd(c.id, { period: e.target.value })} placeholder="bijv. doorlopend, of Q2" style={field} />
                        </div>
                      </div>

                      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Doelgroep op dit kanaal</label>
                          <textarea value={c.audience || ""} onChange={e => upd(c.id, { audience: e.target.value })} rows={3}
                            placeholder="Wie bereik je hier precies? Welke rol, welk type organisatie?" style={{ ...field, resize: "vertical" }} />
                        </div>
                        <div>
                          <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Ervaring tot nu toe</label>
                          <textarea value={c.experience || ""} onChange={e => upd(c.id, { experience: e.target.value })} rows={3}
                            placeholder="Wat levert het op, wat werkte wel en niet? Ook bij 'gestopt': waarom." style={{ ...field, resize: "vertical" }} />
                        </div>
                      </div>

                      <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Notitie</label>
                      <input value={c.note || ""} onChange={e => upd(c.id, { note: e.target.value })} placeholder="korte aantekening, zichtbaar in het overzicht" style={field} />
                    </div>
                  )}
                </Card>
              );
            })}
          </div>}
    </div>
  );
}

function BrandAssets({ confirm }) {
  const [assets, setAssets] = useState(null);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [saved, setSaved] = useState(false);
  const fileRef = useRef();
  const flashSaved = () => { setSaved(true); setTimeout(() => setSaved(false), 1800); };
  const load = async () => {
    try { const r = await fetch("/api/assets", { credentials: "include" }); const d = await r.json(); setAssets(d.assets || []); } catch { setAssets([]); }
  };
  useEffect(() => { load(); }, []);
  const onUpload = async (e) => {
    const files = Array.from(e.target.files || []);
    if (!files.length) return;
    setBusy(true); setErr("");
    for (const f of files) {
      try {
        const buf = await f.arrayBuffer();
        const qs = new URLSearchParams({ name: f.name.replace(/\.[^.]+$/, ""), category: "logo", filename: f.name });
        const r = await fetch("/api/assets?" + qs.toString(), { method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf });
        if (!r.ok) { const d = await r.json().catch(() => ({})); setErr(d.error || "Upload mislukt."); }
      } catch { setErr("Upload mislukt."); }
    }
    if (fileRef.current) fileRef.current.value = "";
    await load(); setBusy(false); flashSaved();
  };
  const update = async (id, patch) => {
    setAssets(prev => prev.map(a => a.id === id ? { ...a, ...patch } : a));
    try { await fetch(`/api/assets/${id}/update`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify(patch) }); flashSaved(); } catch {}
  };
  const remove = (a) => confirm(`Asset "${a.name}" verwijderen?`, async () => {
    try { await fetch(`/api/assets/${a.id}/delete`, { method: "POST", credentials: "include" }); await load(); flashSaved(); } catch {}
  });

  return (
    <Card>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
        <Image size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 14.5 }}>Brand assets</span>
        {saved && <span style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 12, fontWeight: 700, color: C.good }}><Check size={13} /> Opgeslagen</span>}
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>Upload logo's, foto's, achtergronden, iconen en patronen die je in content kunt gebruiken. Geef elk een korte naam en een categorie. Assets worden automatisch opgeslagen; je hoeft hiervoor niet op de knop Opslaan bovenaan te klikken.</div>
      {assets === null
        ? <div style={{ fontSize: 13, color: C.muted }}>Assets laden…</div>
        : <>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(120px, 1fr))", gap: 10, marginBottom: 12 }}>
              {assets.map(a => (
                <div key={a.id} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: 8, position: "relative", background: C.card }}>
                  <div style={{ height: 54, display: "grid", placeItems: "center", marginBottom: 6, background: C.paper, borderRadius: 6, overflow: "hidden" }}>
                    <img src={`/api/assets/${a.id}/file`} alt={a.name} style={{ maxHeight: 48, maxWidth: "100%", objectFit: "contain" }} />
                  </div>
                  <input value={a.name} onChange={e => update(a.id, { name: e.target.value })}
                    style={{ width: "100%", boxSizing: "border-box", padding: "4px 6px", border: `1px solid ${C.line}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 11.5, outline: "none", marginBottom: 5 }} />
                  <select value={a.category || "logo"} onChange={e => update(a.id, { category: e.target.value })}
                    style={{ width: "100%", boxSizing: "border-box", padding: "4px 6px", border: `1px solid ${C.line}`, borderRadius: 6, fontFamily: FONT_BODY, fontSize: 11, background: C.card, outline: "none", color: C.ink2 }}>
                    {ASSET_CATS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
                  </select>
                  <button onClick={() => remove(a)} title="Verwijderen"
                    style={{ position: "absolute", top: 4, right: 4, background: C.card, border: `1px solid ${C.line}`, borderRadius: 6, cursor: "pointer", color: C.bad, width: 22, height: 22, display: "grid", placeItems: "center" }}><X size={12} /></button>
                </div>
              ))}
            </div>
            <label style={{ ...miniBtn(C.paper, C.ink), cursor: busy ? "default" : "pointer", opacity: busy ? .6 : 1 }}>
              <Upload size={13} /> {busy ? "Bezig met uploaden…" : "Assets uploaden"}
              <input ref={fileRef} type="file" accept="image/*" multiple onChange={onUpload} disabled={busy} style={{ display: "none" }} />
            </label>
            {err && <div style={{ fontSize: 12, color: C.bad, marginTop: 8 }}>{err}</div>}
          </>}
    </Card>
  );
}

function Profile({ ctx }) {
  const { profile, saveProfile, companies, saveCompanies, users, saveUsers, confirm } = ctx;
  const [profileTab, setProfileTab] = useState("kennis");
  const [p, setP] = useState(profile);
  const [dirty, setDirty] = useState(false);
  const [sources, setSources] = useState({ website: "", brandbook: "", extra: "" });
  const [aiBusy, setAiBusy] = useState(false);
  const [saved, setSaved] = useState(false);
  const fileRef = useRef();
  const [uploadNames, setUploadNames] = useState([]);

  // Brand templates (bestanden, server-kant per omgeving bewaard).
  const [tpls, setTpls] = useState(null);
  const [tplForm, setTplForm] = useState({ name: "", purpose: "", use: "studio", category: "presentatie" });
  const [tplFile, setTplFile] = useState(null);
  const [tplBusy, setTplBusy] = useState(false);
  const [tplErr, setTplErr] = useState("");
  const tplFileRef = useRef();
  const loadTpls = async () => {
    try { const r = await fetch("/api/templates", { credentials: "include" }); const d = await r.json(); setTpls(d.templates || []); } catch { setTpls([]); }
  };
  useEffect(() => { loadTpls(); }, []);
  const uploadTpl = async () => {
    if (!tplFile) { setTplErr("Kies eerst een bestand."); return; }
    setTplBusy(true); setTplErr("");
    try {
      const qs = new URLSearchParams({ name: tplForm.name || tplFile.name, purpose: tplForm.purpose || "", use: tplForm.use || "studio", category: tplForm.category || "presentatie", filename: tplFile.name });
      const buf = await tplFile.arrayBuffer();
      const r = await fetch("/api/templates?" + qs.toString(), { method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf });
      const d = await r.json();
      if (!r.ok) { setTplErr(d.error || "Upload mislukt."); }
      else { setTplForm({ name: "", purpose: "", use: "studio", category: "presentatie" }); setTplFile(null); if (tplFileRef.current) tplFileRef.current.value = ""; await loadTpls(); }
    } catch { setTplErr("Upload mislukt."); }
    setTplBusy(false);
  };
  const deleteTpl = (t) => confirm(`Template "${t.name}" verwijderen?`, async () => {
    try { await fetch(`/api/templates/${t.id}/delete`, { method: "POST", credentials: "include" }); await loadTpls(); } catch {}
  });
  const fmtSize = (b) => b > 1048576 ? (b / 1048576).toFixed(1) + " MB" : Math.max(1, Math.round(b / 1024)) + " KB";
  const useLabel = (u) => u === "copy" ? "Copy" : u === "both" ? "Copy + Studio" : "Studio";
  const TPL_CATS = [["presentatie", "Presentatie"], ["whitepaper", "Whitepaper"], ["handleiding", "Handleiding (Word)"], ["brochure", "Brochure"], ["voorbeeld", "Voorbeeld"], ["overig", "Overig"]];
  const tplCatLabel = (c) => (TPL_CATS.find(x => x[0] === c) || [null, "Presentatie"])[1];

  useEffect(() => { setP(profile); }, [profile]);
  const set = (k, v) => { setP(prev => ({ ...prev, [k]: v })); setDirty(true); };

  const handleFiles = (e) => {
    const files = Array.from(e.target.files || []);
    setUploadNames(files.map(f => f.name));
    let combined = sources.extra;
    let pending = files.length;
    if (!pending) return;
    files.forEach(f => {
      const reader = new FileReader();
      reader.onload = () => {
        combined += `\n\n[Bestand: ${f.name}]\n` + (typeof reader.result === "string" ? reader.result.slice(0, 4000) : "(binair bestand, naam meegegeven)");
        pending--; if (pending === 0) setSources(s => ({ ...s, extra: combined }));
      };
      reader.onerror = () => { pending--; };
      if (f.type.startsWith("text") || f.name.match(/\.(txt|md|csv|html|json)$/i)) reader.readAsText(f);
      else { combined += `\n\n[Bestand geüpload: ${f.name} — beschrijf inhoud in het tekstveld]`; pending--; if (pending === 0) setSources(s => ({ ...s, extra: combined })); }
    });
  };

  const aiFill = async () => {
    setAiBusy(true);
    const input = `Website/URL: ${sources.website || "—"}\nBrandbook/identiteit (tekst): ${sources.brandbook || "—"}\nOverige aangeleverde info: ${sources.extra || "—"}`;
    const prompt = `Je helpt de brand manager van whitevision (AI-driven IDP-software, buy-and-build, oplossingen in purchase-to-pay, contractmanagement, spend management) een merkprofiel op te bouwen.\n\nOp basis van onderstaande aangeleverde input, vul een merkprofiel in. Waar informatie ontbreekt, doe een onderbouwde, realistische inschatting passend bij dit type bedrijf en markeer die als [aanname].\n\nINPUT:\n${input}\n\nJSON met exact deze sleutels (strings): {"proposition":"","goals":"","usps":"","products":"","tone":"","icps":"","colors":"hex-codes","competitors":""}`;
    const r = await callAI(prompt, { json: true });
    if (r) {
      const next = { ...p, ...r, filled: true };
      setP(next); setDirty(true);
    }
    setAiBusy(false);
  };

  const save = () => { saveProfile({ ...p, filled: true }); setDirty(false); setSaved(true); setTimeout(() => setSaved(false), 1800); };

  // company management
  const [coForm, setCoForm] = useState(null);
  const saveCo = () => {
    if (!coForm.name) return;
    if (coForm.id) saveCompanies(companies.map(c => c.id === coForm.id ? coForm : c));
    else saveCompanies([...companies, { ...coForm, id: Date.now() }]);
    setCoForm(null);
  };
  // user management
  const [uForm, setUForm] = useState(null);
  const saveU = () => {
    if (!uForm.name) return;
    if (uForm.id) saveUsers(users.map(u => u.id === uForm.id ? uForm : u));
    else saveUsers([...users, { ...uForm, id: Date.now() }]);
    setUForm(null);
  };

  return (
    <div>
      <PageHead title="Bedrijfsprofiel" sub="De kennisbasis van je merk. Alles in de tool spiegelt hiertegen."
        right={profileTab !== "acq" ? <Btn variant={dirty ? "yellow" : "ghost"} icon={saved ? Check : Save} onClick={save} disabled={!dirty && !saved}>{saved ? "Opgeslagen" : "Opslaan"}</Btn> : null} />

      <div style={{ display: "flex", gap: 8, marginBottom: 22, borderBottom: `1.5px solid ${C.line}` }}>
        {[["kennis", "Kennisbasis"], ["prod", "Producten"], ["chan", "Kanalen"], ["acq", "Acquisities"]].map(([id, lbl]) => (
          <button key={id} onClick={() => setProfileTab(id)} style={{
            padding: "10px 16px", border: "none", background: "none", cursor: "pointer", fontFamily: FONT_BODY, fontSize: 14.5,
            fontWeight: profileTab === id ? 700 : 500, color: profileTab === id ? C.ink : C.muted,
            borderBottom: `2.5px solid ${profileTab === id ? C.yellow : "transparent"}`, marginBottom: -1.5,
          }}>{lbl}</button>
        ))}
      </div>

      {profileTab === "acq" && <AcquisitionsPanel companies={companies} confirm={confirm} />}
      {profileTab === "prod" && <ProductsPanel p={p} set={set} confirm={confirm} />}
      {profileTab === "chan" && <ChannelsPanel p={p} set={set} confirm={confirm} />}
      {profileTab === "kennis" && <>

      {/* AI start */}
      <Card style={{ marginBottom: 22, background: C.surface, border: "none" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}>
          <Sparkles size={20} style={{ color: C.yellow }} />
          <span style={{ fontWeight: 700, fontSize: 17, color: "#fff", fontFamily: FONT_DISPLAY }}>Start met AI</span>
        </div>
        <div style={{ fontSize: 13.5, color: "#9CA3AF", marginBottom: 16, lineHeight: 1.5 }}>Lever je website, brandbook, templates of logo aan. AI vult het profiel voor je in — daarna pas je het handmatig aan.</div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}>
          <input value={sources.website} onChange={e => setSources(s => ({ ...s, website: e.target.value }))} placeholder="Website-URL (bijv. whitevision.com)" style={darkInput} />
          <button onClick={() => fileRef.current?.click()} style={{ ...darkInput, cursor: "pointer", display: "flex", alignItems: "center", gap: 8, color: "#9CA3AF", textAlign: "left" }}>
            <Upload size={15} /> {uploadNames.length ? `${uploadNames.length} bestand(en)` : "Upload brandbook / template / logo"}
          </button>
          <input ref={fileRef} type="file" multiple onChange={handleFiles} style={{ display: "none" }} />
        </div>
        <textarea value={sources.extra} onChange={e => setSources(s => ({ ...s, extra: e.target.value }))} rows={3} placeholder="Of plak hier vrije input: bestaande propositie, doelstellingen, kleuren, concurrenten…"
          style={{ ...darkInput, width: "100%", boxSizing: "border-box", resize: "vertical", marginBottom: 12, fontFamily: FONT_BODY }} />
        <Btn variant="yellow" icon={Sparkles} onClick={aiFill} disabled={aiBusy}>{aiBusy ? "AI vult het profiel in…" : "Profiel invullen met AI"}</Btn>
        {aiBusy && <div style={{ marginTop: 10 }}><Spinner label="Velden genereren op basis van je input…" /></div>}
      </Card>

      {/* fields */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 18 }}>
        {PROFILE_FIELDS.map(f => (
          <Card key={f.key}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
              <f.icon size={16} style={{ color: C.yellowDark }} />
              <span style={{ fontWeight: 700, fontSize: 14.5 }}>{f.label}</span>
            </div>
            {f.multiline === false
              ? <input value={p[f.key] || ""} onChange={e => set(f.key, e.target.value)} placeholder={`Vul ${f.label.toLowerCase()} in…`}
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", color: C.ink }}
                  onFocus={e => e.target.style.borderColor = C.yellow} onBlur={e => e.target.style.borderColor = C.line} />
              : <textarea value={p[f.key] || ""} onChange={e => set(f.key, e.target.value)} rows={4} placeholder={`Vul ${f.label.toLowerCase()} in of laat AI dit doen…`}
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 13.5, resize: "vertical", outline: "none", lineHeight: 1.5, color: C.ink }}
                  onFocus={e => e.target.style.borderColor = C.yellow} onBlur={e => e.target.style.borderColor = C.line} />}
          </Card>
        ))}
        {/* colors special */}
        <Card>
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
            <Palette size={16} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 14.5 }}>Huisstijlkleuren</span>
          </div>
          <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 10, lineHeight: 1.5 }}>Geef elke kleur een rol (primair, secundair, CTA, tekstkleur…) en stel de kleur in. Een misgelopen AI-suggestie corrigeer je hier.</div>
          {(() => {
            // migratie: oude komma-lijst -> rollen, eenmalig
            let roles = p.colorRoles;
            if (!roles) {
              const old = (p.colors || "").split(",").map(c => c.trim()).filter(Boolean);
              roles = old.map((hex, i) => ({ role: i === 0 ? "Primair" : i === 1 ? "Secundair" : "Overig", hex }));
            }
            const ROLE_OPTS = ["Primair", "Secundair", "Accent", "CTA", "Tekstkleur", "Achtergrond", "Overig"];
            const setRoles = (arr) => { set("colorRoles", arr); set("colors", arr.map(r => r.hex).join(", ")); };
            return <>
              <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 10 }}>
                {roles.map((c, i) => (
                  <div key={i} style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <input type="color" value={/^#[0-9a-fA-F]{6}$/.test(c.hex) ? c.hex : "#000000"} onChange={e => { const a = [...roles]; a[i] = { ...a[i], hex: e.target.value }; setRoles(a); }}
                      style={{ width: 34, height: 34, padding: 0, border: `1px solid ${C.line}`, borderRadius: 8, cursor: "pointer", background: "none", flexShrink: 0 }} />
                    <select value={c.role} onChange={e => { const a = [...roles]; a[i] = { ...a[i], role: e.target.value }; setRoles(a); }}
                      style={{ width: 120, flexShrink: 0, padding: "8px 8px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 12.5, background: C.card, outline: "none" }}>
                      {ROLE_OPTS.map(r => <option key={r} value={r}>{r}</option>)}
                    </select>
                    <input value={c.hex} onChange={e => { const a = [...roles]; a[i] = { ...a[i], hex: e.target.value }; setRoles(a); }}
                      style={{ flex: 1, padding: "8px 10px", border: `1.5px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
                    <button onClick={() => setRoles(roles.filter((_, j) => j !== i))} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>
                  </div>
                ))}
              </div>
              <button onClick={() => setRoles([...roles, { role: "Overig", hex: "#000000" }])} style={{ ...miniBtn(C.paper, C.ink) }}><Plus size={13} /> Kleur toevoegen</button>
            </>;
          })()}
        </Card>

        {/* brand assets */}
        <BrandAssets confirm={confirm} />
      </div>

      <TypographyBlock p={p} set={set} confirm={confirm} />
      <FiguresBlock p={p} set={set} confirm={confirm} />

      {/* brand templates (bestanden, met categorie) */}
      <Card style={{ marginTop: 22 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
          <LayoutTemplate size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Brand templates</span>
        </div>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Verzamel hier je templates en voorbeelden als bestand: presentaties, whitepapers, Word-handleidingen, brochures. Geef per bestand een categorie, een naam, waar het voor is, en of je het bij Copy, Studio of allebei wilt gebruiken. Bij content kun je de template dan kiezen. Let op: het daadwerkelijk vullen van een template tot een afgewerkt document doen we samen per stuk; de tool bewaart en koppelt ze.</div>

        <div style={{ border: `1.5px dashed ${C.line}`, borderRadius: 12, padding: 14, marginBottom: 16 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 10 }}>
            <input value={tplForm.name} onChange={e => setTplForm(f => ({ ...f, name: e.target.value }))} placeholder="Naam (bijv. 'whitevision salesdeck')"
              style={{ padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: 700, outline: "none" }} />
            <select value={tplForm.category} onChange={e => setTplForm(f => ({ ...f, category: e.target.value }))} style={{ padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
              {TPL_CATS.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
            </select>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 10 }}>
            <input value={tplForm.purpose} onChange={e => setTplForm(f => ({ ...f, purpose: e.target.value }))} placeholder="Waarvoor is dit? (bijv. 'klantpresentaties UK')"
              style={{ boxSizing: "border-box", padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
            <select value={tplForm.use} onChange={e => setTplForm(f => ({ ...f, use: e.target.value }))} style={{ padding: "9px 11px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, background: C.card, outline: "none" }}>
              <option value="studio">Gebruiken bij Studio</option>
              <option value="copy">Gebruiken bij Copy</option>
              <option value="both">Bij Copy én Studio</option>
            </select>
          </div>
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
            <input ref={tplFileRef} type="file" accept=".pptx,.potx,.ppt,.key,.pdf,.docx,.doc" onChange={e => { setTplFile(e.target.files?.[0] || null); setTplErr(""); }}
              style={{ fontSize: 13, fontFamily: FONT_BODY }} />
            <Btn variant="yellow" icon={tplBusy ? Loader2 : Plus} onClick={uploadTpl} disabled={tplBusy || !tplFile}>{tplBusy ? "Bezig met uploaden…" : "Toevoegen"}</Btn>
          </div>
          {tplErr && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 8 }}>{tplErr}</div>}
        </div>

        {tpls === null
          ? <div style={{ fontSize: 13, color: C.muted }}>Templates laden…</div>
          : tpls.length === 0
            ? <div style={{ fontSize: 13, color: C.muted }}>Nog geen templates geüpload.</div>
            : <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                {tpls.map(t => (
                  <div key={t.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 13px", border: `1px solid ${C.line}`, borderRadius: 11 }}>
                    <LayoutTemplate size={17} style={{ color: C.yellowDark, flexShrink: 0 }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                        <span style={{ fontWeight: 700, fontSize: 14 }}>{t.name}</span>
                        <Tag color={C.yellowDark}>{tplCatLabel(t.category)}</Tag>
                      </div>
                      <div style={{ fontSize: 12, color: C.muted, marginTop: 2 }}>{t.purpose ? t.purpose + " · " : ""}{useLabel(t.use)} · {t.filename} · {fmtSize(t.size)}</div>
                    </div>
                    <a href={`/api/templates/${t.id}/download`} title="Downloaden" style={{ color: C.ink2, display: "grid", placeItems: "center" }}><Download size={16} /></a>
                    <button onClick={() => deleteTpl(t)} title="Verwijderen" style={iconBtn(C.bad)}><Trash2 size={16} /></button>
                  </div>
                ))}
              </div>}
      </Card>
      <Card style={{ marginTop: 22 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 9 }}><Briefcase size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Overgenomen bedrijven</span></div>

          <Btn size="sm" variant="ghost" icon={Plus} onClick={() => setCoForm({ name: "", country: "", segment: "", status: "endorsed", notes: "", whatTheyDo: "", audience: "", usps: "", markets: "", techstack: "", monitor: false })}>Bedrijf</Btn>
        </div>
        {coForm && (
          <div style={{ padding: 16, background: C.paper, borderRadius: 12, marginBottom: 14, border: `1px solid ${C.line}` }}>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
              <Field label="Naam" value={coForm.name} onChange={v => setCoForm({ ...coForm, name: v })} />
              <Field label="Land" value={coForm.country} onChange={v => setCoForm({ ...coForm, country: v })} />
              <Field label="Segment / focus" value={coForm.segment} onChange={v => setCoForm({ ...coForm, segment: v })} />
              <div style={{ marginBottom: 16 }}>
                <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>Merkstatus</label>
                <select value={coForm.status} onChange={e => setCoForm({ ...coForm, status: e.target.value })} style={{ width: "100%", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", background: C.card }}>
                  <option value="endorsed">Endorsed (overgang)</option>
                  <option value="rebranded">Gerebrand naar whitevision</option>
                  <option value="standalone">Zelfstandig merk</option>
                </select>
              </div>
            </div>
            <Field label="Notities" value={coForm.notes} onChange={v => setCoForm({ ...coForm, notes: v })} multiline rows={2} />
            <div style={{ marginTop: 4, marginBottom: 6, fontSize: 12, fontWeight: 700, color: C.yellowDark, textTransform: "uppercase", letterSpacing: .3 }}>Battlecard</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
              <Field label="Wat doen ze precies" value={coForm.whatTheyDo} onChange={v => setCoForm({ ...coForm, whatTheyDo: v })} multiline rows={2} />
              <Field label="Doelgroep" value={coForm.audience} onChange={v => setCoForm({ ...coForm, audience: v })} multiline rows={2} />
              <Field label="USP's" value={coForm.usps} onChange={v => setCoForm({ ...coForm, usps: v })} multiline rows={2} />
              <Field label="Markten" value={coForm.markets} onChange={v => setCoForm({ ...coForm, markets: v })} multiline rows={2} />
            </div>
            <Field label="Tech stack" value={coForm.techstack} onChange={v => setCoForm({ ...coForm, techstack: v })} multiline rows={2} placeholder="Bijv. .NET, Azure, eigen OCR-engine, SQL Server…" />
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 13px", background: C.card, border: `1.5px solid ${C.line}`, borderRadius: 10, marginBottom: 14 }}>
              <Radar size={16} style={{ color: C.yellowDark }} />
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13.5, fontWeight: 700 }}>Monitoren in de Monitor-sectie</div>
                <div style={{ fontSize: 12, color: C.muted }}>Volg dit bedrijf doorlopend op activiteit en merkafwijkingen.</div>
              </div>
              <Toggle on={!!coForm.monitor} onChange={() => setCoForm({ ...coForm, monitor: !coForm.monitor })} />
            </div>
            <div style={{ display: "flex", gap: 10 }}><Btn variant="yellow" onClick={saveCo}>Opslaan</Btn><Btn variant="ghost" onClick={() => setCoForm(null)}>Annuleren</Btn></div>
          </div>
        )}
        {companies.length === 0 && !coForm && <div style={{ fontSize: 13.5, color: C.muted }}>Nog geen overgenomen bedrijven vastgelegd.</div>}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {companies.map(c => (
            <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", background: C.paper, borderRadius: 11, border: `1px solid ${C.line}` }}>
              <div style={{ width: 36, height: 36, borderRadius: 9, background: C.surface, display: "grid", placeItems: "center", color: C.yellow, fontWeight: 700, fontFamily: FONT_DISPLAY }}>{c.name?.[0]?.toUpperCase() || "?"}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 700, fontSize: 14.5, display: "flex", alignItems: "center", gap: 8 }}>{c.name}
                  <Tag color={c.status === "rebranded" ? C.good : c.status === "endorsed" ? C.yellowDark : C.blue}>{c.status === "rebranded" ? "gerebrand" : c.status === "endorsed" ? "endorsed" : "standalone"}</Tag>
                  {c.monitor && <Tag color={C.ink} bg={C.yellow}>● monitor</Tag>}</div>
                <div style={{ fontSize: 12.5, color: C.muted, marginTop: 2 }}>{[c.country, c.segment].filter(Boolean).join(" · ")}{c.notes ? " — " + c.notes : ""}</div>
              </div>
              <button onClick={() => setCoForm(c)} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted }}><Edit3 size={15} /></button>
              <button onClick={() => confirm(`"${c.name}" wordt uit je overgenomen bedrijven verwijderd.`, () => saveCompanies(companies.filter(x => x.id !== c.id)))} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>
            </div>
          ))}
        </div>
      </Card>
      </>}
    </div>
  );
}
const darkInput = { padding: "11px 13px", borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", get border() { return `1.5px solid ${C.line}`; }, get background() { return C.card; }, get color() { return C.ink; } };


/* ============================== SETTINGS ============================== */
// Instellen van het logo linksboven (globaal, geldt voor de hele tool).
function LogoSettings({ refreshLogo, confirm }) {
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [msg, setMsg] = useState("");
  const [ver, setVer] = useState(0);
  const [hasCustom, setHasCustom] = useState(false);
  const fileRef = useRef();
  useEffect(() => {
    fetch(`/api/app/logo?probe=1`, { credentials: "include" }).then(r => setHasCustom(r.ok)).catch(() => setHasCustom(false));
  }, [ver]);
  const upload = async (e) => {
    const f = e.target.files?.[0]; if (!f) return;
    setBusy(true); setErr(""); setMsg("");
    try {
      const buf = await f.arrayBuffer();
      const r = await fetch("/api/app/logo?filename=" + encodeURIComponent(f.name), { method: "POST", credentials: "include", headers: { "Content-Type": "application/octet-stream" }, body: buf });
      if (!r.ok) { const d = await r.json().catch(() => ({})); setErr(d.error || "Upload mislukt."); }
      else { setMsg("Logo opgeslagen."); setVer(v => v + 1); if (refreshLogo) refreshLogo(); setTimeout(() => setMsg(""), 2000); }
    } catch { setErr("Upload mislukt."); }
    if (fileRef.current) fileRef.current.value = "";
    setBusy(false);
  };
  const reset = () => confirm("Terug naar het standaardlogo?", async () => {
    try { await fetch("/api/app/logo/delete", { method: "POST", credentials: "include" }); setVer(v => v + 1); if (refreshLogo) refreshLogo(); setMsg("Terug naar standaardlogo."); setTimeout(() => setMsg(""), 2000); } catch {}
  });
  return (
    <Card>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Image size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Logo</span>
        {msg && <span style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 12, fontWeight: 700, color: C.good }}><Check size={13} /> {msg}</span>}
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.5 }}>Stel het logo in dat linksboven in de tool verschijnt. Dit geldt voor de hele tool. Het logo staat op een donkere achtergrond, dus gebruik bij voorkeur een lichte of witte versie van je logo (PNG met transparante achtergrond werkt het best).</div>
      <div style={{ display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap" }}>
        <div style={{ background: C.surface, border: `1px solid ${C.line}`, borderRadius: 12, padding: "14px 16px" }}>
          <img src={`/api/app/logo?v=${ver}`} alt="logo" onError={(e) => { if (!e.target.src.endsWith(LOGO)) e.target.src = LOGO; }} style={{ height: 46, maxWidth: 260, objectFit: "contain", display: "block" }} />
        </div>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <label style={{ ...miniBtn(C.yellow, "#fff"), cursor: busy ? "default" : "pointer", opacity: busy ? .6 : 1 }}>
            <Upload size={13} /> {busy ? "Bezig…" : "Logo uploaden"}
            <input ref={fileRef} type="file" accept="image/png,image/jpeg,image/webp,image/svg+xml" onChange={upload} disabled={busy} style={{ display: "none" }} />
          </label>
          {hasCustom && <Btn size="sm" variant="ghost" onClick={reset}>Terug naar standaard</Btn>}
        </div>
      </div>
      {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{err}</div>}
    </Card>
  );
}

// Eigen AI-sleutel per omgeving. Daarmee verbruikt een klant zijn eigen tegoed
// in plaats van dat van de eigenaar van de tool.
function ApiKeySettings({ confirm }) {
  const [status, setStatus] = useState(null);
  const [key, setKey] = useState("");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [msg, setMsg] = useState("");

  const load = async () => {
    try { const r = await fetch("/api/apikey/status", { credentials: "include" }); setStatus(await r.json()); }
    catch { setStatus({ own: false, fallback: false }); }
  };
  useEffect(() => { load(); }, []);

  const save = async () => {
    setBusy(true); setErr(""); setMsg("");
    try {
      const r = await fetch("/api/apikey", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ key: key.trim() }),
      });
      const d = await r.json();
      if (!r.ok) setErr(d.error || "De sleutel kon niet worden opgeslagen.");
      else { setKey(""); setMsg("Sleutel opgeslagen en gecontroleerd."); await load(); setTimeout(() => setMsg(""), 2500); }
    } catch { setErr("Opslaan mislukte."); }
    setBusy(false);
  };

  const remove = () => confirm("De eigen sleutel van deze omgeving wordt verwijderd. Daarna wordt de standaardsleutel van de tool gebruikt, als die er is.", async () => {
    try { await fetch("/api/apikey/delete", { method: "POST", credentials: "include" }); await load(); setMsg("Sleutel verwijderd."); setTimeout(() => setMsg(""), 2500); } catch {}
  });

  return (
    <Card>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Zap size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>AI-sleutel</span>
        {msg && <span style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 12, fontWeight: 700, color: C.good }}><Check size={13} /> {msg}</span>}
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>
        Deze omgeving kan een eigen Anthropic-sleutel gebruiken. Alle AI-functies in deze omgeving verbruiken dan het tegoed van dat account. Vul je niets in, dan wordt de standaardsleutel van de tool gebruikt. De sleutel wordt versleuteld bewaard en is daarna niet meer op te vragen.
      </div>

      {status === null ? <div style={{ fontSize: 13, color: C.muted }}>Laden…</div> : <>
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "11px 13px", border: `1px solid ${C.line}`, borderRadius: 10, marginBottom: 16, flexWrap: "wrap" }}>
          <Tag color={status.own ? C.good : status.fallback ? C.blue : C.bad}>
            {status.own ? "Eigen sleutel actief" : status.fallback ? "Standaardsleutel van de tool" : "Geen sleutel"}
          </Tag>
          {status.own && <span style={{ fontSize: 12.5, color: C.muted }}>{status.hint}</span>}
          {status.own && <Btn size="sm" variant="danger" onClick={remove}>Verwijderen</Btn>}
        </div>

        <label style={{ display: "block", fontSize: 12.5, fontWeight: 700, color: C.muted, marginBottom: 6, letterSpacing: .3, textTransform: "uppercase" }}>
          {status.own ? "Sleutel vervangen" : "Eigen sleutel invullen"}
        </label>
        <input type="password" value={key} onChange={e => { setKey(e.target.value); setErr(""); }} placeholder="sk-ant-…" autoComplete="off"
          style={{ width: "100%", boxSizing: "border-box", padding: "11px 13px", border: `1.5px solid ${C.line}`, borderRadius: 10, fontFamily: FONT_BODY, fontSize: 14, outline: "none", marginBottom: 10 }} />
        <Btn variant="yellow" icon={busy ? Loader2 : Check} onClick={save} disabled={busy || !key.trim()}>
          {busy ? "Controleren…" : "Opslaan en controleren"}
        </Btn>
        {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10 }}>{err}</div>}

        <div style={{ marginTop: 18, padding: "12px 14px", background: C.paper, borderRadius: 10, fontSize: 12.5, color: C.ink2, lineHeight: 1.6 }}>
          <div style={{ fontWeight: 700, color: C.ink, marginBottom: 4 }}>Zelf een sleutel aanmaken</div>
          Maak een account op console.anthropic.com, zet er tegoed op, en maak onder API keys een nieuwe sleutel aan. Kopieer die hier naartoe. Je kunt de sleutel op elk moment bij Anthropic weer intrekken.
        </div>
      </>}
    </Card>
  );
}

// Binnengekomen demo-aanvragen, zodat je ze kunt opvolgen en afvinken.
function DemoRequests({ confirm }) {
  const [list, setList] = useState(null);
  const load = async () => {
    try { const r = await fetch("/api/demo-requests", { credentials: "include" }); const d = await r.json(); setList(d.requests || []); }
    catch { setList([]); }
  };
  useEffect(() => { load(); }, []);
  const setStatus = async (id, status) => {
    setList(l => l.map(x => x.id === id ? { ...x, status } : x));
    try { await fetch(`/api/demo-requests/${id}/status`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status }) }); } catch {}
  };
  const del = (r) => confirm(`Aanvraag van ${r.name} verwijderen?`, async () => {
    try { await fetch(`/api/demo-requests/${r.id}/delete`, { method: "POST", credentials: "include" }); await load(); } catch {}
  });
  const STATUS = [["nieuw", "Nieuw", C.yellowDark], ["contact", "Contact gehad", C.blue], ["klaar", "Afgerond", C.good]];

  return (
    <Card>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Send size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>Demo-aanvragen</span>
        {list && list.length > 0 && <Tag color={C.yellowDark}>{list.filter(r => r.status === "nieuw").length} nieuw</Tag>}
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>
        Aanvragen die binnenkomen via het formulier op <b style={{ color: C.ink2 }}>/demo.html</b>. Je krijgt er ook een e-mail van; de aanvrager krijgt een bevestiging.
      </div>

      {list === null ? <div style={{ fontSize: 13, color: C.muted }}>Laden…</div>
        : list.length === 0 ? <Empty icon={Send} title="Nog geen aanvragen" sub="Zodra iemand het formulier invult, verschijnt de aanvraag hier." />
        : <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {list.map(r => {
              const st = STATUS.find(s => s[0] === r.status) || STATUS[0];
              return (
                <div key={r.id} style={{ border: `1px solid ${C.line}`, borderRadius: 11, padding: "13px 15px" }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: 6 }}>
                    <span style={{ fontWeight: 700, fontSize: 14.5 }}>{r.name}</span>
                    {r.company && <span style={{ fontSize: 13, color: C.muted }}>· {r.company}</span>}
                    <Tag color={st[2]}>{st[1]}</Tag>
                    <span style={{ marginLeft: "auto", fontSize: 12, color: C.muted }}>{new Date(r.at).toLocaleDateString("nl-NL", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}</span>
                  </div>
                  <div style={{ fontSize: 13, color: C.ink2, marginBottom: r.message ? 8 : 10 }}>
                    <a href={`mailto:${r.email}`} style={{ color: C.blue, textDecoration: "none" }}>{r.email}</a>
                    {r.phone && <span style={{ color: C.muted }}> · {r.phone}</span>}
                  </div>
                  {r.message && <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.55, padding: "9px 11px", background: C.paper, borderRadius: 8, marginBottom: 10 }}>{r.message}</div>}
                  <div style={{ display: "flex", gap: 7, flexWrap: "wrap" }}>
                    {STATUS.map(([id, lbl]) => (
                      <button key={id} onClick={() => setStatus(r.id, id)} style={{
                        padding: "5px 11px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 12, fontWeight: 600,
                        border: `1px solid ${r.status === id ? "transparent" : C.line}`,
                        background: r.status === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
                        color: r.status === id ? "#fff" : C.muted,
                      }}>{lbl}</button>
                    ))}
                    <button onClick={() => del(r)} style={{ ...iconBtn(C.bad), marginLeft: "auto" }}><Trash2 size={15} /></button>
                  </div>
                </div>
              );
            })}
          </div>}
    </Card>
  );
}

// WordPress-koppeling: verbinding maken en je eigen paginatypen inrichten, zodat de tool
// weet waar een klantcase, blog of nieuwsbericht op je site terechtkomt.
function WordPressSettings({ confirm }) {
  const [st, setSt] = useState(null);
  const [form, setForm] = useState({ site: "", user: "", pass: "", token: "", defaultStatus: "draft" });
  const [method, setMethod] = useState("plugin");
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const [msg, setMsg] = useState("");
  const [struct, setStruct] = useState(null);
  const [types, setTypes] = useState([]);

  const load = async () => {
    try {
      const r = await fetch("/api/wordpress/status", { credentials: "include" });
      const d = await r.json();
      setSt(d);
      setTypes(d.pageTypes || []);
      setForm(f => ({ ...f, site: d.site || "", user: d.user || "", defaultStatus: d.defaultStatus || "draft" }));
      if (d.configured && d.mode) setMethod(d.mode);
      if (d.configured) loadStruct();
    } catch { setSt({ configured: false }); }
  };
  const loadStruct = async () => {
    try { const r = await fetch("/api/wordpress/structure", { credentials: "include" }); if (r.ok) setStruct(await r.json()); }
    catch {}
  };

  // Eigen velden per berichttype ophalen. Veel sites zetten de tekst niet in het
  // standaard contentveld maar in een eigen veld; dan moet je kunnen kiezen welk.
  const [fieldsByType, setFieldsByType] = useState({});
  const loadFields = async (restBase) => {
    if (!restBase || fieldsByType[restBase]) return;
    // Het berichttype achter deze rest_base opzoeken.
    const pt = (struct?.postTypes || []).find(x => x.restBase === restBase);
    const slug = pt?.slug || "";
    try {
      const r = await fetch(`/api/wordpress/fields?postType=${encodeURIComponent(slug)}`, { credentials: "include" });
      const d = await r.json();
      setFieldsByType(f => ({ ...f, [restBase]: d.acf ? (d.groups || []) : null }));
    } catch { setFieldsByType(f => ({ ...f, [restBase]: null })); }
  };
  useEffect(() => {
    if (!struct) return;
    [...new Set(types.map(t => t.restBase).filter(Boolean))].forEach(loadFields);
  }, [struct, types.length]);
  useEffect(() => { load(); }, []);

  const save = async () => {
    setBusy(true); setErr(""); setMsg("");
    try {
      const r = await fetch("/api/wordpress/config", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify(form),
      });
      const d = await r.json();
      if (!r.ok) setErr(d.error || "Verbinden mislukte.");
      else { setMsg(`Verbonden als ${d.as}.`); setForm(f => ({ ...f, pass: "" })); await load(); setTimeout(() => setMsg(""), 3000); }
    } catch { setErr("Verbinden mislukte."); }
    setBusy(false);
  };
  const savePlugin = async () => {
    setBusy(true); setErr(""); setMsg("");
    try {
      const r = await fetch("/api/wordpress/connect-plugin", {
        method: "POST", credentials: "include", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ site: form.site, token: form.token, defaultStatus: form.defaultStatus }),
      });
      const d = await r.json();
      if (!r.ok) setErr(d.error || "Verbinden mislukte.");
      else { setMsg(`Verbonden met ${d.as}.`); setForm(f => ({ ...f, token: "" })); await load(); setTimeout(() => setMsg(""), 3000); }
    } catch { setErr("Verbinden mislukte."); }
    setBusy(false);
  };

  const disconnect = () => confirm("De WordPress-koppeling van deze omgeving wordt verwijderd.", async () => {
    try { await fetch("/api/wordpress/config/delete", { method: "POST", credentials: "include" }); setStruct(null); await load(); } catch {}
  });

  const saveTypes = async (next) => {
    setTypes(next);
    try { await fetch("/api/wordpress/pagetypes", { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ pageTypes: next }) }); } catch {}
  };
  const addType = () => saveTypes([...types, { id: "pt_" + Date.now().toString(36), name: "", restBase: "posts", categoryId: null, pathPrefix: "", structure: "", blueprint: [] }]);

  // Een bestaande pagina ontleden: welke velden zitten erop en hoe zijn ze gevuld?
  // Daarmee weet de tool hoe zo'n pagina eruitziet en kan hij nieuwe content in
  // dezelfde vorm aanleveren.
  const [sampleUrl, setSampleUrl] = useState({});
  const [sampleBusy, setSampleBusy] = useState("");
  const [sampleErr, setSampleErr] = useState({});
  const analyseSample = async (i, t) => {
    setSampleBusy(t.id); setSampleErr(e => ({ ...e, [t.id]: "" }));
    try {
      const pt = (struct?.postTypes || []).find(x => x.restBase === t.restBase);
      const qs = new URLSearchParams();
      if ((sampleUrl[t.id] || "").trim()) qs.set("url", sampleUrl[t.id].trim());
      else if (pt?.slug) qs.set("postType", pt.slug);
      const r = await fetch(`/api/wordpress/sample?${qs.toString()}`, { credentials: "include" });
      const d = await r.json();
      if (!r.ok) { setSampleErr(e => ({ ...e, [t.id]: d.error || "Ontleden mislukte." })); setSampleBusy(""); return; }
      // Alleen velden met tekst erin zijn interessant als sjabloon.
      const bp = (d.fields || []).filter(f => f.filled).map(f => ({
        name: f.name, key: f.key || "", label: f.label, type: f.type, chars: f.chars, sample: f.sample,
        use: true,
      }));
      if (!bp.length) {
        setSampleErr(e => ({ ...e, [t.id]: `Op "${d.title}" staan geen gevulde eigen velden. Gebruikt dit type misschien gewoon het contentveld?` }));
      }
      updType(i, { blueprint: bp, sampleTitle: d.title || "", sampleUrl: d.url || "" });
    } catch { setSampleErr(e => ({ ...e, [t.id]: "Ontleden mislukte." })); }
    setSampleBusy("");
  };
  const toggleBpField = (i, t, name) => {
    updType(i, { blueprint: (t.blueprint || []).map(f => f.name === name ? { ...f, use: !f.use } : f) });
  };
  const updType = (i, patch) => saveTypes(types.map((t, j) => j === i ? { ...t, ...patch } : t));
  const delType = (i) => saveTypes(types.filter((_, j) => j !== i));

  return (
    <Card>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: 6 }}>
        <Globe size={18} style={{ color: C.yellowDark }} /><span style={{ fontWeight: 700, fontSize: 16 }}>WordPress</span>
        {st?.configured && <Tag color={C.good}>Verbonden</Tag>}
        {msg && <span style={{ display: "flex", alignItems: "center", gap: 4, fontSize: 12, fontWeight: 700, color: C.good }}><Check size={13} /> {msg}</span>}
      </div>
      <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 16, lineHeight: 1.55 }}>
        Koppel je site zodat content direct als concept in WordPress kan worden aangemaakt, in de juiste categorie. Gebruik een applicatiewachtwoord: dat maak je aan in WordPress onder Gebruikers → jouw profiel → Applicatiewachtwoorden. Je gewone wachtwoord heb je hier niet voor nodig; het applicatiewachtwoord wordt versleuteld bewaard.
      </div>

      {st === null ? <div style={{ fontSize: 13, color: C.muted }}>Laden…</div> : <>
        {/* Keuze tussen de twee manieren van koppelen */}
        <div style={{ display: "flex", gap: 8, marginBottom: 16, flexWrap: "wrap" }}>
          {[["plugin", "Via plugin (aanbevolen)"], ["app", "Via applicatiewachtwoord"]].map(([id, lbl]) => (
            <button key={id} onClick={() => { setMethod(id); setErr(""); }} style={{
              padding: "8px 14px", borderRadius: 50, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13, fontWeight: 700,
              border: `1px solid ${method === id ? "transparent" : C.line}`,
              background: method === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent",
              color: method === id ? "#fff" : C.muted,
            }}>{lbl}</button>
          ))}
        </div>

        {method === "plugin" ? (
          <div>
            <div style={{ fontSize: 12.5, color: C.ink2, marginBottom: 14, lineHeight: 1.6 }}>
              Deze manier werkt op elke site, ook als de webserver de standaard inlog-header weghaalt (een veelvoorkomend probleem waar je niets aan kunt doen zonder je server aan te passen). Je installeert eenmalig een klein plugintje; daarna heb je alleen het adres en een koppelcode nodig.
            </div>

            <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 16 }}>
              {[
                ["Download de plugin", <a key="dl" href="/api/wordpress/plugin" style={{ textDecoration: "none" }}><Btn size="sm" variant="ghost" icon={Download}>Plugin downloaden</Btn></a>],
                ["Ga in WordPress naar Plugins → Nieuwe plugin → Plugin uploaden, kies het zip-bestand en klik op Nu installeren.", null],
                ["Klik daarna op Plugin activeren.", null],
                ["Ga naar Instellingen → Brand Command Center. Daar staan het adres en de koppelcode.", null],
                ["Kopieer die twee hieronder en klik op verbinden.", null],
              ].map(([txt, extra], i) => (
                <div key={i} style={{ display: "flex", gap: 11, alignItems: "flex-start" }}>
                  <span style={{ width: 22, height: 22, borderRadius: 11, background: C.yellow + "22", color: C.yellowDark, fontSize: 12, fontWeight: 700, display: "grid", placeItems: "center", flexShrink: 0 }}>{i + 1}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13, color: C.ink2, lineHeight: 1.5 }}>{txt}</div>
                    {extra && <div style={{ marginTop: 7 }}>{extra}</div>}
                  </div>
                </div>
              ))}
            </div>

            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 14 }}>
              <div>
                <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Adres van je site</label>
                <input value={form.site} onChange={e => setForm(f => ({ ...f, site: e.target.value }))} placeholder="https://www.whitevision.com"
                  style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              </div>
              <div>
                <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Aanmaken als</label>
                <select value={form.defaultStatus} onChange={e => setForm(f => ({ ...f, defaultStatus: e.target.value }))}
                  style={{ width: "100%", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", background: C.card }}>
                  <option value="draft">Concept (aanbevolen)</option>
                  <option value="publish">Direct gepubliceerd</option>
                </select>
              </div>
            </div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Koppelcode</label>
            <input value={form.token} onChange={e => setForm(f => ({ ...f, token: e.target.value }))} placeholder="plak hier de code uit WordPress" autoComplete="off"
              style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: "monospace", fontSize: 13, outline: "none", marginBottom: 14 }} />

            <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
              <Btn variant="yellow" icon={busy ? Loader2 : Check} onClick={savePlugin} disabled={busy || !form.site || !form.token}>
                {busy ? "Verbinding testen…" : st.configured ? "Bijwerken en testen" : "Verbinden en testen"}
              </Btn>
              {st.configured && <Btn variant="danger" size="sm" onClick={disconnect}>Ontkoppelen</Btn>}
            </div>
            {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10, lineHeight: 1.55 }}>{err}</div>}
          </div>
        ) : <>
        <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 14, lineHeight: 1.6 }}>
          Deze manier gebruikt een applicatiewachtwoord uit WordPress. Hij is sneller op te zetten, maar werkt niet op elke server: veel webservers verwijderen de inlog-header, en dan mislukt de koppeling. Krijg je de melding dat je "niet ingelogd" bent, gebruik dan de plugin.
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 10 }}>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Adres van je site</label>
            <input value={form.site} onChange={e => setForm(f => ({ ...f, site: e.target.value }))} placeholder="https://www.whitevision.nl"
              style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Gebruikersnaam</label>
            <input value={form.user} onChange={e => setForm(f => ({ ...f, user: e.target.value }))} placeholder="je WordPress-gebruikersnaam" autoComplete="off"
              style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
          </div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 14 }}>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>{st.configured ? "Applicatiewachtwoord vervangen" : "Applicatiewachtwoord"}</label>
            <input type="password" value={form.pass} onChange={e => setForm(f => ({ ...f, pass: e.target.value }))} placeholder="xxxx xxxx xxxx xxxx" autoComplete="new-password"
              style={{ width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
          </div>
          <div>
            <label style={{ display: "block", fontSize: 12, fontWeight: 700, color: C.muted, marginBottom: 5, textTransform: "uppercase" }}>Aanmaken als</label>
            <select value={form.defaultStatus} onChange={e => setForm(f => ({ ...f, defaultStatus: e.target.value }))}
              style={{ width: "100%", padding: "10px 12px", border: `1.5px solid ${C.line}`, borderRadius: 9, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none", background: C.card }}>
              <option value="draft">Concept (aanbevolen)</option>
              <option value="publish">Direct gepubliceerd</option>
            </select>
          </div>
        </div>
        <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
          <Btn variant="yellow" icon={busy ? Loader2 : Check} onClick={save} disabled={busy || !form.site || !form.user || (!form.pass && !st.configured)}>
            {busy ? "Verbinding testen…" : st.configured ? "Bijwerken en testen" : "Verbinden en testen"}
          </Btn>
          {st.configured && <Btn variant="danger" size="sm" onClick={disconnect}>Ontkoppelen</Btn>}
        </div>
        {err && <div style={{ fontSize: 12.5, color: C.bad, marginTop: 10, lineHeight: 1.55 }}>{err}</div>}
        </>}

        {st.configured && (
          <div style={{ marginTop: 22, paddingTop: 18, borderTop: `1px solid ${C.line}` }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 6, flexWrap: "wrap", gap: 8 }}>
              <span style={{ fontWeight: 700, fontSize: 14.5 }}>Paginatypen</span>
              <Btn size="sm" variant="ghost" icon={Plus} onClick={addType}>Type toevoegen</Btn>
            </div>
            <div style={{ fontSize: 12.5, color: C.muted, marginBottom: 12, lineHeight: 1.55 }}>
              Richt hier in waar welk soort pagina terechtkomt. Bijvoorbeeld: "Klantcase" gaat naar berichttype Pagina's met pad <b style={{ color: C.ink2 }}>/customer-cases</b>. In het migratieplan kies je per pagina het type, en dan weet de tool waar het heen moet en welke URL erbij hoort.
            </div>

            {types.length === 0 && <div style={{ fontSize: 13, color: C.muted, marginBottom: 10 }}>Nog geen paginatypen ingericht.</div>}
            <div style={{ display: "flex", flexDirection: "column", gap: 9 }}>
              {types.map((t, i) => (
                <div key={t.id} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: "10px 12px" }}>
                  <div style={{ display: "grid", gridTemplateColumns: "1.2fr 1fr 1fr 1.2fr auto", gap: 8, alignItems: "center" }}>
                    <input value={t.name} onChange={e => updType(i, { name: e.target.value })} placeholder="Naam, bijv. Klantcase"
                      style={{ padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, fontWeight: 700, outline: "none", background: C.card }} />
                    <select value={t.restBase} onChange={e => updType(i, { restBase: e.target.value })}
                      style={{ padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card }}>
                      {(struct?.postTypes || [{ restBase: "posts", label: "Berichten" }, { restBase: "pages", label: "Pagina's" }]).map(pt => (
                        <option key={pt.restBase} value={pt.restBase}>{pt.label}</option>
                      ))}
                    </select>
                    <select value={t.categoryId || ""} onChange={e => updType(i, { categoryId: e.target.value ? Number(e.target.value) : null })}
                      style={{ padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12.5, outline: "none", background: C.card }}>
                      <option value="">Geen categorie</option>
                      {(struct?.categories || []).map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
                    </select>
                    <input value={t.pathPrefix} onChange={e => updType(i, { pathPrefix: e.target.value })} placeholder="/customer-cases"
                      style={{ padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: "monospace", fontSize: 12, outline: "none", background: C.card }} />
                    <button onClick={() => delType(i)} style={iconBtn(C.bad)}><Trash2 size={15} /></button>
                  </div>
                  {/* Opbouw leren van een bestaande pagina */}
                  <div style={{ marginTop: 10, padding: "11px 12px", border: `1px solid ${(t.blueprint || []).length ? C.line : C.yellow}`, borderRadius: 9, background: (t.blueprint || []).length ? "transparent" : C.yellow + "0A" }}>
                    <div style={{ fontSize: 11.5, fontWeight: 700, color: C.muted, marginBottom: 6, textTransform: "uppercase" }}>Opbouw van dit paginatype</div>
                    {(t.blueprint || []).length === 0 ? (
                      <div style={{ fontSize: 12, color: C.ink2, lineHeight: 1.55, marginBottom: 9 }}>
                        Laat de tool een bestaande pagina van dit type ontleden. Hij kijkt dan welke velden er zijn en hoe die zijn ingevuld, en kan nieuwe content in dezelfde vorm aanleveren. Zonder dit komt content in het standaard contentveld, en bij een template met eigen velden blijft de pagina dan leeg.
                      </div>
                    ) : (
                      <div style={{ fontSize: 12, color: C.ink2, lineHeight: 1.5, marginBottom: 9 }}>
                        Ontleed op basis van <b>{t.sampleTitle || "een bestaande pagina"}</b>. Vink aan welke velden gevuld moeten worden.
                      </div>
                    )}

                    <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: (t.blueprint || []).length ? 10 : 0 }}>
                      <input value={sampleUrl[t.id] || ""} onChange={e => setSampleUrl(u => ({ ...u, [t.id]: e.target.value }))}
                        placeholder="URL van een voorbeeldpagina (leeg = nieuwste van dit type)"
                        style={{ flex: 1, minWidth: 220, boxSizing: "border-box", padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12, outline: "none", background: C.card }} />
                      <Btn size="sm" variant={(t.blueprint || []).length ? "ghost" : "yellow"} icon={sampleBusy === t.id ? Loader2 : Microscope}
                        onClick={() => analyseSample(i, t)} disabled={!!sampleBusy}>
                        {sampleBusy === t.id ? "Ontleden…" : (t.blueprint || []).length ? "Opnieuw ontleden" : "Ontleed voorbeeldpagina"}
                      </Btn>
                    </div>
                    {sampleErr[t.id] && <div style={{ fontSize: 11.5, color: C.bad, marginTop: 7, lineHeight: 1.5 }}>{sampleErr[t.id]}</div>}

                    {(t.blueprint || []).length > 0 && (
                      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                        {t.blueprint.map(f => (
                          <label key={f.name} style={{ display: "flex", gap: 9, alignItems: "flex-start", padding: "7px 9px", border: `1px solid ${f.use ? C.yellow + "55" : C.line}`, borderRadius: 7, cursor: "pointer", background: f.use ? C.yellow + "0A" : "transparent" }}>
                            <input type="checkbox" checked={!!f.use} onChange={() => toggleBpField(i, t, f.name)} style={{ marginTop: 3 }} />
                            <div style={{ flex: 1, minWidth: 0 }}>
                              <div style={{ display: "flex", alignItems: "center", gap: 7, flexWrap: "wrap" }}>
                                <span style={{ fontSize: 12.5, fontWeight: 700 }}>{f.label}</span>
                                <Tag color={C.muted}>{f.type}</Tag>
                                <span style={{ fontSize: 11, color: C.muted }}>ongeveer {f.chars} tekens</span>
                              </div>
                              {f.sample && <div style={{ fontSize: 11.5, color: C.muted, lineHeight: 1.45, marginTop: 3, overflow: "hidden", textOverflow: "ellipsis", display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical" }}>{f.sample}</div>}
                            </div>
                          </label>
                        ))}
                      </div>
                    )}
                  </div>

                  <textarea value={t.structure || ""} onChange={e => updType(i, { structure: e.target.value })} rows={2}
                    placeholder="Hoe is dit paginatype opgebouwd? Bijv.: intro, over de partner, wat we samen doen, klantvoordeel, oproep tot actie. Dit gebruikt de tool bij het herschrijven."
                    style={{ width: "100%", boxSizing: "border-box", marginTop: 8, padding: "7px 9px", border: `1px solid ${C.line}`, borderRadius: 7, fontFamily: FONT_BODY, fontSize: 12, outline: "none", background: C.card, resize: "vertical", lineHeight: 1.5 }} />
                </div>
              ))}
            </div>
            {!struct && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 10 }}>Berichttypen en categorieën uit je site worden opgehaald…</div>}
          </div>
        )}
      </>}
    </Card>
  );
}

function SettingsPage({ ctx }) {
  const { users, saveUsers, appSettings, saveAppSettings, confirm, refreshLogo } = ctx;
  const [tab, setTab] = useState(() => {
    const t = (typeof window !== "undefined" && window.__settingsTab) || "users";
    if (typeof window !== "undefined") window.__settingsTab = null;
    return t;
  });
  const [uForm, setUForm] = useState(null);
  const settings = appSettings || { channelLibrary: [] };
  const lib = settings.channelLibrary || [];

  const saveU = () => {
    if (!uForm.name) return;
    if (uForm.id) saveUsers(users.map(u => u.id === uForm.id ? uForm : u));
    else saveUsers([...users, { ...uForm, id: Date.now() }]);
    setUForm(null);
  };
  const setLib = (arr) => saveAppSettings({ ...settings, channelLibrary: arr });
  const addChannel = () => setLib([...lib, { id: Date.now(), label: "", img: null }]);
  const onChannelLogo = (id, e) => { const f = e.target.files?.[0]; if (!f) return; const r = new FileReader(); r.onload = () => setLib(lib.map(c => c.id === id ? { ...c, img: r.result } : c)); r.readAsDataURL(f); e.target.value = ""; };

  const tabBtn = (id, label) => (
    <button onClick={() => setTab(id)} style={{ padding: "8px 14px", borderRadius: 50, border: `1px solid ${tab === id ? "transparent" : C.line}`, cursor: "pointer", fontFamily: FONT_BODY, fontSize: 13.5, fontWeight: tab === id ? 700 : 500, background: tab === id ? `linear-gradient(135deg, ${C.yellow}, ${C.yellowDark})` : "transparent", color: tab === id ? "#fff" : C.muted, boxShadow: tab === id ? "0 4px 20px rgba(139,92,246,.30)" : "none" }}>{label}</button>
  );

  return (
    <div>
      <PageHead title="Instellingen" sub="Beheer gebruikers, je kanaalbibliotheek en koppelingen met externe tools." />
      <div style={{ display: "flex", gap: 6, marginBottom: 20, background: C.paper, padding: 5, borderRadius: 11, width: "fit-content" }}>
        {tabBtn("users", "Gebruikers")}
        {tabBtn("omgevingen", "Omgevingen")}
        {tabBtn("notifications", "Notificaties")}
        {tabBtn("channels", "Kanaalbibliotheek")}
        {tabBtn("logo", "Logo")}
        {tabBtn("apikey", "AI-sleutel")}
        {tabBtn("wordpress", "WordPress")}
        {tabBtn("usage", "Verbruik")}
        {tabBtn("integrations", "Koppelingen")}
      </div>

      {tab === "logo" && <LogoSettings refreshLogo={refreshLogo} confirm={confirm} />}
      {tab === "apikey" && <ApiKeySettings confirm={confirm} />}
      {tab === "wordpress" && <WordPressSettings confirm={confirm} />}
      {tab === "usage" && <UsagePanel />}

      {tab === "omgevingen" && <WorkspacesSettings confirm={confirm} />}

      {tab === "users" && <div>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 14 }}>
          <div style={{ fontSize: 13, color: C.muted }}>Beheer wie toegang heeft en hun rol.</div>
          <Btn size="sm" variant="ghost" icon={Plus} onClick={() => setUForm({ name: "", email: "", role: "Teamlid" })}>Gebruiker</Btn>
        </div>
        {uForm && (
          <div style={{ padding: 16, background: C.paper, borderRadius: 12, marginBottom: 14, border: `1px solid ${C.line}` }}>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 10 }}>
              <Field label="Naam" value={uForm.name} onChange={v => setUForm({ ...uForm, name: v })} />
              <Field label="E-mail" value={uForm.email} onChange={v => setUForm({ ...uForm, email: v })} />
              <Field label="Rol" value={uForm.role} onChange={v => setUForm({ ...uForm, role: v })} />
            </div>
            <div style={{ display: "flex", gap: 10 }}><Btn variant="yellow" onClick={saveU}>Opslaan</Btn><Btn variant="ghost" onClick={() => setUForm(null)}>Annuleren</Btn></div>
          </div>
        )}
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {(users || []).map(u => (
            <div key={u.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 14px", background: C.paper, borderRadius: 11, border: `1px solid ${C.line}` }}>
              <div style={{ width: 36, height: 36, borderRadius: 99, background: C.yellow, display: "grid", placeItems: "center", color: C.ink, fontWeight: 700 }}>{u.name?.[0]?.toUpperCase() || "?"}</div>
              <div style={{ flex: 1 }}><div style={{ fontWeight: 700, fontSize: 14.5 }}>{u.name}</div><div style={{ fontSize: 12.5, color: C.muted }}>{u.email} · {u.role}</div></div>
              <button onClick={() => setUForm(u)} style={{ background: "none", border: "none", cursor: "pointer", color: C.muted }}><Edit3 size={15} /></button>
              {(users || []).length > 1 && <button onClick={() => confirm(`Gebruiker "${u.name || ""}" wordt verwijderd.`, () => saveUsers(users.filter(x => x.id !== u.id)))} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={15} /></button>}
            </div>
          ))}
        </div>
      </div>}

      {tab === "channels" && <div>
        <div style={{ fontSize: 13, color: C.muted, marginBottom: 14, lineHeight: 1.5 }}>Stel hier je kanalen en hun logo's één keer in. Bij Campagnes kun je deze kanalen kiezen zonder telkens opnieuw een logo te uploaden.</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 14 }}>
          {lib.map(c => (
            <div key={c.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", background: C.paper, borderRadius: 11, border: `1px solid ${C.line}` }}>
              <label title="Logo uploaden" style={{ width: 38, height: 38, flexShrink: 0, border: `1px solid ${C.line}`, borderRadius: 8, display: "grid", placeItems: "center", cursor: "pointer", background: C.card, overflow: "hidden" }}>
                {c.img ? <img src={c.img} alt="" style={{ maxWidth: "100%", maxHeight: "100%", objectFit: "contain" }} /> : <Upload size={15} style={{ color: C.muted }} />}
                <input type="file" accept="image/*" onChange={e => onChannelLogo(c.id, e)} style={{ display: "none" }} />
              </label>
              <input value={c.label} onChange={e => setLib(lib.map(x => x.id === c.id ? { ...x, label: e.target.value } : x))} placeholder="Naam van het kanaal (bijv. LinkedIn)"
                style={{ flex: 1, padding: "9px 11px", border: `1px solid ${C.line}`, borderRadius: 8, fontFamily: FONT_BODY, fontSize: 13.5, outline: "none" }} />
              <button onClick={() => setLib(lib.filter(x => x.id !== c.id))} style={{ background: "none", border: "none", cursor: "pointer", color: C.bad }}><Trash2 size={16} /></button>
            </div>
          ))}
          {lib.length === 0 && <div style={{ fontSize: 13, color: C.muted }}>Nog geen kanalen ingesteld.</div>}
        </div>
        <Btn variant="ghost" size="sm" icon={Plus} onClick={addChannel}>Kanaal toevoegen</Btn>
      </div>}

      {tab === "notifications" && <div><NotificationSettings /></div>}

      {tab === "integrations" && <IntegrationsTab confirm={confirm} />}
    </div>
  );
}

// Koppelingen-tab: beheert het verbinden/ontkoppelen van Google (Analytics + Search Console).
function IntegrationsTab({ confirm }) {
  const [ga, setGa] = useState({ checked: false, connected: false, connectedAt: null, propertyId: "", gscSite: "" });
  const [gscOk, setGscOk] = useState(null); // null=onbekend, true/false na check
  const [ads, setAds] = useState({ tokenConfigured: false, customerId: "", ok: null });

  const loadStatus = async () => {
    try {
      const r = await fetch("/api/google/status", { credentials: "include" });
      const d = await r.json();
      setGa({ checked: true, connected: !!d.connected, connectedAt: d.connectedAt, propertyId: d.propertyId || "", gscSite: d.gscSite || "" });
      if (d.connected) {
        // Test of de Search Console-toestemming aanwezig is.
        try { const s = await fetch("/api/google/gsc/sites", { credentials: "include" }); setGscOk(s.ok); } catch { setGscOk(false); }
        // Google Ads-status
        try {
          const a = await fetch("/api/google/ads/status", { credentials: "include" });
          const ad = await a.json();
          let ok = null;
          if (ad.tokenConfigured) { try { const c = await fetch("/api/google/ads/customers", { credentials: "include" }); ok = c.ok; } catch { ok = false; } }
          setAds({ tokenConfigured: !!ad.tokenConfigured, customerId: ad.customerId || "", ok });
        } catch {}
      }
    } catch { setGa({ checked: true, connected: false }); }
  };
  useEffect(() => { loadStatus(); }, []);

  const disconnect = async () => {
    confirm("Google ontkoppelen? Je moet daarna opnieuw verbinden om data op te halen.", async () => {
      try { await fetch("/api/google/disconnect", { method: "POST", credentials: "include" }); } catch {}
      setGa(g => ({ ...g, connected: false })); setGscOk(null);
    });
  };

  const OTHERS = [
    { name: "Leadinfo", desc: "Bedrijfsherkenning van websitebezoekers." },
    { name: "LinkedIn Ads", desc: "B2B-advertentiecampagnes." },
    { name: "Microsoft Ads", desc: "Bing-zoekcampagnes." },
    { name: "Meta Ads", desc: "Facebook en Instagram." },
  ];

  const row = (title, desc, statusEl, actionEl) => (
    <div style={{ display: "flex", alignItems: "flex-start", gap: 12, padding: "14px 16px", borderBottom: `1px solid ${C.line}` }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontWeight: 700, fontSize: 14.5 }}>{title}</div>
        <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, lineHeight: 1.45 }}>{desc}</div>
      </div>
      <div style={{ flexShrink: 0, display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 8 }}>
        {statusEl}
        {actionEl}
      </div>
    </div>
  );
  const badge = (text, color) => <span style={{ fontSize: 11.5, fontWeight: 700, color, background: color + "1c", padding: "3px 10px", borderRadius: 20, whiteSpace: "nowrap" }}>{text}</span>;
  const connectBtn = (label) => <Btn size="sm" variant="yellow" icon={ArrowUpRight} onClick={() => { window.location.href = "/api/google/auth"; }}>{label}</Btn>;
  const disconnectBtn = <Btn size="sm" variant="ghost" onClick={disconnect}>Ontkoppelen</Btn>;

  return (
    <div>
      <div style={{ fontSize: 13, color: C.muted, marginBottom: 16, lineHeight: 1.5, maxWidth: 720 }}>Koppel externe tools om hun data in de Brand Command Center te gebruiken. De data zelf zie je onder Performance.</div>

      <Card style={{ padding: 0, overflow: "hidden", marginBottom: 16 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "13px 16px", background: C.paper, borderBottom: `1px solid ${C.line}` }}>
          <Globe size={17} style={{ color: C.yellowDark }} />
          <span style={{ fontWeight: 700, fontSize: 14 }}>Google</span>
        </div>
        {row("Google Analytics",
          ga.connected ? `Gekoppeld${ga.propertyId ? " · property " + ga.propertyId : ""}. Kerncijfers staan onder Performance → Analytics.` : "Websiteverkeer, sessies en paginaweergaven.",
          !ga.checked ? null : ga.connected ? badge("Verbonden", C.good) : badge("Niet verbonden", C.muted),
          !ga.checked ? null : ga.connected ? disconnectBtn : connectBtn("Verbinden"))}
        {row("Google Search Console",
          ga.connected
            ? (gscOk === false ? "Verbonden met Google, maar de toestemming voor Search Console ontbreekt nog. Klik op 'Toestemming geven'; je Analytics-koppeling blijft gewoon werken." : `Gekoppeld${ga.gscSite ? " · " + ga.gscSite : ". Kies een site onder Performance → Analytics."}`)
            : "Zoekopdrachten waarop je in Google verschijnt, met vertoningen en posities.",
          !ga.checked ? null : !ga.connected ? badge("Niet verbonden", C.muted) : gscOk === false ? badge("Toestemming nodig", C.bad) : badge("Verbonden", C.good),
          !ga.checked ? null : !ga.connected ? connectBtn("Verbinden") : gscOk === false ? connectBtn("Toestemming geven") : disconnectBtn)}
        {row("Google Ads",
          !ga.connected ? "Campagnes, advertentiegroepen, zoekwoorden, vertoningen, klikken en kosten. Verbind eerst hierboven met Google (via Analytics of Search Console); Google Ads gebruikt dezelfde verbinding en verschijnt dan vanzelf."
            : !ads.tokenConfigured ? "Verbonden met Google, maar er is nog geen Google Ads developer token op de server ingesteld. Die vraag je eenmalig aan bij Google; daarna vult de beheerder hem in."
            : ads.ok === false ? "Token ingesteld, maar het ophalen van accounts lukt nog niet. Verbind opnieuw zodat de Ads-toestemming wordt meegegeven, of controleer of de token is goedgekeurd."
            : `Gekoppeld${ads.customerId ? " · account " + ads.customerId : ". Kies een account onder Performance → Analytics → Ads."}`,
          !ga.checked ? null : !ga.connected ? badge("Niet verbonden", C.muted) : !ads.tokenConfigured ? badge("Token nodig", C.muted) : ads.ok === false ? badge("Aandacht nodig", C.bad) : badge("Verbonden", C.good),
          !ga.checked || !ga.connected ? null : (!ads.tokenConfigured ? null : ads.ok === false ? connectBtn("Opnieuw verbinden") : null))}
        <div style={{ padding: "10px 16px", fontSize: 11.5, color: C.muted, lineHeight: 1.5, background: C.paper }}>Analytics, Search Console en Google Ads gebruiken dezelfde Google-verbinding. Google Ads vraagt daarnaast eenmalig een developer token die Google moet goedkeuren; zolang die ontbreekt, blijft Ads op "Token nodig" staan.</div>
      </Card>

      <div style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: ".04em", marginBottom: 10 }}>Binnenkort</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 12 }}>
        {OTHERS.map(it => (
          <Card key={it.name} style={{ opacity: .7 }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
              <div>
                <div style={{ fontWeight: 700, fontSize: 15 }}>{it.name}</div>
                <div style={{ fontSize: 12.5, color: C.muted, marginTop: 3, lineHeight: 1.45 }}>{it.desc}</div>
              </div>
              <Tag color={C.muted}>binnenkort</Tag>
            </div>
          </Card>
        ))}
      </div>
    </div>
  );
}

/* ============================== MOUNT ============================== */
const root = createRoot(document.getElementById("root"));
root.render(React.createElement(App));
