/* global React, ReactDOM, useTweaks, TweaksPanel, TweakSection, TweakSlider, TweakToggle, TweakRadio, TweakText, TweakColor */

const { useState, useEffect, useRef, useCallback, useMemo } = React;

// ============================================================
// TWEAKABLE DEFAULTS — persisted by host
// ============================================================
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "timeAllowed": 64,
  "riderName": "Karl Slezak",
  "horseName": "Hot Bobo",
  "country": "CAN",
  "classLabel": "CCI4*-L",
  "order": 14,
  "totalOrder": 32,
  "showRider": true,
  "layout": "stacked",
  "soundOn": false
}/*EDITMODE-END*/;

// ============================================================
// SCORING — USEF Eventing EV152/EV153 (simplified, 3-strike refusals)
//   Knockdown: 4 penalties
//   Refusal: 1st = 4, 2nd = 4, 3rd = ELIMINATION
//   Time allowed exceeded: 0.4 per commenced second (tenths round up to whole)
//   Time limit (2× time allowed) exceeded: ELIMINATION
//   Fall of horse or rider: ELIMINATION
// ============================================================
const REFUSAL_LIMIT = 3;
function refusalPoints(count) {
  if (count <= 0) return 0;
  if (count >= REFUSAL_LIMIT) return (REFUSAL_LIMIT - 1) * 4;
  return count * 4;
}
function isEliminatedByRefusal(count) {
  return count >= REFUSAL_LIMIT;
}
function isEliminatedByTime(elapsedMs, timeAllowed) {
  return elapsedMs / 1000 > 2 * timeAllowed;
}
function timeFaults(elapsedMs, timeAllowed) {
  const over = elapsedMs / 1000 - timeAllowed;
  if (over <= 0) return 0;
  return Math.round(Math.ceil(over) * 0.4 * 10) / 10;
}
function totalScore(rails, refusals, timeF) {
  return rails * 4 + refusalPoints(refusals) + timeF;
}

// ============================================================
// Time formatter — m:ss.t
// ============================================================
function formatClock(ms) {
  const total = Math.max(0, ms);
  const m = Math.floor(total / 60000);
  const s = Math.floor((total % 60000) / 1000);
  const t = Math.floor((total % 1000) / 100);
  return { m, s: String(s).padStart(2, "0"), t };
}
function ordinal(n) {
  return n === 1 ? "1st" : n === 2 ? "2nd" : n === 3 ? "3rd" : `${n}th`;
}

// ============================================================
// useClock — high-precision ticking clock with pause/resume
// ============================================================
function useClock() {
  const [elapsed, setElapsed] = useState(0);
  const [running, setRunning] = useState(false);
  const lastRef = useRef(null);     // wall-clock of previous frame
  const accumRef = useRef(0);       // simulated elapsed (ms)
  const speedRef = useRef(1);       // multiplier — updated live
  const rafRef = useRef(null);

  const tick = useCallback(() => {
    const now = performance.now();
    if (lastRef.current != null) {
      accumRef.current += (now - lastRef.current) * speedRef.current;
    }
    lastRef.current = now;
    setElapsed(accumRef.current);
    rafRef.current = requestAnimationFrame(tick);
  }, []);

  const start = useCallback(() => {
    if (running) return;
    lastRef.current = performance.now();
    setRunning(true);
    rafRef.current = requestAnimationFrame(tick);
  }, [running, tick]);

  const pause = useCallback(() => {
    if (!running) return;
    if (lastRef.current != null) {
      accumRef.current += (performance.now() - lastRef.current) * speedRef.current;
    }
    lastRef.current = null;
    setRunning(false);
    cancelAnimationFrame(rafRef.current);
    setElapsed(accumRef.current);
  }, [running]);

  const resume = useCallback(() => {
    if (running) return;
    lastRef.current = performance.now();
    setRunning(true);
    rafRef.current = requestAnimationFrame(tick);
  }, [running, tick]);

  const reset = useCallback(() => {
    cancelAnimationFrame(rafRef.current);
    lastRef.current = null;
    accumRef.current = 0;
    speedRef.current = 1;
    setElapsed(0);
    setRunning(false);
  }, []);

  // Live speed setter — affects ongoing accumulation immediately.
  const setSpeed = useCallback((s) => {
    speedRef.current = s;
  }, []);

  useEffect(() => () => cancelAnimationFrame(rafRef.current), []);

  return { elapsed, running, start, pause, resume, reset, setSpeed };
}

