/* global React, Eyebrow, Display, FigureNum, Wordmark, QRCode */
const { useState: useStateProj, useEffect: useEffectProj, useRef: useRefProj } = React;

// ProjectorScreen — een "publieksscherm" voor op groot scherm. Toont vraag +
// opties zonder het juiste antwoord te onthullen, plus aantal binnengekomen
// antwoorden. Bij start: lobby met QR + code. Bij einde: scoreboard.
//
// Wordt geopend via /projector?code=KW-XXXX in een nieuw venster.
function ProjectorScreen({ theme, code }) {
  const [session, setSession] = useStateProj(null);
  const [quiz, setQuiz] = useStateProj(null);
  const [players, setPlayers] = useStateProj([]);
  const [question, setQuestion] = useStateProj(null);
  const [reveal, setReveal] = useStateProj(null); // QPT-05: { questionId, correctOptionId, correctAnswer }
  const [answeredCount, setAnsweredCount] = useStateProj(0);
  const [phase, setPhase] = useStateProj("lobby"); // lobby | playing | finished
  const [secondsLeft, setSecondsLeft] = useStateProj(null);
  const [error, setError] = useStateProj(null);
  const [autoStartBusy, setAutoStartBusy] = useStateProj(false);
  const [standingsOverlay, setStandingsOverlay] = useStateProj(null); // null or sorted players[]
  const [prevScores, setPrevScores] = useStateProj({}); // playerId -> score before last refresh (for delta animation)
  const wsRef = useRefProj(null);
  const answeredIdsRef = useRefProj(new Set());

  const refetchPlayers = async () => {
    try {
      const r = await fetch(`/api/sessions/code/${encodeURIComponent(code)}`);
      if (!r.ok) return;
      const data = await r.json();
      setPlayers((curr) => {
        const prevMap = {};
        curr.forEach((p) => { prevMap[p.id] = p.score || 0; });
        setPrevScores(prevMap);
        return data.players || [];
      });
    } catch { /* ignore */ }
  };

  useEffectProj(() => {
    if (!code) { setError("Geen code in de URL — open dit venster via je hostsessie."); return; }
    let cancelled = false;
    (async () => {
      try {
        const r = await fetch(`/api/sessions/code/${encodeURIComponent(code)}`);
        if (!r.ok) throw new Error(`HTTP ${r.status}`);
        const data = await r.json();
        if (cancelled) return;
        setSession(data.session); setQuiz(data.quiz); setPlayers(data.players || []);
        if (data.session.status === "finished") {
          setPhase("finished");
          // Sort players by score for podium
          setPlayers([...(data.players || [])].sort((a, b) => b.score - a.score));
        } else if (data.session.status === "running") {
          setPhase("playing");
        }
      } catch (e) {
        setError(e.message);
      }
    })();

    const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
    const ws = new WebSocket(`${proto}//${window.location.host}/ws/sessions/${code}`);
    wsRef.current = ws;
    ws.onmessage = (ev) => {
      let msg = null;
      try { msg = JSON.parse(ev.data); } catch { return; }
      if (msg.type === "player:joined" && msg.player) {
        setPlayers((curr) => curr.some((p) => p.id === msg.player.id) ? curr : [...curr, msg.player]);
      } else if (msg.type === "session:started") {
        setPhase("playing");
      } else if (msg.type === "question:show" && msg.question) {
        // Refresh scores from previous question before showing the next
        refetchPlayers();
        setQuestion(msg.question);
        setReveal(null);
        setStandingsOverlay(null);
        answeredIdsRef.current = new Set();
        setAnsweredCount(0);
        setPhase("playing");
      } else if (msg.type === "question:reveal") {
        setReveal({ questionId: msg.questionId, correctOptionId: msg.correctOptionId, correctAnswer: msg.correctAnswer });
      } else if (msg.type === "standings:show") {
        if (msg.show) setStandingsOverlay(msg.players || []);
        else setStandingsOverlay(null);
      } else if (msg.type === "answer:received" && msg.playerId) {
        if (!answeredIdsRef.current.has(msg.playerId)) {
          answeredIdsRef.current.add(msg.playerId);
          setAnsweredCount(answeredIdsRef.current.size);
        }
      } else if (msg.type === "session:finished") {
        setPhase("finished");
        // Re-fetch to get final scores
        fetch(`/api/sessions/code/${encodeURIComponent(code)}`)
          .then((r) => r.json())
          .then((d) => setPlayers([...(d.players || [])].sort((a, b) => b.score - a.score)))
          .catch(() => {});
      }
    };
    ws.onclose = () => { wsRef.current = null; };
    return () => { cancelled = true; ws.close(); };
  }, [code]);

  // Countdown for the active question
  useEffectProj(() => {
    if (!question || !question.timeLimit) { setSecondsLeft(null); return; }
    setSecondsLeft(question.timeLimit);
    const id = setInterval(() => setSecondsLeft((s) => (s === null || s <= 0) ? s : s - 1), 1000);
    return () => clearInterval(id);
  }, [question?.id]);

  if (error) {
    return (
      <div style={{ minHeight: "100vh", background: theme.bg, display: "grid", placeItems: "center", padding: 40 }}>
        <div style={{ textAlign: "center" }}>
          <Display theme={theme} size={56}>Hmm.</Display>
          <p style={{ marginTop: 16, color: theme.fgDim, fontSize: 18 }}>{error}</p>
        </div>
      </div>
    );
  }

  const accent = quiz?.color || theme.accent;
  const joinHost = "kwisthet.be";

  return (
    <div style={{ minHeight: "100vh", background: theme.bg, display: "flex", flexDirection: "column" }}>
      <header style={{
        borderBottom: `3px double ${theme.rule}`,
        padding: "20px 56px",
        display: "grid", gridTemplateColumns: "1fr auto 1fr", alignItems: "center",
      }}>
        <Eyebrow theme={theme} style={{ color: theme.signal || theme.accent }}>
          ● PROJECTOR · {code}
        </Eyebrow>
        <Wordmark size={120} />
        <div style={{ textAlign: "right" }}>
          <Eyebrow theme={theme}>{quiz?.title?.toUpperCase() || "QUIZ"}</Eyebrow>
        </div>
      </header>

      {phase === "lobby" && (
        <LobbyProjector theme={theme} quiz={quiz} code={code} players={players} accent={accent} joinHost={joinHost}
          autoAdvance={!!session?.autoAdvance} autoStartBusy={autoStartBusy}
          onAutoStart={async () => {
            if (autoStartBusy) return;
            setAutoStartBusy(true);
            try {
              const r = await fetch(`/api/sessions/${encodeURIComponent(code)}/auto-start`, { method: "POST" });
              if (!r.ok) {
                const err = await r.json().catch(() => ({}));
                setError(err.error || `Kon niet starten (status ${r.status})`);
              }
            } catch (e) {
              setError(e.message);
            } finally {
              setAutoStartBusy(false);
            }
          }} />
      )}

      {phase === "playing" && (
        <PlayingProjector theme={theme} question={question} reveal={reveal} answeredCount={answeredCount} players={players} prevScores={prevScores} secondsLeft={secondsLeft} accent={accent} />
      )}

      {phase === "finished" && (
        <FinishedProjector theme={theme} players={players} accent={accent} />
      )}

      {standingsOverlay && (
        <StandingsOverlay theme={theme} players={standingsOverlay} prevScores={prevScores} accent={accent} />
      )}
    </div>
  );
}

