const { useState, useEffect, useRef } = React;

const QRJAM_PREVIEW_ENDPOINT = "https://www.qrjam.nl/api/spotify-preview.php";
const QRJAM_DEFAULT_PLAYER_URL = window.QRJAM_PLAYER_BASE_URL || "https://www.qrjam.nl/webplayer/";

function qrjamShouldAutoplay() {
  const params = new URLSearchParams(window.location.search);
  return params.get("autoplay") !== "0";
}

function safeDecodeQrjam(value) {
  const text = String(value || "").trim();

  try {
    return decodeURIComponent(text);
  } catch (_) {
    return text;
  }
}

function cleanTrackId(id) {
  const decoded = safeDecodeQrjam(id);
  const m = decoded.match(/([A-Za-z0-9]{22})/);
  return m ? m[1] : "";
}

function extractSpotifyTrackId(input) {
  if (!input) return "";

  let raw = String(input).trim();

  try {
    raw = decodeURIComponent(raw);
  } catch (_) {}

  let m = raw.match(/spotify:track:([A-Za-z0-9]{22})/i);
  if (m) return m[1];

  m = raw.match(/open\.spotify\.com\/(?:track|trck)\/([A-Za-z0-9]{22})/i);
  if (m) return m[1];

  try {
    const url = new URL(raw);
    const nested =
      url.searchParams.get("track_id") ||
      url.searchParams.get("track") ||
      url.searchParams.get("spotify") ||
      url.searchParams.get("spotify_url") ||
      url.searchParams.get("url") ||
      url.searchParams.get("s") ||
      url.searchParams.get("spotify_id");

    if (nested) {
      const nestedId = extractSpotifyTrackId(nested);
      if (nestedId) return nestedId;
    }

    m = url.pathname.match(/\/(?:track|trck)\/([A-Za-z0-9]{22})/i);
    if (m) return m[1];
  } catch (_) {}

  m = raw.match(/([A-Za-z0-9]{22})/);
  return m ? m[1] : "";
}

function spotifyOpenUrlFor(track) {
  const id = extractSpotifyTrackId(track?.spotifyId || track?.spotify_id || track?.spotify_url || track?.sourceUrl || track?.source_url || "");
  return id ? `https://open.spotify.com/track/${id}` : (track?.spotify_url || track?.source_url || "");
}

function qrjamPlayerUrlFromScan(raw) {
  const text = String(raw || "").trim();
  if (!text) return "";

  try {
    const url = new URL(text);

    if (url.hostname.includes("qrjam.nl") && (url.pathname.startsWith("/speel") || url.pathname.startsWith("/play"))) {
      return text;
    }

    const id = extractSpotifyTrackId(text);
    if (id) {
      return `${QRJAM_DEFAULT_PLAYER_URL}?track_id=${encodeURIComponent(id)}&autoplay=1&_=${Date.now()}`;
    }

    return "";
  } catch (_) {
    const id = extractSpotifyTrackId(text);
    return id ? `${QRJAM_DEFAULT_PLAYER_URL}?spotify=${encodeURIComponent(`https://open.spotify.com/track/${id}`)}` : "";
  }
}

