/* Posy — marketing site */

const { useState, useEffect, useRef, useMemo } = React;

/* ————— Scroll reveal hook ————— */
function useReveal() {
  useEffect(() => {
    const revealed = new WeakSet();
    const markIn = (el) => {
      revealed.add(el);
      if (!el.classList.contains("in")) el.classList.add("in");
    };
    const els = document.querySelectorAll(".reveal");
    if (!("IntersectionObserver" in window)) {
      els.forEach(markIn);
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          markIn(e.target);
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.14, rootMargin: "0px 0px -8% 0px" });
    els.forEach(el => io.observe(el));

    // Re-assert .in if React re-renders strip it from an already-revealed element
    const mo = new MutationObserver((muts) => {
      for (const m of muts) {
        if (m.type === "attributes" && m.attributeName === "class") {
          const el = m.target;
          if (revealed.has(el) && !el.classList.contains("in")) {
            el.classList.add("in");
          }
        }
      }
    });
    els.forEach(el => mo.observe(el, { attributes: true, attributeFilter: ["class"] }));

    return () => { io.disconnect(); mo.disconnect(); };
  }, []);
}

/* ————— Words: splits text into staggered span children for word-by-word reveal ————— */
function Words({ children, className = "", tag: Tag = "span" }) {
  const words = typeof children === "string" ? children.split(/(\s+)/) : [children];
  return (
    <Tag className={"words reveal " + className}>
      {words.map((w, i) =>
        /^\s+$/.test(w) ? w : <span className="word" style={{ "--i": i }} key={i}>{w}</span>
      )}
    </Tag>
  );
}

/* ————— Scroll progress + parallax ————— */
function useScrollFx() {
  useEffect(() => {
    if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const bar = document.querySelector(".scroll-progress");
    const hv = document.querySelector(".hero-visual");
    const enso = document.querySelector(".hero .enso-wrap");
    const hanko = document.querySelector(".hanko-floating");
    const waitlistEnso = document.querySelector(".waitlist-enso");
    const tiltEls = Array.from(document.querySelectorAll(".scroll-tilt, .scroll-drift-x, .scroll-rotate"));
    let raf = 0;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        raf = 0;
        const y = window.scrollY;
        const vh = window.innerHeight;
        const max = Math.max(1, document.documentElement.scrollHeight - vh);
        const pct = Math.min(100, (y / max) * 100);
        if (bar) bar.style.width = pct + "%";
        if (hv) hv.style.transform = `translateY(${y * -0.08}px)`;
        if (enso) enso.style.transform = `translateY(${y * 0.14}px) rotate(${y * 0.02}deg)`;
        if (hanko) hanko.style.transform = `translateY(${y * -0.04}px) rotate(${Math.sin(y * 0.002) * 6}deg)`;
        if (waitlistEnso) {
          const r = waitlistEnso.getBoundingClientRect();
          const sy = 1 - Math.max(0, Math.min(1, (r.top + r.height / 2) / vh));
          waitlistEnso.style.setProperty("--sy", sy.toFixed(3));
        }
        for (const el of tiltEls) {
          const r = el.getBoundingClientRect();
          // 0 when element center is at bottom of viewport, 1 at top
          const center = r.top + r.height / 2;
          const sy = 1 - Math.max(0, Math.min(1, center / vh));
          el.style.setProperty("--sy", sy.toFixed(3));
        }
      });
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, []);
}

/* Shared data */
const BEDS = [
  { id: "thesis",  name: "Thesis · Biomimicry", sigil: 0, count: 14, updated: "2d" },
  { id: "q2",      name: "Q2 Product Brief",    sigil: 2, count: 9,  updated: "4h" },
  { id: "kyoto",   name: "Kyoto Journals",      sigil: 1, count: 23, updated: "today" },
  { id: "kitchen", name: "Kitchen Notebook",    sigil: 5, count: 31, updated: "1w" },
];

