/* scope.jsx — The Oscilloscope: a primer on seeing electricity.
   Reading the screen (knob math) → healthy vs. sick signals (hookup
   debugging) → predict-then-probe stations (calculate, then verify on
   the trace). Uses ScopeScreen + SCOPE_SIGS from scope-screen.jsx. */

const { useState, useEffect } = React;

const SCOPE_TWEAKS = /*EDITMODE-BEGIN*/{
  "theme": "paper",
  "audience": "adult"
}/*EDITMODE-END*/;

/* ─── §02 reading the screen — live knobs ──────────────────────────────── */
function ScopeKnobsSection({ kids }) {
  const [vDiv, setVDiv] = useState(2);
  const [tDiv, setTDiv] = useState(50);
  // the mystery signal: 4 V peak, 5 Hz sine
  const amp = 4, hz = 5;
  const heightDivs = (2 * amp) / vDiv;
  const periodDivs = (1000 / hz) / tDiv;
  return (
    <section className="section" id="knobs" data-screen-label="02 The two knobs">
      <div className="marker">§ 02 · the two knobs</div>
      <div className="section-inner">
        <div className="two-col">
          <div>
            <h2 className="serif">Count squares,<br/>multiply by the <em>knob</em>.</h2>
            <p className="lede">
              {kids
                ? <>The screen is graph paper. Two knobs decide what each square is worth: one for up-and-down (volts per square) and one for left-and-right (time per square). Reading a scope is just counting squares!</>
                : <>The graticule is graph paper with adjustable units. <b>V/div</b> sets what a vertical division is worth; <b>t/div</b> sets the horizontal. Every scope measurement is the same move: count divisions, multiply by the knob.</>}
            </p>
            <p>
              {kids
                ? <>This wave is {heightDivs.toFixed(1)} squares tall. At {vDiv} volts a square, that's <b>{(heightDivs * vDiv).toFixed(0)} volts top-to-bottom</b>. One full wiggle takes {periodDivs.toFixed(1)} squares — at {tDiv} per square, that's <b>{(periodDivs * tDiv).toFixed(0)} ms per wiggle</b>.</>
                : <>Right now the trace spans {heightDivs.toFixed(1)} div × {vDiv} V/div = <b>{(heightDivs * vDiv).toFixed(0)} V peak-to-peak</b>. One cycle spans {periodDivs.toFixed(1)} div × {tDiv} ms/div = <b>{(periodDivs * tDiv).toFixed(0)} ms</b>, so f = 1/T = <b>{hz} Hz</b>. That's the entire skill.</>}
            </p>
            <div className="marg" style={{ marginTop: 14 }}>
              {kids
                ? <>Turn the knobs! The WAVE never changes — only how it's displayed. Zoomed wrong, a wave can look flat or like noise. Right-sized, it fills the screen.</>
                : <>Turn the knobs and notice: the signal never changes — only the framing. Too coarse and the wave flattens into a line; too fine and you see meaningless wall. First job at a real bench: turn knobs until ~2–3 cycles fill the screen.</>}
            </div>
          </div>
          <div>
            <div className="card" style={{ padding: "18px 20px" }}>
              <div className="eyebrow" style={{ marginBottom: 10 }}>mystery wave · adjust the knobs</div>
              <ScopeScreen signal={SCOPE_SIGS.sine(amp, hz)} vDiv={vDiv} tDiv={tDiv}
                           label={kids ? "what is this wave?" : "measure me"} />
              <div style={{ marginTop: 14 }}>
                <Slider name={kids ? "Volts per square" : "Vertical · V/div"} value={vDiv} min={0.5} max={5} step={0.5}
                        unit=" V" accent="water" onChange={setVDiv} />
                <Slider name={kids ? "Time per square" : "Horizontal · t/div"} value={tDiv} min={10} max={100} step={5}
                        unit=" ms" accent="current" onChange={setTDiv} />
              </div>
              <div className="branch-readout" style={{ marginTop: 10 }}>
                <span>{kids ? "squares tall × knob" : "p-p = divs × V/div"}</span>
                <span className="mono">{heightDivs.toFixed(1)} × {vDiv} = <b style={{ color: "var(--water-deep)" }}>{(heightDivs * vDiv).toFixed(0)} V</b></span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ─── §03 healthy vs sick gallery ──────────────────────────────────────── */
function ScopeGallerySection({ kids }) {
  const cases = [
    {
      name: kids ? "Healthy battery power" : "Healthy DC",
      sig: SCOPE_SIGS.dc(5), vDiv: 2, tDiv: 50,
      read: kids ? "A flat line above zero. Steady push, no surprises. This is what a good battery or supply looks like."
                 : "Flat line at +5 V. A power rail should be boring — boring is correct.",
      verdict: "ok",
    },
    {
      name: kids ? "Power with ripple" : "Rail with ripple",
      sig: (t) => 5 + 0.8 * Math.sin(2 * Math.PI * 12 * t / 1000) + 0.3 * Math.sin(2 * Math.PI * 24 * t / 1000), vDiv: 2, tDiv: 50,
      read: kids ? "Mostly flat but wobbling. The smoothing bucket (capacitor) is too small, tired, or missing — the bumps are leaking through."
                 : "DC with a periodic wobble = ripple. Undersized/aged smoothing cap, or load too heavy. The wobble's frequency tells you the source: mains-related ripple shows at 50/60 Hz (or 100/120 after a bridge).",
      verdict: "warn",
    },
    {
      name: kids ? "Clean wiggle (AC)" : "Healthy sine",
      sig: SCOPE_SIGS.sine(3, 8), vDiv: 2, tDiv: 25,
      read: kids ? "A smooth, even slosh — same height up as down, evenly spaced. A healthy AC signal, like audio."
                 : "Symmetric, smooth, periodic. Audio lines, function generators, transformer outputs should look like this.",
      verdict: "ok",
    },
    {
      name: kids ? "Flat-topped wiggle" : "Clipped sine",
      sig: SCOPE_SIGS.clippedSine(5, 8, 3.2), vDiv: 2, tDiv: 25,
      read: kids ? "The tops are sliced flat — the signal hit the ceiling! An amplifier was asked for more push than its supply has. (This is the buzzy sound of too-loud speakers.)"
                 : "Flat tops = clipping: the stage ran out of supply headroom (ch. 12's CLIPPED bars, live). Fix: less gain, or a taller supply. Distortion you can SEE.",
      verdict: "bad",
    },
    {
      name: kids ? "One-way bumps" : "Half-wave rectified",
      sig: SCOPE_SIGS.halfWave(4, 8), vDiv: 2, tDiv: 25,
      read: kids ? "Only the up-halves of a slosh — the down-halves are chopped off. A one-way valve (diode) is at work. If you EXPECTED smooth power, a valve is in the wrong spot!"
                 : "Positive humps, dead gaps = a diode passing one polarity (ch. 7). Correct inside a rectifier before smoothing; a fault if you expected symmetric AC — suspect an unintended diode path.",
      verdict: "warn",
    },
    {
      name: kids ? "Square blink" : "Healthy square wave",
      sig: SCOPE_SIGS.square(0, 5, 100), vDiv: 2, tDiv: 50,
      read: kids ? "On-off-on-off with sharp corners — a blinker or computer signal doing its job."
                 : "Two flat levels, fast edges: 555 outputs and logic signals. Soft, rounded edges would hint at too much capacitance loading the line.",
      verdict: "ok",
    },
    {
      name: kids ? "Fuzzy mess" : "Floating probe / no ground",
      sig: SCOPE_SIGS.noise(2.2), vDiv: 2, tDiv: 50,
      read: kids ? "Random fuzz that means nothing. Usually the scope's clip ISN'T CONNECTED — you're seeing the room's electrical noise, not your circuit. Check the ground clip first!"
                 : "Aimless hash, often 50/60 Hz-ish: the probe ground clip is off, or you're probing an unconnected node. Rule one of scope debugging: flat-or-fuzzy nonsense → check the ground clip before blaming the circuit.",
      verdict: "bad",
    },
    {
      name: kids ? "The nasty spike" : "Inductive kickback",
      sig: SCOPE_SIGS.spike(2, 12), vDiv: 4, tDiv: 50,
      read: kids ? "Running fine, then a huge needle the moment something switched off — chapter 9's slam! That spike chews up switches. The fix: the one-way-valve escape loop (flyback diode)."
                 : "Steady level, then a tall narrow spike at switch-off: ch. 9's collapsing field. If you see this across a transistor driving a coil, add the flyback diode before the transistor dies. Note the V/div — spikes dwarf the supply.",
      verdict: "bad",
    },
  ];
  const badge = (v) => v === "ok"
    ? { t: kids ? "✓ healthy" : "✓ HEALTHY", c: "var(--water-deep)" }
    : v === "warn"
    ? { t: kids ? "△ suspicious" : "△ SUSPECT", c: "oklch(0.58 0.13 80)" }
    : { t: kids ? "✗ trouble" : "✗ FAULT SIGNATURE", c: "oklch(0.55 0.17 35)" };
  return (
    <section className="section" id="gallery" data-screen-label="03 Field guide" style={{ background: "var(--bg-deeper)" }}>
      <div className="marker">§ 03 · a field guide to traces</div>
      <div className="section-inner">
        <h2 className="serif">Healthy signals,<br/>and <em>sick</em> ones.</h2>
        <p className="lede" style={{ maxWidth: 640 }}>
          {kids
            ? <>Doctors learn what healthy heartbeats look like so the weird ones jump out. Same here — learn these eight screens and you can diagnose almost anything you'll build.</>
            : <>Scope debugging is pattern recognition. These eight traces cover most of what a hobby bench ever shows; learn the signatures and the instrument starts answering "is it hooked up right?" at a glance.</>}
        </p>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: 18, marginTop: 26 }}>
          {cases.map((c, i) => {
            const b = badge(c.verdict);
            return (
              <div key={i} className="card" style={{ padding: "16px 18px" }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 10, gap: 8 }}>
                  <div className="eyebrow">{c.name}</div>
                  <span style={{ fontFamily: "IBM Plex Mono, monospace", fontSize: 11, color: b.c, whiteSpace: "nowrap" }}>{b.t}</span>
                </div>
                <ScopeScreen signal={c.sig} vDiv={c.vDiv} tDiv={c.tDiv} mini />
                <p style={{ fontSize: 14, lineHeight: 1.55, margin: "10px 0 0", color: "var(--ink-soft)" }}>{c.read}</p>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

/* ─── §04 predict-then-probe stations ──────────────────────────────────── */
function ProbeStation({ kids, n, title, setup, prompt, answerLabel, scope, check, reveal }) {
  const [shown, setShown] = useState(false);
  return (
    <div className="card" style={{ padding: "20px 24px" }}>
      <div className="eyebrow" style={{ marginBottom: 8 }}>station {n} · {title}</div>
      <p style={{ margin: "0 0 6px", fontSize: 15.5, lineHeight: 1.6 }}>{setup}</p>
      <p style={{ margin: "0 0 14px", fontSize: 15.5, lineHeight: 1.6 }}><b>{prompt}</b></p>
      {!shown ? (
        <button className="quiz-retry" style={{ fontSize: 13.5, padding: "10px 18px" }} onClick={() => setShown(true)}>
          {kids ? "I made my guess — probe it!" : "Prediction made — probe the node"}
        </button>
      ) : (
        <>
          <ScopeScreen signal={scope.sig} vDiv={scope.vDiv} tDiv={scope.tDiv} label={scope.label} mini />
          <div className="branch-readout" style={{ marginTop: 10 }}>
            <span>{answerLabel}</span>
            <span className="mono" style={{ color: "var(--water-deep)" }}><b>{check}</b></span>
          </div>
          <p style={{ fontSize: 14, lineHeight: 1.55, margin: "10px 0 0", color: "var(--ink-soft)" }}>{reveal}</p>
        </>
      )}
    </div>
  );
}

function ScopeLabSection({ kids }) {
  return (
    <section className="section" id="lab" data-screen-label="04 Predict then probe">
      <div className="marker">§ 04 · predict, then probe</div>
      <div className="section-inner">
        <h2 className="serif">Do the math.<br/>Then make the scope <em>agree</em>.</h2>
        <p className="lede" style={{ maxWidth: 640 }}>
          {kids
            ? <>This is the real engineering loop: figure out what the screen SHOULD show, then look. If they match — you understand the circuit. If they don't — the scope is telling you where your model is wrong. Guess first, every time!</>
            : <>The professional loop: calculate the expected trace, then probe. Agreement validates your model; disagreement localizes the bug (yours or the circuit's). Commit to a number before revealing each trace — that's the whole discipline.</>}
        </p>
        <div style={{ display: "grid", gap: 18, marginTop: 26, maxWidth: 720 }}>
          <ProbeStation kids={kids} n="1" title={kids ? "the divider tap" : "voltage divider"}
            setup={kids
              ? <>A 12 V barrel feeds two equal squeezes in a row (chapter 11's divider). The probe goes on the tap between them.</>
              : <>12 V across R₁ = 6k then R₂ = 6k (ch. 11). Probe at the midpoint. Scope set to 2 V/div.</>}
            prompt={kids ? "How many squares above zero should the flat line sit, at 2 volts per square?" : "Predict the trace: shape, and height in divisions."}
            answerLabel={kids ? "the line sits at" : "measured"}
            scope={{ sig: SCOPE_SIGS.dc(6), vDiv: 2, tDiv: 50, label: "divider midpoint" }}
            check={kids ? "3 squares = 6 V ✓" : "flat DC, +3.0 div = 6.0 V ✓"}
            reveal={kids
              ? <>Equal squeezes split the push in half: 12 → 6. And 6 volts ÷ 2 per square = 3 squares. The scope agrees — your math works on real screens!</>
              : <>V_out = 12 · 6k/12k = 6 V; 6 V ÷ 2 V/div = 3 divisions above center. A flat line, because a resistive divider on DC has no time behavior. If this read 4 V instead, you'd suspect a load on the tap (§ 11.4).</>} />
          <ProbeStation kids={kids} n="2" title={kids ? "the blinker" : "555 output"}
            setup={kids
              ? <>Chapter 10's blinker, set so its bucket-and-pinch rhythm should be 5 blinks per second. Probe on the output wire. Time knob: 50 ms per square.</>
              : <>A 555 astable configured for f = 5 Hz (ch. 10). Probe the output pin; t/div = 50 ms.</>}
            prompt={kids ? "One full on-off cycle should be how many squares wide?" : "Predict the period in ms, and its width in divisions."}
            answerLabel={kids ? "one cycle is" : "measured period"}
            scope={{ sig: SCOPE_SIGS.square(0, 5, 200), vDiv: 2, tDiv: 50, label: "555 pin 3" }}
            check={kids ? "4 squares = 200 ms ✓" : "T = 4 div = 200 ms → 5 Hz ✓"}
            reveal={kids
              ? <>5 blinks a second means each blink-cycle takes 1/5 of a second — 200 ms. At 50 ms per square: 4 squares. Count them on the screen!</>
              : <>T = 1/f = 200 ms; 200/50 = 4 divisions per cycle. A square wave, because the 555 snaps between rails (§ 10). If the tops sloped, you'd suspect the output loaded down or the cap leaking.</>} />
          <ProbeStation kids={kids} n="3" title={kids ? "the filling bucket" : "RC charge"}
            setup={kids
              ? <>Chapter 4's bucket: a 1k squeeze filling a big cap, landmark time τ = 100 ms. We flip the switch right as the screen starts. Aiming push: 8.</>
              : <>An RC charge: R = 1k, C = 100 µF, so τ = 100 ms, charging toward 8 V from t = 0. t/div = 100 ms.</>}
            prompt={kids ? "After ONE square (one landmark time), about how full should it be?" : "Predict the voltage at t = 1 div (= 1τ), and the overall shape."}
            answerLabel={kids ? "after one square" : "at t = 1τ"}
            scope={{ sig: SCOPE_SIGS.rcCharge(8, 100), vDiv: 2, tDiv: 100, label: "across the cap" }}
            check={kids ? "about 5 (63% of 8) ✓" : "≈ 5.0 V = 63% of 8 ✓"}
            reveal={kids
              ? <>The tired-climb! Fast at first, leveling off. After one landmark time it's about 63% there: 0.63 × 8 ≈ 5. By five squares it's basically full. (That's M·3's landmark rule, live on screen.)</>
              : <>The saturating exponential (M·3): 8 × 0.63 ≈ 5.0 V at 1τ, ~99% by 5τ = half a second. A scope is the honest way to measure τ in a real circuit: find where the curve crosses 63% and read the time off the graticule.</>} />
        </div>
        <div className="marg" style={{ marginTop: 22, maxWidth: 640 }}>
          {kids
            ? <>That loop — guess, probe, compare — is the whole secret of fixing circuits. The Workbench is a great place to keep practicing it!</>
            : <>Every chapter's practice problems can be closed this way on a real bench: compute, probe, compare. When you graduate to physical hardware, a $30 USB scope or a multimeter with a Hz mode covers everything this course builds.</>}
        </div>
      </div>
    </section>
  );
}

/* ─── App ─────────────────────────────────────────────────────────────── */
function App() {
  const [t, setTweak] = useTweaks(SCOPE_TWEAKS);
  useCrossChapterPersistence(t, setTweak);
  useEffect(() => { document.body.setAttribute("data-theme", t.theme); }, [t.theme]);
  const kids = t.audience === "kids";

  const navItems = [
    { id: "cover", label: "Cover" },
    { id: "knobs", label: kids ? "The knobs" : "Knob math" },
    { id: "gallery", label: "Field guide" },
    { id: "lab", label: kids ? "Probe it!" : "Predict & probe" },
    { id: "quiz", label: "Checkpoint" },
  ];

  return (
    <>
      <ProgressBar />
      <TopBar chapterLabel="The Oscilloscope" audience={t.audience}
              setAudience={(a) => setTweak("audience", a)} />
      <ChapterNav items={navItems} />

      <main>
        <section className="cover-page" id="cover" data-screen-label="Scope Cover">
          <div className="hero-grid"></div>
          <div className="cover-inner">
            <div style={{ display: "flex", alignItems: "baseline", gap: 16, marginBottom: 30 }}>
              <span className="eyebrow">{kids ? "A guide for builders" : "A field guide"}</span>
              <span className="eyebrow" style={{ color: "var(--ink-faint)" }}>·</span>
              <span className="eyebrow">bench skills · the instrument</span>
            </div>
            <h1 className="serif" style={{ fontSize: "clamp(40px, 6vw, 72px)", margin: "0 0 18px" }}>
              The <em>Oscilloscope</em>.
            </h1>
            <p className="lede" style={{ maxWidth: 620 }}>
              {kids
                ? <>Everything in this course animated electricity for you. A real bench has a machine that does exactly that for REAL circuits — a camera for electricity. Here's how to read its pictures like a pro.</>
                : <>Every animation in this course has been pretending to be one of these. A scope plots voltage against time — the camera for electricity. Ten minutes here teaches you to read it: the knob math, the healthy-vs-sick gallery, and the predict-then-probe discipline.</>}
            </p>
            <div className="marg" style={{ marginTop: 18 }}>
              bench skills · optional · ≈ 15 min · pairs well with The Workbench and Level 2
            </div>
          </div>
        </section>

        <ScopeKnobsSection kids={kids} />
        <ScopeGallerySection kids={kids} />
        <ScopeLabSection kids={kids} />

        <CheckpointQuiz kids={kids} label="Checkpoint" pick={3}
          title={kids ? "Quick check!" : "Did it stick?"}
          questions={[
            { q: kids ? "A wave is 3 squares tall and the knob says 2 volts per square. The wave is…" : "Trace spans 3 divisions at 2 V/div. Peak-to-peak voltage:",
              options: ["6 V", "1.5 V", "3 V", "32 V"], correct: 0,
              explain: kids ? "Squares × knob: 3 × 2 = 6." : "divs × V/div. The only formula a scope needs." },
            { q: kids ? "One full wiggle is 5 squares wide at 20 ms per square. How long is one wiggle?" : "One cycle spans 5 div at 20 ms/div. The period is…",
              options: ["100 ms", "4 ms", "25 ms", "1 s"], correct: 0,
              explain: kids ? "5 × 20 = 100 ms per wiggle — that's 10 wiggles a second." : "T = 100 ms → f = 10 Hz. Count, multiply, invert." },
            { q: kids ? "The screen shows random fuzz that means nothing. First thing to check?" : "Aimless noise on screen. First debugging move:",
              options: [kids ? "the ground clip — it's probably not connected" : "the probe's ground clip", kids ? "buy a new circuit" : "replace the IC", kids ? "the battery" : "the mains fuse", kids ? "your eyes" : "the trigger holdoff"], correct: 0,
              explain: kids ? "No ground clip = the scope is sniffing the room, not your circuit!" : "An open ground (or unconnected probe) shows ambient hash. Rule one: check the clip before the circuit." },
            { q: kids ? "A smooth wiggle has its tops sliced flat. What happened?" : "A sine shows flat tops. Diagnosis:",
              options: [kids ? "it hit the ceiling — the amplifier ran out of push" : "clipping — the stage ran out of supply headroom", kids ? "the scope is broken" : "ripple", kids ? "too much time per square" : "rectification", kids ? "nothing — normal" : "ringing"], correct: 0,
              explain: kids ? "Asked for more push than the supply has — the wave gets a flat haircut (chapter 12's CLIPPED!)." : "Output demanded > rails available. Less gain or more supply (ch. 12)." },
            { q: kids ? "A steady line with only up-bumps and gaps between them means…" : "Positive humps with dead gaps between them indicate…",
              options: [kids ? "a one-way valve chopped off the down-halves" : "half-wave rectification — a diode in the path", kids ? "a dying battery" : "a shorted cap", kids ? "fuzz" : "clipping", kids ? "a perfect signal" : "inductive kick"], correct: 0,
              explain: kids ? "Chapter 7's valve at work — only one direction gets through." : "One polarity passes, one is blocked: a diode — intended (rectifier) or not (fault)." },
            { q: kids ? "Why guess BEFORE you look at the screen?" : "Why predict before probing?",
              options: [kids ? "so a wrong screen can teach you something" : "disagreement then localizes the error — agreement validates the model", kids ? "it's faster" : "scopes require it", kids ? "you shouldn't" : "to save probe wear", kids ? "for luck" : "tradition"], correct: 0,
              explain: kids ? "If you guess first, a surprise means you LEARNED something. If you just look, you learn nothing!" : "A prediction turns the trace into a test. Without one, every screen looks plausible." },
          ]} />

        <section className="section" id="onward" data-screen-label="Onward" style={{ background: "var(--bg-deeper)", paddingBottom: "12vh" }}>
          <div className="marker">where to use it</div>
          <div className="section-inner">
            <h2 className="serif">Now go <em>probe</em> something.</h2>
            <div style={{ display: "flex", gap: 12, marginTop: 24, flexWrap: "wrap" }}>
              <NextChapterButton href="sandbox.html" label="The Workbench — build & probe" />
              <PrevChapterButton href="chapter8.html" label="Ch. 8 — where waves live" />
              <PrevChapterButton href="map.html" label="Course map" />
            </div>
          </div>
        </section>
      </main>

      <TweaksPanel title="Tweaks">
        <CommonTweaks t={t} setTweak={setTweak} />
      </TweaksPanel>
      <GlossaryFab />
    </>
  );
}

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