/* =========================================================================
   App root — lang state, routing, tweaks. Layout locked: 碑 / stele.
   ========================================================================= */
const { useState: uS, useEffect: uE, useRef: uRef, useCallback: uCb } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "serif": "newsreader",
  "cjkfont": "楷 · 霞鹜文楷",
  "accent": "黛紫 · Violet",
  "motif": "calm"
}/*EDITMODE-END*/;

/* Accent: hue + chroma pairs — chosen to be clearly distinct, all porcelain-compatible */
const ACCENT = {
  "青花 · Cobalt": { h: 209, c: 0.09 },
  "黛紫 · Violet": { h: 292, c: 0.07 },
  "松石 · Teal": { h: 183, c: 0.08 },
  "鎏金 · Gold": { h: 84, c: 0.1 },
};

/* Latin serif — six genuinely different temperaments (CJK rides along via --font-cjk).
   newsreader + playfair lean into the calligraphic/dramatic registers that pair with 霞鹜文楷. */
const SERIF_STACK = {
  source: "'Source Serif 4', var(--font-cjk), Georgia, serif",
  spectral: "'Spectral', var(--font-cjk), Georgia, serif",
  newsreader: "'Newsreader', var(--font-cjk), Georgia, serif",
  playfair: "'Playfair Display', var(--font-cjk), Georgia, serif",
  cormorant: "'Cormorant Garamond', var(--font-cjk), Georgia, serif",
  garamond: "'EB Garamond', var(--font-cjk), Georgia, serif",
  caveat: "'Caveat', var(--font-cjk), 'Segoe Script', cursive",
  kalam: "'Kalam', var(--font-cjk), 'Segoe Script', cursive",
  petit: "'Petit Formal Script', var(--font-cjk), 'Snell Roundhand', cursive",
};

/* CJK face — the change that actually matters in 中文 mode */
const CJK_STACK = {
  "宋 · 思源宋体": "'Noto Serif SC', serif",
  "楷 · 霞鹜文楷": "'LXGW WenKai TC', 'Noto Serif SC', serif",
};

