// EUMORPH — system primitives: wordmark, redaction, bottles, chrome, footer

const { useState: useS, useEffect: useE, useRef: useR, useMemo: useM, useCallback: useC } = React;

// ---------- ISSUE — single source of truth for issue/volume/date stamps ----------
// Read from across the site so footer, hero running-head, the journal,
// and the membership card never drift apart.
const ISSUE = {
  number: 47,
  volume: "III",
  weekOf: "16 March 2026",     // the Monday the issue ships
  monthLabel: "March 2026",
  yearStart: "spring 2024",    // when Eumorph started publishing
};
window.EUMORPH_ISSUE = ISSUE;

// ---------- WORDMARK ----------
function Wordmark({ size = "nav", showIPA = true }) {
  return (
    <span className={`wordmark wm-${size}`}>
      <span className="wm-mark">eu<span className="wm-dot">·</span>morph</span>
      {showIPA && <span className="wm-ipa">/ˈyoō-môrf/</span>}
    </span>
  );
}

// Stacked logo lockup — wordmark + IPA tucked underneath, always visible
function WordmarkStack({ size = "hero" }) {
  return (
    <span className={`wordmark-stack wms-${size}`}>
      <span className="wms-mark">eu<span className="wms-dot">·</span>morph</span>
      <span className="wms-rule"></span>
      <span className="wms-ipa">/ˈyoō-môrf/  ·  adj.  ·  well-formed</span>
    </span>
  );
}

// ---------- REDACTION (now: just renders brand text) ----------
function Redact({ children }) {
  return <span className="redact-text">{children}</span>;
}

