/* ============================================================
   Workshop app — 100 questions, dependent fields, autosave.
   Loads window.WORKSHOP_QUESTIONS from workshop-questions.jsx
   Loads useTweaks / TweaksPanel / TweakRadio etc. from tweaks-panel.jsx
   ============================================================ */

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

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "density": "regular",
  "anim": "slide",
  "autoCollapse": true,
  "showJump": true,
  "requiredStyle": "asterisk",
  "actionStyle": "card"
}/*EDITMODE-END*/;

/* ---------- Icons ---------- */
const I = {
  check:    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5"/></svg>,
  x:        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>,
  dash:     <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M5 12h14"/></svg>,
  plus:     <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M12 5v14M5 12h14"/></svg>,
  warn:     <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>,
  user:     <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>,
  flag:     <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M4 22V4a2 2 0 0 1 2-2h11l-2 4 2 4H6"/></svg>,
  cal:      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>,
  chevron:  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6"/></svg>,
  arrowUp:  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>,
};

/* ---------- Empty answer factory ---------- */
function emptyAnswer() {
  return {
    q1: null, q2: null,
    // Per-branch state preserved when toggling away
    q1NoFindings: "", q1NoAction: null,
    q2NoFindings: "", q2NoAction: null,
    otherComments: "",
    // Meta
    savedAt: null,
  };
}
function emptyAction() {
  return { title: "", desc: "", owner: "", priority: "med", due: "" };
}

/* ---------- Derive completion state ---------- */
function deriveStatus(a) {
  // returns: 'empty' | 'partial' | 'done'
  if (a.q1 === null) return "empty";
  if (a.q1 === "na") return "done";
  if (a.q1 === "no") {
    const ok = a.q1NoFindings.trim() && a.q1NoAction && a.q1NoAction.title.trim();
    return ok ? "done" : "partial";
  }
  // q1 === yes
  if (a.q2 === null) return "partial";
  if (a.q2 === "yes" || a.q2 === "na") return "done";
  if (a.q2 === "no") {
    const ok = a.q2NoFindings.trim() && a.q2NoAction && a.q2NoAction.title.trim();
    return ok ? "done" : "partial";
  }
  return "partial";
}

function answerSummary(a) {
  // Returns short text describing the answer for the collapsed row.
  if (a.q1 === null) return null;
  if (a.q1 === "na") return { pills: [{ v: "na", label: "N/A" }] };
  if (a.q1 === "no") {
    return { pills: [{ v: "no", label: "No" }], note: "Finding + action recorded" };
  }
  // yes
  if (a.q2 === null) return { pills: [{ v: "yes", label: "Yes" }], note: "Q2 pending" };
  if (a.q2 === "yes") return { pills: [{ v: "yes", label: "Yes" }, { v: "yes", label: "Evidence: Yes" }] };
  if (a.q2 === "na")  return { pills: [{ v: "yes", label: "Yes" }, { v: "na",  label: "Evidence: N/A" }] };
  if (a.q2 === "no")  return { pills: [{ v: "yes", label: "Yes" }, { v: "no",  label: "Evidence: No" }], note: "Finding + action recorded" };
  return null;
}

/* ---------- YNA segmented ---------- */
function YNA({ value, onChange }) {
  const opts = [
    { v: "yes", label: "Yes", cls: "sel-yes", icon: I.check },
    { v: "no",  label: "No",  cls: "sel-no",  icon: I.x },
    { v: "na",  label: "N/A", cls: "sel-na",  icon: I.dash },
  ];
  return (
    <div className="yna-row" role="radiogroup">
      {opts.map(o => (
        <button
          key={o.v}
          type="button"
          role="radio"
          aria-checked={value === o.v}
          className={"yna" + (value === o.v ? " " + o.cls : "")}
          onClick={() => onChange(o.v)}
        >
          {value === o.v ? o.icon : null}
          {o.label}
        </button>
      ))}
    </div>
  );
}