const APP_THEMES = {
  party: {
    display: '"Bricolage Grotesque", "Space Grotesk", sans-serif',
    bg: "#270f3d",
    bgImage:
      "radial-gradient(circle at 12% 6%, rgba(255,61,138,.34) 0, transparent 46%), radial-gradient(circle at 90% 10%, rgba(61,138,255,.26) 0, transparent 48%), radial-gradient(circle at 52% 106%, rgba(255,216,61,.20) 0, transparent 54%)",
    ink: "#fff", inkSoft: "rgba(255,255,255,.62)", line: "rgba(255,255,255,.16)",
    lineStrong: "rgba(255,255,255,.3)", soft: "rgba(255,255,255,.08)", accent: "#ff3d8a",
    discBg: "#2a1340", grooveA: "#3a1d57", grooveB: "#150824",
    mysteryTex: "radial-gradient(circle at 30% 25%, rgba(255,61,138,.28) 0, transparent 55%), radial-gradient(circle at 80% 80%, rgba(61,138,255,.22) 0, transparent 55%)",
    playShadow: "0 8px 26px -8px rgba(255,61,138,.7)",
  },
  neon: {
    display: '"Bricolage Grotesque", "Space Grotesk", sans-serif',
    bg: "#0b0716",
    bgImage:
      "radial-gradient(circle at 10% 0%, rgba(255,43,209,.16) 0, transparent 44%), radial-gradient(circle at 92% 100%, rgba(0,255,229,.12) 0, transparent 46%)",
    ink: "#fff", inkSoft: "rgba(255,255,255,.55)", line: "rgba(255,255,255,.12)",
    lineStrong: "rgba(255,255,255,.24)", soft: "rgba(255,255,255,.06)", accent: "#ff2bd1",
    discBg: "#0a0613", grooveA: "#1c1430", grooveB: "#05030c",
    mysteryTex: "radial-gradient(circle at 50% 42%, rgba(255,43,209,.22) 0, transparent 60%)",
    playShadow: "0 0 0 1px var(--wp-accent), 0 0 30px rgba(255,43,209,.45)",
  },
  retro: {
    display: '"Bricolage Grotesque", "Space Grotesk", sans-serif',
    bg: "#f7f1e6",
    bgImage: "radial-gradient(circle at 50% -10%, rgba(232,93,58,.10) 0, transparent 55%)",
    ink: "#1c130a", inkSoft: "#80715c", line: "rgba(28,19,10,.12)",
    lineStrong: "rgba(28,19,10,.22)", soft: "rgba(28,19,10,.05)", accent: "#e85d3a",
    discBg: "#1a1612", grooveA: "#2a241d", grooveB: "#0a0805",
    mysteryTex: "none", playShadow: "0 10px 24px -10px rgba(232,93,58,.65)",
  },
};

const THEME_LABEL = { Party: "party", Neon: "neon", Retro: "retro" };
const APP_TRACKS = (window.MOCK_PLAYLIST?.tracks) || [];

function trackFromUrl() {
  const params = new URLSearchParams(window.location.search);

  const raw =
    params.get("track_id") ||
    params.get("track") ||
    params.get("spotify") ||
    params.get("spotify_url") ||
    params.get("url") ||
    params.get("s") ||
    params.get("spotify_id") ||
    "";

  const spotifyId = extractSpotifyTrackId(raw);

  if (spotifyId) {
    return {
      id: `sp_${spotifyId}`,
      spotifyId,
      title: "",
      artist: "",
      year: "",
      spotify_url: `https://open.spotify.com/track/${spotifyId}`,
    };
  }

  return {
    id: "missing",
    spotifyId: "",
    title: "",
    artist: "",
    year: "",
  };
}


