/* projectkit.jsx — generic build engine shared by the standalone projects
   (Spinner, Sequencer, Mood Lamp, Buzzer). Reads one descriptor from
   window.PROJECT_BUILD = { id, name, em, kicker, hero:{adult,kids},
   stages, learned, finale:{adult,kids}, bench:{adult,kids} } and renders the
   same staged part-picker UI as the Beacon — but unlocked sequentially
   (finish a stage to earn the next part) rather than by chapter.
   Progress persists per-project in localStorage 'hte-build-<id>'.
   All names Pk-/pk prefixed (shared global babel scope). */

const { useState: pkUseState, useEffect: pkUseEffect } = React;

const PK_TWEAK_DEFAULTS = { theme: "paper", audience: "adult", difficulty: "engineer" };

const PK_TIERS = {
  apprentice: { label: "Apprentice", hints: true,  explainer: true,  formulaUpfront: true,  blurb: "Full guidance — hints, the explainer, and the formula up front." },
  engineer:   { label: "Engineer",   hints: false, explainer: true,  formulaUpfront: true,  blurb: "Hints off. Read the spec and choose your parts." },
  pro:        { label: "Pro",        hints: false, explainer: false, formulaUpfront: false, blurb: "No hints, no explainer — the formula appears only once you meet spec." },
};
const PK_TIER_ORDER = ["apprentice", "engineer", "pro"];

/* ─── persistence (per build) ───────────────────────────────────────────── */
function usePkState(buildId) {
  const key = "hte-build-" + buildId;
  const [st, setSt] = pkUseState(() => {
    try { return JSON.parse(localStorage.getItem(key) || "{}"); } catch { return {}; }
  });
  const save = (next) => {
    setSt(next);
    try { localStorage.setItem(key, JSON.stringify(next)); } catch {}
  };
  const setPick = (sid, pid, val) => {
    const picks = { ...(st.picks || {}) };
    picks[sid] = { ...(picks[sid] || {}), [pid]: val };
    const done = { ...(st.done || {}) };
    delete done[sid];
    save({ ...st, picks, done });
  };
  const lockIn = (sid) => save({ ...st, done: { ...(st.done || {}), [sid]: true } });
  const reset = () => { if (confirm("Scrap this build and start over?")) save({}); };
  return { st, setPick, lockIn, reset };
}

/* ─── part glyphs for the ribbon ────────────────────────────────────────── */
function PkGlyph({ part, on }) {
  const c = on ? "var(--current)" : "var(--ink-faint)";
  const common = { fill: "none", stroke: c, strokeWidth: 2, strokeLinecap: "round" };
  return (
    <svg viewBox="0 0 40 28" width="40" height="28" aria-hidden="true">
      {part === "led" && (<g {...common}><circle cx="20" cy="14" r="7" /><path d="M16 18 L24 10 M22 10 L24 10 L24 12" /></g>)}
      {part === "led3" && (<g {...common}>{[10, 20, 30].map(x => <circle key={x} cx={x} cy="14" r="4.5" />)}</g>)}
      {part === "batt" && (<g {...common}><line x1="14" y1="7" x2="14" y2="21" /><line x1="22" y1="10" x2="22" y2="18" strokeWidth="3" /><line x1="6" y1="14" x2="14" y2="14" /><line x1="22" y1="14" x2="34" y2="14" /></g>)}
      {part === "cap" && (<g {...common}><line x1="6" y1="14" x2="17" y2="14" /><line x1="17" y1="6" x2="17" y2="22" /><line x1="23" y1="6" x2="23" y2="22" /><line x1="23" y1="14" x2="34" y2="14" /></g>)}
      {part === "switch" && (<g {...common}><circle cx="10" cy="14" r="2" /><circle cx="30" cy="14" r="2" /><line x1="12" y1="14" x2="28" y2="7" /></g>)}
      {part === "transistor" && (<g {...common}><circle cx="20" cy="14" r="9" /><line x1="8" y1="14" x2="15" y2="14" /><line x1="15" y1="9" x2="15" y2="19" strokeWidth="3" /><line x1="15" y1="11" x2="27" y2="6" /><line x1="15" y1="17" x2="27" y2="22" /></g>)}
      {part === "diode" && (<g {...common}><line x1="6" y1="14" x2="16" y2="14" /><path d="M16 8 L26 14 L16 20 Z" /><line x1="26" y1="8" x2="26" y2="20" /><line x1="26" y1="14" x2="34" y2="14" /></g>)}
      {part === "ic555" && (<g {...common}><rect x="12" y="6" width="16" height="16" rx="2" /><circle cx="16" cy="9" r="1" fill={c} stroke="none" /></g>)}
      {part === "ic4017" && (<g {...common}><rect x="9" y="5" width="22" height="18" rx="2" /><circle cx="13" cy="8.5" r="1" fill={c} stroke="none" /></g>)}
      {part === "motor" && (<g {...common}><circle cx="20" cy="14" r="9" /><path d="M20 9 v10 M15 14 h10" /></g>)}
      {part === "pot" && (<g {...common}><rect x="9" y="10" width="22" height="8" rx="1.5" /><path d="M20 4 v5 M16 7 l4 2 4 -2" /></g>)}
      {part === "piezo" && (<g {...common}><path d="M9 10 h6 l7 -5 v18 l-7 -5 H9 z" /><path d="M27 9 a6 6 0 0 1 0 10" /></g>)}
      {part === "coil" && (<g {...common}><line x1="5" y1="14" x2="10" y2="14" /><path d="M10 14 q3 -7 6 0 q3 -7 6 0 q3 -7 6 0" /><line x1="30" y1="14" x2="35" y2="14" /></g>)}
    </svg>
  );
}