/* ---------- Reveal ---------- */
function Reveal({ shown, anim, children }) {
  const ref = useRef(null);
  const [h, setH] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const update = () => {
      const inner = ref.current.firstElementChild;
      if (inner) setH(inner.scrollHeight);
    };
    update();
    const ro = new ResizeObserver(update);
    if (ref.current.firstElementChild) ro.observe(ref.current.firstElementChild);
    return () => ro.disconnect();
  }, [children]);
  return (
    <div
      className="reveal"
      data-anim={anim}
      data-shown={shown ? "true" : "false"}
      ref={ref}
      style={{ "--reveal-h": (h + 4) + "px" }}
      aria-hidden={!shown}
    >
      <div className="reveal-inner">{children}</div>
    </div>
  );
}

/* ---------- Action stub + card ---------- */
function ActionStub({ required, onClick }) {
  return (
    <button type="button" className={"action-stub" + (required ? " required" : "")} onClick={onClick}>
      {I.plus} Add one action <span style={{ color: "var(--text-tertiary)", fontWeight: 400, marginLeft: 4 }}>· required</span>
    </button>
  );
}

function ActionCard({ value, onChange, onRemove, keyId }) {
  const prio = { high: "High", med: "Medium", low: "Low" };
  return (
    <div className="action-card">
      <div className="action-card-head">
        <span className="key">{keyId}</span>
        <span className="domain"><span className="dot"></span>Action</span>
        <button type="button" className="close" aria-label="Remove action" onClick={onRemove}>{I.x}</button>
      </div>
      <div className="action-card-body">
        <input
          type="text" className="title"
          placeholder="Action title — what needs to happen?"
          value={value.title}
          onChange={e => onChange({ ...value, title: e.target.value })}
        />
        <textarea
          className="desc"
          placeholder="Short description"
          value={value.desc}
          onChange={e => onChange({ ...value, desc: e.target.value })}
        />
        <div className="meta-row">
          <button type="button" className="meta-pill"
            data-empty={value.owner ? "false" : "true"}
            onClick={() => onChange({ ...value, owner: value.owner ? "" : "Sarah Mensah" })}>
            <span className="ico">{I.user}</span>
            <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
              <span className="label">Owner</span>
              <span className="val">{value.owner || "Unassigned"}</span>
            </div>
          </button>
          <button type="button" className="meta-pill" data-priority={value.priority}
            onClick={() => {
              const o = ["low", "med", "high"];
              const next = o[(o.indexOf(value.priority) + 1) % o.length];
              onChange({ ...value, priority: next });
            }}>
            <span className="ico">{I.flag}</span>
            <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
              <span className="label">Priority</span>
              <span className="val">{prio[value.priority]}</span>
            </div>
          </button>
          <button type="button" className="meta-pill"
            data-empty={value.due ? "false" : "true"}
            onClick={() => onChange({ ...value, due: value.due ? "" : "Jun 14, 2026" })}>
            <span className="ico">{I.cal}</span>
            <div style={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
              <span className="label">Due</span>
              <span className="val">{value.due || "No due date"}</span>
            </div>
          </button>
        </div>
      </div>
    </div>
  );
}

function SimpleActionRow({ value, onChange, onRemove }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      <input
        type="text" className="field"
        placeholder="Action title — what needs to happen?"
        value={value.title}
        onChange={e => onChange({ ...value, title: e.target.value })}
      />
      <button type="button" onClick={onRemove}
        style={{ alignSelf: "flex-start", background: "transparent", border: "none", padding: 0,
                 color: "var(--text-tertiary)", font: "500 12px/16px var(--font-sans)",
                 cursor: "pointer", textDecoration: "underline" }}>
        Remove action
      </button>
    </div>
  );
}