const APlay = ({ s = 26 }) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><path d="M8 5.5v13a1 1 0 0 0 1.54.84l10-6.5a1 1 0 0 0 0-1.68l-10-6.5A1 1 0 0 0 8 5.5Z"/></svg>);
const APause = ({ s = 26 }) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="5" width="4.2" height="14" rx="1.4"/><rect x="13.8" y="5" width="4.2" height="14" rx="1.4"/></svg>);
const AReplay = ({ s = 22 }) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"><path d="M3 11a9 9 0 1 1 2.4 6.1"/><path d="M3 20v-5h5"/></svg>);
const AScan = ({ s = 19 }) => (<svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"><path d="M4 7V5a1 1 0 0 1 1-1h2"/><path d="M17 4h2a1 1 0 0 1 1 1v2"/><path d="M20 17v2a1 1 0 0 1-1 1h-2"/><path d="M7 20H5a1 1 0 0 1-1-1v-2"/><rect x="8" y="8" width="3" height="3"/><rect x="14" y="8" width="2" height="2"/><rect x="8" y="14" width="2" height="2"/><path d="M14 14h2v2h-2z"/><path d="M17 17h.01"/></svg>);

const fmtA = (sec) => `0:${String(Math.max(0, Math.min(30, Math.floor(sec || 0)))).padStart(2, "0")}`;

function AppMystery({ size }) {
  const disc = Math.round(size * 0.78);
  return (
    <div className="wp-mystery" style={{ borderRadius: 0 }}>
      <div className="wp-mystery-grain" />
      <div className="wp-disc" style={{ width: disc, height: disc }}>
        <div className="wp-disc-sheen" />
        <div className="wp-disc-label"><span style={{ fontSize: disc * 0.16, lineHeight: 1 }}>?</span></div>
        <div className="wp-disc-hole" style={{ position: "absolute" }} />
      </div>
      <div className="wp-mystery-cap">
        <span className="wp-q">Welk nummer is dit?</span>
        <span className="wp-eq" aria-hidden="true"><span /><span /><span /><span /><span /></span>
      </div>
    </div>
  );
}

function computeArt() {
  const w = window.innerWidth || document.documentElement.clientWidth || 390;

  // Mobiel: vaste grootte op basis van schermbreedte.
  // Niet op window.innerHeight baseren, want die verandert tijdens scrollen
  // door de mobiele browserbalk. Daardoor veranderde het artwork van grootte.
  if (w < 860) {
    if (w <= 380) return 200;
    if (w <= 480) return 220;
    if (w <= 640) return 240;
    return 250;
  }

  // Desktop mag op hoogte reageren.
  const h = window.innerHeight || document.documentElement.clientHeight || 800;
  return Math.min(404, Math.max(300, h - 300));
}

const APP_DEFAULTS = {
  theme: "Party",
  perStyleAccent: true,
  accent: "#ff3d8a",
  anim: "flip",
};

function WPApp() {
  const [tw, setTweak] = useTweaks(APP_DEFAULTS);
  const theme = APP_THEMES[THEME_LABEL[tw.theme] || "party"];
  const accent = tw.perStyleAccent ? theme.accent : tw.accent;

  const [baseTrack, setBaseTrack] = useState(trackFromUrl);
  const [track, setTrack] = useState(baseTrack);
  const [previewUrl, setPreviewUrl] = useState(baseTrack.preview_url || baseTrack.previewUrl || "");
  const [previewSource, setPreviewSource] = useState("");
  const [audioStatus, setAudioStatus] = useState(previewUrl ? "ready" : "loading");
  const [audioMessage, setAudioMessage] = useState(previewUrl ? "" : "Preview laden…");
  const [t, setT] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [art, setArt] = useState(computeArt);
  const [scannerOpen, setScannerOpen] = useState(false);
  const [scannerMessage, setScannerMessage] = useState("");

  const audioRef = useRef(null);
  const scannerRef = useRef(null);
  const DUR = 30;
  const wantsAutoplay = qrjamShouldAutoplay();

  useEffect(() => {
    let lastWidth = window.innerWidth || document.documentElement.clientWidth || 0;

    const r = () => {
      const nextWidth = window.innerWidth || document.documentElement.clientWidth || 0;

      // Op mobiel veroorzaakt scrollen vaak resize-events doordat de adresbalk
      // inklapt/uitklapt. Als de breedte niet echt verandert, niets doen.
      if (nextWidth < 860 && Math.abs(nextWidth - lastWidth) < 2) {
        return;
      }

      lastWidth = nextWidth;
      setArt(computeArt());
    };

    window.addEventListener("resize", r);
    window.addEventListener("orientationchange", r);

    return () => {
      window.removeEventListener("resize", r);
      window.removeEventListener("orientationchange", r);
    };
  }, []);

  useEffect(() => {
    let cancelled = false;
    const spotifyId = extractSpotifyTrackId(baseTrack.spotifyId || baseTrack.spotify_id || baseTrack.spotify_url || baseTrack.source_url || "");
    const localPreview = baseTrack.preview_url || baseTrack.previewUrl || "";

    setTrack(baseTrack);
    setPreviewUrl(localPreview);
    setPreviewSource(localPreview ? "eigen bron" : "");
    setT(0);
    setPlaying(false);

    if (localPreview) {
      setAudioStatus("ready");
      setAudioMessage("");
      return () => { cancelled = true; };
    }

    if (!spotifyId) {
      setAudioStatus("missing");
      setAudioMessage("Deze QR bevat geen herkenbare Spotify-track.");
      return () => { cancelled = true; };
    }

    setAudioStatus("loading");
    setAudioMessage("Preview zoeken…");

    fetch(`${QRJAM_PREVIEW_ENDPOINT}?track_id=${encodeURIComponent(spotifyId)}&_=${Date.now()}`, {
      headers: { "Accept": "application/json" },
      cache: "no-store",
    })
      .then(async (res) => {
        const body = await res.json().catch(() => ({}));
        if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`);
        return body;
      })
      .then((json) => {
        if (cancelled) return;

        if (json.track) {
          setTrack((current) => ({
            ...current,
            ...json.track,
            spotifyId,
          }));
        }

        if (json.preview_url) {
          setPreviewUrl(json.preview_url);
          setPreviewSource(json.preview_source || "");
          setAudioStatus("ready");
          setAudioMessage("");
        } else {
          setPreviewUrl("");
          setPreviewSource("");
          setAudioStatus("missing");
          setAudioMessage(`Geen 30 seconden preview gevonden voor dit nummer. Track: ${spotifyId}`);
        }
      })
      .catch((err) => {
        if (cancelled) return;
        setAudioStatus("error");
        setAudioMessage(`Preview kon niet worden geladen: ${err.message}`);
      });

    return () => { cancelled = true; };
  }, [baseTrack]);

  useEffect(() => {
    const audio = audioRef.current;
    if (!audio || !previewUrl) return;

    audio.currentTime = 0;
    audio.volume = 1;
    setT(0);

    if (!wantsAutoplay) {
      setAudioStatus("ready");
      setAudioMessage("");
      return;
    }

    let cancelled = false;

    const tryStart = async () => {
      if (cancelled) return;

      try {
        await audio.play();
        if (cancelled) return;
        setPlaying(true);
        setAudioStatus("playing");
        setAudioMessage("");
      } catch (_) {
        if (cancelled) return;
        setPlaying(false);
        setAudioStatus("blocked");
        setAudioMessage("Automatisch starten is geblokkeerd door je browser. Tik één keer op play.");
      }
    };

    // Meerdere momenten proberen: direct, na metadata en zodra audio kan spelen.
    tryStart();

    const onCanPlay = () => tryStart();
    const onLoaded = () => tryStart();

    audio.addEventListener("canplay", onCanPlay, { once: true });
    audio.addEventListener("loadedmetadata", onLoaded, { once: true });

    const retryTimer = window.setTimeout(tryStart, 450);

    return () => {
      cancelled = true;
      window.clearTimeout(retryTimer);
      audio.removeEventListener("canplay", onCanPlay);
      audio.removeEventListener("loadedmetadata", onLoaded);
    };
  }, [previewUrl, wantsAutoplay]);

  useEffect(() => {
    if (!scannerOpen) return;

    let stopped = false;
    let scanner = null;

    const startScanner = async () => {
      try {
        if (!window.Html5Qrcode) {
          setScannerMessage("QR-scanner kon niet worden geladen. Gebruik eventueel de normale camera-app.");
          return;
        }

        scanner = new Html5Qrcode("qrjam-next-reader", { verbose: false });
        scannerRef.current = scanner;

        setScannerMessage("Camera starten…");

        await scanner.start(
          { facingMode: "environment" },
          {
            fps: 10,
            qrbox: (w, h) => {
              const size = Math.floor(Math.min(w, h) * 0.76);
              return { width: size, height: size };
            },
          },
          async (decodedText) => {
            const nextUrl = qrjamPlayerUrlFromScan(decodedText);

            if (!nextUrl) {
              setScannerMessage("QR gevonden, maar dit is geen QRJam- of Spotify-tracklink.");
              return;
            }

            if (stopped) return;
            stopped = true;
            setScannerMessage("QR gevonden. Nieuwe kaart openen…");

            try {
              await scanner.stop();
              scanner.clear();
            } catch (_) {}

            window.location.href = nextUrl;
          },
          () => {}
        );

        setScannerMessage("Richt je camera op de volgende QR-code.");
      } catch (err) {
        setScannerMessage(`Camera kon niet starten: ${err.message || err}`);
      }
    };

    setTimeout(startScanner, 80);

    return () => {
      stopped = true;
      const active = scannerRef.current;
      scannerRef.current = null;

      if (active) {
        try {
          active.stop().then(() => active.clear()).catch(() => {});
        } catch (_) {}
      }
    };
  }, [scannerOpen]);

  const pct = Math.max(0, Math.min(100, (t / DUR) * 100));
  const ended = t >= DUR - 0.05;
  const spotifyOpenUrl = spotifyOpenUrlFor(track);

  const playAudio = async (restart = false) => {
    const audio = audioRef.current;
    if (!audio || !previewUrl) return;

    if (restart || audio.currentTime >= DUR - 0.05) audio.currentTime = 0;

    try {
      await audio.play();
      setPlaying(true);
      setAudioStatus("playing");
      setAudioMessage("");
    } catch (_) {
      setPlaying(false);
      setAudioStatus("blocked");
      setAudioMessage("Tik nogmaals op play als je browser autoplay blokkeert.");
    }
  };

  const replay = () => playAudio(true);

  const toggle = () => {
    const audio = audioRef.current;
    if (!audio || !previewUrl) return;

    if (ended || audio.ended) return playAudio(true);

    if (audio.paused) return playAudio(false);

    audio.pause();
    setPlaying(false);
    setAudioStatus("ready");
  };

  const handleTimeUpdate = () => {
    const audio = audioRef.current;
    if (!audio) return;

    const current = Math.min(audio.currentTime || 0, DUR);
    setT(current);

    if (current >= DUR) {
      audio.pause();
      setPlaying(false);
      setAudioStatus("ended");
    }
  };

  const handleEnded = () => {
    setT(DUR);
    setPlaying(false);
    setAudioStatus("ended");
  };

  const handleAudioError = () => {
    setPlaying(false);
    setAudioStatus("error");
    setAudioMessage("Preview gevonden, maar deze audio kon niet worden afgespeeld. Scan eventueel de volgende kaart of open Spotify.");
  };

  const openScanner = () => {
    const audio = audioRef.current;
    if (audio) audio.pause();
    setPlaying(false);
    setScannerMessage("");
    setScannerOpen(true);
  };

  const closeScanner = () => {
    setScannerOpen(false);
    setScannerMessage("");
  };

  const statusLabel = (() => {
    if (audioStatus === "loading") return "Preview zoeken";
    if (audioStatus === "missing") return "Geen preview";
    if (audioStatus === "error") return "Fout";
    if (audioStatus === "blocked") return "Tik op play";
    if (audioStatus === "ended") return "Preview klaar";
    return playing ? "Nu speelt" : "Preview klaar";
  })();

  const vars = {
    "--wp-bg": theme.bg,
    "--wp-bg-image": theme.bgImage || "none",
    "--wp-ink": theme.ink,
    "--wp-ink-soft": theme.inkSoft,
    "--wp-line": theme.line,
    "--wp-line-strong": theme.lineStrong,
    "--wp-soft": theme.soft,
    "--wp-display": theme.display,
    "--wp-accent": accent,
    "--wp-disc-bg": theme.discBg,
    "--wp-groove-a": theme.grooveA,
    "--wp-groove-b": theme.grooveB,
    "--wp-mystery-tex": theme.mysteryTex || "none",
    "--wp-play-shadow": theme.playShadow,
  };

  return (
    <div className="wpa" style={vars} data-paused={!playing} data-audio-status={audioStatus}>
      <audio
        ref={audioRef}
        src={previewUrl || undefined}
        preload="auto"
        autoPlay={wantsAutoplay}
        playsInline
        onTimeUpdate={handleTimeUpdate}
        onEnded={handleEnded}
        onError={handleAudioError}
      />

      <header className="wpa-head">
        <div className="wpa-brand-block">
          <div className="brand wp-brand">
            <VinylQLogo size={34} />
            <span>QR<em>Jam</em><span className="brand-nl">.nl</span></span>
          </div>
          <a className="wpa-back-home" href="/">← Terug</a>
        </div>
        <span className="wp-tag"><span className="wp-live-dot" />{statusLabel}</span>
      </header>

      <div className="wpa-stage">
        <div className="wpa-main">
          <div className="wpa-art" style={{ width: art, height: art }}>
            <div className="wp-flip" style={{ width: art, height: art }}>
              <div className="wp-face back" style={{ visibility: "visible" }}>
                <AppMystery size={art} />
              </div>
            </div>
          </div>

          <div className="wpa-panel">
            <div className="wpa-meta">
              <div className="wpa-eyebrow">QRJam Webplayer</div>
              <h1 className="wpa-title">Welk nummer is dit?</h1>
</div>

            <div className="wpa-transport">
              <div className="wpa-bar-wrap">
                <div className="wp-bar">
                  <div className="wp-bar-fill" style={{ width: `${pct}%` }} />
                  <div className="wp-bar-thumb" style={{ left: `${pct}%` }} />
                </div>
                <div className="wp-times"><span>{fmtA(t)}</span><span>0:30</span></div>
              </div>

              {audioMessage ? <div className="wpa-audio-note">{audioMessage}</div> : null}

              <div className="wp-controls wpa-controls">
                <button className="wp-ctrl ghost" onClick={replay} aria-label="Opnieuw afspelen" disabled={!previewUrl}><AReplay /></button>
                <button className="wp-play wp-ctrl" onClick={toggle} aria-label={playing ? "Pauze" : "Afspelen"} disabled={!previewUrl || audioStatus === "loading"}>
                  {playing ? <APause /> : <APlay />}
                </button>
                <span style={{ width: 46 }} />
              </div>

              <div className="wpa-action-row">
                <button className="wp-cta wpa-cta secondary" onClick={replay} disabled={!previewUrl}>
                  <AReplay s={18} /> Speel nog eens
                </button>

                <button className="wp-cta wpa-cta wpa-scan-next" type="button" onClick={openScanner}>
                  <AScan s={18} /> Scan volgende QR
                </button>
              </div>

              <div className="wpa-ios-note">
                <p>Je hoort hier previews van 30 seconden. Wil je nummers volledig beluisteren? Dan heb je onze iOS app nodig.</p>
                <a className="qrjam-stable-appstore" href="https://apps.apple.com/us/app/qrjam-player/id6771815102" target="_blank" rel="noopener noreferrer" aria-label="Download QRJam Player in de App Store">
                  <svg width="22" height="22" viewBox="0 0 24 24" fill="white" aria-hidden="true"><path d="M18.7 19.5c-.8 1.2-1.7 2.4-3.1 2.5-1.3 0-1.8-.8-3.3-.8s-2 .8-3.3.8c-1.3.1-2.3-1.3-3.1-2.5-1.7-2.5-3-7.1-1.3-10.1.9-1.5 2.4-2.5 4.1-2.5 1.3 0 2.5.9 3.3.9s2.3-1.1 3.8-.9c.7 0 2.5.3 3.7 2-.1.1-2.2 1.3-2.1 3.8 0 3 2.6 4 2.7 4-.1.1-.4 1.5-1.4 2.8zM13 3.5c.7-.8 1.9-1.5 2.9-1.5.1 1.2-.3 2.4-1 3.2-.7.8-1.8 1.5-3 1.4-.1-1.1.4-2.3 1.1-3.1z"/></svg>
                </a>
              </div>


              {(audioStatus === "missing" || audioStatus === "error") && spotifyOpenUrl ? (
                <a className="wpa-source-link" href={spotifyOpenUrl} target="_blank" rel="noopener noreferrer">Open in Spotify</a>
              ) : null}
            </div>
          </div>
        </div>
      </div>

      {scannerOpen ? (
        <div className="qr-scan-overlay" role="dialog" aria-modal="true">
          <div className="qr-scan-panel">
            <div className="qr-scan-head">
              <div>
                <div className="qr-scan-eyebrow">Volgende kaart</div>
                <h2>Scan de volgende QR</h2>
              </div>
              <button className="qr-scan-close" type="button" onClick={closeScanner}>×</button>
            </div>

            <div id="qrjam-next-reader" className="qr-scan-reader" />

            <p className="qr-scan-message">{scannerMessage || "Camera voorbereiden…"}</p>

            <button className="wp-cta wpa-cta secondary" type="button" onClick={closeScanner}>
              Sluiten
            </button>
          </div>
        </div>
      ) : null}

      <TweaksPanel>
        <TweakSection label="Stijl" />
        <TweakRadio label="Thema" value={tw.theme} options={["Party", "Neon", "Retro"]} onChange={(v) => setTweak("theme", v)} />
        <TweakSection label="Accentkleur" />
        <TweakToggle label="Eigen accent per stijl" value={tw.perStyleAccent} onChange={(v) => setTweak("perStyleAccent", v)} />
        <TweakColor label="Accent (overal)" value={tw.accent} options={["#ff3d8a", "#ff2bd1", "#e85d3a", "#3d8aff", "#00d39a"]} onChange={(v) => setTweak({ accent: v, perStyleAccent: false })} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<WPApp />);