/* ─── running "what we know" ledger ─────────────────────────────────────── */
function PkLedger({ build, doneMap }) {
  const done = build.stages.filter(s => doneMap[s.id]);
  return (
    <section className="pj-ledger" data-screen-label="What we know so far">
      <div className="pj-ledger-head">
        <span className="eyebrow" style={{ color: "var(--current)" }}>Field notes · what you've proven</span>
        <span className="pj-ledger-count mono">{done.length}/{build.stages.length}</span>
      </div>
      {done.length === 0 ? (
        <p className="pj-ledger-empty">
          Lock in a stage and the principle you used gets recorded here — a running
          engineering cheat-sheet that grows with your build.
        </p>
      ) : (
        <ol className="pj-ledger-list">
          {done.map(s => (
            <li key={s.id} className="pj-ledger-item">
              <span className="pj-ledger-stage mono">{String(s.n).padStart(2, "0")} · {s.title}</span>
              <ul className="pj-ledger-facts">
                {(build.learned[s.id] || []).map((f, i) => <li key={i}><Eq>{f}</Eq></li>)}
              </ul>
            </li>
          ))}
        </ol>
      )}
    </section>
  );
}

function PkRibbon({ build, doneMap }) {
  return (
    <div className="pj-ribbon" aria-label="The build so far">
      {build.stages.map((s, i) => {
        const on = !!doneMap[s.id];
        return (
          <React.Fragment key={s.id}>
            {i > 0 && <span className={`pj-rib-link ${on ? "on" : ""}`} aria-hidden="true"></span>}
            <div className={`pj-rib-part ${on ? "on" : ""}`} title={s.title}>
              <PkGlyph part={s.part} on={on} />
              <span className="pj-rib-num">{s.n}</span>
            </div>
          </React.Fragment>
        );
      })}
    </div>
  );
}

/* ─── one part picker ───────────────────────────────────────────────────── */
function PkPicker({ pick, value, onChange, showHint = true }) {
  return (
    <div className="pj-pick">
      <div className="pj-pick-head">
        <span className="pj-pick-label">{pick.label}</span>
        {showHint && pick.hint && <span className="pj-pick-hint">{pick.hint}</span>}
      </div>
      <div className="pj-pick-opts">
        {pick.options.map(o => (
          <button key={String(o.value)}
                  className={value === o.value ? "active" : ""}
                  onClick={() => onChange(o.value)}>
            {o.label}
          </button>
        ))}
      </div>
    </div>
  );
}