/* ---------- Findings + action block (Q1=No or Q2=No) ---------- */
function FindingsAndAction({ keyId, t, findings, action, setFindings, setAction, branch }) {
  return (
    <div className={"nested " + branch}>
      <div className="field-block">
        <label className="field-label" htmlFor={keyId + "-findings"}>
          Finding details
          {t.requiredStyle === "asterisk" && <span className="req">*</span>}
          {t.requiredStyle === "label"    && <span className="req">Required</span>}
        </label>
        <div className="field-help">Explain what's missing or why this control isn't met.</div>
        <textarea
          id={keyId + "-findings"} className="field"
          placeholder="e.g. Walks happen ad-hoc. No formal cadence, no follow-up register."
          value={findings}
          onChange={e => setFindings(e.target.value)}
        />
      </div>
      <div className="field-block">
        <label className="field-label">
          Action
          {t.requiredStyle === "asterisk" && <span className="req">*</span>}
          {t.requiredStyle === "label"    && <span className="req">Required</span>}
          <span className="opt">· only one</span>
        </label>
        {!action && <ActionStub required onClick={() => setAction(emptyAction())} />}
        {action && (
          t.actionStyle === "card"
            ? <ActionCard value={action} onChange={setAction} onRemove={() => setAction(null)} keyId={keyId} />
            : <SimpleActionRow value={action} onChange={setAction} onRemove={() => setAction(null)} />
        )}
      </div>
    </div>
  );
}

/* ---------- Other comments ---------- */
function OtherComments({ value, onChange }) {
  return (
    <div className="field-block">
      <label className="field-label">Other comments <span className="opt">Optional</span></label>
      <textarea className="field"
        placeholder="What can be improved? What are we doing well?"
        value={value}
        onChange={e => onChange(e.target.value)}
      />
    </div>
  );
}

/* ============================================================
   QUESTION ROW
   ============================================================ */