const SOURCES_BY_BED = {
  thesis: [
    { id: "s1", kind: "PDF", name: "Benyus — Biomimicry.pdf", meta: "287 pp · annotated", used: true },
    { id: "s2", kind: "PDF", name: "Gecko adhesion nanostructure.pdf", meta: "42 pp", used: true },
    { id: "s3", kind: "IMG", name: "lotus-leaf-sem.png", meta: "scan · 4.2mb", used: true },
    { id: "s4", kind: "DOC", name: "advisor notes — March.docx", meta: "6 pp", used: true },
    { id: "s6", kind: "URL", name: "nature.com/s41586-022-04833", meta: "article", used: true },
    { id: "s7", kind: "TXT", name: "research — outline.md", meta: "1.2kb", used: false },
  ],
  q2: [
    { id: "q1", kind: "DOC", name: "Kickoff brief.docx", meta: "6 pp", used: true },
    { id: "q2", kind: "PDF", name: "User research — April.pdf", meta: "33 pp", used: true },
    { id: "q3", kind: "IMG", name: "whiteboard-retro.jpg", meta: "photo", used: false },
    { id: "q4", kind: "URL", name: "competitor teardown", meta: "notion", used: true },
  ],
  kyoto: [
    { id: "k1", kind: "IMG", name: "ryoanji-morning.jpg", meta: "photo · 8.1mb", used: true },
    { id: "k2", kind: "IMG", name: "kodaiji-bamboo.jpg", meta: "photo · 6.4mb", used: true },
    { id: "k3", kind: "AUD", name: "voice memo — tea ceremony.m4a", meta: "04:12", used: true },
    { id: "k4", kind: "TXT", name: "journal — day 3.md", meta: "2.1kb", used: true },
    { id: "k5", kind: "HW", name: "travel notebook scan.pdf", meta: "14 pp", used: false },
  ],
  kitchen: [
    { id: "kt1", kind: "IMG", name: "sourdough-crumb.jpg", meta: "photo", used: true },
    { id: "kt2", kind: "TXT", name: "tartine formula.md", meta: "0.8kb", used: true },
    { id: "kt3", kind: "VID", name: "lamination demo.mp4", meta: "02:44", used: false },
  ],
};