function usePersisted(key, init) {
  const [v, setV] = uS(() => {
    try { const s = localStorage.getItem(key); if (s !== null) return JSON.parse(s); } catch (e) {}
    return typeof init === "function" ? init() : init;
  });
  uE(() => { try { localStorage.setItem(key, JSON.stringify(v)); } catch (e) {} }, [key, v]);
  return [v, setV];
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [lang, setLang] = usePersisted("zx-lang", "zh");
  const [route, setRoute] = uS({ view: "home" });
  const [scrolled, setScrolled] = uS(false);
  const pendingAnchor = uRef(null);
  const ui = SITE.ui[lang];

  /* apply tweak tokens to <html> (dark + stele locked) */
  uE(() => {
    const r = document.documentElement;
    r.setAttribute("data-theme", "dark");
    r.setAttribute("data-dir", "stele");
    r.setAttribute("data-motif", t.motif);
    const a = ACCENT[t.accent] || ACCENT["青花 · Cobalt"];
    r.style.setProperty("--accent-h", a.h);
    r.style.setProperty("--accent-c", a.c);
    r.style.setProperty("--font-serif", SERIF_STACK[t.serif] || SERIF_STACK.source);
    r.setAttribute("data-serif", /^(caveat|kalam|petit)$/.test(t.serif) ? "script" : "book");
    r.style.setProperty("--font-cjk", CJK_STACK[t.cjkfont] || CJK_STACK["宋 · 思源宋体"]);
  }, [t.motif, t.accent, t.serif, t.cjkfont]);

  /* scroll state: brand reveal + section reveal (single proven listener) */
  uE(() => {
    const onScroll = () => {
      setScrolled(window.scrollY > window.innerHeight * 0.5);
      const vh = window.innerHeight || 800;
      document.querySelectorAll(".rise:not(.in)").forEach((e) => {
        if (e.getBoundingClientRect().top < vh * 0.92) e.classList.add("in");
      });
    };
    onScroll();
    requestAnimationFrame(onScroll);
    const tm = setTimeout(onScroll, 400);
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      clearTimeout(tm);
    };
  }, [route.view, lang]);

  /* navigation with optional anchor */
  const go = uCb((next, anchor) => {
    pendingAnchor.current = anchor || null;
    setRoute(next);
    if (next.view !== "home" || !anchor) window.scrollTo({ top: 0, behavior: "auto" });
  }, []);

  uE(() => {
    if (route.view === "home" && pendingAnchor.current) {
      const id = pendingAnchor.current; pendingAnchor.current = null;
      requestAnimationFrame(() => {
        const el = document.getElementById(id);
        if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 80, behavior: "smooth" });
      });
    }
  }, [route]);

  const brandShown = scrolled || route.view !== "home";

  return (
    <React.Fragment>
      <Cosmos motif={t.motif} />

      {/* top bar */}
      <div className="topbar">
        <a className={"brand " + (brandShown ? "show" : "")} onClick={() => go({ view: "home" })} role="button" tabIndex="0"
           onKeyDown={(e) => e.key === "Enter" && go({ view: "home" })}>
          <img className="brand__mark" src="assets/emblem.png" alt="" />
          <span className="brand__name">
            <span style={{ fontFamily: "var(--font-cjk)", fontWeight: 600 }}>{SITE.identity.cjk}</span>
            <small>{SITE.identity.romaji}</small>
          </span>
        </a>
        <div className="controls">
          <LangToggle lang={lang} onToggle={() => setLang(lang === "zh" ? "en" : "zh")} t={ui} />
        </div>
      </div>

      {/* routed views */}
      <main id="root-main">
        {route.view === "home" && (
          <div className="home" key={"home-" + lang}>
            <Hero lang={lang} ui={ui} identity={SITE.identity} />
            <div className="stack" style={{ paddingTop: "var(--s9)", paddingBottom: "var(--s8)" }}>
              <About lang={lang} />
              <Writing lang={lang} go={go} />
              <Lab lang={lang} go={go} />
            </div>
            <Footer lang={lang} />
          </div>
        )}
        {route.view === "reader" && <Reader id={route.id} lang={lang} go={go} />}
        {route.view === "module" && <ModuleView id={route.id} lang={lang} go={go} accentKey={t.accent} />}
      </main>

      {/* Tweaks */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="字体 · Type" />
        <TweakRadio label="中文字形" value={t.cjkfont} options={Object.keys(CJK_STACK)}
          onChange={(v) => setTweak("cjkfont", v)} />
        <TweakSelect label="西文衬线" value={t.serif}
          options={[
            { value: "source", label: "Source Serif — 当代 · 锋利" },
            { value: "spectral", label: "Spectral — 沉稳 · 书卷" },
            { value: "newsreader", label: "Newsreader — 文气 · 墨润" },
            { value: "playfair", label: "Playfair — 戏剧 · 张力" },
            { value: "cormorant", label: "Cormorant — 纤细 · 高对比" },
            { value: "garamond", label: "EB Garamond — 古典 · 温润" },
            { value: "caveat", label: "Caveat — 手记 · 工整" },
            { value: "kalam", label: "Kalam — 钢笔 · 端正" },
            { value: "petit", label: "Petit Formal — 花体 · 端丽" },
          ]}
          onChange={(v) => setTweak("serif", v)} />
        <TweakSection label="色彩 · Accent" />
        <TweakRadio label="主色（光晕与点缀）" value={t.accent} options={Object.keys(ACCENT)}
          onChange={(v) => setTweak("accent", v)} />
        <TweakSection label="星空 · Sky" />
        <TweakRadio label="夜空层次" value={t.motif} options={["off", "calm", "lush"]}
          onChange={(v) => setTweak("motif", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

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