/* ─── result panel ──────────────────────────────────────────────────────── */
function PkResult({ result, kids, reveal = true }) {
  if (!result) return <div className="pj-result pj-result-empty">Pick every part to test your design.</div>;
  return (
    <div className={`pj-result ${result.ok ? "ok" : "no"}`}>
      <div className="pj-result-top">
        <span className="pj-result-flag">{result.ok ? "MEETS SPEC ✓" : "OUT OF SPEC"}</span>
        <span className="pj-result-headline"><Eq>{result.headline}</Eq></span>
      </div>
      {reveal ? (
        <>
          <div className="pj-result-formula mono"><Eq>{result.formula}</Eq></div>
          <div className="pj-result-lines">
            {result.lines.map((l, i) => (
              <div key={i} className={`pj-line ${l.ok === true ? "good" : l.ok === false ? "bad" : ""}`}>
                <span className="pj-line-k"><Eq>{l.k}</Eq></span>
                <span className="pj-line-v mono"><Eq>{l.v}</Eq>{l.ok === true ? " ✓" : l.ok === false ? " ✕" : ""}</span>
              </div>
            ))}
          </div>
        </>
      ) : (
        <div className="pj-result-veiled mono">Work the number yourself — the formula reveals once you meet the spec.</div>
      )}
      <div className="pj-result-note">{kids ? result.note.kids : result.note.adult}</div>
    </div>
  );
}

/* ─── one stage ─────────────────────────────────────────────────────────── */
function PkStage({ build, stage, picks, unlocked, done, kids, tier, onPick, onLock }) {
  const [peek, setPeek] = pkUseState(false);
  const open = unlocked || peek;
  const vals = picks || {};
  const allPicked = stage.picks.every(p => vals[p.id] !== undefined);
  const result = allPicked ? stage.check(vals) : null;
  const total = build.stages.length;

  return (
    <section className={`pj-stage ${done ? "done" : ""} ${stage.final ? "final" : ""} ${unlocked ? "" : "locked"}`}
             id={`stage-${stage.id}`} data-screen-label={`Stage ${stage.n} ${stage.title}`}>
      <div className="pj-stage-rail">
        <div className="pj-stage-num">{done ? "✓" : String(stage.n).padStart(2, "0")}</div>
        {stage.n < total && <div className="pj-stage-wire"></div>}
      </div>

      <div className="pj-stage-body">
        <div className="pj-stage-head">
          <div>
            <div className="pj-stage-kicker">
              <span>Stage {stage.n} / {total}</span>
              <span className="pj-dot">·</span>
              <span className="pj-stage-ch">{stage.teaches}</span>
            </div>
            <h2 className="pj-stage-title serif">{stage.title}</h2>
            <p className="pj-stage-tag">{stage.tagline}</p>
          </div>
          <div className="pj-stage-part"><PkGlyph part={stage.part} on={done} /></div>
        </div>

        {!unlocked && !peek && (
          <div className="pj-lock">
            <span className="pj-lock-icon" aria-hidden="true">⏏</span>
            <div>
              <strong>Locked.</strong> Lock in the stage before this one to earn the parts for <em>{stage.title}</em>.
              <button className="pj-peek" onClick={() => setPeek(true)}>Peek anyway</button>
            </div>
          </div>
        )}

        {open && (
          <>
            {!unlocked && peek && (
              <div className="pj-peek-note">Previewing ahead — lock in the earlier stages to make this count toward the build.</div>
            )}
            <p className="pj-stage-intro">{kids ? stage.intro.kids : stage.intro.adult}</p>
            <div className="pj-schem-wrap"><PkSchematic id={stage.schem} picks={vals} /></div>
            <div className="pj-spec"><span className="pj-spec-tag">TARGET SPEC</span><Eq>{stage.spec}</Eq></div>
            {tier.explainer && stage.explain && (
              <div className="pj-explain">
                <span className="pj-explain-tag">how the math works</span>
                <Eq>{stage.explain}</Eq>
              </div>
            )}

            {stage.givens && (
              <div className="pj-givens">
                <span className="pj-givens-tag">what you're given</span>
                <div className="pj-givens-chips">
                  {stage.givens.map((g, i) => <span key={i} className="pj-given"><Eq>{g}</Eq></span>)}
                </div>
              </div>
            )}
            {stage.steps && (
              <ol className="pj-substeps">
                {stage.steps.map((stp, i) => (
                  <li key={i}><Eq>{kids && stp.kids ? stp.kids : (stp.adult || stp)}</Eq></li>
                ))}
              </ol>
            )}

            <div className="pj-picks">
              {stage.picks.map(p => (
                <PkPicker key={p.id} pick={p} value={vals[p.id]} showHint={tier.hints}
                          onChange={(val) => onPick(stage.id, p.id, val)} />
              ))}
            </div>

            <PkResult result={result} kids={kids} reveal={tier.formulaUpfront || (result && result.ok)} />

            <div className="pj-stage-actions">
              {done ? (
                <span className="pj-locked-in">✓ Locked into the build — change a part to revise.</span>
              ) : (
                <button className="pj-lockbtn" disabled={!result || !result.ok} onClick={() => onLock(stage.id)}>
                  {result && result.ok ? "Lock it into the build →" : "Meet the spec to lock it in"}
                </button>
              )}
            </div>
          </>
        )}
      </div>
    </section>
  );
}

