/* advalgebralab.jsx — AdvAlgebraTutor: the advanced (precalculus) algebra workbench.
   Six modules: polynomial roots & Vieta, rational asymptotes, logarithms, the ellipse,
   geometric series, and complex numbers on the Argand plane. Built on window.BenchKit
   (loaded first), so this file is just the FIGURES + their field computations. Each
   computeFields() mirrors server/benches/advalgebra.js simulate() EXACTLY, so client and
   server agree on every graded field. */
(function () {
  const { useState, useMemo, useEffect } = React;
  const K = window.BenchKit, C = K.C, SVG_BG = K.SVG_BG, fmt = K.fmt;
  const TaskStrip = K.TaskStrip, StatMeter = K.StatMeter;

  /* ════ numerics — byte-for-byte identical to server/benches/advalgebra.js ════ */
  const rnd1 = (x) => Math.round(x * 10) / 10;
  const rnd2 = (x) => Math.round(x * 100) / 100;
  const rnd3 = (x) => Math.round(x * 1000) / 1000;

  function fieldsPoly(s) {
    const a = s.a, r1 = s.r1, r2 = s.r2, r3 = s.r3, prod = r1 * r2 * r3;
    return {
      lead: rnd2(a), sumRoots: rnd2(r1 + r2 + r3), sumPairs: rnd2(r1 * r2 + r1 * r3 + r2 * r3),
      prodRoots: rnd2(prod), yIntercept: rnd2(-a * prod), distinctRoots: new Set([r1, r2, r3]).size,
    };
  }
  function fieldsRational(s) {
    const a = s.ra, p = s.rp, k = s.rk, crosses = Math.abs(k) > 1e-9;
    return { vAsymptote: rnd2(p), hAsymptote: rnd2(k), scale: rnd2(a), zeroX: crosses ? rnd2(p - a / k) : 9999 };
  }
  function fieldsLogs(s) {
    const b = Math.max(1.1, s.lb), x = Math.max(1e-6, s.lx);
    return { logValue: rnd3(Math.log(x) / Math.log(b)), base: rnd2(b), input: rnd2(x), decades: rnd3(Math.log10(x)) };
  }
  function fieldsConic(s) {
    const A = Math.max(0.1, s.eA), B = Math.max(0.1, s.eB);
    const major = Math.max(A, B), minor = Math.min(A, B), c = Math.sqrt(major * major - minor * minor);
    return { semiMajor: rnd2(major), semiMinor: rnd2(minor), focal: rnd2(c), eccentricity: rnd3(c / major), area: rnd2(Math.PI * A * B) };
  }
  function fieldsSeries(s) {
    const a = s.ga, r = s.gr, conv = Math.abs(r) < 1;
    return { ratio: rnd2(r), converges: conv, sumInf: conv ? rnd2(a / (1 - r)) : 0, term5: rnd2(a * Math.pow(r, 4)), firstTerm: rnd2(a) };
  }
  function fieldsComplex(s) {
    const x = s.cx, y = s.cy;
    return { reZ: rnd2(x), imZ: rnd2(y), modulus: rnd2(Math.sqrt(x * x + y * y)), argument: rnd1(Math.atan2(y, x) * 180 / Math.PI), modSq: rnd2(x * x + y * y) };
  }

  /* ════ plot helpers (visualization only) ════ */
  const PW = 660, PH = 300, PADL = 46, PADR = 16, PADT = 16, PADB = 32;
  function XF(xdom, ydom) {
    const x0 = PADL, x1 = PW - PADR, y0 = PH - PADB, y1 = PADT;
    return { sx: (x) => x0 + (x - xdom[0]) / (xdom[1] - xdom[0]) * (x1 - x0), sy: (y) => y0 + (y - ydom[0]) / ((ydom[1] - ydom[0]) || 1) * (y1 - y0), x0, x1, y0, y1, xdom, ydom };
  }
  function curveD(f, xf, n = 260) { let d = ""; for (let i = 0; i <= n; i++) { const x = xf.xdom[0] + (xf.xdom[1] - xf.xdom[0]) * i / n; d += (i ? "L" : "M") + xf.sx(x).toFixed(1) + " " + xf.sy(f(x)).toFixed(1); } return d; }
  // a curve that lifts the pen across an asymptote / off-screen excursion (for rational curves)
  function brokenD(f, xf, breakAt, n = 320) {
    let d = "", pen = false;
    for (let i = 0; i <= n; i++) {
      const x = xf.xdom[0] + (xf.xdom[1] - xf.xdom[0]) * i / n;
      if (breakAt != null && Math.abs(x - breakAt) < 0.06) { pen = false; continue; }
      const y = f(x);
      if (!isFinite(y) || y < xf.ydom[0] - 2 || y > xf.ydom[1] + 2) { pen = false; continue; }
      d += (pen ? "L" : "M") + xf.sx(x).toFixed(1) + " " + xf.sy(y).toFixed(1); pen = true;
    }
    return d;
  }
  function Axis({ xf, ticks, yticks, unit, yzero }) {
    const y0 = yzero != null ? xf.sy(yzero) : xf.y0;
    return (<g>
      <line x1={xf.x0} y1={y0} x2={xf.x1} y2={y0} stroke={C.faint} strokeWidth="1.2" />
      {ticks.map((t, i) => (<g key={i}><line x1={xf.sx(t)} y1={y0} x2={xf.sx(t)} y2={y0 + 5} stroke={C.faint} strokeWidth="1" /><text x={xf.sx(t)} y={y0 + 17} textAnchor="middle" fill={C.mute} fontSize="11" fontFamily="'IBM Plex Mono',monospace">{t}</text></g>))}
      {(yticks || []).map((t, i) => (<g key={`y${i}`}><line x1={xf.x0 - 4} y1={xf.sy(t)} x2={xf.x0} y2={xf.sy(t)} stroke={C.faint} strokeWidth="1" /><text x={xf.x0 - 7} y={xf.sy(t) + 3} textAnchor="end" fill={C.mute} fontSize="10" fontFamily="'IBM Plex Mono',monospace">{t}</text></g>))}
      {unit ? <text x={xf.x1} y={y0 - 5} textAnchor="end" fill={C.faint} fontSize="10" fontFamily="'IBM Plex Mono',monospace">{unit}</text> : null}
    </g>);
  }
  function Slider({ label, value, set, min, max, step, unit, color, fmtV }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 8 }}>
        <span style={{ fontSize: 12, color: C.mute, width: 150 }}><K.T>{label}</K.T></span>
        <input type="range" min={min} max={max} step={step} value={value} onChange={(e) => set(parseFloat(e.target.value))} style={{ flex: 1 }} />
        <span style={{ color: color || C.amber, width: 66, textAlign: "right", fontFamily: "'IBM Plex Mono',monospace" }}>{(fmtV ? fmtV(value) : value)}{unit ? ` ${unit}` : ""}</span>
      </div>
    );
  }
  function Frame({ children }) {
    return <svg viewBox={`0 0 ${PW} ${PH}`} style={{ width: "100%", background: SVG_BG, border: `1px solid ${C.line}`, borderRadius: 8 }}>{children}</svg>;
  }
  function Meters({ items }) {
    return <div style={{ display: "grid", gridTemplateColumns: `repeat(${items.length},1fr)`, gap: 8, marginTop: 12 }}>{items.map((m, i) => <StatMeter key={i} label={m.label} v={m.v} d={m.d} unit={m.unit} color={m.color} />)}</div>;
  }
  function Status({ ok, children }) {
    return <div style={{ marginTop: 10, padding: "8px 12px", borderRadius: 6, background: C.panel, border: `1px solid ${ok ? C.gold : C.line}`, color: ok ? C.gold : C.mute, fontSize: 12.5 }}>{children}</div>;
  }

  /* ════ Module 1: polynomial roots & Vieta ════ */
  function PolyFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ a: 1, r1: -2, r2: 0, r3: 2 });
    const fld = useMemo(() => fieldsPoly(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const rs = [s.r1, s.r2, s.r3], a = s.a || 1e-6;
    const f = (x) => a * (x - s.r1) * (x - s.r2) * (x - s.r3);
    const lo = Math.min(...rs) - 1.2, hi = Math.max(...rs) + 1.2;
    let m = 1; for (let i = 0; i <= 40; i++) { m = Math.max(m, Math.abs(f(lo + (hi - lo) * i / 40))); }
    m = Math.min(Math.max(m * 1.2, 3), 40);
    const xf = XF([-6, 6], [-m, m]);
    const t = tasks.find((x) => x.id === task) || tasks[0];
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        <Axis xf={xf} ticks={[-6, -3, 0, 3, 6]} unit="x" yzero={0} />
        <line x1={xf.sx(0)} y1={xf.y1} x2={xf.sx(0)} y2={xf.y0} stroke={C.faint} strokeWidth="0.8" />
        <path d={curveD(f, xf)} fill="none" stroke={C.teal} strokeWidth="2.4" />
        {rs.map((r, i) => <circle key={i} cx={xf.sx(r)} cy={xf.sy(0)} r="4.5" fill={C.crimson} />)}
        <circle cx={xf.sx(0)} cy={xf.sy(Math.max(-m, Math.min(m, f(0))))} r="4" fill={C.gold} />
        <text x={xf.x0 + 6} y={xf.y1 + 12} fill={C.gold} fontSize="12" fontFamily="'IBM Plex Mono',monospace">Σr = {fmt(fld.sumRoots, 2)} · ∏r = {fmt(fld.prodRoots, 2)}</text>
      </Frame>
      <Status ok={fld.distinctRoots === 3}>roots {fmt(s.r1, 1)}, {fmt(s.r2, 1)}, {fmt(s.r3, 1)} · {fld.distinctRoots} distinct · y-intercept = {fmt(fld.yIntercept, 2)} · <K.T>Vieta reads the sum and product straight off the roots.</K.T></Status>
      <Slider label="lead coeff a" value={s.a} set={set("a")} min={-3} max={3} step={0.5} color="#a98fd0" fmtV={(v) => fmt(v, 1)} />
      <Slider label="root r₁" value={s.r1} set={set("r1")} min={-5} max={5} step={0.5} color={C.crimson} fmtV={(v) => fmt(v, 1)} />
      <Slider label="root r₂" value={s.r2} set={set("r2")} min={-5} max={5} step={0.5} color={C.crimson} fmtV={(v) => fmt(v, 1)} />
      <Slider label="root r₃" value={s.r3} set={set("r3")} min={-5} max={5} step={0.5} color={C.crimson} fmtV={(v) => fmt(v, 1)} />
      <Meters items={[{ label: "sum Σr", v: fld.sumRoots, d: 2, color: C.gold }, { label: "pair sum", v: fld.sumPairs, d: 2, color: C.blue }, { label: "product ∏r", v: fld.prodRoots, d: 2, color: C.teal }, { label: "distinct roots", v: fld.distinctRoots, color: C.mute }]} />
    </>);
  }

  /* ════ Module 2: rational functions — asymptotes ════ */
  function RationalFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ ra: 1, rp: 0, rk: 0 });
    const fld = useMemo(() => fieldsRational(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const f = (x) => s.ra / (x - s.rp) + s.rk;
    const xf = XF([-8, 8], [-8, 8]);
    const t = tasks.find((x) => x.id === task) || tasks[0];
    const crosses = Math.abs(s.rk) > 1e-9;
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        <Axis xf={xf} ticks={[-8, -4, 0, 4, 8]} yticks={[-8, -4, 4, 8]} unit="x" yzero={0} />
        <line x1={xf.sx(0)} y1={xf.y1} x2={xf.sx(0)} y2={xf.y0} stroke={C.faint} strokeWidth="0.8" />
        <line x1={xf.sx(s.rp)} y1={xf.y1} x2={xf.sx(s.rp)} y2={xf.y0} stroke={C.crimson} strokeWidth="1.4" strokeDasharray="4 4" />
        <line x1={xf.x0} y1={xf.sy(s.rk)} x2={xf.x1} y2={xf.sy(s.rk)} stroke={C.crimson} strokeWidth="1.4" strokeDasharray="4 4" />
        <path d={brokenD(f, xf, s.rp)} fill="none" stroke={C.teal} strokeWidth="2.4" />
        {crosses ? <circle cx={xf.sx(fld.zeroX)} cy={xf.sy(0)} r="4.5" fill={C.gold} /> : null}
        <text x={xf.x0 + 6} y={xf.y1 + 12} fill={C.crimson} fontSize="12" fontFamily="'IBM Plex Mono',monospace">V: x = {fmt(fld.vAsymptote, 1)} · H: y = {fmt(fld.hAsymptote, 1)}</text>
      </Frame>
      <Status ok={crosses}>vertical asymptote x = {fmt(fld.vAsymptote, 1)} · horizontal y = {fmt(fld.hAsymptote, 1)} · {crosses ? <K.T>zero at x = </K.T> : <K.T>no zero (curve approaches y = 0).</K.T>}{crosses ? fmt(fld.zeroX, 2) : ""}</Status>
      <Slider label="scale a" value={s.ra} set={set("ra")} min={-6} max={6} step={0.5} color="#a98fd0" fmtV={(v) => fmt(v, 1)} />
      <Slider label="V-asymptote p" value={s.rp} set={set("rp")} min={-5} max={5} step={0.5} color={C.crimson} fmtV={(v) => fmt(v, 1)} />
      <Slider label="H-asymptote k" value={s.rk} set={set("rk")} min={-5} max={5} step={0.5} color={C.crimson} fmtV={(v) => fmt(v, 1)} />
      <Meters items={[{ label: "V-asymptote x", v: fld.vAsymptote, d: 1, color: C.crimson }, { label: "H-asymptote y", v: fld.hAsymptote, d: 1, color: C.crimson }, { label: "zero at x", v: crosses ? fld.zeroX : "—", d: 2, color: C.gold }, { label: "scale a", v: fld.scale, d: 1, color: C.blue }]} />
    </>);
  }

  /* ════ Module 3: logarithms ════ */
  function LogsFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ lb: 2, lx: 8 });
    const fld = useMemo(() => fieldsLogs(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const b = Math.max(1.1, s.lb);
    const f = (x) => Math.log(Math.max(1e-6, x)) / Math.log(b);
    const xf = XF([0, 16], [-4, 5]);
    const xPt = Math.min(16, Math.max(0.05, s.lx)), yPt = Math.max(-4, Math.min(5, f(xPt)));
    const t = tasks.find((x) => x.id === task) || tasks[0];
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        <Axis xf={xf} ticks={[0, 4, 8, 12, 16]} yticks={[-4, -2, 2, 4]} unit="x" yzero={0} />
        <line x1={xf.sx(1)} y1={xf.y1} x2={xf.sx(1)} y2={xf.y0} stroke={C.faint} strokeWidth="0.8" strokeDasharray="3 3" />
        <path d={brokenD(f, xf, null)} fill="none" stroke={C.teal} strokeWidth="2.4" />
        <line x1={xf.sx(xPt)} y1={xf.sy(0)} x2={xf.sx(xPt)} y2={xf.sy(yPt)} stroke={C.crimson} strokeWidth="1.3" strokeDasharray="4 4" />
        <line x1={xf.x0} y1={xf.sy(yPt)} x2={xf.sx(xPt)} y2={xf.sy(yPt)} stroke={C.crimson} strokeWidth="1.3" strokeDasharray="4 4" />
        <circle cx={xf.sx(xPt)} cy={xf.sy(yPt)} r="4.5" fill={C.gold} />
        <text x={xf.x0 + 6} y={xf.y1 + 12} fill={C.gold} fontSize="13" fontFamily="'IBM Plex Mono',monospace">log_{fmt(fld.base, 1)}({fmt(fld.input, 1)}) = {fmt(fld.logValue, 3)}</text>
      </Frame>
      <Status ok={Math.abs(fld.logValue - Math.round(fld.logValue)) < 0.05}>base b = {fmt(fld.base, 1)} · input x = {fmt(fld.input, 1)} · {fmt(fld.base, 1)}^{fmt(fld.logValue, 2)} = x · <K.T>a logarithm is the exponent that rebuilds x.</K.T></Status>
      <Slider label="base b" value={s.lb} set={set("lb")} min={2} max={10} step={0.5} color="#a98fd0" fmtV={(v) => fmt(v, 1)} />
      <Slider label="input x" value={s.lx} set={set("lx")} min={0.1} max={100} step={0.1} color={C.gold} fmtV={(v) => fmt(v, 1)} />
      <Meters items={[{ label: "log_b(x)", v: fld.logValue, d: 3, color: C.gold }, { label: "base b", v: fld.base, d: 1, color: C.blue }, { label: "input x", v: fld.input, d: 1, color: C.teal }, { label: "decades", v: fld.decades, d: 2, color: C.mute }]} />
    </>);
  }

  /* ════ Module 4: the ellipse & eccentricity ════ */
  function ConicFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ eA: 5, eB: 3 });
    const fld = useMemo(() => fieldsConic(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const A = Math.max(0.1, s.eA), B = Math.max(0.1, s.eB);
    const half = Math.max(A, B) * 1.18;
    const cx = PADL + (PW - PADL - PADR) / 2, cy = PADT + (PH - PADT - PADB) / 2;
    const sc = Math.min((PW - PADL - PADR) / (2 * half), (PH - PADT - PADB) / (2 * half));
    const horiz = A >= B, c = fld.focal;
    const t = tasks.find((x) => x.id === task) || tasks[0];
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        <line x1={cx - half * sc} y1={cy} x2={cx + half * sc} y2={cy} stroke={C.faint} strokeWidth="1" />
        <line x1={cx} y1={cy - half * sc} x2={cx} y2={cy + half * sc} stroke={C.faint} strokeWidth="1" />
        <ellipse cx={cx} cy={cy} rx={A * sc} ry={B * sc} fill={C.gold} fillOpacity="0.14" stroke={C.teal} strokeWidth="2.4" />
        {[-1, 1].map((sg, i) => <circle key={i} cx={cx + (horiz ? sg * c * sc : 0)} cy={cy - (horiz ? 0 : sg * c * sc)} r="4" fill={C.crimson} />)}
        <text x={PADL + 6} y={PADT + 12} fill={C.gold} fontSize="13" fontFamily="'IBM Plex Mono',monospace">e = {fmt(fld.eccentricity, 3)} · area = {fmt(fld.area, 1)}</text>
      </Frame>
      <Status ok={fld.eccentricity < 0.05}>semi-axes A = {fmt(A, 1)}, B = {fmt(B, 1)} · c = {fmt(fld.focal, 2)} · {fld.eccentricity < 0.05 ? <K.T>a circle (e ≈ 0).</K.T> : <K.T>an ellipse; the foci sit at ±c on the major axis.</K.T>}</Status>
      <Slider label="semi-axis A" value={s.eA} set={set("eA")} min={1} max={10} step={0.5} color={C.blue} fmtV={(v) => fmt(v, 1)} />
      <Slider label="semi-axis B" value={s.eB} set={set("eB")} min={1} max={10} step={0.5} color="#a98fd0" fmtV={(v) => fmt(v, 1)} />
      <Meters items={[{ label: "eccentricity", v: fld.eccentricity, d: 3, color: C.gold }, { label: "focal c", v: fld.focal, d: 2, color: C.crimson }, { label: "area πAB", v: fld.area, d: 1, color: C.teal }, { label: "semi-major", v: fld.semiMajor, d: 1, color: C.blue }]} />
    </>);
  }

  /* ════ Module 5: geometric series ════ */
  function SeriesFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ ga: 1, gr: 0.5 });
    const fld = useMemo(() => fieldsSeries(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const N = 11, terms = Array.from({ length: N + 1 }, (_, k) => s.ga * Math.pow(s.gr, k));
    let mAbs = 0.5; terms.forEach((v) => { mAbs = Math.max(mAbs, Math.abs(v)); });
    const xf = XF([-0.5, N + 0.5], [-mAbs * 1.1, mAbs * 1.1]);
    const bw = Math.max(2, (xf.sx(1) - xf.sx(0)) * 0.7);
    const t = tasks.find((x) => x.id === task) || tasks[0];
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        <Axis xf={xf} ticks={[0, 3, 6, 9]} yticks={[]} unit="k" yzero={0} />
        {terms.map((v, k) => { const y = xf.sy(v), y0 = xf.sy(0); return <rect key={k} x={xf.sx(k) - bw / 2} y={Math.min(y, y0)} width={bw} height={Math.abs(y0 - y)} rx="1.5" fill={v >= 0 ? C.teal : C.crimson} fillOpacity="0.7" />; })}
        {fld.converges ? <line x1={xf.x0} y1={xf.sy(fld.sumInf)} x2={xf.x1} y2={xf.sy(fld.sumInf)} stroke={C.gold} strokeWidth="1.3" strokeDasharray="5 4" /> : null}
        <text x={xf.x0 + 6} y={xf.y1 + 12} fill={C.gold} fontSize="13" fontFamily="'IBM Plex Mono',monospace">{fld.converges ? `Σ = ${fmt(fld.sumInf, 2)}` : "diverges"}</text>
      </Frame>
      <Status ok={fld.converges}>a = {fmt(fld.firstTerm, 1)} · r = {fmt(fld.ratio, 2)} · {fld.converges ? <K.T>converges — sum to infinity a/(1−r).</K.T> : <K.T>|r| ≥ 1, so the terms grow and the series diverges.</K.T>}</Status>
      <Slider label="first term a" value={s.ga} set={set("ga")} min={-10} max={10} step={0.5} color={C.blue} fmtV={(v) => fmt(v, 1)} />
      <Slider label="common ratio r" value={s.gr} set={set("gr")} min={-1.5} max={1.5} step={0.05} color={C.gold} fmtV={(v) => fmt(v, 2)} />
      <Meters items={[{ label: "ratio r", v: fld.ratio, d: 2, color: C.gold }, { label: "converges", v: fld.converges ? "yes" : "no", color: fld.converges ? C.teal : C.crimson }, { label: "sum ∞", v: fld.converges ? fld.sumInf : "—", d: 2, color: C.blue }, { label: "5th term", v: fld.term5, d: 2, color: C.mute }]} />
    </>);
  }

  /* ════ Module 6: complex numbers — the Argand plane ════ */
  function ComplexFig({ task, setTask, tasks, report, done }) {
    const [s, setS] = useState({ cx: 3, cy: 4 });
    const fld = useMemo(() => fieldsComplex(s), [s]);
    useEffect(() => { report(fld); }, [s]); // eslint-disable-line
    const set = (k) => (v) => setS((o) => ({ ...o, [k]: v }));
    const half = 9, cx = PADL + (PW - PADL - PADR) / 2, cy = PADT + (PH - PADT - PADB) / 2;
    const sc = Math.min((PW - PADL - PADR) / (2 * half), (PH - PADT - PADB) / (2 * half));
    const px = cx + s.cx * sc, py = cy - s.cy * sc, r = fld.modulus;
    const arcR = Math.min(34, r * sc * 0.6), ang = Math.atan2(s.cy, s.cx);
    const t = tasks.find((x) => x.id === task) || tasks[0];
    return (<>
      <TaskStrip tasks={tasks} cur={task} setCur={setTask} done={done} goal={t && t.goal} />
      <Frame>
        {[-8, -4, 4, 8].map((g, i) => <g key={i}><line x1={cx + g * sc} y1={cy - half * sc} x2={cx + g * sc} y2={cy + half * sc} stroke={C.line} strokeWidth="0.5" /><line x1={cx - half * sc} y1={cy - g * sc} x2={cx + half * sc} y2={cy - g * sc} stroke={C.line} strokeWidth="0.5" /></g>)}
        <line x1={cx - half * sc} y1={cy} x2={cx + half * sc} y2={cy} stroke={C.faint} strokeWidth="1.2" />
        <line x1={cx} y1={cy - half * sc} x2={cx} y2={cy + half * sc} stroke={C.faint} strokeWidth="1.2" />
        {r > 0.2 ? <path d={`M ${cx + arcR} ${cy} A ${arcR} ${arcR} 0 ${Math.abs(ang) > Math.PI ? 1 : 0} ${ang < 0 ? 1 : 0} ${cx + arcR * Math.cos(ang)} ${cy - arcR * Math.sin(ang)}`} fill="none" stroke={C.gold} strokeWidth="1.4" /> : null}
        <line x1={cx} y1={cy} x2={px} y2={py} stroke={C.teal} strokeWidth="2.4" />
        <circle cx={px} cy={py} r="5" fill={C.crimson} />
        <text x={px + 8} y={py - 6} fill={C.crimson} fontSize="12" fontFamily="'IBM Plex Mono',monospace">{fmt(fld.reZ, 1)}{fld.imZ >= 0 ? "+" : "−"}{fmt(Math.abs(fld.imZ), 1)}i</text>
        <text x={PADL + 6} y={PADT + 12} fill={C.gold} fontSize="13" fontFamily="'IBM Plex Mono',monospace">|z| = {fmt(fld.modulus, 2)} · arg = {fmt(fld.argument, 1)}°</text>
      </Frame>
      <Status ok={Math.abs(fld.modulus - 5) < 0.1}>z = {fmt(fld.reZ, 1)} {fld.imZ >= 0 ? "+" : "−"} {fmt(Math.abs(fld.imZ), 1)}i · |z| = {fmt(fld.modulus, 2)} · arg = {fmt(fld.argument, 1)}° · <K.T>the modulus is the distance from the origin.</K.T></Status>
      <Slider label="real part x" value={s.cx} set={set("cx")} min={-8} max={8} step={0.5} color={C.blue} fmtV={(v) => fmt(v, 1)} />
      <Slider label="imaginary part y" value={s.cy} set={set("cy")} min={-8} max={8} step={0.5} color="#a98fd0" fmtV={(v) => fmt(v, 1)} />
      <Meters items={[{ label: "modulus |z|", v: fld.modulus, d: 2, color: C.gold }, { label: "argument °", v: fld.argument, d: 1, color: C.teal }, { label: "real x", v: fld.reZ, d: 1, color: C.blue }, { label: "|z|²", v: fld.modSq, d: 1, color: C.mute }]} />
    </>);
  }

  const FIGS = { poly: PolyFig, rational: RationalFig, logs: LogsFig, conic: ConicFig, series: SeriesFig, complex: ComplexFig };
  const MODULE_LABEL = {
    poly: "Polynomial roots — the factor theorem & Vieta", rational: "Rational functions — zeros & asymptotes",
    logs: "Logarithms — the inverse of exponentiation", conic: "Conic sections — the ellipse & eccentricity",
    series: "Geometric series — convergence & the sum to infinity", complex: "Complex numbers — modulus & argument",
  };

  function AdvAlgebraFig(props) {
    const module = FIGS[props.spec && props.spec.module] ? props.spec.module : "poly";
    const Fig = FIGS[module];
    return <Fig {...props} />;
  }

  window.AdvAlgebraTutor = K.makeTutor(AdvAlgebraFig, {
    benchId: "advalgebra",
    moduleLabel: (spec) => MODULE_LABEL[spec && spec.module] || "Advanced algebra workbench",
  });
})();