// ---------- BOTTLE SVGs (six recognizable archetypes) ----------
function Bottle({ kind = "cream", color = "#3B2F26", height = 160 }) {
  // SVGs are authored on a 100×160 grid — keep aspect when the caller asks
  // for a different height (otherwise the bottle floats lonely in a tall card)
  const h = height;
  const w = Math.round(height * (100 / 160));
  const cx = 50;
  const bottles = {
    cream: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="32" ry="3" fill="#000" opacity="0.06"/>
        <rect x="22" y="62" width="56" height="84" rx="3" fill={color}/>
        <rect x="22" y="62" width="56" height="84" rx="3" fill="url(#cream-shine)" opacity="0.18"/>
        <rect x="32" y="50" width="36" height="14" rx="2" fill="#1A1A1A" opacity="0.85"/>
        <rect x="34" y="100" width="32" height="22" fill="#F5F1E8" opacity="0.92"/>
        <line x1="38" y1="108" x2="62" y2="108" stroke="#1A1A1A" strokeWidth="0.4"/>
        <line x1="38" y1="113" x2="58" y2="113" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <defs>
          <linearGradient id="cream-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.5" stopColor="#fff" stopOpacity="1"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
    serum: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="28" ry="3" fill="#000" opacity="0.06"/>
        <rect x="28" y="36" width="44" height="14" fill="#0B0B0B"/>
        <rect x="44" y="20" width="12" height="18" fill="#1A1A1A"/>
        <path d="M 28 50 L 28 140 Q 28 152 40 152 L 60 152 Q 72 152 72 140 L 72 50 Z" fill={color}/>
        <path d="M 28 50 L 72 50 L 72 140 Q 72 152 60 152 L 40 152 Q 28 152 28 140 Z" fill="url(#serum-shine)" opacity="0.2"/>
        <rect x="34" y="92" width="32" height="30" fill="#F5F1E8" opacity="0.94"/>
        <line x1="40" y1="100" x2="60" y2="100" stroke="#1A1A1A" strokeWidth="0.5"/>
        <line x1="40" y1="106" x2="56" y2="106" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <defs>
          <linearGradient id="serum-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.4" stopColor="#fff" stopOpacity="0.7"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
    fragrance: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="34" ry="3" fill="#000" opacity="0.06"/>
        <rect x="38" y="14" width="24" height="16" rx="2" fill="#0B0B0B"/>
        <rect x="42" y="30" width="16" height="18" fill={color}/>
        <rect x="20" y="48" width="60" height="100" rx="5" fill={color}/>
        <rect x="20" y="48" width="60" height="100" rx="5" fill="url(#frag-shine)" opacity="0.25"/>
        <rect x="30" y="86" width="40" height="34" fill="#F5F1E8" opacity="0.96"/>
        <text x="50" y="103" textAnchor="middle" fontFamily="Fraunces, serif" fontSize="8" fill="#1A1A1A" fontStyle="italic">No. </text>
        <line x1="34" y1="108" x2="66" y2="108" stroke="#1A1A1A" strokeWidth="0.3"/>
        <line x1="38" y1="114" x2="62" y2="114" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <defs>
          <linearGradient id="frag-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.45" stopColor="#fff" stopOpacity="0.85"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
    tube: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="22" ry="3" fill="#000" opacity="0.06"/>
        <rect x="36" y="14" width="28" height="10" rx="1" fill="#1A1A1A"/>
        <path d="M 30 24 L 70 24 L 76 152 L 24 152 Z" fill={color}/>
        <path d="M 30 24 L 70 24 L 76 152 L 24 152 Z" fill="url(#tube-shine)" opacity="0.2"/>
        <rect x="32" y="60" width="36" height="40" fill="#F5F1E8" opacity="0.95"/>
        <line x1="38" y1="72" x2="62" y2="72" stroke="#1A1A1A" strokeWidth="0.4"/>
        <line x1="38" y1="80" x2="58" y2="80" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <defs>
          <linearGradient id="tube-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.5" stopColor="#fff" stopOpacity="0.7"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
    apothecary: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="32" ry="3" fill="#000" opacity="0.06"/>
        <rect x="34" y="14" width="32" height="20" fill="#0B0B0B"/>
        <rect x="40" y="34" width="20" height="14" fill={color} opacity="0.8"/>
        <rect x="22" y="48" width="56" height="100" fill={color}/>
        <rect x="22" y="48" width="56" height="100" fill="url(#apo-shine)" opacity="0.18"/>
        <rect x="30" y="80" width="40" height="46" fill="#F5F1E8" opacity="0.94"/>
        <text x="50" y="97" textAnchor="middle" fontFamily="Fraunces, serif" fontSize="9" fill="#1A1A1A" fontStyle="italic">P50</text>
        <line x1="34" y1="105" x2="66" y2="105" stroke="#1A1A1A" strokeWidth="0.3"/>
        <line x1="38" y1="112" x2="62" y2="112" stroke="#1A1A1A" strokeWidth="0.25" opacity="0.5"/>
        <line x1="38" y1="117" x2="58" y2="117" stroke="#1A1A1A" strokeWidth="0.25" opacity="0.5"/>
        <defs>
          <linearGradient id="apo-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.5" stopColor="#fff" stopOpacity="0.6"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
    pump: (
      <svg viewBox="0 0 100 160" width={w} height={h}>
        <ellipse cx={cx} cy="156" rx="30" ry="3" fill="#000" opacity="0.06"/>
        <rect x="44" y="10" width="12" height="6" fill="#1A1A1A"/>
        <rect x="40" y="16" width="20" height="10" fill="#0B0B0B"/>
        <rect x="46" y="26" width="8" height="14" fill={color}/>
        <rect x="24" y="40" width="52" height="108" rx="3" fill={color}/>
        <rect x="24" y="40" width="52" height="108" rx="3" fill="url(#pump-shine)" opacity="0.22"/>
        <rect x="32" y="80" width="36" height="36" fill="#F5F1E8" opacity="0.95"/>
        <line x1="38" y1="92" x2="62" y2="92" stroke="#1A1A1A" strokeWidth="0.4"/>
        <line x1="38" y1="100" x2="58" y2="100" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <line x1="38" y1="106" x2="56" y2="106" stroke="#1A1A1A" strokeWidth="0.3" opacity="0.5"/>
        <defs>
          <linearGradient id="pump-shine" x1="0" x2="1" y1="0" y2="0">
            <stop offset="0" stopColor="#fff" stopOpacity="0"/>
            <stop offset="0.5" stopColor="#fff" stopOpacity="0.75"/>
            <stop offset="1" stopColor="#fff" stopOpacity="0"/>
          </linearGradient>
        </defs>
      </svg>
    ),
  };
  return <span className="bottle">{bottles[kind] || bottles.cream}</span>;
}