function QuestionRow({ q, answer, setAnswer, expanded, onExpand, onCollapse, t, saveState }) {
  const status = deriveStatus(answer);
  const summary = answerSummary(answer);

  const showQ2     = answer.q1 === "yes";
  const showQ1No   = answer.q1 === "no";
  const showQ2No   = answer.q1 === "yes" && answer.q2 === "no";
  const showOtherC = answer.q1 === "yes" && (answer.q2 === "yes" || answer.q2 === "na");

  const isAnswered = status === "done";
  const collapsed = !expanded;

  const setField = useCallback((p) => setAnswer({ ...answer, ...p }), [answer, setAnswer]);

  return (
    <div className={"ws-row " + (expanded ? "expanded " : "collapsed ") + (status === "done" ? "answered " : (status === "partial" ? "answered " : "unanswered "))}>
      <div className="ws-row-collapsed" onClick={() => expanded ? onCollapse() : onExpand()}>
        <div className={"marker " + (status === "done" ? "done" : status === "partial" ? "partial" : "empty")}>
          {status === "done" && I.check}
          {status === "partial" && I.warn}
        </div>
        <div className="ws-qnum">{q.num}</div>
        <div className="ws-qsummary">
          <div className="ws-qtext">{q.text}</div>
          <div className="ws-qsubsummary">
            {!summary && <span>{q.eyebrow}</span>}
            {summary && (
              <>
                {summary.pills.map((p, i) => (
                  <span key={i} className={"ws-pill " + p.v}>{p.label}</span>
                ))}
                {summary.note && (
                  <>
                    <span className="ws-dotsep"></span>
                    <span>{summary.note}</span>
                  </>
                )}
              </>
            )}
          </div>
        </div>
        <div className="ws-row-aside">
          {saveState === "saving" && <span style={{ color: "#b54708" }}>Saving…</span>}
          {saveState === "saved"  && answer.savedAt && <span>Saved {relTime(answer.savedAt)}</span>}
          <span className="chevron">{I.chevron}</span>
        </div>
      </div>

      <div className="ws-row-expand" data-shown={expanded ? "true" : "false"}>
        {expanded && (
          <>
            <div className="ws-row-expand-inner">
              <div>
                <div className="qhead-eyebrow">{q.eyebrow} · {q.num}</div>
                <h3 className="qhead-text">{q.text}</h3>
                <div className="qhead-example">{q.example}</div>
              </div>

              {/* Q1 */}
              <div className="field-block">
                <label className="field-label">
                  Do we have this in a process / system?
                  {t.requiredStyle === "asterisk" && <span className="req">*</span>}
                  {t.requiredStyle === "label"    && <span className="req">Required</span>}
                </label>
                <YNA value={answer.q1} onChange={v => setField({ q1: v, ...(v !== "yes" ? { q2: null } : {}) })} />
              </div>

              {/* Q1=Yes → Q2 */}
              <Reveal shown={showQ2} anim={t.anim}>
                <div className="nested yes">
                  <div className="field-block">
                    <label className="field-label">
                      Do we have evidence?
                      {t.requiredStyle === "asterisk" && <span className="req">*</span>}
                      {t.requiredStyle === "label"    && <span className="req">Required</span>}
                    </label>
                    <YNA value={answer.q2} onChange={v => setField({ q2: v })} />
                  </div>
                  <Reveal shown={showQ2No} anim={t.anim}>
                    <FindingsAndAction
                      keyId={"ACT-" + q.id.toUpperCase() + "-E"}
                      t={t}
                      findings={answer.q2NoFindings}
                      action={answer.q2NoAction}
                      setFindings={v => setField({ q2NoFindings: v })}
                      setAction={v => setField({ q2NoAction: v })}
                      branch="no"
                    />
                  </Reveal>
                </div>
              </Reveal>

              {/* Q1=No → findings + action */}
              <Reveal shown={showQ1No} anim={t.anim}>
                <FindingsAndAction
                  keyId={"ACT-" + q.id.toUpperCase()}
                  t={t}
                  findings={answer.q1NoFindings}
                  action={answer.q1NoAction}
                  setFindings={v => setField({ q1NoFindings: v })}
                  setAction={v => setField({ q1NoAction: v })}
                  branch="no"
                />
              </Reveal>

              {/* Q1=Yes & Q2 in (yes,na) → other comments */}
              <Reveal shown={showOtherC} anim={t.anim}>
                <OtherComments value={answer.otherComments} onChange={v => setField({ otherComments: v })} />
              </Reveal>
            </div>

            <div className="ws-row-footer">
              <span className="hint save-hint">
                {saveState === "saving" && <><span className="dot"></span>Saving draft…</>}
                {saveState === "saved" && answer.savedAt && <><span className="dot"></span>Saved {relTime(answer.savedAt)} · autosaves while you type</>}
                {saveState !== "saving" && !answer.savedAt && <>Autosaves as you answer</>}
              </span>
              <span className="spacer"></span>
              <button className="btn btn-secondary" onClick={onCollapse}>
                Done — collapse
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

const MemoQuestionRow = memo(QuestionRow, (a, b) =>
  a.answer === b.answer && a.expanded === b.expanded && a.t === b.t && a.saveState === b.saveState
);

/* ---------- Relative time ---------- */
function relTime(ts) {
  const s = Math.max(0, Math.floor((Date.now() - ts) / 1000));
  if (s < 5)   return "just now";
  if (s < 60)  return s + "s ago";
  if (s < 3600) return Math.floor(s / 60) + "m ago";
  return Math.floor(s / 3600) + "h ago";
}

/* ============================================================
   APP
   ============================================================ */
function App() {
  const [t, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
  const questions = window.WORKSHOP_QUESTIONS;

  // Per-question answer state
  const [answers, setAnswers] = useState(() => {
    const seed = {};
    questions.forEach(q => seed[q.id] = emptyAnswer());
    return seed;
  });

  // Per-question save state ('idle' | 'saving' | 'saved')
  const [saveStates, setSaveStates] = useState({});

  // Which question is currently expanded (only one at a time)
  const [expandedId, setExpandedId] = useState(questions[0].id);

  // Autosave timers
  const timersRef = useRef({});

  // Set body density attribute
  useEffect(() => {
    document.body.setAttribute("data-density", t.density);
  }, [t.density]);

  // Update a single answer + trigger autosave
  const updateAnswer = useCallback((id, next) => {
    setAnswers(prev => ({ ...prev, [id]: next }));
    // saving indicator
    setSaveStates(prev => ({ ...prev, [id]: "saving" }));
    clearTimeout(timersRef.current[id]);
    timersRef.current[id] = setTimeout(() => {
      setAnswers(prev => ({ ...prev, [id]: { ...prev[id], savedAt: Date.now() } }));
      setSaveStates(prev => ({ ...prev, [id]: "saved" }));
    }, 700);
  }, []);

  // Cleanup
  useEffect(() => () => Object.values(timersRef.current).forEach(clearTimeout), []);

  // Aggregate progress
  const { done, total } = useMemo(() => {
    let d = 0;
    questions.forEach(q => { if (deriveStatus(answers[q.id]) === "done") d += 1; });
    return { done: d, total: questions.length };
  }, [answers, questions]);

  const pct = Math.round((done / total) * 100);

  // Update sticky header
  useEffect(() => {
    document.getElementById("progress-fill").style.width = pct + "%";
    document.getElementById("progress-done").textContent = done;
    document.getElementById("progress-total").textContent = total;
    document.getElementById("progress-pct").textContent = pct + "%";
  }, [done, total, pct]);

  // Global save indicator: aggregate over all questions
  useEffect(() => {
    const states = Object.values(saveStates);
    const anySaving = states.some(s => s === "saving");
    const anySaved  = states.some(s => s === "saved");
    const el = document.getElementById("save-indicator");
    if (!el) return;
    el.classList.toggle("saving", anySaving);
    el.classList.toggle("saved",  !anySaving && anySaved);
    el.querySelector("span").textContent = anySaving ? "Saving…" : (anySaved ? "All saved" : "Not started");
    const svg = el.querySelector("svg");
    if (anySaving) {
      // replace svg with spinner if not already
      if (!el.querySelector(".ring")) {
        const ring = document.createElement("span");
        ring.className = "ring";
        el.replaceChild(ring, svg);
      }
    } else {
      if (!el.querySelector("svg")) {
        const ring = el.querySelector(".ring");
        const newSvg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        newSvg.setAttribute("viewBox", "0 0 24 24");
        newSvg.setAttribute("fill", "none");
        newSvg.setAttribute("stroke", "currentColor");
        newSvg.setAttribute("stroke-width", "2");
        newSvg.setAttribute("stroke-linecap", "round");
        newSvg.setAttribute("stroke-linejoin", "round");
        const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
        path.setAttribute("d", "M20 6 9 17l-5-5");
        newSvg.appendChild(path);
        el.replaceChild(newSvg, ring);
      }
    }
  }, [saveStates]);

  // Re-render to refresh "Saved X ago" timestamps every 20s
  const [, tickRerender] = useState(0);
  useEffect(() => {
    const i = setInterval(() => tickRerender(x => x + 1), 20000);
    return () => clearInterval(i);
  }, []);

  // Handlers
  const onExpand = useCallback((id) => setExpandedId(id), []);
  const onCollapse = useCallback(() => setExpandedId(null), []);

  // Auto-collapse when a Q transitions to 'done' (if tweak enabled)
  const prevStatusRef = useRef({});
  useEffect(() => {
    if (!t.autoCollapse) return;
    questions.forEach(q => {
      const s = deriveStatus(answers[q.id]);
      const prev = prevStatusRef.current[q.id];
      prevStatusRef.current[q.id] = s;
      if (prev !== "done" && s === "done" && expandedId === q.id) {
        // small delay so user sees the green check before collapse
        setTimeout(() => setExpandedId(curr => curr === q.id ? null : curr), 600);
      }
    });
  }, [answers, t.autoCollapse, expandedId, questions]);

  // Floating "jump to next unanswered"
  const nextUnansweredId = useMemo(() => {
    for (const q of questions) {
      if (deriveStatus(answers[q.id]) !== "done") return q.id;
    }
    return null;
  }, [answers, questions]);

  const jumpToNext = useCallback(() => {
    if (!nextUnansweredId) return;
    setExpandedId(nextUnansweredId);
    // scroll into view using offsetTop (avoid scrollIntoView)
    setTimeout(() => {
      const el = document.getElementById("row-" + nextUnansweredId);
      if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 130, behavior: "smooth" });
    }, 50);
  }, [nextUnansweredId]);

  return (
    <>
      <div className="ws-list">
        {questions.map(q => (
          <div key={q.id} id={"row-" + q.id}>
            <MemoQuestionRow
              q={q}
              answer={answers[q.id]}
              setAnswer={(next) => updateAnswer(q.id, next)}
              expanded={expandedId === q.id}
              onExpand={() => onExpand(q.id)}
              onCollapse={onCollapse}
              t={t}
              saveState={saveStates[q.id] || "idle"}
            />
          </div>
        ))}
      </div>

      {/* End-of-list summary */}
      <div className="ws-summary">
        <h3>{done === total ? "Workshop complete" : "Workshop in progress"}</h3>
        <p>{done === total
            ? "All 100 questions are answered. Review your findings and actions before submitting."
            : `${total - done} question${total - done === 1 ? "" : "s"} still to answer.`}</p>
        <div className="stats">
          <div className="stat"><span className="n">{done}</span><span className="l">Complete</span></div>
          <div className="stat"><span className="n">{Object.values(answers).filter(a => deriveStatus(a) === "partial").length}</span><span className="l">In progress</span></div>
          <div className="stat"><span className="n">{Object.values(answers).filter(a => a.q1 === "no" || a.q2 === "no").length}</span><span className="l">Findings raised</span></div>
        </div>
        <button className="btn btn-primary" disabled={done !== total} aria-disabled={done !== total}>
          Submit workshop
        </button>
      </div>

      {/* Floating jump-to-next */}
      {t.showJump && nextUnansweredId && (
        <button className="ws-fab" title="Jump to next unanswered" onClick={jumpToNext}>
          {I.chevron}
        </button>
      )}

      {/* Tweaks */}
      <window.TweaksPanel title="Tweaks">
        <window.TweakSection label="Density" />
        <window.TweakRadio
          label="Spacing" value={t.density}
          options={[
            { value: "compact", label: "Tight" },
            { value: "regular", label: "Regular" },
            { value: "comfy",   label: "Comfy" },
          ]}
          onChange={v => setTweak("density", v)}
        />

        <window.TweakSection label="Behaviour" />
        <window.TweakToggle
          label="Auto-collapse on save" value={t.autoCollapse}
          onChange={v => setTweak("autoCollapse", v)}
        />
        <window.TweakToggle
          label="Jump-to-next button" value={t.showJump}
          onChange={v => setTweak("showJump", v)}
        />

        <window.TweakSection label="Reveal animation" />
        <window.TweakRadio
          label="Style" value={t.anim}
          options={[
            { value: "slide", label: "Slide" },
            { value: "snap",  label: "Instant" },
          ]}
          onChange={v => setTweak("anim", v)}
        />

        <window.TweakSection label="Field details" />
        <window.TweakRadio
          label="Required" value={t.requiredStyle}
          options={[
            { value: "asterisk", label: "*" },
            { value: "label",    label: "Required" },
            { value: "none",     label: "None" },
          ]}
          onChange={v => setTweak("requiredStyle", v)}
        />
        <window.TweakRadio
          label="Action" value={t.actionStyle}
          options={[
            { value: "card", label: "Full card" },
            { value: "row",  label: "Single row" },
          ]}
          onChange={v => setTweak("actionStyle", v)}
        />
      </window.TweaksPanel>
    </>
  );
}

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