/* project-data.jsx — "The Beacon" cumulative build.
   One device that grows, chapter by chapter. Each stage gives a target spec
   and a parts bin; the check() runs the real engineering formula on the
   picked values and reports pass/fail with the working shown.

   All names PJ_/pj prefixed (shared global babel scope). */

const PJ_VF = 2.0;     // red LED forward voltage (V)
const PJ_VB = 9.0;     // 9 V supply

const pjMA = (a) => `${a.toFixed(a < 10 ? 1 : 0)} mA`;
const pjOhm = (r) => r >= 1000 ? `${(r / 1000).toFixed(r % 1000 ? 1 : 0)} kΩ` : `${r} Ω`;
const pjFar = (c) => c >= 1e-6 ? `${(c * 1e6).toFixed(0)} µF` : `${(c * 1e9).toFixed(0)} nF`;

/* Each stage:
   { id, n, ch, chTitle, part, title, tagline, intro:{adult,kids},
     spec, picks:[{id,label,hint,unit,options:[{label,value}]}],
     check(v) -> { ok, headline, formula, lines:[{k,v,ok}], note:{adult,kids} } } */
const PJ_STAGES = [
  {
    id: "light", n: 1, ch: "01", chTitle: "The Flow", part: "led",
    title: "First Light",
    tagline: "One LED, lit without letting the smoke out.",
    intro: {
      adult: "Every beacon starts with a single LED. Connect it straight across 9 V and it pops. Pick a series resistor that pins the current near 15 mA — comfortably bright, safely cool.",
      kids: "A beacon starts with one light. Hook an LED straight to the battery and POP — too much flow. Add a resistor to pinch the flow down to a safe, bright level.",
    },
    spec: "Red LED (uses up — “drops” — a fixed 2 V) running at 10–20 mA from a 9 V supply.",
    givens: ["Supply = 9 V", "Each red LED drops 2 V", "Target current = 10–20 mA", "I = (9 V − 2 V) ÷ R"],
    explain: "An LED always 'drops' (uses up) about 2 V no matter what. That leaves 9 − 2 = 7 V across the resistor. Ohm's law rearranged gives the current: leftover voltage ÷ resistor — bigger resistor, less current.",
    picks: [{
      id: "R", label: "Series resistor", unit: "Ω",
      hint: "Sets how hard the current is squeezed.",
      options: [220, 330, 470, 680, 1000].map(r => ({ label: pjOhm(r), value: r })),
    }],
    check: (v) => {
      const I = (PJ_VB - PJ_VF) / v.R * 1000;
      const ok = I >= 10 && I <= 20;
      return {
        ok,
        headline: `LED current ≈ ${pjMA(I)}`,
        formula: `I = (9 V − 2 V) ÷ ${pjOhm(v.R)} = ${pjMA(I)}`,
        lines: [{ k: "Target", v: "10–20 mA" }, { k: "Your design", v: pjMA(I), ok }],
        note: {
          adult: ok ? "Dead-on. 470 Ω lands almost exactly at 15 mA." : I > 20 ? "Too much current — the LED runs hot and dies young. Squeeze harder (bigger resistor)." : "Too little — it'll be dim. Ease off (smaller resistor).",
          kids: ok ? "Just right — bright and safe!" : I > 20 ? "Too much flow! Use a bigger resistor." : "Too dim — use a smaller resistor.",
        },
      };
    },
  },
  {
    id: "array", n: 2, ch: "02", chTitle: "The Branching", part: "led3",
    title: "Make It a Beacon",
    tagline: "Three LEDs — but how do you wire them?",
    intro: {
      adult: "One light isn't a beacon. You want three. Each red LED still drops about 2 V. Series shares one current through all of them (so they need 3×2 = 6 V of drops); parallel gives each its own branch — and triples the battery draw. Pick the arrangement and resistor so every LED sits in its safe band.",
      kids: "Three lights make a real beacon! Each red light still uses up about 2 V. You can line them up (series) so they share one flow, or split them (parallel) so each gets its own. Pick the wiring and a resistor that keeps every LED happy.",
    },
    spec: "Three red LEDs (each drops 2 V), each running at 10–20 mA, from a 9 V supply.",
    givens: ["Supply = 9 V", "Each red LED drops 2 V", "Three LEDs total", "Target = 10–20 mA per LED"],
    explain: "Series shares one current through every LED; parallel gives each its own branch, so the battery current adds up. Same Ohm's law, different wiring.",
    picks: [
      {
        id: "mode", label: "Arrangement", hint: "Series shares current; parallel splits it.",
        options: [{ label: "Series (one string)", value: "series" }, { label: "Parallel (three branches)", value: "parallel" }],
      },
      {
        id: "R", label: "Resistor", unit: "Ω", hint: "Series needs one; parallel needs one per branch.",
        options: [120, 220, 330, 470].map(r => ({ label: pjOhm(r), value: r })),
      },
    ],
    check: (v) => {
      let each, total, formula;
      if (v.mode === "series") {
        each = (PJ_VB - 3 * PJ_VF) / v.R * 1000; total = each;
        formula = `I = (9 V − 3×2 V) ÷ ${pjOhm(v.R)} = ${pjMA(each)} through all three`;
      } else {
        each = (PJ_VB - PJ_VF) / v.R * 1000; total = each * 3;
        formula = `each = (9 V − 2 V) ÷ ${pjOhm(v.R)} = ${pjMA(each)}; total = 3× = ${pjMA(total)}`;
      }
      const ok = each >= 10 && each <= 20;
      return {
        ok,
        headline: `${pjMA(each)} per LED · ${pjMA(total)} from the battery`,
        formula,
        lines: [
          { k: "Per-LED target", v: "10–20 mA" },
          { k: "Per LED", v: pjMA(each), ok },
          { k: "Battery draw", v: pjMA(total) },
        ],
        note: {
          adult: !ok ? "Each LED is out of band — adjust the resistor." : v.mode === "series" ? "Efficient: one current, ~14 mA total. This is the choice that makes the battery last." : "Works, but parallel pulls 3× the current from the battery. Series would last three times longer.",
          kids: !ok ? "Not safe yet — try another resistor." : v.mode === "series" ? "Smart! Series sips the battery." : "It works — but parallel drinks 3× more battery. Series lasts longer.",
        },
      };
    },
  },
  {
    id: "power", n: 3, ch: "03", chTitle: "Power & Heat", part: "batt",
    title: "Cool & Long-Lasting",
    tagline: "Will the resistor cook? How long will it run?",
    intro: {
      adult: "Your series beacon draws about 14 mA through its 220 Ω resistor. Two questions: (1) the resistor turns that current into heat — P = I²R ≈ 41 mW — so pick a power rating with at least 2× margin over that. (2) Runtime = battery capacity ÷ current draw, and you need it to last ≥ 10 hours. Pick a resistor rating and a battery that satisfy both.",
      kids: "Your beacon pulls about 14 mA. First: the resistor turns some flow into heat (a tiny 41 mW) — pick one rated to take more than double that. Second: the battery lasts capacity ÷ 14 mA hours — pick one big enough to glow for 10 hours.",
    },
    spec: "Resistor rated ≥ 2× its 41 mW heat, and runtime ≥ 10 hours at ~14 mA.",
    givens: ["Series string draws ≈ 14 mA", "Through a 220 Ω resistor", "Heat: P = I²R", "Runtime = capacity ÷ current"],
    steps: [
      { adult: "① Heat check: compute P = I²R ≈ 41 mW, then pick a resistor rated at least 2× that (so ≥ 82 mW).", kids: "① The resistor makes a little heat (P = flow × flow × pinch). Pick one rated for more than double it." },
      { adult: "② Runtime check: divide the battery's capacity by ~14 mA — it must reach 10 hours.", kids: "② Battery size ÷ flow = hours. It needs to last 10 hours." },
    ],
    explain: "Power becomes heat at P = I²R, so the resistor's rating must beat that. Runtime is simply battery capacity ÷ current draw.",
    picks: [
      {
        id: "rating", label: "Resistor power rating", unit: "W",
        hint: "The rating is a heat budget, not ohms — it must beat 2× the heat the part actually makes.",
        options: [{ label: "1/16 W (0.0625)", value: 0.0625 }, { label: "⅛ W (0.125)", value: 0.125 }, { label: "¼ W (0.25)", value: 0.25 }, { label: "½ W (0.5)", value: 0.5 }],
      },
      {
        id: "batt", label: "Battery", hint: "Capacity ÷ current = hours. It must reach 10 h.",
        options: [{ label: "9 V carbon-zinc · 120 mAh", value: 120 }, { label: "9 V alkaline · 550 mAh", value: 550 }, { label: "9 V lithium · 1200 mAh", value: 1200 }],
      },
    ],
    check: (v) => {
      const I = 0.0136, R = 220;            // series string from stage 2
      const P = I * I * R;                  // ≈ 0.041 W
      const life = v.batt / 13.6;           // hours
      const ratingOk = v.rating >= 2 * P;
      const lifeOk = life >= 10;
      const ok = ratingOk && lifeOk;
      return {
        ok,
        headline: `Heat ≈ ${(P * 1000).toFixed(0)} mW · runtime ≈ ${life.toFixed(0)} h`,
        formula: `P = I²R = (0.0136)² × 220 = ${(P * 1000).toFixed(0)} mW · life = ${v.batt} mAh ÷ 13.6 mA = ${life.toFixed(0)} h`,
        lines: [
          { k: "Resistor heat", v: `${(P * 1000).toFixed(0)} mW` },
          { k: "Rating ≥ 2× heat", v: `${v.rating} W`, ok: ratingOk },
          { k: "Runtime ≥ 10 h", v: `${life.toFixed(0)} h`, ok: lifeOk },
        ],
        note: {
          adult: ok
            ? (v.rating === 0.125
                ? "⅛ W squeaks by with ~3× headroom — legitimate, though ¼ W is the cheap, common pick."
                : "Plenty of headroom on heat, and it runs all night. A ¼ W resistor is the sensible pick.")
            : !ratingOk
              ? (v.rating === 0.0625
                  ? "1/16 W is rated for only 62 mW — under the 82 mW bar (2× the 41 mW heat). It would slowly cook."
                  : "That resistor can't shed the heat safely — go bigger.")
              : "Runs flat too soon — 120 mAh ÷ 13.6 mA is under 9 hours. Pick a higher-capacity battery.",
          kids: ok ? "Cool and long-lasting — nice engineering!" : !ratingOk ? "That resistor would cook — pick a tougher one." : "Battery dies too fast — pick a bigger one.",
        },
      };
    },
  },
  {
    id: "soft", n: 4, ch: "04", chTitle: "The Bucket", part: "cap",
    title: "Soft Start",
    tagline: "A gentle one-second fade-in instead of a hard snap.",
    intro: {
      adult: "A timing network — a resistor feeding a capacitor — gives the beacon a smooth turn-on. The time constant τ = R·C sets the pace. Pick R and C so it eases on over about a second.",
      kids: "Add a bucket (capacitor) filled through a pipe (resistor) and the beacon fades in instead of snapping on. The fill time is τ = R × C. Pick parts for about a one-second fade.",
    },
    spec: "Turn-on time constant τ ≈ 1 s (accept 0.5–2 s).",
    explain: "An RC time constant: τ = R × C is roughly how long the capacitor takes to charge. After about one τ it's two-thirds of the way up.",
    picks: [
      {
        id: "R", label: "Timing resistor", unit: "Ω",
        options: [10000, 100000, 1000000].map(r => ({ label: pjOhm(r), value: r })),
      },
      {
        id: "C", label: "Timing capacitor", unit: "F",
        options: [1e-6, 1e-5, 1e-4].map(c => ({ label: pjFar(c), value: c })),
      },
    ],
    check: (v) => {
      const tau = v.R * v.C;
      const ok = tau >= 0.5 && tau <= 2;
      return {
        ok,
        headline: `τ ≈ ${tau.toFixed(2)} s`,
        formula: `τ = R·C = ${pjOhm(v.R)} × ${pjFar(v.C)} = ${tau.toFixed(2)} s`,
        lines: [{ k: "Target", v: "≈ 1 s (0.5–2 s)" }, { k: "Your τ", v: `${tau.toFixed(2)} s`, ok }],
        note: {
          adult: ok ? "Smooth. 100 kΩ × 10 µF gives a textbook 1-second ramp." : tau < 0.5 ? "Too quick to notice — go bigger on R or C." : "Sluggish — that's a long wait. Drop R or C.",
          kids: ok ? "A perfect gentle fade-in!" : tau < 0.5 ? "Too fast to see — use bigger parts." : "Too slow — use smaller parts.",
        },
      };
    },
  },
  {
    id: "arm", n: 5, ch: "05", chTitle: "The Switch", part: "switch",
    title: "Arm & Fire",
    tagline: "Lights only when KEY and ARM are both on.",
    intro: {
      adult: "A safety beacon shouldn't fire by accident. You want two switches — a key and an arm — where both must be on. That's an AND: switches in series. Wire them in parallel and you've built an OR instead.",
      kids: "You don't want the beacon turning on by mistake. Use two switches that BOTH have to be on. Two switches in a row (series) means AND — both needed. Side by side (parallel) means OR — either one.",
    },
    spec: "Beacon energises only when both switches are closed (logical AND).",
    explain: "Boolean logic in hardware: switches in series make an AND (all must close); in parallel they make an OR (any one closes the path).",
    picks: [{
      id: "wire", label: "Wire the two enable switches…", hint: "Series = AND, parallel = OR.",
      options: [{ label: "In series (AND)", value: "series" }, { label: "In parallel (OR)", value: "parallel" }],
    }],
    check: (v) => {
      const ok = v.wire === "series";
      return {
        ok,
        headline: ok ? "AND — both switches required" : "OR — either switch fires it",
        formula: ok ? "KEY · ARM  →  on only when both closed" : "KEY + ARM  →  on if either is closed",
        lines: [{ k: "Goal", v: "both required (AND)" }, { k: "Your wiring", v: ok ? "series → AND" : "parallel → OR", ok }],
        note: {
          adult: ok ? "Correct. Series switches form an AND gate — the beacon is armed only deliberately." : "Parallel makes an OR — either switch alone fires the beacon. Put them in series for AND.",
          kids: ok ? "Yes! Both switches needed — nice and safe." : "That's OR — either one turns it on. Put them in a row for AND.",
        },
      };
    },
  },
  {
    id: "drive", n: 6, ch: "06", chTitle: "The Transistor", part: "transistor",
    title: "Drive the Big Lamp",
    tagline: "A tiny signal switching a 100 mA lamp.",
    intro: {
      adult: "Beacons need to be seen, so swap in a 100 mA lamp — far more than a logic signal can drive. A transistor (β ≈ 100) does the heavy lifting: a small base current controls the big load. Pick a base resistor so the base current is at least 2× what's needed to saturate.",
      kids: "A real beacon is bright — 100 mA bright. That's too much for a little switch to handle, so a transistor does the muscle work: a small trickle into its base opens a big valve. Pick the base resistor so the trickle is big enough.",
    },
    spec: "Saturate an NPN (β = 100) driving 100 mA: base current I_b ≥ 2 mA.",
    explain: "To switch fully on, a transistor needs base current of at least collector-current ÷ gain (I_c/β); use a 2× margin. A bigger base resistor means less base current.",
    picks: [{
      id: "Rb", label: "Base resistor", unit: "Ω",
      hint: "Sets the control (base) current. Ib = (9 − 0.7) ÷ Rb.",
      options: [470, 1000, 4700, 10000].map(r => ({ label: pjOhm(r), value: r })),
    }],
    check: (v) => {
      const Ib = (PJ_VB - 0.7) / v.Rb * 1000;     // mA
      const need = 100 / 100;                       // Ic/β = 1 mA
      const ok = Ib >= 2 && Ib <= 30;
      return {
        ok,
        headline: `Base current I_b ≈ ${pjMA(Ib)}`,
        formula: `I_b = (9 V − 0.7 V) ÷ ${pjOhm(v.Rb)} = ${pjMA(Ib)}   (need ≥ 2× I_c/β = 2 mA)`,
        lines: [
          { k: "Just-saturate (I_c/β)", v: "1 mA" },
          { k: "Target (2× margin)", v: "≥ 2 mA" },
          { k: "Your I_b", v: pjMA(Ib), ok },
        ],
        note: {
          adult: ok ? "Solidly saturated. 1 kΩ gives ~8 mA of base drive — the transistor acts like a closed switch." : Ib < 2 ? "Not enough base current — the transistor stays half-open and gets hot. Drop Rb." : "Way more base current than needed — wasteful and hard on the source. Raise Rb.",
          kids: ok ? "The valve is fully open — bright lamp!" : Ib < 2 ? "Not enough trickle — the valve barely opens. Smaller resistor." : "Too much trickle — wasteful. Bigger resistor.",
        },
      };
    },
  },
  {
    id: "protect", n: 7, ch: "07", chTitle: "The One-Way Valve", part: "diode",
    title: "Protect It",
    tagline: "Survive a battery put in backwards.",
    intro: {
      adult: "Field gear gets abused — someone will jam the battery in backwards. A series diode is a one-way valve: it passes normal current and blocks reverse. Pick one rated comfortably above the 100 mA load (aim for ≥ 2.5× margin).",
      kids: "Someone will put the battery in backwards someday. A diode is a one-way valve — water flows one way only. Pick one that can carry more than the beacon's 100 mA without complaining.",
    },
    spec: "Reverse-protection diode rated ≥ 2.5× the 100 mA load.",
    explain: "A protection diode's current rating must exceed the load it carries, with margin. Its ~0.7 V forward drop is the small price for blocking reverse current.",
    picks: [{
      id: "d", label: "Protection diode", hint: "Look at the current rating vs the 100 mA load.",
      options: [{ label: "1N4148 · 200 mA", value: 0.2 }, { label: "1N4001 · 1 A", value: 1.0 }],
    }],
    check: (v) => {
      const load = 0.1, need = 0.25;
      const ok = v.d >= need;
      return {
        ok,
        headline: `${(v.d * 1000).toFixed(0)} mA rated vs ${(load * 1000).toFixed(0)} mA load`,
        formula: `need ≥ 2.5 × 100 mA = 250 mA · you picked ${v.d >= 1 ? v.d + " A" : (v.d * 1000) + " mA"}`,
        lines: [
          { k: "Load", v: "100 mA" },
          { k: "Need (2.5× margin)", v: "≥ 250 mA" },
          { k: "Your diode", v: v.d >= 1 ? `${v.d} A` : `${v.d * 1000} mA`, ok },
        ],
        note: {
          adult: ok ? "The 1N4001 (1 A) shrugs off the 100 mA load and protects against reversed power. Expect ~0.7 V drop across it." : "The 1N4148 is a small-signal diode — only 200 mA, too close to the load. Use the 1N4001.",
          kids: ok ? "Tough enough — backwards batteries can't hurt it now!" : "That one's too small for the job — pick the bigger 1 A diode.",
        },
      };
    },
  },
  {
    id: "smooth", n: 8, ch: "08", chTitle: "The Wave", part: "ac",
    title: "Tame the Wave",
    tagline: "Run the installed version off the wall — and flatten the ripple.",
    intro: {
      adult: "The portable beacon runs on a battery, but the installed Mk II plugs into the wall. A bridge rectifier flips AC into lumpy DC that pulses at 120 Hz; a reservoir capacitor fills the dips between peaks — bigger cap, smaller ripple. Pick one so the 100 mA lamp sees under half a volt of wobble.",
      kids: "Plugged into the wall, the power comes in as waves — up and down, 120 times a second. A big bucket (capacitor) smooths those waves into a steady level. Pick a bucket big enough that the light never flickers.",
    },
    spec: "Rectified-AC ripple under 0.5 V at a 100 mA load (ripple pulses at 120 Hz).",
    explain: "Reservoir-cap ripple is ΔV = I ÷ (f·C): a bigger capacitor (or higher ripple frequency) holds the voltage up between the rectified AC peaks.",
    picks: [{
      id: "C", label: "Reservoir capacitor", unit: "F",
      hint: "Ripple ΔV ≈ I ÷ (f · C). Bigger smooths more.",
      options: [100e-6, 470e-6, 1000e-6, 2200e-6].map(c => ({ label: pjFar(c), value: c })),
    }],
    check: (v) => {
      const I = 0.1, f = 120;
      const dv = I / (f * v.C);
      const ok = dv < 0.5;
      const fmtV = (x) => x < 1 ? `${(x * 1000).toFixed(0)} mV` : `${x.toFixed(2)} V`;
      return {
        ok,
        headline: `Ripple ≈ ${fmtV(dv)}`,
        formula: `ΔV = I ÷ (f·C) = 0.1 A ÷ (120 Hz × ${pjFar(v.C)}) = ${fmtV(dv)}`,
        lines: [{ k: "Target", v: "< 0.5 V" }, { k: "Your ripple", v: fmtV(dv), ok }],
        note: {
          adult: ok ? "Smooth enough — the lamp holds rock steady. 2200 µF keeps ripple around 0.4 V." : "Still too lumpy — the lamp will visibly throb at 120 Hz. Go bigger on the reservoir cap.",
          kids: ok ? "Nice and steady — no flicker!" : "Still flickery — use a bigger bucket.",
        },
      };
    },
  },
  {
    id: "kick", n: 9, ch: "09", chTitle: "The Flywheel", part: "coil",
    title: "Catch the Kick",
    tagline: "Add a warning horn — and survive the coil's flyback.",
    intro: {
      adult: "The beacon earns a warning horn: a relay coil pulling 0.4 A. An inductor resists any change in its current — switch it off and the collapsing field spikes hundreds of volts, frying the transistor. A flyback diode across the coil gives that current somewhere to go, clamping the spike to about Vcc + 0.7 V. Add it, and size it for the full coil current.",
      kids: "Add a horn! Its coil is like a spinning flywheel — cut the power and it keeps shoving, so the voltage shoots sky-high. A flyback diode across the coil catches that kick so nothing breaks. Pick one big enough to carry the coil's current.",
    },
    spec: "Tame a 0.4 A relay coil's switch-off spike and keep the transistor alive.",
    explain: "An inductor fights current changes: cut it fast and V = L·(di/dt) spikes huge. A flyback diode gives that current a loop to fade out in, clamping the spike.",
    picks: [
      {
        id: "fb", label: "Handle the coil's kick…", hint: "Where does the current go when the switch opens?",
        options: [{ label: "No diode", value: "none" }, { label: "Flyback diode across the coil", value: "across" }],
      },
      {
        id: "d", label: "Flyback diode rating", hint: "Must carry the full coil current at switch-off.",
        options: [{ label: "1N4148 · 200 mA", value: 0.2 }, { label: "1N4001 · 1 A", value: 1.0 }],
      },
    ],
    check: (v) => {
      const coil = 0.4;
      const placed = v.fb === "across";
      const rated = v.d >= coil;
      const ok = placed && rated;
      const clamp = placed ? "clamped to Vcc + 0.7 ≈ 9.7 V (safe)" : "hundreds of volts — transistor destroyed";
      return {
        ok,
        headline: placed ? (rated ? "Spike clamped · diode sized right" : "Spike clamped — but diode too small") : "No path for the current — huge spike",
        formula: `switch-off spike → ${clamp} · diode must carry the ${coil} A coil current`,
        lines: [
          { k: "Flyback path", v: placed ? "across coil" : "none", ok: placed },
          { k: "Diode ≥ coil current (0.4 A)", v: v.d >= 1 ? `${v.d} A` : `${v.d * 1000} mA`, ok: rated },
        ],
        note: {
          adult: ok ? "Textbook flyback: the 1N4001 routes the coil's current and clamps the spike to a safe ~9.7 V." : !placed ? "With no flyback path the collapsing field spikes the transistor to destruction. Put a diode across the coil." : "Right idea — but a 200 mA diode can't carry the 0.4 A coil current. Use the 1 A.",
          kids: ok ? "Caught it! The kick is tamed and the horn is safe." : !placed ? "Ouch — the kick has nowhere to go and breaks the transistor. Add a diode across the coil." : "Right idea, too small — use the bigger 1 A diode.",
        },
      };
    },
  },
  {
    id: "blink", n: 10, ch: "10", chTitle: "The Blinker", part: "ic555", final: true,
    title: "Make It Blink",
    tagline: "A 555 astable flashing the beacon ~2 times a second.",
    intro: {
      adult: "A beacon blinks. A 555 in astable mode is a self-running square-wave oscillator — exactly the AC-style waveform from the wave chapter, made by charging and discharging a capacitor. Pick R1, R2 and C so it flashes at about 2 Hz: f = 1.44 / ((R1 + 2·R2)·C).",
      kids: "A beacon flashes! The 555 chip flips on and off by itself, over and over, by filling and draining a bucket. Pick the parts so it blinks about twice a second.",
    },
    spec: "555 astable blink rate of 1.5–2.5 Hz. This is the finale — get it and the Beacon is complete.",
    explain: "The 555 astable frequency is f = 1.44 ÷ ((R_1 + 2·R_2)·C): it charges C through R_1+R_2 and discharges through R_2, making a square wave. Bigger parts → slower blink.",
    picks: [
      { id: "R1", label: "R1", unit: "Ω", options: [1000, 10000].map(r => ({ label: pjOhm(r), value: r })) },
      { id: "R2", label: "R2", unit: "Ω", options: [22000, 33000, 47000].map(r => ({ label: pjOhm(r), value: r })) },
      { id: "C", label: "Timing cap", unit: "F", options: [1e-6, 1e-5].map(c => ({ label: pjFar(c), value: c })) },
    ],
    check: (v) => {
      const f = 1.44 / ((v.R1 + 2 * v.R2) * v.C);
      const ok = f >= 1.5 && f <= 2.5;
      return {
        ok,
        headline: `Blink rate ≈ ${f.toFixed(2)} Hz`,
        formula: `f = 1.44 ÷ ((${pjOhm(v.R1)} + 2×${pjOhm(v.R2)}) × ${pjFar(v.C)}) = ${f.toFixed(2)} Hz`,
        lines: [{ k: "Target", v: "1.5–2.5 Hz" }, { k: "Your rate", v: `${f.toFixed(2)} Hz`, ok }],
        note: {
          adult: ok ? "That's a proper beacon flash. The 555 puts out a square wave at this frequency — your beacon is complete." : f > 2.5 ? "Flashing too fast — stretch the period with a bigger R2 or C." : "Too slow and sleepy — shrink R2 or C.",
          kids: ok ? "Blink… blink… blink — the Beacon is ALIVE! 🎉" : f > 2.5 ? "Way too fast — use bigger timing parts." : "Too slow — use smaller timing parts.",
        },
      };
    },
  },
];