// Map product → bottle archetype + color
function bottleFor(p) {
  if (!p) return { kind: "cream", color: "#3B2F26" };
  const line = (p.line || "").toLowerCase();
  if (line.includes("fragrance") || line.includes("eau") || line.includes("perfume")) return { kind: "fragrance", color: p.accent || "#5A3520" };
  if (line.includes("serum")) return { kind: "serum", color: p.accent || "#3B2F26" };
  if (line.includes("treatment") || line.includes("lotion")) return { kind: "apothecary", color: p.accent || "#5A3520" };
  if (line.includes("hair") || line.includes("body") || line.includes("oil")) return { kind: "pump", color: p.accent || "#3B2F26" };
  if (line.includes("balm") || line.includes("cleanser")) return { kind: "tube", color: p.accent || "#3B2F26" };
  return { kind: "cream", color: p.accent || "#3B2F26" };
}

// ---------- CIRCLE PEEK — nav button with auto-open arrival teaser ----------
function CirclePeek({ go, route }) {
  const [open, setOpen] = useS(false);
  const [armed, setArmed] = useS(false); // true while the auto-open teaser is showing
  const closeTimer = useR(null);
  const PEEK_KEY = "eumorph.circle.peek.shown.v2";

  // Auto-open ~1.2s after arrival on the home page only — nowhere
  // else does it make narrative sense, and it blocks the topbar over
  // an unrelated screen otherwise. Closes on any scroll, click anywhere
  // outside, or after a generous timeout.
  useE(() => {
    if (route !== "home") return; // only on the issue cover
    let t;
    try {
      if (localStorage.getItem(PEEK_KEY)) return;
    } catch (e) {}

    t = setTimeout(() => {
      setOpen(true);
      setArmed(true);
      try { localStorage.setItem(PEEK_KEY, "1"); } catch (e) {}
    }, 1200);

    return () => clearTimeout(t);
  }, [route]);

  // While armed, dismiss on scroll / outside-click / esc
  useE(() => {
    if (!armed) return;
    const onScroll = () => { if (window.scrollY > 30) { setOpen(false); setArmed(false); } };
    const onKey = (e) => { if (e.key === "Escape") { setOpen(false); setArmed(false); } };
    const onClick = (e) => {
      if (!e.target.closest(".circle-peek-wrap")) { setOpen(false); setArmed(false); }
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    document.addEventListener("keydown", onKey);
    document.addEventListener("click", onClick);
    // Failsafe: close after 12s even if user is idle
    const fail = setTimeout(() => { setOpen(false); setArmed(false); }, 12000);
    return () => {
      window.removeEventListener("scroll", onScroll);
      document.removeEventListener("keydown", onKey);
      document.removeEventListener("click", onClick);
      clearTimeout(fail);
    };
  }, [armed]);

  const onEnter = () => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    setOpen(true);
  };
  const onLeave = () => {
    if (armed) return; // armed-mode controls its own close
    closeTimer.current = setTimeout(() => setOpen(false), 180);
  };

  const goToCircle = (e) => {
    e.preventDefault();
    setOpen(false);
    setArmed(false);
    go("the-circle");
  };

  const dismiss = (e) => {
    e.stopPropagation();
    setOpen(false);
    setArmed(false);
  };

  return (
    <div
      className={`circle-peek-wrap ${armed ? "circle-peek-armed" : ""}`}
      onMouseEnter={onEnter}
      onMouseLeave={onLeave}
    >
      <a href="#" className="util-circle" onClick={goToCircle}>
        <span className="util-circle-mono">C</span>
        <span className="util-circle-label">Circle</span>
        <span className="util-circle-price">$50/yr</span>
        <span className="util-circle-caret" aria-hidden="true">▾</span>
      </a>

      <div className={`circle-peek ${open ? "circle-peek-open" : ""}`} role="dialog" aria-label="The Circle">
        <header className="cp-head">
          <div className="cp-head-l">
            <span className="cp-mono">C</span>
            <span className="cp-eyebrow">The Circle · since 2024</span>
          </div>
          <button className="cp-close" onClick={dismiss} aria-label="dismiss">×</button>
        </header>

        <div className="cp-hero">
          <h3 className="cp-title">$50, locked.</h3>
          <p className="cp-sub">A flat annual rate, kept at the price of the day you join. <em>Not a coupon — a contract.</em></p>
        </div>

        <ul className="cp-perks">
          <li>
            <span className="cp-num">01</span>
            <span className="cp-perk-body">
              <strong>9% off, kept.</strong> Held at today's rate until you cancel.
            </span>
          </li>
          <li>
            <span className="cp-num">02</span>
            <span className="cp-perk-body">
              <strong>Four sample editions.</strong> A small box, hand-tied, on each equinox and solstice.
            </span>
          </li>
          <li>
            <span className="cp-num">03</span>
            <span className="cp-perk-body">
              <strong>Cécile, by name.</strong> Replies in 23h, until 23h — every day.
            </span>
          </li>
        </ul>

        <a href="#" className="cp-cta" onClick={goToCircle}>
          <span>Read the contract</span>
          <span className="cp-arr">→</span>
        </a>

        <div className="cp-foot">
          <span>Six things, kept.</span>
          <span>Cancel any time, in one line.</span>
        </div>
      </div>
    </div>
  );
}