/* ─── completion ────────────────────────────────────────────────────────── */
function PkComplete({ build, st, kids }) {
  const partsOf = (s) => {
    const v = (st.picks || {})[s.id] || {};
    return s.picks.map(p => {
      const o = p.options.find(o => o.value === v[p.id]);
      return o ? o.label : "—";
    }).join(" + ");
  };
  const resultOf = (s) => {
    const v = (st.picks || {})[s.id] || {};
    return s.picks.every(p => v[p.id] !== undefined) ? s.check(v) : null;
  };
  const today = new Date().toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" });
  const total = build.stages.length;

  return (
    <section className="pj-complete" data-screen-label={build.name + " complete"}>
      <div className="pj-screenonly">
        <div className="pj-beacon" aria-hidden="true">
          <svg viewBox="0 0 80 80" width="92" height="92">
            <circle cx="40" cy="40" r="14" fill="var(--current)">
              <animate attributeName="opacity" values="1;0.25;1" dur="0.9s" repeatCount="indefinite" />
            </circle>
            <circle cx="40" cy="40" r="22" fill="none" stroke="var(--current)" strokeWidth="2">
              <animate attributeName="r" values="16;34" dur="0.9s" repeatCount="indefinite" />
              <animate attributeName="opacity" values="0.7;0" dur="0.9s" repeatCount="indefinite" />
            </circle>
          </svg>
        </div>
        <div className="eyebrow" style={{ color: "var(--current)" }}>Build complete</div>
        <h2 className="serif">{build.name} is <em>alive</em>.</h2>
        <p className="lede">{kids ? build.finale.kids : build.finale.adult}</p>
      </div>

      <div className="pj-specsheet">
        <div className="pj-ss-head">
          <div>
            <div className="pj-ss-title">{build.name.toUpperCase()} — BUILD SPECIFICATION</div>
            <div className="pj-ss-sub mono">Fathohm · designed &amp; verified by the builder</div>
          </div>
          <div className="pj-ss-stamp mono">
            <span>{total} / {total} STAGES ✓</span>
            <span>{today}</span>
          </div>
        </div>
        <div className="pj-ss-table">
          <div className="pj-ss-row pj-ss-colhead mono">
            <span>#</span><span>Stage</span><span>Components</span><span>Result</span>
          </div>
          {build.stages.map(s => {
            const r = resultOf(s);
            return (
              <div key={s.id} className="pj-ss-row">
                <span className="pj-ss-n mono">{String(s.n).padStart(2, "0")}</span>
                <span className="pj-ss-t">{s.title}</span>
                <span className="pj-ss-c mono">{partsOf(s)}</span>
                <span className="pj-ss-r mono"><Eq>{r ? r.headline : "—"}</Eq>{r && r.ok ? " ✓" : ""}</span>
              </div>
            );
          })}
        </div>
        <div className="pj-ss-foot mono">{build.specFoot}</div>
      </div>

      <div className="pj-actions">
        <button className="pj-print" onClick={() => window.print()}>Print / save spec sheet</button>
        <span className="pj-actions-note">Opens your print dialog — choose “Save as PDF” to keep or share it.</span>
      </div>

      <div className="pj-bench">
        <div className="eyebrow" style={{ color: "var(--current)" }}>Take it to the bench</div>
        <h3 className="serif">Now build the one you designed.</h3>
        <p>{kids ? build.bench.kids : build.bench.adult}</p>
        <ol className="pj-bench-steps">
          {build.stages.map(s => (
            <li key={s.id}>
              <span className="pj-bench-t">{s.title}</span>
              <span className="pj-bench-p mono">{partsOf(s)}</span>
            </li>
          ))}
        </ol>
        <a href="sandbox.html" className="cta cta-primary">Wire it for real in the Workbench →</a>
      </div>
    </section>
  );
}

