/* App.jsx — assembles the manifesto, tracks the live article counter on scroll,
   handles jump-to-article, and the closing "ratify" interaction. */

function RatifySection() {
  const [stamped, setStamped] = React.useState(false);
  return (
    <section className="ratify" id="ratify">
      <div className="ratify-inner">
        <Eyebrow color="var(--sand-text)">ARTICLE XII · OMITTED FOR CONVENIENCE</Eyebrow>
        <h2 className="ratify-title u-h1">Do you solemnly affirm<br />the Unprinciples?</h2>
        <p className="u-clause" style={{ maxWidth: 560, margin: "0 auto", color: "var(--ash)" }}>
          By ratifying, you agree to uphold each principle fully —{" "}
          <em>unless</em> doing so becomes effortful, awkward, slightly inconvenient,
          or noticed by no one.<sup style={{ color: "var(--ember)" }}>†</sup>
        </p>

        <div className="ratify-action">
          {!stamped ? (
            <Button variant="primary" onClick={() => setStamped(true)}
              style={{ background: "var(--bone)", color: "var(--ink)", boxShadow: "3px 3px 0 0 var(--ember)" }}>
              Ratify (Conditionally)
            </Button>
          ) : (
            <div className="ratify-result">
              <Stamp rotate={-6} size={26} thunk>APPROVED — CONDITIONALLY</Stamp>
              <p className="u-fine" style={{ color: "var(--sand-text)", marginTop: 18 }}>
                Thank you. Your conditional agreement has been filed, noted, and will be
                honored whenever convenient. You may now observe the work from a safe distance.
              </p>
            </div>
          )}
        </div>
        <p className="u-fine ratify-fine" style={{ color: "var(--sand-text)" }}>
          † This affirmation is non-binding, retroactively editable, and may be disavowed
          the moment it stops serving you.
        </p>
      </div>
    </section>
  );
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "mode": "Exposé",
  "accent": "#C2511E",
  "marginNotes": true,
  "grain": true
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [current, setCurrent] = React.useState(0);
  const total = window.UNPRINCIPLES.length;

  // apply tweak-driven globals to <body> / :root
  React.useEffect(() => {
    document.body.classList.toggle("mode-expose", t.mode === "Expos\u00e9");
    document.body.classList.toggle("hide-notes", !t.marginNotes);
    document.documentElement.style.setProperty("--ember", t.accent);
  }, [t.mode, t.accent, t.marginNotes]);

  // live article counter + reveal-on-scroll, via a reliable scroll listener
  // (IntersectionObserver is throttled/unreliable in some preview frames).
  React.useEffect(() => {
    const articles = Array.from(document.querySelectorAll(".article"));
    let ticking = false;

    const update = () => {
      ticking = false;
      const mid = window.scrollY + window.innerHeight / 2;
      const revealLine = window.scrollY + window.innerHeight * 0.88;
      let active = 0;
      articles.forEach((el) => {
        const top = el.offsetTop;
        const bottom = top + el.offsetHeight;
        if (top < revealLine) el.classList.add("is-in");
        if (mid >= top && mid < bottom) {
          const n = parseInt(el.id.replace("article-", ""), 10);
          if (!isNaN(n)) active = n;
        }
      });
      setCurrent(active);
    };

    const onScroll = () => { if (!ticking) { ticking = true; requestAnimationFrame(update); } };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    update();
    // safety net: never leave content hidden if something stalls
    const t = setTimeout(() => articles.forEach((el) => el.classList.add("is-in")), 1500);

    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      clearTimeout(t);
    };
  }, []);

  const jump = (n) => {
    const el = document.getElementById("article-" + n);
    if (el) window.scrollTo({ top: el.offsetTop - 80, behavior: "smooth" });
  };
  const begin = () => jump(1);

  return (
    <React.Fragment>
      {t.grain && <GrainOverlay opacity={0.045} />}
      <TopBar current={current} total={total} onJump={jump} />
      <main className="page">
        <Hero onBegin={begin} />
        <section className="articles">
          <div className="articles-head">
            <Eyebrow color="var(--ember)">THE ELEVEN ARTICLES, IN FULL</Eyebrow>
            <p className="u-fine" style={{ color: "var(--sand-text)", marginTop: 6 }}>
              Each principle is reproduced exactly as believed, then exactly as practiced.
            </p>
          </div>
          {window.UNPRINCIPLES.map((p, i) => <Article key={p.n} data={p} index={i} />)}
          <hr className="rule-double" style={{ margin: 0 }} />
        </section>
        <RatifySection />
        <Footer />
      </main>

      <TweaksPanel>
        <TweakSection label="Direction" />
        <TweakRadio label="Mode" value={t.mode}
          options={["Deadpan", "Expos\u00e9"]}
          onChange={(v) => setTweak("mode", v)} />
        <TweakSection label="Details" />
        <TweakColor label="Accent" value={t.accent}
          options={["#C2511E", "#E0A21A", "#C8302A", "#1C1813"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakToggle label="Margin scrawls" value={t.marginNotes}
          onChange={(v) => setTweak("marginNotes", v)} />
        <TweakToggle label="Paper grain" value={t.grain}
          onChange={(v) => setTweak("grain", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

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