function LobbyProjector({ theme, quiz, code, players, accent, joinHost, autoAdvance, autoStartBusy, onAutoStart }) {
  const joinUrl = code ? `${window.location.origin}/?code=${encodeURIComponent(code)}` : "";
  return (
    <div style={{ flex: 1, display: "grid", gridTemplateColumns: "1.5fr 1fr" }}>
      <section style={{
        background: accent, color: "#fff",
        padding: "60px 72px",
        display: "flex", flexDirection: "column", justifyContent: "space-between",
      }}>
        <div>
          <Eyebrow theme={theme} style={{ color: "rgba(255,255,255,0.8)" }}>EDITIE · {(quiz?.category || "ALGEMEEN").toUpperCase()}</Eyebrow>
          <div style={{
            fontFamily: theme.fonts.display, fontSize: "clamp(72px, 9vw, 160px)",
            lineHeight: 0.92, letterSpacing: "-0.025em", marginTop: 14,
          }}>{quiz?.title || "Quiz"}</div>
          <div style={{ marginTop: 24, fontFamily: theme.fonts.mono, fontSize: 18, letterSpacing: "0.1em", opacity: 0.9 }}>
            <b>{quiz?.questions || 0}</b> VRAGEN · ~<b>{Math.round((quiz?.questions || 20) * 22 / 60)}</b> MIN · <b>{players.length}</b> SPELERS
          </div>
        </div>
        <div>
          <div style={{ fontFamily: theme.fonts.mono, fontSize: 16, letterSpacing: "0.15em", opacity: 0.85, marginBottom: 16 }}>
            ── DOE MEE OP {joinHost.toUpperCase()}/JOIN
          </div>
          <div style={{ display: "flex", alignItems: "flex-end", gap: 40 }}>
            <div style={{
              fontFamily: theme.fonts.display, fontSize: "clamp(100px, 13vw, 200px)",
              lineHeight: 0.9, letterSpacing: "0.02em",
            }}>{code || "··-····"}</div>
            {joinUrl && (
              <div style={{ paddingBottom: 8 }}>
                <div style={{
                  fontFamily: theme.fonts.mono, fontSize: 12, letterSpacing: "0.12em",
                  opacity: 0.8, marginBottom: 8, textAlign: "center",
                }}>── OF SCAN</div>
                <QRCode theme={theme} size={160} value={joinUrl} />
              </div>
            )}
          </div>
        </div>
      </section>

      <section style={{ display: "flex", flexDirection: "column", padding: "48px 56px" }}>
        <Eyebrow theme={theme} style={{ marginBottom: 12 }}>── DE GASTEN</Eyebrow>
        <FigureNum theme={theme} size={120} accent>{players.length}</FigureNum>
        <div style={{ marginTop: 24, flex: 1, overflowY: "auto" }}>
          {players.map((p, i) => (
            <div key={p.id} style={{
              padding: "10px 0",
              fontFamily: theme.fonts.display, fontSize: 22,
              borderBottom: i < players.length - 1 ? `1px dashed ${theme.rule}` : "none",
            }}>{p.name}</div>
          ))}
          {players.length === 0 && (
            <Eyebrow theme={theme} style={{ color: theme.fgDim, marginTop: 12 }}>WACHTEN OP SPELERS…</Eyebrow>
          )}
        </div>
        {autoAdvance && (
          <div style={{ marginTop: 24, paddingTop: 24, borderTop: `1px solid ${theme.rule}` }}>
            <Eyebrow theme={theme} style={{ marginBottom: 8 }}>── AUTOPILOT KLAAR</Eyebrow>
            <button
              onClick={onAutoStart}
              disabled={autoStartBusy || players.length === 0}
              style={{
                width: "100%",
                padding: "20px 24px",
                background: players.length === 0 ? theme.rule : accent,
                color: "#fff",
                border: "none",
                cursor: (autoStartBusy || players.length === 0) ? "not-allowed" : "pointer",
                fontFamily: theme.fonts.display,
                fontSize: 28, letterSpacing: "-0.01em",
                opacity: autoStartBusy ? 0.6 : 1,
                transition: "opacity .2s",
              }}>
              {autoStartBusy ? "Bezig…" : players.length === 0 ? "Wacht op spelers" : `Start de quiz · ${players.length} ${players.length === 1 ? "speler" : "spelers"} →`}
            </button>
            <div style={{ marginTop: 10, fontSize: 12, color: theme.fgDim, lineHeight: 1.45 }}>
              Vanaf nu loopt alles vanzelf. Niemand hoeft de vragen te bedienen — de timer gaat automatisch verder.
            </div>
          </div>
        )}
      </section>
    </div>
  );
}