/* ─── App ───────────────────────────────────────────────────────────────── */
function ProjectKitApp() {
  const build = window.PROJECT_BUILD;
  const [t, setTweak] = useTweaks(PK_TWEAK_DEFAULTS);
  useCrossChapterPersistence(t, setTweak, ["audience", "theme", "difficulty"]);
  pkUseEffect(() => { document.body.setAttribute("data-theme", t.theme); }, [t.theme]);
  const kids = t.audience === "kids";
  const tier = PK_TIERS[t.difficulty] || PK_TIERS.engineer;

  const { st, setPick, lockIn, reset } = usePkState(build.id);
  const doneMap = st.done || {};
  const doneCount = build.stages.filter(s => doneMap[s.id]).length;
  const total = build.stages.length;
  const allDone = doneCount === total;
  const pct = Math.round((doneCount / total) * 100);

  // sequential unlock: a stage is open once every earlier stage is locked in
  const isUnlocked = (idx) => build.stages.slice(0, idx).every(s => doneMap[s.id]);

  return (
    <>
      <TopBar chapterLabel={build.name} currentN={"project-" + build.id}
              audience={t.audience} setAudience={(v) => setTweak("audience", v)} />

      <main className="pj-page">
        <header className="pj-hero">
          <div className="pj-hero-left">
            <div className="eyebrow" style={{ color: "var(--current)" }}>{build.kicker}</div>
            <h1 className="serif">{build.em ? <>The <em>{build.em}</em>.</> : build.name + "."}</h1>
            <p className="lede">{kids ? build.hero.kids : build.hero.adult}</p>
            <div className="pj-hero-meta">
              <span className="mono">{doneCount}/{total} stages soldered</span>
              <button className="reset-progress" onClick={reset}>Reset build</button>
              <a href="projects.html" style={{ color: "var(--ink-faint)", fontSize: 12 }}>← Project shelf</a>
            </div>
            <div className="pj-bar"><div style={{ width: pct + "%" }}></div></div>
            <div className="pj-difficulty">
              <span className="pj-diff-label">Difficulty</span>
              <div className="pj-diff-seg">
                {PK_TIER_ORDER.map(k => (
                  <button key={k} className={t.difficulty === k ? "active" : ""}
                          onClick={() => setTweak("difficulty", k)}>{PK_TIERS[k].label}</button>
                ))}
              </div>
              <span className="pj-diff-blurb">{tier.blurb}</span>
            </div>
          </div>
        </header>

        <PkRibbon build={build} doneMap={doneMap} />
        <PkLedger build={build} doneMap={doneMap} />

        <div className="pj-stages">
          {build.stages.map((s, i) => (
            <PkStage key={s.id} build={build} stage={s}
                     picks={(st.picks || {})[s.id]}
                     unlocked={isUnlocked(i)}
                     done={!!doneMap[s.id]}
                     kids={kids} tier={tier}
                     onPick={setPick} onLock={lockIn} />
          ))}
        </div>

        {allDone && <PkComplete build={build} st={st} kids={kids} />}

        <footer className="home-footer" style={{ marginTop: 64 }}>
          <span>Fath<span style={{ color: "var(--current)" }}>ohm</span> · {build.name}</span>
          <span style={{ display: "flex", gap: 22 }}>
            <a href="projects.html">Project shelf</a>
            <a href="map.html">Course map</a>
            <a href="about.html">About</a>
          </span>
        </footer>
      </main>

      <TweaksPanel title="Tweaks">
        <CommonTweaks t={t} setTweak={setTweak} />
        <TweakSection label="Project" />
        <TweakRadio label="Difficulty"
          value={t.difficulty}
          options={PK_TIER_ORDER.map(k => ({ value: k, label: PK_TIERS[k].label }))}
          onChange={(v) => setTweak("difficulty", v)} />
      </TweaksPanel>
      <GlossaryFab />
    </>
  );
}

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