// ---------- TOPBAR ----------
function TopBar({ route, go, bagCount, onTalkCecile }) {
  const navItems = [
    { k: "this-week", label: "This week" },
    { k: "her-shelf", label: "Her shelf" },
    { k: "in-residence", label: "On the shelf" },
    { k: "the-file", label: "The file" },
  ];
  return (
    <header className="topbar-v2">
      <div className="container">
        <div className="row">
          <nav className="nav">
            {navItems.map(n => (
              <a key={n.k} href="#" className={route === n.k ? "active" : ""}
                 onClick={(e) => { e.preventDefault(); go(n.k); }}>
                {n.label}
              </a>
            ))}
          </nav>
          <a href="#" onClick={(e) => { e.preventDefault(); go("home"); }}>
            <Wordmark size="nav" showIPA={true} />
          </a>
          <div className="utils">
            <CirclePeek go={go} route={route} />
            <a href="#" onClick={(e) => { e.preventDefault(); onTalkCecile && onTalkCecile(); }}>
              <span style={{ fontStyle: "italic" }}>Cécile</span>
            </a>
            <a href="#" onClick={(e) => { e.preventDefault(); go("account"); }}>Account</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go("cart"); }} className="bag-pill">
              Bag · {bagCount || 0}
            </a>
          </div>
        </div>
      </div>
    </header>
  );
}

// ---------- ARRIVAL RIBBON (only when actually arrived) ----------
function ArrivalRibbon({ arrival, onDismiss }) {
  if (!arrival || arrival.kind === "cold") return null;
  const echoes = {
    warm: "You arrived from a conversation. Picking up where it left off.",
    agent: "Hello agent. Structured catalog at /catalog.json. Checkout via /api/order.",
  };
  return (
    <div className="arrival-ribbon">
      <div className="container">
        <div className="ar-row">
          <span>
            {arrival.kind === "warm" && <>via {arrival.via || "Claude"} · {arrival.time || "just now"}</>}
            {arrival.kind === "agent" && <>agent session · {arrival.via || "machine-readable"}</>}
          </span>
          <span className="ar-echo">"{arrival.query || echoes[arrival.kind]}"</span>
          <span className="ar-dismiss" onClick={onDismiss}>dismiss ✕</span>
        </div>
      </div>
    </div>
  );
}