const PJ_META = {
  name: "The Beacon",
  total: PJ_STAGES.length,
};

/* Cumulative "what we know" — the principle each locked-in stage proves.
   Routed through <Eq> so subscripts render. */
const PJ_LEARNED = {
  light:   ["A resistor sets LED current: I = (V_supply − V_LED) ÷ R.", "An LED 'drops' a fixed ~2 V no matter the current."],
  array:   ["Series shares one current through every part; parallel gives each branch its own.", "Parallel battery draw = the sum of the branch currents."],
  power:   ["Resistors turn current into heat at P = I²R — rate them with margin.", "Runtime = battery capacity ÷ current draw."],
  soft:    ["An R feeding a C charges over a time constant τ = R × C.", "After about one τ, a capacitor is ~63% charged."],
  arm:     ["Switches in series make an AND — all must close.", "Switches in parallel make an OR — any one closes the path."],
  drive:   ["A transistor lets a small base current switch a big load.", "To fully switch on, make I_b ≥ I_c ÷ β (with a 2× margin)."],
  protect: ["A series diode is a one-way valve — it blocks reverse current.", "Rate a diode above the current it must carry."],
  smooth:  ["A reservoir cap smooths rectified AC: ripple ΔV = I ÷ (f·C).", "Bigger capacitor → smaller ripple."],
  kick:    ["An inductor fights current change; cutting it fast spikes V = L·(di/dt).", "A flyback diode clamps that spike to a safe level."],
  blink:   ["A 555 astable self-oscillates at f = 1.44 ÷ ((R_1 + 2·R_2)·C).", "It charges and discharges a cap to make a square wave."],
};

Object.assign(window, { PJ_STAGES, PJ_META, PJ_LEARNED, pjOhm, pjFar });