// ============================================================
// MAIN APP
// ============================================================
function App() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Phase: pre | live | paused | done
  const [phase, setPhase] = useState("pre");
  const [rails, setRails] = useState(0);
  const [refusals, setRefusals] = useState(0);
  const [eliminated, setEliminated] = useState(false);
  const [elimReason, setElimReason] = useState(null);
  const [timeCorrection, setTimeCorrection] = useState(0); // seconds added (rider/judge)
  const [history, setHistory] = useState([]); // undo log
  const [toast, setToast] = useState(null);
  const [flashId, setFlashId] = useState(0);

  const clock = useClock();
  const toastTimerRef = useRef(null);

  // ---- penalty actions
  const pushHistory = (entry) => setHistory((h) => [...h, entry]);
  const showToast = (what, sub) => {
    clearTimeout(toastTimerRef.current);
    setToast({ what, sub });
    toastTimerRef.current = setTimeout(() => setToast(null), 3200);
  };

  const finish = (reason) => {
    setEliminated(true);
    setElimReason(reason);
    clock.pause();
    setPhase("done");
  };

  const addRail = () => {
    if (phase !== "live") return;
    setRails((r) => r + 1);
    pushHistory({ type: "rail", t: clock.elapsed });
    setFlashId((x) => x + 1);
    showToast("+4 knockdown", "tap to undo");
  };
  const addRefusal = () => {
    if (phase !== "live" && phase !== "paused") return;
    const next = refusals + 1;
    setRefusals(next);
    pushHistory({ type: "refusal", t: clock.elapsed });
    setFlashId((x) => x + 1);
    if (next >= REFUSAL_LIMIT) {
      finish(`${ordinal(next)} refusal`);
      showToast(`Eliminated — ${ordinal(next)} refusal`, "");
    } else {
      showToast(`+4 refusal (${next}/${REFUSAL_LIMIT})`, "tap to undo");
    }
  };
  // Refusal-with-knockdown — single combined incident (EV153.4):
  //   refusal + 6s time correction. No extra rail penalty.
  const addRefusalWithKnockdown = () => {
    if (phase !== "live" && phase !== "paused") return;
    setTimeCorrection((c) => c + 6);
    pushHistory({ type: "refusal+knockdown", t: clock.elapsed });
    addRefusal(); // also flashes + may eliminate
    showToast("+6s correction · refusal w/ knockdown", "");
  };
  const onFall = () => {
    if (phase !== "live" && phase !== "paused") return;
    pushHistory({ type: "fall", t: clock.elapsed });
    finish("Fall of horse or rider");
    showToast("Eliminated — fall", "");
  };
  const adjustTimeCorrection = (delta) => {
    setTimeCorrection((c) => Math.max(0, c + delta));
    pushHistory({ type: "tc", delta });
  };
  const undo = () => {
    setHistory((h) => {
      if (!h.length) return h;
      const last = h[h.length - 1];
      if (last.type === "rail") setRails((r) => Math.max(0, r - 1));
      if (last.type === "refusal") {
        setRefusals((r) => Math.max(0, r - 1));
        if (eliminated && elimReason && elimReason.includes("refusal")) {
          setEliminated(false);
          setElimReason(null);
          setPhase("live");
          clock.resume();
        }
      }
      if (last.type === "refusal+knockdown") {
        setRefusals((r) => Math.max(0, r - 1));
        setTimeCorrection((c) => Math.max(0, c - 6));
        if (eliminated && elimReason && elimReason.includes("refusal")) {
          setEliminated(false);
          setElimReason(null);
          setPhase("live");
          clock.resume();
        }
      }
      if (last.type === "fall") {
        setEliminated(false);
        setElimReason(null);
        setPhase("live");
        clock.resume();
      }
      if (last.type === "tc") setTimeCorrection((c) => Math.max(0, c - last.delta));
      showToast(`Undone: ${last.type}`, "");
      return h.slice(0, -1);
    });
  };

  // Direct elimination (judge call)
  const eliminate = () => {
    if (phase !== "live" && phase !== "paused") return;
    pushHistory({ type: "eliminate", t: clock.elapsed });
    finish("Eliminated by official");
  };

  // ---- phase transitions
  const onStart = () => {
    setPhase("live");
    clock.start();
  };
  const onPauseToggle = () => {
    if (phase === "live") {
      clock.pause();
      setPhase("paused");
    } else if (phase === "paused") {
      clock.resume();
      setPhase("live");
    }
  };
  const onStop = () => {
    clock.pause();
    setPhase("done");
  };

  const resetAll = () => {
    clock.reset();
    setRails(0);
    setRefusals(0);
    setEliminated(false);
    setElimReason(null);
    setTimeCorrection(0);
    setHistory([]);
    setToast(null);
    setPhase("pre");
  };

  const nextRider = () => {
    setTweak("order", Math.min(tweaks.totalOrder, tweaks.order + 1));
    resetAll();
  };

  // ---- keyboard shortcuts (operator console feel)
  useEffect(() => {
    const onKey = (e) => {
      if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return;
      if (phase === "pre" && (e.code === "Space" || e.key === "Enter")) {
        e.preventDefault(); onStart();
      } else if (phase === "live") {
        if (e.key === "r" || e.key === "R") addRail();
        else if (e.key === "f" || e.key === "F") addRefusal();
        else if (e.key === "p" || e.key === "P") { e.preventDefault(); onPauseToggle(); }
        else if (e.code === "Space") { e.preventDefault(); onStop(); }
        else if ((e.key === "z" || e.key === "Z") && (e.metaKey || e.ctrlKey)) { e.preventDefault(); undo(); }
      } else if (phase === "paused") {
        if (e.key === "p" || e.key === "P" || e.code === "Space") { e.preventDefault(); onPauseToggle(); }
      } else if (phase === "done") {
        if (e.key === "Enter") nextRider();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [phase, refusals, eliminated]);

  // ---- automatic time-limit elimination (>2× time allowed)
  useEffect(() => {
    if (phase === "live" && isEliminatedByTime(clock.elapsed, tweaks.timeAllowed)) {
      finish("Exceeded time limit");
      showToast(`Eliminated — exceeded time limit (${2 * tweaks.timeAllowed}s)`, "");
    }
  }, [clock.elapsed, phase, tweaks.timeAllowed]);

  // ---- derived
  const adjustedElapsed = clock.elapsed + timeCorrection * 1000;
  const tFaults = timeFaults(adjustedElapsed, tweaks.timeAllowed);
  const finalScore = eliminated ? null : totalScore(rails, refusals, tFaults);
  const overTime = adjustedElapsed / 1000 > tweaks.timeAllowed;
  const timeLimit = 2 * tweaks.timeAllowed;
  const nearLimit = clock.elapsed / 1000 > timeLimit * 0.85;

  return (
    <div className="app" data-variant={tweaks.layout}>
      <div className="stage">
        {phase === "pre" && (
          <PreStartScreen tweaks={tweaks} setTweak={setTweak} onStart={onStart} />
        )}
        {(phase === "live" || phase === "paused") && (
          <LiveScreen
            elapsed={clock.elapsed}
            adjustedElapsed={adjustedElapsed}
            rails={rails}
            refusals={refusals}
            tFaults={tFaults}
            score={finalScore ?? 0}
            timeAllowed={tweaks.timeAllowed}
            timeLimit={timeLimit}
            nearLimit={nearLimit}
            overTime={overTime}
            timeCorrection={timeCorrection}
            paused={phase === "paused"}
            flashId={flashId}
            layout={tweaks.layout}
            onRail={addRail}
            onRefusal={addRefusal}
            onPauseToggle={onPauseToggle}
            onStop={onStop}
            onSetSpeed={clock.setSpeed}
            onUndo={undo}
            canUndo={history.length > 0}
            onEliminate={eliminate}
            onFall={onFall}
          />
        )}
        {phase === "paused" && (
          <PausedOverlay
            onResume={onPauseToggle}
            onRefusalWithKnockdown={addRefusalWithKnockdown}
            onAddCorrection={() => adjustTimeCorrection(+6)}
            onRemoveCorrection={() => adjustTimeCorrection(-6)}
            timeCorrection={timeCorrection}
          />
        )}
        {phase === "done" && (
          <SummaryScreen
            elapsed={clock.elapsed}
            adjustedElapsed={adjustedElapsed}
            rails={rails}
            refusals={refusals}
            tFaults={tFaults}
            score={finalScore}
            eliminated={eliminated}
            elimReason={elimReason}
            timeAllowed={tweaks.timeAllowed}
            timeCorrection={timeCorrection}
            tweaks={tweaks}
            onRedo={resetAll}
            onNext={nextRider}
          />
        )}
      </div>

      {toast && (
        <div className="toast-wrap">
          <div className="toast">
            <div className="what">
              {toast.what}
              {toast.sub && <small>{toast.sub}</small>}
            </div>
            <button onClick={undo} disabled={!history.length}>Undo</button>
          </div>
        </div>
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Layout">
          <TweakRadio
            label="Variant"
            value={tweaks.layout}
            options={[
              { value: "stacked", label: "Stacked" },
              { value: "ergo", label: "Ergo" },
              { value: "grid", label: "Grid 2×2" },
            ]}
            onChange={(v) => setTweak("layout", v)}
          />
          <TweakToggle
            label="Show rider context"
            value={tweaks.showRider}
            onChange={(v) => setTweak("showRider", v)}
          />
        </TweakSection>
        <TweakSection label="Class">
          <TweakSlider
            label="Time allowed (s)"
            min={40} max={120} step={1}
            value={tweaks.timeAllowed}
            onChange={(v) => setTweak("timeAllowed", v)}
          />
          <TweakText
            label="Class label"
            value={tweaks.classLabel}
            onChange={(v) => setTweak("classLabel", v)}
          />
        </TweakSection>
        <TweakSection label="Rider">
          <TweakText
            label="Name"
            value={tweaks.riderName}
            onChange={(v) => setTweak("riderName", v)}
          />
          <TweakText
            label="Horse"
            value={tweaks.horseName}
            onChange={(v) => setTweak("horseName", v)}
          />
          <TweakText
            label="ISO country"
            value={tweaks.country}
            onChange={(v) => setTweak("country", v.toUpperCase().slice(0,3))}
          />
          <TweakSlider
            label="Draw order"
            min={1} max={tweaks.totalOrder} step={1}
            value={tweaks.order}
            onChange={(v) => setTweak("order", v)}
          />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

// ============================================================
// TOP BAR
// ============================================================
function Top({ tweaks }) {
  return (
    <div className="top">
      <div className="venue">
        <span className="bug" />
        <span>Bromont&nbsp;2026</span>
      </div>
    </div>
  );
}

function RiderStrip({ tweaks }) {
  return (
    <div className="rider-strip">
      <div className="order">
        <span className="lbl">Order</span>
        {String(tweaks.order).padStart(2, "0")}
        <span className="of">/ {tweaks.totalOrder}</span>
      </div>
      <div className="who">
        <div className="name">{tweaks.riderName}</div>
        <div className="horse">{tweaks.horseName}</div>
      </div>
      <div className="flag">{tweaks.country}</div>
    </div>
  );
}

// ============================================================
// PRE-START
// ============================================================
function PreStartScreen({ tweaks, setTweak, onStart }) {
  const adjust = (delta) => {
    const next = Math.max(20, Math.min(180, tweaks.timeAllowed + delta));
    if (next !== tweaks.timeAllowed) setTweak("timeAllowed", next);
  };
  return (
    <div className="pre">
      <div className="cue">
        <div className="countdown-label">Tap when rider crosses the start line</div>
        <button className="start-btn" onClick={onStart} aria-label="Start the clock">
          <div className="word">Start</div>
        </button>
      </div>
      <div className="pre-info">
        <div className="cell time-allowed">
          <div className="lbl">Time<br/>allowed</div>
          <div className="ta-controls">
            <button
              className="ta-step"
              onClick={() => adjust(-1)}
              aria-label="Decrease time allowed"
            >−</button>
            <div className="val">{tweaks.timeAllowed}<span className="unit">s</span></div>
            <button
              className="ta-step"
              onClick={() => adjust(+1)}
              aria-label="Increase time allowed"
            >+</button>
          </div>
        </div>
        <div className="cell tlimit">
          <div className="lbl">Time<br/>limit</div>
          <div className="val">{2 * tweaks.timeAllowed}<span className="unit">s</span></div>
        </div>
      </div>
    </div>
  );
}

// ============================================================
// FAULT BUTTON — tap = rail (+4), hold = refusal (+4 / 3rd = elim)
// ============================================================
function FaultButton({ onRail, onRefusal, rails, refusals }) {
  const HOLD_MS = 500;
  const [holding, setHolding] = React.useState(false);
  const timerRef = React.useRef(null);
  const firedRef = React.useRef(false);

  const begin = (e) => {
    e.preventDefault();
    firedRef.current = false;
    setHolding(true);
    timerRef.current = setTimeout(() => {
      firedRef.current = true;
      setHolding(false);
      onRefusal();
    }, HOLD_MS);
  };
  const end = () => {
    if (!timerRef.current && !holding) return;
    clearTimeout(timerRef.current);
    timerRef.current = null;
    const wasHolding = holding;
    setHolding(false);
    if (wasHolding && !firedRef.current) onRail();
  };
  const cancel = () => {
    clearTimeout(timerRef.current);
    timerRef.current = null;
    setHolding(false);
  };

  return (
    <button
      className="action penalty fault-btn"
      onPointerDown={begin}
      onPointerUp={end}
      onPointerLeave={cancel}
      onPointerCancel={cancel}
      data-holding={holding ? "true" : "false"}
      aria-label="Tap for knockdown, hold for refusal"
    >
      <div className="fill" />
      <div className="label">Fault</div>
      <div className="right">
        <div className="pts">+4</div>
        <div className="hint">
          <span className="tap">Tap · rail ×{rails}</span>
          <span className="hold">Hold · refusal {refusals}/3</span>
        </div>
      </div>
    </button>
  );
}

// ============================================================
// SYNC SLIDER — press/hold to nudge the clock faster (right) or
// slower (left) so the operator can match the official stopwatch.
// On release the thumb snaps back to center and speed returns to 1×.
// ============================================================
function SyncSlider({ onSetSpeed, paused }) {
  // Range: -1 .. 0 .. +1 (normalized). speed = 1 + offset * MAX_DELTA
  const MAX_DELTA = 1.0; // ±100% means clock runs 0×..2× wall clock
  const trackRef = React.useRef(null);
  const [offset, setOffset] = React.useState(0); // -1..+1
  const [dragging, setDragging] = React.useState(false);
  const draggingRef = React.useRef(false);
  const rafRef = React.useRef(null);

  const setFromClientX = React.useCallback((clientX) => {
    const r = trackRef.current.getBoundingClientRect();
    const x = clientX - r.left;
    const half = r.width / 2;
    // -1 at left edge, 0 at center, +1 at right edge
    let off = (x - half) / half;
    // Dead-zone in the middle so tiny accidental wobble doesn't drift
    if (Math.abs(off) < 0.04) off = 0;
    off = Math.max(-1, Math.min(1, off));
    setOffset(off);
    onSetSpeed(1 + off * MAX_DELTA);
  }, [onSetSpeed]);

  const onPointerDown = (e) => {
    if (paused) return;
    e.preventDefault();
    draggingRef.current = true;
    setDragging(true);
    trackRef.current.setPointerCapture(e.pointerId);
    setFromClientX(e.clientX);
  };
  const onPointerMove = (e) => {
    if (!draggingRef.current) return;
    setFromClientX(e.clientX);
  };
  const release = () => {
    if (!draggingRef.current) return;
    draggingRef.current = false;
    setDragging(false);
    // Snap thumb back to center; restore 1× speed immediately.
    onSetSpeed(1);
    // Animate offset back to 0
    const start = performance.now();
    const from = offset;
    const dur = 180;
    const step = (now) => {
      const k = Math.min(1, (now - start) / dur);
      const eased = 1 - Math.pow(1 - k, 3);
      const v = from * (1 - eased);
      setOffset(v);
      if (k < 1) rafRef.current = requestAnimationFrame(step);
    };
    rafRef.current = requestAnimationFrame(step);
  };

  React.useEffect(() => () => {
    cancelAnimationFrame(rafRef.current);
  }, []);

  const pct = ((offset + 1) / 2) * 100; // 0..100
  const speed = 1 + offset * MAX_DELTA;
  const speedLabel = speed === 1 ? "1.00×" : `${speed.toFixed(2)}×`;
  const direction = offset === 0 ? null : (offset > 0 ? "fast" : "slow");

  return (
    <div className="sync" data-dragging={dragging ? "true" : "false"} data-dir={direction || "none"}>
      <div className="sync-meta">
        <span className="lbl">Sync</span>
        <span className="readout">{speedLabel}</span>
      </div>
      <div
        ref={trackRef}
        className="sync-track"
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={release}
        onPointerCancel={release}
        onLostPointerCapture={release}
      >
        <div className="sync-rail" />
        <div className="sync-center" />
        <div
          className="sync-fill"
          style={{
            left: offset >= 0 ? "50%" : `${pct}%`,
            width: `${Math.abs(offset) * 50}%`,
          }}
        />
        <div className="sync-thumb" style={{ left: `${pct}%` }}>
          <span className="sync-arrow left">‹</span>
          <span className="sync-grip" />
          <span className="sync-arrow right">›</span>
        </div>
        <span className="sync-end slow">SLOW</span>
        <span className="sync-end fast">FAST</span>
      </div>
    </div>
  );
}

// ============================================================
// LIVE
// ============================================================
function LiveScreen({
  elapsed, adjustedElapsed, rails, refusals, tFaults, score, timeAllowed,
  timeLimit, nearLimit, overTime, timeCorrection,
  paused, flashId, layout,
  onRail, onRefusal, onPauseToggle, onStop, onSetSpeed, onUndo, canUndo, onEliminate, onFall,
}) {
  const { m, s, t } = formatClock(elapsed);
  const rPoints = refusalPoints(refusals);
  const totalShownRaw = rails * 4 + rPoints + tFaults;
  const totalShown = Number.isInteger(totalShownRaw) ? totalShownRaw : totalShownRaw.toFixed(1);
  return (
    <div className="live">
      <div
        className="clock-band"
        data-paused={paused ? "true" : "false"}
        data-over={overTime ? "true" : "false"}
        data-near-limit={nearLimit && !overTime ? "true" : "false"}
        data-faults={totalShownRaw > 0 ? "true" : "false"}
      >
        <div className="row1">
          <div className="live-tag">
            <span className="dot" />
            {paused ? "Held" : "Live"}
          </div>
          <div className="allowed">
            {overTime
              ? <>Over time <b>+{((adjustedElapsed/1000) - timeAllowed).toFixed(1)}s</b></>
              : <>Allowed <b>{timeAllowed}s</b> · Limit <b>{timeLimit}s</b></>}
          </div>
        </div>
        <div className="row2">
          <div className="clock">
            <span className="min">{m}</span>
            <span className="sep">:</span>
            <span className="sec">{s}</span>
            <span className="ms">.{t}</span>
          </div>
          <div className="faults">
            <div className="v" key={flashId}>{totalShown}</div>
            <div className="l">Faults</div>
          </div>
        </div>
        {timeCorrection > 0 && (
          <div className="correction-strip">
            <span>Time correction</span><b>+{timeCorrection}s</b>
          </div>
        )}
      </div>

      <SyncSlider onSetSpeed={onSetSpeed} paused={paused} />

      <div className="actions">
        <FaultButton onRail={onRail} onRefusal={onRefusal} rails={rails} refusals={refusals} />
        <button
          className="action pause"
          onClick={onPauseToggle}
          data-active={paused ? "true" : "false"}
          aria-label="Pause or resume the clock"
        >
          <div className="label">{paused ? "Resume" : "Pause"}</div>
          <div className="right">
            <div>Hold clock</div>
          </div>
        </button>
        <button
          className="action stop"
          onClick={onStop}
          aria-label="Stop the clock — finish line"
        >
          <div className="label">Stop</div>
          <div className="right">
            <div>Finish line</div>
          </div>
        </button>
      </div>

      <div className="foot">
        <button className="chip" onClick={onUndo} disabled={!canUndo}>
          <span className="arrow">↶</span> Undo last
        </button>
        <span className="foot-sep" />
        <button className="chip danger" onClick={onFall}>Fall</button>
        <button className="chip danger" onClick={onEliminate}>Retire / DQ</button>
      </div>
    </div>
  );
}

// ============================================================
// PAUSED OVERLAY
// ============================================================
function PausedOverlay({ onResume, onRefusalWithKnockdown, onAddCorrection, onRemoveCorrection, timeCorrection }) {
  return (
    <div className="paused-card" onClick={(e) => e.stopPropagation()}>
      <div className="paused-inner">
        <div className="badge">Clock held</div>

        <button className="paused-action" onClick={onRefusalWithKnockdown}>
          <span className="pa-l">Refusal with knockdown</span>
          <span className="pa-r">+4 · +6s</span>
        </button>

        <button className="resume" onClick={onResume}>Resume clock</button>
      </div>
    </div>
  );
}

// ============================================================
// SUMMARY
// ============================================================
function SummaryScreen({
  elapsed, adjustedElapsed, rails, refusals, tFaults, score, eliminated, elimReason,
  timeAllowed, timeCorrection, tweaks, onRedo, onNext,
}) {
  const { m, s, t } = formatClock(elapsed);
  const adj = formatClock(adjustedElapsed);
  const rPoints = refusalPoints(refusals);
  const totalFaultsRaw = rails * 4 + rPoints + tFaults;
  const totalFaults = Number.isInteger(totalFaultsRaw) ? totalFaultsRaw : totalFaultsRaw.toFixed(1);
  const overSec = adjustedElapsed / 1000 - timeAllowed;
  const clean = !eliminated && totalFaultsRaw === 0 && overSec <= 0;

  let verdict = "Round complete";
  let stamp = "Clear with time";
  if (eliminated) {
    verdict = "Eliminated";
    stamp = elimReason || "Retired";
  } else if (clean) {
    verdict = "Clear round";
    stamp = "Inside time";
  } else if (totalFaultsRaw > 0) {
    verdict = `${totalFaults} faults`;
    stamp = overSec > 0 ? "Faulted · over time" : "Faulted";
  }

  return (
    <div className="summary" data-elim={eliminated ? "true" : "false"} data-faults={totalFaultsRaw > 0 ? "true" : "false"}>
      <div className="result-head">
        <div className="stamp">{stamp}</div>
        <div className="verdict">{verdict}</div>
      </div>

      <div className="totalcard">
        <div>
          <div className="l">Round time{timeCorrection > 0 ? " (adjusted)" : ""}</div>
          <div className="time">
            {adj.m}:{adj.s}<span style={{ color: "var(--air-fg-on-dark-mute)", fontSize: "60%" }}>.{adj.t}</span>
          </div>
          {timeCorrection > 0 && (
            <div className="sub-time">raw {m}:{s}.{t} · +{timeCorrection}s correction</div>
          )}
        </div>
        <div className="penalty-total">
          <div className="l">Total penalties</div>
          <div className="n">{eliminated ? "EL" : totalFaults}</div>
        </div>
      </div>

      <div className="bd">
        <div className="bd-row" data-zero={rails === 0 ? "true" : "false"} data-faulted={rails > 0 ? "true" : "false"}>
          <div className="name">Knockdowns</div>
          <div className="count">×{rails}</div>
          <div className="pts">+{rails * 4}</div>
        </div>
        <div className="bd-row" data-zero={refusals === 0 ? "true" : "false"} data-faulted={refusals > 0 ? "true" : "false"}>
          <div className="name">Refusals</div>
          <div className="count">×{refusals}</div>
          <div className="pts">{eliminated && elimReason && elimReason.includes("refusal") ? "EL" : `+${rPoints}`}</div>
        </div>
        {timeCorrection > 0 && (
          <div className="bd-row" data-faulted="true">
            <div className="name">Time correction</div>
            <div className="count">+{timeCorrection}s</div>
            <div className="pts">—</div>
          </div>
        )}
        <div className="bd-row" data-zero={tFaults === 0 ? "true" : "false"} data-faulted={tFaults > 0 ? "true" : "false"}>
          <div className="name">Time faults</div>
          <div className="count">{tFaults > 0 ? `+${Math.ceil(overSec)}s` : "—"}</div>
          <div className="pts">+{tFaults.toFixed(1)}</div>
        </div>
      </div>

      <div className="cta-row">
        <button className="cta" onClick={onRedo}>Redo</button>
        <button className="cta primary" onClick={onNext}>
          Confirm · next rider →
        </button>
      </div>
    </div>
  );
}

// ============================================================
// MOUNT
// ============================================================
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