function PlayingProjector({ theme, question, reveal, answeredCount, players, prevScores, secondsLeft, accent }) {
  const isRevealed = reveal && question && reveal.questionId === question.id;
  const totalPlayers = players.length;
  const top = [...players].sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, 5);
  const maxScore = Math.max(1, ...top.map((p) => p.score || 0));
  if (!question) {
    return (
      <div style={{ flex: 1, display: "grid", placeItems: "center" }}>
        <div style={{ textAlign: "center" }}>
          <Eyebrow theme={theme} style={{ color: accent, marginBottom: 16 }}>── DE QUIZ IS BEGONNEN</Eyebrow>
          <Display theme={theme} size={120}>Klaar?</Display>
          <p style={{ marginTop: 24, color: theme.fgDim, fontSize: 22 }}>De eerste vraag komt eraan…</p>
        </div>
      </div>
    );
  }
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", padding: "48px 72px" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 32 }}>
        <Eyebrow theme={theme} style={{ color: accent, fontSize: 14 }}>── VRAAG {(question.position ?? 0) + 1}</Eyebrow>
        {secondsLeft !== null && (
          <FigureNum theme={theme} size={56} accent={secondsLeft <= 5}>
            {secondsLeft}<span style={{ fontSize: 24, marginLeft: 6, color: theme.fgDim }}>sec</span>
          </FigureNum>
        )}
      </div>

      <Display theme={theme} size={88} style={{ lineHeight: 1.05, marginBottom: 40 }}>{question.prompt}</Display>

      {question.type === "mc" && Array.isArray(question.options) && (
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, flex: 1 }}>
          {question.options.map((o, i) => {
            const isCorrect = isRevealed && o.id === reveal.correctOptionId;
            const isWrong = isRevealed && reveal.correctOptionId && o.id !== reveal.correctOptionId;
            return (
              <div key={o.id || i} style={{
                padding: "28px 32px",
                border: `2px solid ${isCorrect ? "#1db954" : theme.rule}`,
                background: isCorrect ? "#1db954" : theme.bg,
                color: isCorrect ? "#fff" : (isWrong ? theme.fgDim : theme.fg),
                opacity: isWrong ? 0.4 : 1,
                fontFamily: theme.fonts.body, fontSize: 28,
                display: "flex", alignItems: "center", gap: 20,
                transition: "background .3s, opacity .3s, border-color .3s",
              }}>
                <span style={{
                  fontFamily: theme.fonts.display, fontSize: 36,
                  color: isCorrect ? "#fff" : accent, minWidth: 40,
                }}>{String.fromCharCode(65 + i)}</span>
                <span>{o.text}</span>
                {isCorrect && <span style={{ marginLeft: "auto", fontSize: 28 }}>✓</span>}
              </div>
            );
          })}
        </div>
      )}

      {question.type === "tf" && (
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, flex: 1 }}>
          {["WAAR", "NIET WAAR"].map((v) => {
            const tfAnswer = isRevealed ? String(reveal.correctAnswer || "").toLowerCase() : null;
            const isCorrect = isRevealed && (
              (v === "WAAR" && (tfAnswer === "waar" || tfAnswer === "true")) ||
              (v === "NIET WAAR" && (tfAnswer === "niet waar" || tfAnswer === "false"))
            );
            const isWrong = isRevealed && !isCorrect;
            return (
              <div key={v} style={{
                padding: "40px",
                border: `2px solid ${isCorrect ? "#1db954" : theme.rule}`,
                background: isCorrect ? "#1db954" : theme.bg,
                color: isCorrect ? "#fff" : theme.fg,
                opacity: isWrong ? 0.4 : 1,
                fontFamily: theme.fonts.display, fontSize: 56,
                display: "grid", placeItems: "center",
                transition: "background .3s, opacity .3s, border-color .3s",
              }}>{v}</div>
            );
          })}
        </div>
      )}

      {question.type === "open" && (
        <div style={{
          padding: "60px",
          border: `2px dashed ${theme.rule}`,
          fontFamily: theme.fonts.display, fontSize: 32, color: theme.fgDim,
          textAlign: "center",
        }}>Antwoord op je telefoon →</div>
      )}

      <div style={{ marginTop: 32, display: "flex", justifyContent: "space-between", alignItems: "center", borderTop: `1px solid ${theme.rule}`, paddingTop: 20 }}>
        <Eyebrow theme={theme}>── ANTWOORDEN BINNEN</Eyebrow>
        <div style={{ display: "flex", alignItems: "baseline", gap: 14 }}>
          <FigureNum theme={theme} size={56} accent>{answeredCount}</FigureNum>
          <div style={{ fontFamily: theme.fonts.display, fontSize: 28, color: theme.fgDim }}>/ {totalPlayers}</div>
        </div>
      </div>
      <div style={{ marginTop: 8, height: 8, background: theme.rule, position: "relative" }}>
        <div style={{
          position: "absolute", top: 0, left: 0, height: "100%",
          width: `${totalPlayers ? (answeredCount / totalPlayers) * 100 : 0}%`,
          background: accent, transition: "width .3s",
        }} />
      </div>

      {top.length > 0 && (
        <div style={{ marginTop: 28 }}>
          <Eyebrow theme={theme} style={{ marginBottom: 12 }}>── TOP {top.length}</Eyebrow>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {top.map((p, i) => {
              const score = p.score || 0;
              const prev = prevScores[p.id] || 0;
              const delta = score - prev;
              const widthPct = (score / maxScore) * 100;
              return (
                <div key={p.id} style={{ display: "grid", gridTemplateColumns: "32px 140px 1fr 100px", alignItems: "center", gap: 14 }}>
                  <FigureNum theme={theme} size={20}>{i + 1}</FigureNum>
                  <div style={{ fontFamily: theme.fonts.display, fontSize: 20, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</div>
                  <div style={{ height: 18, background: theme.rule, position: "relative", overflow: "hidden" }}>
                    <div style={{
                      position: "absolute", top: 0, left: 0, height: "100%",
                      width: `${widthPct}%`,
                      background: i === 0 ? accent : theme.fg,
                      transition: "width 0.8s cubic-bezier(.2,.8,.2,1)",
                    }} />
                  </div>
                  <div style={{ textAlign: "right", fontFamily: theme.fonts.mono, fontSize: 16, color: theme.fg }}>
                    {score.toLocaleString("nl-NL")}
                    {delta > 0 && (
                      <span style={{ color: accent, marginLeft: 6, fontFamily: theme.fonts.display, fontSize: 14 }}>+{delta}</span>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

const PAUSE_JOKES = [
  "Tijd voor een sanitaire stop. We wachten.",
  "Even de hersenen laten afkoelen.",
  "De quizmaster heeft dorst. Iemand een pintje?",
  "Wie nu naar de WC gaat, mist niets. Beloofd.",
  "Intussen bij de nummer laatste: 'Ik doe gewoon mee voor de sfeer.'",
  "Fun fact: de gemiddelde Belg eet 8kg frieten per jaar.",
  "Rustig, het is maar een quiz. Of toch niet?",
  "Zet je gsm op stil. Of toch niet, je hebt hem nodig.",
  "Geen zorgen, de quizmaster kent de antwoorden ook niet altijd.",
  "Dit is het perfecte moment om te roddelen over de scores.",
  "Proost! Op de volgende vraag.",
  "Wist je dat quizzen je brein jonger houdt? Gij zijt hier goed bezig.",
  "Even adempauze. In. Uit. Klaar voor de volgende ronde?",
  "Ondertussen in de keuken: nog iemand bitterballen?",
  "De spanning is te snijden. Met een buttermes, maar toch.",
];

function StandingsOverlay({ theme, players, prevScores, accent }) {
  const sorted = [...players].sort((a, b) => (b.score || 0) - (a.score || 0));
  const maxScore = Math.max(1, ...sorted.map((p) => p.score || 0));
  const totalAnswered = sorted.reduce((s, p) => s + (p.score > 0 ? 1 : 0), 0);
  const totalScore = sorted.reduce((s, p) => s + (p.score || 0), 0);
  const [joke] = useStateProj(() => PAUSE_JOKES[Math.floor(Math.random() * PAUSE_JOKES.length)]);
  const [tick, setTick] = useStateProj(0);

  useEffectProj(() => {
    const id = setInterval(() => setTick((t) => t + 1), 4000);
    return () => clearInterval(id);
  }, []);

  const dots = ".".repeat((tick % 3) + 1);

  return (
    <div style={{
      position: "fixed", inset: 0,
      background: theme.bg,
      zIndex: 200,
      display: "grid", gridTemplateColumns: "1fr 1fr",
      animation: "fadein 0.4s ease-out",
    }}>
      <div style={{
        background: accent, color: "#fff",
        padding: "56px 64px",
        display: "flex", flexDirection: "column", justifyContent: "space-between",
      }}>
        <div>
          <Eyebrow theme={theme} style={{ color: "rgba(255,255,255,0.7)" }}>── EVEN PAUZE</Eyebrow>
          <div style={{
            fontFamily: theme.fonts.display,
            fontSize: "clamp(64px, 8vw, 140px)",
            lineHeight: 0.92, letterSpacing: "-0.025em", marginTop: 14,
          }}>Pauze&shy;momentje.</div>
          <div style={{
            marginTop: 36,
            fontFamily: theme.fonts.body, fontSize: 26,
            lineHeight: 1.5,
            opacity: 0.9,
            fontStyle: "italic",
            maxWidth: 520,
          }}>"{joke}"</div>
        </div>
        <div>
          <div style={{
            display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 24,
            borderTop: "1px solid rgba(255,255,255,0.25)", paddingTop: 28, marginTop: 32,
          }}>
            <div>
              <div style={{ fontFamily: theme.fonts.display, fontSize: 56, lineHeight: 1 }}>{sorted.length}</div>
              <div style={{ fontFamily: theme.fonts.mono, fontSize: 13, letterSpacing: "0.12em", opacity: 0.7, marginTop: 4 }}>SPELERS</div>
            </div>
            <div>
              <div style={{ fontFamily: theme.fonts.display, fontSize: 56, lineHeight: 1 }}>{totalScore.toLocaleString("nl-NL")}</div>
              <div style={{ fontFamily: theme.fonts.mono, fontSize: 13, letterSpacing: "0.12em", opacity: 0.7, marginTop: 4 }}>TOTAAL PUNTEN</div>
            </div>
            <div>
              <div style={{ fontFamily: theme.fonts.display, fontSize: 56, lineHeight: 1 }}>{totalAnswered}/{sorted.length}</div>
              <div style={{ fontFamily: theme.fonts.mono, fontSize: 13, letterSpacing: "0.12em", opacity: 0.7, marginTop: 4 }}>HEBBEN GESCOORD</div>
            </div>
          </div>
          <div style={{
            fontFamily: theme.fonts.mono, fontSize: 14, letterSpacing: "0.15em",
            opacity: 0.6, marginTop: 28,
          }}>WE GAAN ZO VERDER{dots}</div>
        </div>
      </div>

      <div style={{
        padding: "56px 48px",
        display: "flex", flexDirection: "column",
        overflowY: "auto",
      }}>
        <div style={{ marginBottom: 28 }}>
          <Eyebrow theme={theme} style={{ color: accent }}>── DE TUSSENSTAND</Eyebrow>
          <Display theme={theme} size={48} style={{ marginTop: 8 }}>Wie staat vooraan?</Display>
        </div>
        <div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 12 }}>
          {sorted.slice(0, 10).map((p, i) => {
            const score = p.score || 0;
            const prev = prevScores[p.id] || 0;
            const delta = score - prev;
            const widthPct = (score / maxScore) * 100;
            return (
              <div key={p.id} style={{
                display: "grid", gridTemplateColumns: "48px 180px 1fr 140px",
                alignItems: "center", gap: 14,
              }}>
                <FigureNum theme={theme} size={32} accent={i < 3}>{i + 1}</FigureNum>
                <div style={{ fontFamily: theme.fonts.display, fontSize: 26, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</div>
                <div style={{ height: 24, background: theme.rule, position: "relative", overflow: "hidden" }}>
                  <div style={{
                    position: "absolute", top: 0, left: 0, height: "100%",
                    width: `${widthPct}%`,
                    background: i === 0 ? accent : i < 3 ? theme.fg : theme.fgDim,
                    transition: "width 1.2s cubic-bezier(.2,.8,.2,1)",
                  }} />
                </div>
                <div style={{ textAlign: "right", fontFamily: theme.fonts.display, fontSize: 26 }}>
                  {score.toLocaleString("nl-NL")}
                  {delta > 0 && (
                    <span style={{ color: accent, marginLeft: 8, fontSize: 18 }}>+{delta}</span>
                  )}
                </div>
              </div>
            );
          })}
        </div>
        {sorted.length > 10 && (
          <Eyebrow theme={theme} style={{ marginTop: 16, color: theme.fgDim }}>
            EN NOG {sorted.length - 10} ANDERE DEELNEMERS
          </Eyebrow>
        )}
      </div>
      <style>{`@keyframes fadein { from { opacity: 0 } to { opacity: 1 } }`}</style>
    </div>
  );
}

function FinishedProjector({ theme, players, accent }) {
  const podium = players.slice(0, 3);
  const rest = players.slice(3, 10);
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
      <div style={{
        background: accent, color: "#fff",
        padding: "60px 72px", textAlign: "center",
      }}>
        <Eyebrow theme={theme} style={{ color: "rgba(255,255,255,0.85)" }}>── EINDSTAND</Eyebrow>
        <div style={{ fontFamily: theme.fonts.display, fontSize: "clamp(72px, 10vw, 180px)", lineHeight: 1, marginTop: 14 }}>
          De winnaars.
        </div>
        {podium.length > 0 && (
          <div style={{
            marginTop: 56, display: "grid", gridTemplateColumns: "1fr 1fr 1fr",
            gap: 24, alignItems: "end", maxWidth: 1100, marginLeft: "auto", marginRight: "auto",
          }}>
            {[1, 0, 2].map((rankIdx) => {
              const p = podium[rankIdx];
              if (!p) return <div key={rankIdx} />;
              const heights = [220, 160, 120];
              return (
                <div key={p.id} style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }}>
                  <div style={{ fontFamily: theme.fonts.display, fontSize: 32 }}>{p.name}</div>
                  <div style={{ fontFamily: theme.fonts.mono, fontSize: 18, opacity: 0.9 }}>{(p.score || 0).toLocaleString("nl-NL")} pt</div>
                  <div style={{
                    width: "100%", height: heights[rankIdx],
                    background: rankIdx === 0 ? "rgba(255,255,255,0.95)" : rankIdx === 1 ? "rgba(255,255,255,0.7)" : "rgba(255,255,255,0.5)",
                    color: accent,
                    fontFamily: theme.fonts.display, fontSize: 96,
                    display: "grid", placeItems: "center",
                  }}>{rankIdx + 1}</div>
                </div>
              );
            })}
          </div>
        )}
      </div>
      {rest.length > 0 && (
        <div style={{ padding: "32px 72px", flex: 1 }}>
          <Eyebrow theme={theme} style={{ marginBottom: 16 }}>── EN VERDER</Eyebrow>
          {rest.map((p, i) => (
            <div key={p.id} style={{
              display: "grid", gridTemplateColumns: "60px 1fr auto",
              alignItems: "center", gap: 18,
              padding: "14px 0",
              borderBottom: i < rest.length - 1 ? `1px dashed ${theme.rule}` : "none",
            }}>
              <FigureNum theme={theme} size={28}>{i + 4}</FigureNum>
              <div style={{ fontFamily: theme.fonts.display, fontSize: 26 }}>{p.name}</div>
              <FigureNum theme={theme} size={28} accent>{(p.score || 0).toLocaleString("nl-NL")}</FigureNum>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

window.ProjectorScreen = ProjectorScreen;