// ---------- FOOTER ----------
function Footer({ go }) {
  return (
    <footer className="footer-v2">
      <div className="container">
        <div className="footer-mast">
          <p className="footer-quote">
            What is<br/><em>correctly proportioned</em><br/>to the body that holds it.
            <span className="fq-attr">— Eumorph · n. · adj. · proper noun, occasionally.</span>
          </p>
          <div className="footer-signup">
            <div className="fs-label">— A letter, every Monday at 06:00</div>
            <div className="fs-row">
              <input type="email" placeholder="your address" />
              <button onClick={(e) => e.preventDefault()}>Subscribe ↵</button>
            </div>
            <div className="fs-tag">Cécile writes the letter. No tracking pixels. Unsubscribe at any line.</div>
          </div>
        </div>
        <div className="footer-cols">
          <div className="col">
            <div className="col-title">The desk</div>
            <a href="mailto:cecile@eumorph.co">cecile@eumorph.co</a>
            <a href="#" onClick={(e) => { e.preventDefault(); window.dispatchEvent(new CustomEvent("eumorph:open-cecile")); }}>Write to Cécile →</a>
            <span className="footer-static">Replies before 23h CET, Mon–Fri</span>
          </div>
          <div className="col">
            <div className="col-title">Read</div>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("this-week"); }}>This week</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("home"); }}>The Issue</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("in-residence"); }}>The atlas</a>
          </div>
          <div className="col">
            <div className="col-title">Shop</div>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("her-shelf", { filter: "skin" }); }}>Skin</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("her-shelf", { filter: "scent" }); }}>Scent</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("her-shelf", { filter: "hair" }); }}>Hair</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("her-shelf", { filter: "body" }); }}>Body</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("her-shelf"); }}>Everything</a>
          </div>
          <div className="col">
            <div className="col-title">House</div>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("the-circle"); }}>The Circle</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("the-file"); }}>The file</a>
            <a href="#" onClick={(e) => { e.preventDefault(); go && go("cart"); }}>Bag</a>
          </div>
        </div>
        <div className="footer-disclaimer">
          Eumorph is an independent retailer. We are not affiliated with,
          sponsored by, or an authorized dealer of the brands carried on our
          shelf. All product names and trademarks are the property of their
          respective owners and are used for descriptive purposes only.
          Goods are sourced through the global distribution market and
          inspected on arrival in Brooklyn before they ship.
        </div>
        <div className="footer-colophon">
          <span>© 2026 Eumorph · all rights reserved</span>
          <span className="fc-mast">— edited by Cécile · printed weekly —</span>
          <span>Issue {ISSUE.number} · Vol. {ISSUE.volume}</span>
        </div>
      </div>
    </footer>
  );
}

// ---------- CHAPTER MARK — section opener ----------
// Renders like:  ─── I ─────────────  THE HOUSE OF THE WEEK · §01
function ChapterMark({ numeral, label, sub, folio }) {
  return (
    <div className="chapter-mark">
      <span className="cm-num">{numeral}</span>
      <span className="cm-rule"></span>
      <span className="cm-label">{label}</span>
      {sub && <span className="cm-sub">{sub}</span>}
      {folio && <span className="cm-folio">{folio}</span>}
    </div>
  );
}

// ---------- RUNNING HEAD — page-top issue stamp ----------
function RunningHead({ left, right }) {
  return (
    <div className="running-head">
      <span>{left}</span>
      <span className="rh-rule"></span>
      <span>{right}</span>
    </div>
  );
}

Object.assign(window, { Wordmark, WordmarkStack, Redact, Bottle, bottleFor, TopBar, ArrivalRibbon, Footer, ChapterMark, RunningHead });