const NOTES = {
  thesis: {
    kicker: "Synthesis · 12 seedlings",
    titleHtml: 'Surfaces that <em>refuse</em> to hold the rain.',
    deck: "A working synthesis on hydrophobic microstructure, from the lotus leaf to silicon.",
    meta: [["Greenhouse","Thesis · Biomimicry"], ["Revised","two hours ago"], ["Seedlings","8 cited"], ["Words","2,418"]],
    body: [
      { num: "I.", h: "The lotus effect, in four lines", p: <>Wilhelm Barthlott's 1977 micrographs of <em>Nelumbo nucifera</em> showed that the leaf's surface is not smooth but densely covered in wax papillae between 10 and 20 microns across <span className="chip">S1 · Benyus</span>. Water, landing, contacts only the peaks of these bumps — and so rolls, carrying dust with it.</> },
    ]
  },
  q2: {
    kicker: "Brief · 4 seedlings",
    titleHtml: 'The <em>quiet</em> quarter, condensed.',
    deck: "Three product bets, eleven risks, and the question the offsite kept returning to.",
    meta: [["Greenhouse","Q2 Product Brief"], ["Revised","four hours ago"], ["Seedlings","3 cited"], ["Words","1,104"]],
    body: [
      { num: "I.", h: "Three bets, in order of conviction", p: <>The April research cohort surfaced a consistent pattern: power users do not want more features, they want fewer interruptions <span className="chip">Q2 · research</span>. The three proposed bets bend toward quiet.</> },
    ]
  },
  kyoto: {
    kicker: "Journal · 5 seedlings",
    titleHtml: 'The week of <em>standing still</em>.',
    deck: "Seven days in Kyoto, compiled from photos, voice memos, and a travel notebook.",
    meta: [["Greenhouse","Kyoto Journals"], ["Revised","this morning"], ["Seedlings","5 cited"], ["Words","2,016"]],
    body: [
      { num: "I.", h: "Ryōan-ji, before the buses", p: <>Six-forty in the morning, the gravel still damp from the night watering. The voice memo is almost entirely silence with one bamboo pipe <span className="chip">K3 · memo</span>. Fifteen stones; fourteen visible from any one spot.</> },
    ]
  },
  kitchen: {
    kicker: "Recipe journal · 3 seedlings",
    titleHtml: 'A <em>slow</em> loaf, documented.',
    deck: "Three bakes, one formula, and what the crumb is telling me about hydration.",
    meta: [["Greenhouse","Kitchen Notebook"], ["Revised","last week"], ["Seedlings","2 cited"], ["Words","612"]],
    body: [
      { num: "I.", h: "The formula, as written", p: <>Eighty percent hydration country loaf, twelve percent whole wheat, four percent levain, two percent salt <span className="chip">KT2 · formula</span>. Sunday's crumb was open but gummy at center.</> },
    ]
  },
};

const SEASONS = [
  { id: "spring", swatch: "#e2c7c8", jp: "haru · 春", name: "Spring",  tagline: "When the plum blossoms, and everything thinks about beginning again.", pigment: "pigment · shell pink" },
  { id: "summer", swatch: "#a6b58a", jp: "natsu · 夏", name: "Summer", tagline: "Moss green, dragonflies, the paper goes warm in the afternoon light.",   pigment: "pigment · moss green" },
  { id: "autumn", swatch: "#c9a167", jp: "aki · 秋",  name: "Autumn", tagline: "Persimmon dye, a maple leaf drifting slantwise across the page.",         pigment: "pigment · persimmon" },
  { id: "winter", swatch: "#b6c1c3", jp: "fuyu · 冬", name: "Winter", tagline: "The garden goes quiet; the paper, pale as frost on the window.",        pigment: "pigment · stone blue" },
];

/* ————— Small icon set ————— */
const NavIco = ({ name }) => {
  const paths = {
    greenhouse: "M3 10 L12 3 L21 10 L21 21 L3 21 Z M3 10 L21 10 M12 3 L12 21",
    beds: "M3 6 H21 M3 12 H21 M3 18 H21",
    seedling: "M12 20 V11 M12 11 C12 7 8 6 6 8 C8 8 12 9 12 13 M12 11 C12 8 16 7 18 9 C16 9 12 10 12 13",
    almanac: "M5 3 H17 L19 5 V21 H5 Z M9 3 V21 M5 8 H19",
    compost: "M4 7 H20 L18 21 H6 Z M9 11 V17 M15 11 V17 M9 3 H15 V7",
    search: "M10 10 m -6 0 a 6 6 0 1 0 12 0 a 6 6 0 1 0 -12 0 M14.5 14.5 L20 20",
    sparkle: "M12 3 L13.5 10 L21 12 L13.5 14 L12 21 L10.5 14 L3 12 L10.5 10 Z",
  };
  return (
    <svg className="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round">
      <path d={paths[name]} />
    </svg>
  );
};

/* ————— NAV ————— */
function Nav({ season, setSeason }) {
  return (
    <nav className="nav">
      <div className="nav-brand">
        <span className="bm"><BrandMark size={26} /></span>
        <span className="name">Posy</span>
      </div>
      <div className="nav-links">
        <a className="nav-link" href="#grammar">The grammar</a>
        <a className="nav-link" href="#tending">Tending</a>
        <a className="nav-link" href="#demo">Live</a>
        <a className="nav-link" href="#seasons">Seasons</a>
        <a className="nav-link" href="#waitlist">Beta</a>
      </div>
      <div className="nav-season-mini">
        <span className="lbl">{season}</span>
        <span className="dots">
          {SEASONS.map(s => (
            <button key={s.id}
              className={season === s.id ? "on" : ""}
              style={{ "--swatch": s.swatch, background: s.swatch }}
              onClick={() => setSeason(s.id)}
              title={s.name}
              aria-label={"Switch to " + s.name} />
          ))}
        </span>
      </div>
      <a href="#waitlist" className="nav-cta">Join beta</a>
    </nav>
  );
}

/* ————— HERO ————— */
function Hero() {
  return (
    <section className="hero">
      <div className="hero-eyebrow reveal">
        <span className="dot" />
        a zen digital garden
      </div>
      <div className="hero-grid">
        <div>
          <h1 className="reveal reveal-rise">
            Research,<br />
            <em>tended</em> like<br />
            a garden.
          </h1>
          <p className="hero-deck reveal" style={{ "--rd": "220ms" }}>
            Posy is a quiet place to keep your reading, your clippings, and the slow kind of
            writing that happens when you are listening. Every source is a seedling.
            Every working draft, a bed. Every season, a mood the paper remembers.
          </p>
          <div className="hero-cta-row reveal" style={{ "--rd": "420ms" }}>
            <a href="#waitlist" className="primary-btn">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
                <path d="M12 3 L13.5 10 L21 12 L13.5 14 L12 21 L10.5 14 L3 12 L10.5 10 Z" />
              </svg>
              Join the beta
            </a>
            <div className="hero-meta">
              <span>v0.1 · beta</span>
            </div>
          </div>
        </div>
        <div className="hero-visual reveal reveal-right" style={{ "--rd": "180ms" }}>
          <div className="enso-wrap"><Enso size={480} opacity={0.22} /></div>
          <div className="hanko-floating"><Hanko size={66} /></div>
          <WatercolorBlob top={120} right={-40} size={280} color="var(--sansui-2)" dur={22} delay={1} opacity={0.9} />
          <WatercolorBlob top={360} right={140} size={180} color="var(--sansui-1)" dur={18} delay={3} opacity={0.6} />
          <div className="scroll-tilt">
            <article className="specimen bloom-in">
              <div className="specimen-label">
                <span>keepsake · no. 012</span>
              <span className="r">today</span>
            </div>
            <h3>Surfaces that <em>refuse</em> to hold the rain.</h3>
            <p>
              Wilhelm Barthlott's 1977 micrographs of <em>Nelumbo nucifera</em> showed that
              the leaf's surface is not smooth but densely covered in wax papillae between
              ten and twenty microns across.
            </p>
            <p>
              Water, landing, contacts only the peaks — and so rolls, carrying dust with it.
              The phenomenon is mechanical, not chemical.
            </p>
            <div className="specimen-chips">
              <span className="specimen-chip">S1 · Benyus</span>
              <span className="specimen-chip">S3 · SEM scan</span>
              <span className="specimen-chip">S6 · Nature</span>
            </div>
            <div className="specimen-sig">
              <span>Thesis · Biomimicry</span>
              <span>bed of 14</span>
            </div>
          </article>
          </div>
        </div>
      </div>
      <div className="scroll-hint"><span className="line" /> scroll · wander</div>
    </section>
  );
}

/* ————— GRAMMAR ————— */
function Grammar() {
  const cells = [
    { sigil: 3, ko: "01 · beds", title: <>A <em>bed</em> is where the thinking lives.</>, body: "A canvas, long-form, where drafts grow. Every bed has its own season, its own seedlings, its own weight of thought. Open twenty; tend two.", foot: "panel · centre" },
    { sigil: 5, ko: "02 · seedlings", title: <>Every source is a <em>seedling</em>.</>, body: "Drop a PDF, a photograph, a voice memo, a handwritten scan. Mark the ones you've cited. Archive the rest. Nothing is lost; things are only still.", foot: "panel · right" },
    { sigil: 1, ko: "03 · seasons", title: <>The paper <em>remembers</em> the season.</>, body: "Spring plum. Summer moss. Persimmon autumn. Winter stone. Four palettes that change the way the light falls on your work, and the mood of your reading.", foot: "ambient · always" },
  ];
  return (
    <section className="section" id="grammar">
      <div className="section-head">
        <div className="section-mark reveal"><span className="num">一</span>the grammar</div>
        <div>
          <h2 className="reveal reveal-rise">Three words, a <em>whole</em> vocabulary.</h2>
          <p className="section-lede reveal" style={{ "--rd": "200ms" }}>
            Posy borrows its language from the garden. Beds. Seedlings. Seasons.
            Small words that do big quiet work.
          </p>
        </div>
      </div>
      <div className="grammar-grid">
        {cells.map((c, i) => (
          <div className="grammar-cell reveal reveal-tilt" key={i} style={{ "--rd": (i * 160) + "ms" }}>
            <div className="grammar-sigil"><BedSigil variant={c.sigil} size={58} /></div>
            <div className="ko">{c.ko}</div>
            <h3>{c.title}</h3>
            <p>{c.body}</p>
            <div className="footnote">{c.foot}</div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ————— TENDING (watering) ————— */
function Tending() {
  const [stage, setStage] = useState(1); // 0..3: alive, wilting, dormant, revived

  // Auto-advance on first reveal, then freeze so user can scrub.
  const secRef = useRef(null);
  const autoRef = useRef(false);
  useEffect(() => {
    if (!("IntersectionObserver" in window)) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting && !autoRef.current) {
          autoRef.current = true;
          let i = 0;
          const id = setInterval(() => {
            i += 1;
            if (i > 3) { clearInterval(id); return; }
            setStage(i);
          }, 1400);
        }
      });
    }, { threshold: 0.5 });
    if (secRef.current) io.observe(secRef.current);
    return () => io.disconnect();
  }, []);

  const stages = [
    { key: "alive",    ko: "01 · alive",    label: "Tended",   day: "day 2",    note: "Read twice this week. One new seedling planted.", vitality: 1.0 },
    { key: "wilting",  ko: "02 · wilting",  label: "Wilting",  day: "day 12",   note: "Untouched for a week. The ink remembers.",         vitality: 0.62 },
    { key: "dormant",  ko: "03 · dormant",  label: "Dormant",  day: "day 29",   note: "Four weeks quiet. The bed rests, read-only.",      vitality: 0.22 },
    { key: "revived",  ko: "04 · watered",  label: "Watered",  day: "today",    note: "One line of reflection. The paper warms again.",    vitality: 1.0 },
  ];
  const s = stages[stage];

  return (
    <section className="section tending-section" ref={secRef} id="tending">
      <div className="section-head">
        <div className="section-mark reveal"><span className="num">二</span>what tending looks like</div>
        <div>
          <h2 className="reveal reveal-rise">A <em>garden</em>, not a graveyard.</h2>
          <p className="section-lede reveal" style={{ "--rd": "200ms" }}>
            Most notes apps optimize for frictionless capture — write forever, store
            forever, revisit nothing. Posy asks something smaller and stranger of you:
            come back. A bed that's tended stays alive. A bed that isn't quietly wilts,
            then rests. Watering is how you bring it back — one line about what the bed
            still means to you. What you keep, you keep on purpose.
          </p>
        </div>
      </div>

      <div className="tending-stage reveal reveal-scale" style={{ "--rd": "260ms" }} data-stage={s.key}>
        <div className="tending-scene">
          <WatercolorBlob top={-30} left={-30} size={260} color="var(--sansui-2)" dur={24} opacity={0.55 * s.vitality} />
          <WatercolorBlob top={"auto"} bottom={-40} right={-20} size={220} color="var(--sansui-1)" dur={28} opacity={0.42 * s.vitality} />

          <div className="tending-card">
            <div className="tending-card-head">
              <div className="tending-card-sigil" style={{ opacity: 0.55 + 0.45 * s.vitality }}>
                <BedSigil variant={3} size={44} />
              </div>
              <div className="tending-card-meta">
                <div className="ko">greenhouse · thesis</div>
                <div className="name">Biomimicry, <em>slowly</em></div>
              </div>
              <div className={"tending-chip chip-" + s.key}>
                <span className="chip-dot" />
                {s.label}
              </div>
            </div>

            <div className="tending-card-body" style={{ opacity: 0.55 + 0.45 * s.vitality }}>
              <p className={"tending-prose " + s.key}>
                {s.key === "revived"
                  ? <>Re-read Benyus on the plane home. The <em>idea</em> of mimicry is not about copying; it is about listening for the shape a problem has already solved.</>
                  : <>The <em>idea</em> of biomimicry, as I'm starting to understand it, is less a method than a manner of attention — a way of letting what already works in a forest or a shell become the shape of the question.</>
                }
              </p>
              <div className="tending-seedlings">
                {[0,1,2,3,4].map(i => (
                  <span key={i} className="seedling-pip" style={{ opacity: 0.25 + 0.75 * s.vitality, transform: `translateY(${(1 - s.vitality) * 4}px)` }} />
                ))}
              </div>
            </div>

            <div className="tending-card-foot">
              <span className="ko">{s.day}</span>
              <span className="dot-sep">·</span>
              <span className="tending-note">{s.note}</span>
            </div>

            {s.key === "dormant" && (
              <div className="tending-overlay">
                <div className="overlay-rule" />
                <div className="overlay-text">
                  <span className="jp">眠 · nemuri</span>
                  <span>resting · read only</span>
                </div>
                <div className="overlay-rule" />
              </div>
            )}
            {s.key === "revived" && (
              <div className="tending-droplet" aria-hidden>
                <svg viewBox="0 0 24 32" width="22" height="30">
                  <path d="M12 2 C5 14, 3 20, 12 30 C21 20, 19 14, 12 2 Z" fill="var(--accent)" opacity="0.72"/>
                </svg>
              </div>
            )}
          </div>
        </div>

        <aside className="tending-side">
          <div className="tending-side-mark">pick a moment</div>
          <div className="tending-timeline">
            {stages.map((st, i) => (
              <button key={st.key}
                className={"tending-step " + (stage === i ? "on" : "") + (i < stage ? " past" : "")}
                onClick={() => setStage(i)}
                aria-label={"Show " + st.label + " state"}>
                <span className="step-ko">{st.ko}</span>
                <span className="step-name">{st.label}</span>
                <span className="step-bar"><span className="step-bar-fill" style={{ width: (st.vitality * 100) + "%" }} /></span>
              </button>
            ))}
          </div>

          <div className="tending-watering">
            <div className="ko">what watering is</div>
            <p>
              One sentence. What the bed still means. Not a summary — a
              re-<em>acquaintance</em>.
            </p>
            <div className="watering-sample">
              <span className="prompt">›</span>
              <span className="typed">still curious about the way lotus leaves stay dry</span>
              <span className="caret" />
            </div>
          </div>
        </aside>
      </div>

      <blockquote className="tending-quote reveal">
        <div className="quote-mark">「</div>
        <p>
          <Words>A garden doesn't reward effort. It rewards</Words> <em>return.</em>
        </p>
        <div className="quote-mark right">」</div>
      </blockquote>
    </section>
  );
}

/* ————— LIVE DEMO ————— */
function Demo({ season }) {
  const [bedId, setBedId] = useState("thesis");
  const [sources, setSources] = useState(SOURCES_BY_BED);
  const toggle = (sid) => setSources(prev => ({
    ...prev,
    [bedId]: prev[bedId].map(s => s.id === sid ? { ...s, used: !s.used } : s)
  }));

  const note = NOTES[bedId];
  const bedSources = sources[bedId] || [];
  const usedCount = bedSources.filter(s => s.used).length;
  const bed = BEDS.find(b => b.id === bedId);

  return (
    <section className="section" id="demo">
      <div className="section-head">
        <div className="section-mark reveal"><span className="num">三</span>the living demo</div>
        <div>
          <h2 className="reveal reveal-rise">A piece of the <em>greenhouse</em>, here on the page.</h2>
          <p className="section-lede reveal" style={{ "--rd": "200ms" }}>
            Click a bed. Toggle a seedling. Watch the seal settle. The demo below is
            the real app, running in miniature — the same components, the same season
            you've chosen above.
          </p>
        </div>
      </div>

      <div className="demo-wrap reveal reveal-rise" style={{ "--rd": "260ms" }}>
        <div className="demo-chrome">
          <span className="demo-dot r" />
          <span className="demo-dot y" />
          <span className="demo-dot g" />
          <span className="demo-chrome-title">posy · beds / {bed?.name.toLowerCase()} / tend</span>
        </div>

        <div className="demo-app">
          <aside className="demo-col demo-sidebar">
            <div className="brand-row">
              <BrandMark size={20} />
              <span className="name">Posy</span>
            </div>
            <div className="nav-label">Garden</div>
            {[
              { i: "greenhouse", l: "Greenhouse", on: true },
              { i: "beds", l: "Beds" },
              { i: "seedling", l: "Seedlings" },
              { i: "almanac", l: "Almanac" },
            ].map(n => (
              <div key={n.i} className={"demo-nav-item " + (n.on ? "on" : "")}>
                <NavIco name={n.i} />
                <span>{n.l}</span>
                <span className="dot" />
              </div>
            ))}
            <div className="nav-label">Library</div>
            <div className="demo-nav-item"><NavIco name="search" /><span>Search</span></div>
            <div className="demo-nav-item"><NavIco name="compost" /><span>Compost</span></div>
          </aside>

          <aside className="demo-col demo-beds">
            <div className="panel-title">Your <em>greenhouses</em></div>
            <div className="panel-sub">four tended</div>
            {BEDS.map(b => (
              <div key={b.id}
                   className={"demo-bed " + (bedId === b.id ? "on" : "")}
                   onClick={() => setBedId(b.id)}>
                <BedSigil variant={b.sigil} size={28} />
                <div className="demo-bed-meta">
                  <div className="name">{b.name}</div>
                  <div className="stats">{b.count} seedlings · {b.updated}</div>
                </div>
              </div>
            ))}
          </aside>

          <main className="demo-col demo-canvas" key={bedId}>
            <WatercolorBlob top={-20} left={-20} size={220} color="var(--sansui-2)" dur={22} opacity={0.7} />
            <div className="demo-kicker bloom-in"><span className="dot" /> {note.kicker}</div>
            <h2 className="demo-title bloom-in" dangerouslySetInnerHTML={{ __html: note.titleHtml }} />
            <p className="demo-deck bloom-in">{note.deck}</p>
            <div className="demo-meta bloom-in">
              {note.meta.map(([l, v], i) => (
                <div key={i}>
                  <span className="label">{l}</span>
                  <span className="value">{v}</span>
                </div>
              ))}
            </div>
            <div className="demo-body">
              {note.body.map((b, i) => (
                <div key={i} className="bloom-in" style={{ animationDelay: (200 + i * 120) + "ms" }}>
                  <h4><span className="num">{b.num}</span> {b.h}</h4>
                  <p>{b.p}</p>
                </div>
              ))}
            </div>
            <div className="demo-footer">
              <svg className="spark" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round">
                <path d="M12 3 L13.5 10 L21 12 L13.5 14 L12 21 L10.5 14 L3 12 L10.5 10 Z" />
              </svg>
              Ask the garden — weave these sources into a draft…
              <button className="cult">cultivate</button>
            </div>
          </main>

          <aside className="demo-col demo-sources">
            <div className="panel-title">Seedlings</div>
            <div className="panel-sub">{usedCount} planted · {bedSources.length - usedCount} waiting</div>
            {bedSources.map(s => (
              <div key={s.id} className={"demo-source " + (s.used ? "used" : "")} onClick={() => toggle(s.id)}>
                <span className="k">{s.kind}</span>
                <div className="demo-source-body">
                  <div className="n">{s.name}</div>
                  <div className="m">
                    <span>{s.meta}</span>{s.used && <span className="cited"> · cited</span>}
                  </div>
                </div>
                <span className="dot" />
              </div>
            ))}
          </aside>
        </div>
      </div>

      <div className="demo-caption">
        <div><em>Click</em> a greenhouse to change beds.</div>
        <div><em>Tap</em> a seedling to mark it cited.</div>
        <div>The season above colors all of it.</div>
      </div>
    </section>
  );
}

/* ————— SEASONS ————— */
function SeasonsSection({ season, setSeason }) {
  return (
    <section className="section seasons-section" id="seasons">
      <div className="section-head">
        <div className="section-mark reveal"><span className="num">四</span>four seasons, four moods</div>
        <div>
          <h2 className="reveal reveal-rise">The same garden, <em>lit</em> differently.</h2>
          <p className="section-lede reveal" style={{ "--rd": "200ms" }}>
            Pick the palette that matches the way you're working. Posy shifts its paper,
            its ink, its ambient drift, and the hue of its vermilion seal to suit.
          </p>
        </div>
      </div>
      <div className="season-tiles">
        {SEASONS.map((s, i) => (
          <div key={s.id}
               className={"season-tile " + (season === s.id ? "on" : "")}
               style={{ "--rd": (i * 120) + "ms" }}
               onClick={() => setSeason(s.id)}>
            <div className={"season-tile-swatch ss-" + s.id} />
            <div>
              <div className="jp">{s.jp}</div>
              <div className="name">{s.name}</div>
            </div>
            <p>{s.tagline}</p>
            <div className="pigment">
              <span>{s.pigment}</span>
              <span>{season === s.id ? "· applied" : "· tap"}</span>
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

/* ————— WAITLIST ————— */
function Waitlist() {
  const [email, setEmail] = useState("");
  const [submitted, setSubmitted] = useState(false);
  return (
    <section className="section" id="waitlist">
      <div className="waitlist reveal reveal-rise">
        <div className="waitlist-enso scroll-rotate"><Enso size={420} opacity={0.35} /></div>
        <div className="waitlist-eyebrow">· the beta ·</div>
        <h2>Leave a <em>name</em> by the gate.</h2>
        <p className="waitlist-deck">
          Posy is in quiet beta. Join, and we'll send an invitation
          when there's a plot to tend — no announcements in between,
          no marketing wind through the trees.
        </p>
        {!submitted ? (
          <form className="waitlist-form" onSubmit={(e) => { e.preventDefault(); if (email.trim()) setSubmitted(true); }}>
            <input
              type="email"
              required
              placeholder="you@somewhere.quiet"
              value={email}
              onChange={(e) => setEmail(e.target.value)} />
            <button type="submit">sow</button>
          </form>
        ) : (
          <div className="waitlist-thanks">
            <Hanko size={36} />
            Sealed. Your seedling is in the soil — we'll write when it stirs.
          </div>
        )}
        <div className="waitlist-meta">
          <span>unsubscribe anytime</span>
        </div>
      </div>
    </section>
  );
}

/* ————— FOOTER ————— */
function Footer() {
  return (
    <footer className="site-footer">
      <div className="left">
        <span>Posy · a zen digital garden</span>
      </div>
      <div className="center"><BrandMark size={22} /></div>
      <div className="right">
        <span>2026</span>
      </div>
    </footer>
  );
}

/* ————— TWEAKS ————— */
function TweaksPanel({ open, season, setSeason, motion, setMotion }) {
  return (
    <div className={"tweaks-panel " + (open ? "open" : "")}>
      <h4>Tweaks</h4>
      <div className="sub">tend your garden</div>
      <div className="tweak-row">
        <label>Season</label>
        <div style={{ display: "flex", gap: 4 }}>
          {SEASONS.map(s => (
            <button key={s.id}
                    className={"tweak-btn " + (season === s.id ? "on" : "")}
                    onClick={() => setSeason(s.id)}>{s.name.toLowerCase()}</button>
          ))}
        </div>
      </div>
      <div className="tweak-row">
        <label>Motion · {Math.round(motion * 100)}%</label>
        <input type="range" min="0" max="1" step="0.05"
               value={motion}
               onChange={e => setMotion(parseFloat(e.target.value))} />
      </div>
    </div>
  );
}

/* ————— App ————— */
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "season": "summer",
  "motion": 0.6
}/*EDITMODE-END*/;

function App() {
  const [season, setSeason] = useState(() => localStorage.getItem("posy.site.season") || TWEAK_DEFAULTS.season);
  const [motion, setMotion] = useState(() => {
    const v = localStorage.getItem("posy.site.motion");
    return v == null ? TWEAK_DEFAULTS.motion : parseFloat(v);
  });
  const [tweaksOpen, setTweaksOpen] = useState(false);

  useEffect(() => {
    document.body.dataset.season = season;
    localStorage.setItem("posy.site.season", season);
  }, [season]);
  useEffect(() => { localStorage.setItem("posy.site.motion", String(motion)); }, [motion]);

  useEffect(() => {
    const handler = (e) => {
      if (e.data?.type === "__activate_edit_mode") setTweaksOpen(true);
      if (e.data?.type === "__deactivate_edit_mode") setTweaksOpen(false);
    };
    window.addEventListener("message", handler);
    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
    return () => window.removeEventListener("message", handler);
  }, []);

  useEffect(() => {
    window.parent.postMessage({ type: "__edit_mode_set_keys", edits: { season, motion } }, "*");
  }, [season, motion]);

  useReveal();
  useScrollFx();

  return (
    <>
      <div className="scroll-progress" />
      <Particles season={season} intensity={motion} />
      <div className="site">
        <Nav season={season} setSeason={setSeason} />
        <Hero />
        <Grammar />
        <Tending />
        <Demo season={season} />
        <SeasonsSection season={season} setSeason={setSeason} />
        <Waitlist />
        <Footer />
      </div>
      <TweaksPanel open={tweaksOpen} season={season} setSeason={setSeason} motion={motion} setMotion={setMotion} />
    </>
  );
}

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