// LoreMaster landing — main app const { useState, useEffect, useRef, useMemo } = React; // ── Inline icons for features / footer ────────────────────── const Icon = { Network: () => , Import: () => , Sparkle: () => , Chat: () => , Mail: () => , Arrow: () => , Check: () => }; const FEATURE_ICONS = [Icon.Network, Icon.Import, Icon.Sparkle, Icon.Chat]; // ── Hook: scroll parallax ───────────────────────────────── function useScroll() { const [y, setY] = useState(0); useEffect(() => { let raf = null; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { setY(window.scrollY); raf = null; }); }; window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); return () => window.removeEventListener("scroll", onScroll); }, []); return y; } // ── NAV ─────────────────────────────────────────────────── function Nav({ lang, setLang, copy }) { return (
{/* App-Icon from Figma /components/AppIcon — exact paths with proper transforms & gradients */} {/* Bow (Vector-2.svg) — darker gradient, behind */} {/* Feathered arrow (Vector.svg) — lighter gradient, on top */} LoreMaster
{copy.nav.cta}
); } // ── HERO ────────────────────────────────────────────────── function Hero({ lang, copy, scrollY, parallaxK }) { const p = parallaxK; const castleY = Math.min(scrollY * 0.4 * p, 400); const orbY1 = scrollY * 0.2 * p; const orbY2 = scrollY * 0.35 * p; const copyY = scrollY * -0.05 * p; const stageOffset = scrollY * 0.07 * p; return (
{copy.hero.badge}

{copy.hero.titleA} {copy.hero.titleB}

{copy.hero.sub}

{copy.hero.ctaPrimary} {copy.hero.ctaSecondary}
{copy.hero.proof}
{copy.hero.scroll}
); } // ── FEATURES ────────────────────────────────────────────── function Features({ copy }) { return (
{copy.features.eyebrow}

{copy.features.titleA}{" "} {copy.features.titleB}

{copy.features.sub}

{copy.features.cards.map((c, i) => { const I = FEATURE_ICONS[i]; return (

{c.t}

{c.d}

); })}
); } // ── SHOWCASE — sticky scene panel (slide content only, no iPhone), driven by scroll function ShowcasePanel({ step, copy }) { if (step === 0) { const w = copy.iphone.worlds; return (
{w[0].name} NEW
{w[0].desc}
{w[0].tags.map(([e, t], j) => ( {e}{t} ))}
{w[1].name}
{w[1].desc}
{w[1].tags.map(([e, t], j) => ( {e}{t} ))}
); } if (step === 1) { const r = copy.iphone.region; return (
{r.name}
{r.kind}
{r.labels.government}{r.government.icon}{r.government.value}
{r.labels.population}{r.population.icon}{r.population.value}
{r.labels.capital}{r.capital.icon}{r.capital.value}
{r.bubble}
); } if (step === 2) { const a = copy.iphone.ai; return (
{a.userMsg}
{a.leadPre}{a.leadChip}
{a.ready}
{a.diff.map((d, i) => (
{d.k}: {d.v}
))}
{a.composing}
); } const r = copy.iphone.rp; return (
📜 {r.quest.label} {r.quest.title}
{r.dialogue}
); } // ── SHOWCASE — compact 4-up horizontal step cards function Showcase({ copy }) { const ref = useRef(null); useEffect(() => { if (!ref.current) return; const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) e.target.classList.add("in"); }); }, { threshold: 0.2, rootMargin: "0px 0px -10% 0px" }); ref.current.querySelectorAll(".sc-step").forEach(el => io.observe(el)); return () => io.disconnect(); }, []); return (
{copy.showcase.eyebrow}

{copy.showcase.titleA} {copy.showcase.titleEm}

{copy.showcase.steps.map((s, i) => (
{s.n}

{s.t}

{s.d}

))}
); } // ── HOW IT WORKS ────────────────────────────────────────── function HowItWorks({ copy }) { return (
{copy.how.eyebrow}

{copy.how.titleA} {copy.how.titleEm}

{copy.how.steps.map((s, i) =>
{["01", "02", "03"][i]}

{s.t}

{s.d}

)}
); } // ── WAITLIST FORM ───────────────────────────────────────── function Waitlist({ copy }) { const [submitted, setSubmitted] = useState(false); const L = copy.waitlist.labels; function handleSubmit(e) { e.preventDefault(); setSubmitted(true); } return (
{copy.waitlist.eyebrow}

{copy.waitlist.titleA} {copy.waitlist.titleEm}

{copy.waitlist.sub}

{copy.waitlist.statsLabel.map((l, i) =>
{copy.waitlist.statsValue[i]}
{l}
)}
{!submitted ?
:
{copy.waitlist.success.title}
{copy.waitlist.success.body}
}
); } // ── NEWSLETTER STRIP ────────────────────────────────────── function Newsletter({ copy }) { const [done, setDone] = useState(false); return (
{copy.newsletter.label}

{copy.newsletter.body}

{!done ?
{e.preventDefault();setDone(true);}}>
:
{copy.waitlist.success.title}
}
); } // ── FAQ ─────────────────────────────────────────────────── function FAQ({ copy }) { return (
{copy.faq.items.map((it, i) =>
{it.q}
{it.a}
)}
); } // ── FOOTER ──────────────────────────────────────────────── function Footer({ copy }) { return ( ); } // ── Tweaks defaults ─────────────────────────────────────── const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "parallax": 100 } /*EDITMODE-END*/; // ── ROOT ────────────────────────────────────────────────── function App() { const [lang, setLang] = useState(() => { const saved = localStorage.getItem("lm.lang"); if (saved) return saved; return (navigator.language || "").toLowerCase().startsWith("ru") ? "ru" : "en"; }); useEffect(() => {localStorage.setItem("lm.lang", lang);}, [lang]); useEffect(() => {document.documentElement.lang = lang;}, [lang]); const copy = window.COPY[lang]; const scrollY = useScroll(); const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS); const parallaxK = tweaks.parallax / 100; return ( <>