/* ProTGen site — shared primitives, chrome, motifs, router */

/* ============================================================
   ROUTER (hash-based) + reveal hook
   ============================================================ */
const ROUTES = ["home","platform","program","investors","about","resources","contact","article"];

function useRoute() {
  const parse = () => {
    const h = (window.location.hash || "#/home").replace(/^#\/?/, "");
    const [path, q] = h.split("?");
    const seg = path.split("/").filter(Boolean);
    const page = ROUTES.includes(seg[0]) ? seg[0] : "home";
    return { page, sub: seg[1] || null, raw: h };
  };
  const [route, setRoute] = React.useState(parse());
  React.useEffect(() => {
    const fn = () => { setRoute(parse()); window.scrollTo({ top: 0, behavior: "auto" }); };
    window.addEventListener("hashchange", fn);
    return () => window.removeEventListener("hashchange", fn);
  }, []);
  return route;
}
function go(page, sub) { window.location.hash = "#/" + page + (sub ? "/" + sub : ""); }

function useReveal() {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const els = ref.current ? ref.current.querySelectorAll(".reveal") : [];
    if (!els.length) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
    }, { threshold: 0.12 });
    els.forEach((el, i) => { el.style.transitionDelay = Math.min(i * 60, 360) + "ms"; io.observe(el); });
    return () => io.disconnect();
  });
  return ref;
}

/* ============================================================
   PRIMITIVES
   ============================================================ */
function Mark({ size = 30, dark = false }) {
  const ring = dark ? "#46CAD3" : "#0F7C80";
  const node = dark ? "#2A9498" : "#0F7C80";
  const sig  = dark ? "#F7F2E8" : "#46CAD3";
  return (
    <svg width={size} height={size} viewBox="0 0 48 48" fill="none" aria-hidden="true">
      <path d="M 39.9 15.6 A 18 18 0 1 0 39.9 32.4" stroke={ring} strokeWidth="3" strokeLinecap="round"/>
      <circle cx="24" cy="15" r="2.2" fill={node}/>
      <circle cx="24" cy="33" r="2.2" fill={node}/>
      <circle cx="16.5" cy="19.5" r="2.2" fill={node}/>
      <circle cx="16.5" cy="28.5" r="2.2" fill={node}/>
      <circle cx="32" cy="24" r="2.4" fill={node}/>
      <circle cx="24" cy="24" r="3.4" fill={sig}/>
    </svg>
  );
}

function Logo({ dark = false }) {
  return (
    <a className={"logo" + (dark ? " on-dark" : "")} onClick={() => go("home")} style={{ cursor: "pointer" }}>
      <Mark size={30} dark={dark} />
      <span className="w">ProTGen</span>
    </a>
  );
}

function Eyebrow({ children, tone }) {
  return <span className={"eyebrow" + (tone ? " " + tone : "")}>{children}</span>;
}

function Icon({ name, size = 18 }) {
  const p = {
    arrow: <><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></>,
    arrowDown: <><path d="M12 5v14"/><path d="m19 12-7 7-7-7"/></>,
    target: <><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></>,
    check: <path d="M20 6 9 17l-5-5"/>,
    branch: <><line x1="6" y1="3" x2="6" y2="15"/><circle cx="18" cy="6" r="3"/><circle cx="6" cy="18" r="3"/><path d="M18 9a9 9 0 0 1-9 9"/></>,
    shield: <><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></>,
    layers: <><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"/></>,
    flask: <><path d="M10 2v7.31"/><path d="M14 9.3V2"/><path d="M8.5 2h7"/><path d="M14 9.3a6.5 6.5 0 1 1-4 0"/><path d="M5.52 16h12.96"/></>,
    milestone: <><path d="M12 13v8"/><path d="M12 3v3"/><path d="M4 6h13l3 3.5L17 13H4z"/></>,
    dot: <circle cx="12" cy="12" r="4"/>,
    commit: <><circle cx="12" cy="12" r="3"/><line x1="3" y1="12" x2="9" y2="12"/><line x1="15" y1="12" x2="21" y2="12"/></>,
    activity: <path d="M22 12h-4l-3 9L9 3l-3 9H2"/>,
    plus: <><path d="M5 12h14"/><path d="M12 5v14"/></>,
    download: <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></>,
    mail: <><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></>,
    file: <><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/></>,
    menu: <><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></>,
    sliders: <><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></>,
    pin: <><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/></>,
    clock: <><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></>,
  }[name];
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor"
      strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">{p}</svg>
  );
}

/* ============================================================
   MOTIFS — signal architecture vocabulary
   ============================================================ */

/* Repertoire constellation: sparse left → ordered right, gate in middle */
function ConstellationMotif({ animate = true }) {
  return (
    <svg width="440" height="440" viewBox="0 0 96 96" fill="none" style={{ maxWidth: "100%" }} className={animate ? "motif-anim" : ""}>
      <circle cx="10" cy="58" r="2.4" fill="#3A535B" className="c-dep" style={{ "--d": "0ms" }}/>
      <circle cx="16" cy="40" r="2.4" fill="#3A535B" className="c-dep" style={{ "--d": "120ms" }}/>
      <circle cx="13" cy="74" r="2.4" fill="#3A535B" className="c-dep" style={{ "--d": "240ms" }}/>
      <path d="M40 22 C 50 36, 50 60, 40 74" stroke="#2A9498" strokeWidth="2.4" strokeLinecap="round" className="c-arc"/>
      <path d="M48 28 C 56 39, 56 57, 48 68" stroke="#46CAD3" strokeWidth="2.4" strokeLinecap="round" className="c-arc"/>
      <circle cx="44" cy="48" r="4" fill="#46CAD3" className="c-sig"/>
      {[26,40,54,68].map((y,i)=><circle key={"a"+y} cx="68" cy={y} r="2.8" fill={i%2?"#0F7C80":"#2A9498"} className="c-ord" style={{ "--d": (300+i*90)+"ms" }}/>)}
      {[26,40,54,68].map((y,i)=><circle key={"b"+y} cx="84" cy={y} r="2.8" fill={i===2?"#46CAD3":"#2A9498"} className="c-ord" style={{ "--d": (360+i*90)+"ms" }}/>)}
    </svg>
  );
}

/* Restoration gradient field with sparse→dense node lattice */
function RestorationField() {
  return (
    <svg width="460" height="420" viewBox="0 0 120 110" fill="none" style={{ maxWidth: "100%" }} className="motif-anim">
      <defs>
        <linearGradient id="rg" x1="0" y1="0" x2="120" y2="0" gradientUnits="userSpaceOnUse">
          <stop offset="0" stopColor="#0B2E3A"/><stop offset="0.58" stopColor="#0F7C80"/><stop offset="1" stopColor="#46CAD3"/>
        </linearGradient>
        <linearGradient id="rgfade" x1="0" y1="0" x2="120" y2="0" gradientUnits="userSpaceOnUse">
          <stop offset="0" stopColor="#46CAD3" stopOpacity="0"/><stop offset="1" stopColor="#46CAD3" stopOpacity=".5"/>
        </linearGradient>
      </defs>
      <rect x="6" y="50" width="108" height="8" rx="4" fill="url(#rg)"/>
      {Array.from({length:6}).map((_,r)=>Array.from({length:9}).map((_,c)=>{
        const x=12+c*12.4, y=14+r*16; const t=c/8;
        const show = Math.random() < (0.25 + t*0.7);
        const big = t>0.7 && r%2===0;
        return show ? <circle key={r+"-"+c} cx={x} cy={y>50?y+8:y} r={big?2.6:1.9}
          fill={t>0.85?"#46CAD3":t>0.5?"#0F7C80":"#3A535B"} className="c-ord" style={{ "--d": (t*500)+"ms" }}/> : null;
      }))}
    </svg>
  );
}

/* Signal gate — large centered aperture */
function GateMotif({ size = 360, open = true }) {
  return (
    <svg width={size} height={size} viewBox="0 0 120 120" fill="none" style={{ maxWidth: "100%" }} className="motif-anim">
      <circle cx="60" cy="60" r="46" stroke="rgba(255,255,255,.10)" strokeWidth="1"/>
      <circle cx="60" cy="60" r="32" stroke="rgba(255,255,255,.08)" strokeWidth="1"/>
      <path d="M44 28 C 60 44, 60 76, 44 92" stroke="#2A9498" strokeWidth="3" strokeLinecap="round" className="c-arc"/>
      <path d="M76 28 C 60 44, 60 76, 76 92" stroke="#46CAD3" strokeWidth="3" strokeLinecap="round" className="c-arc"/>
      <circle cx="60" cy="60" r="6.5" fill="#46CAD3" className="c-sig"/>
      <circle cx="60" cy="60" r="11" stroke="#46CAD3" strokeWidth="1" opacity=".5" className="c-ring"/>
    </svg>
  );
}

/* Thymic architecture — nested simplified curves */
function ThymicArch({ size = 360 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 120 120" fill="none" style={{ maxWidth: "100%" }} className="motif-anim">
      {[0,1,2,3].map(i=>(
        <path key={i} d={`M${30-i*5} ${40-i*4} C ${60} ${20-i*3}, ${60} ${20-i*3}, ${90+i*5} ${40-i*4}
          C ${100+i*4} ${60}, ${100+i*4} ${60}, ${90+i*5} ${80+i*4}
          C ${60} ${100+i*3}, ${60} ${100+i*3}, ${30-i*5} ${80+i*4}
          C ${20-i*4} ${60}, ${20-i*4} ${60}, ${30-i*5} ${40-i*4} Z`}
          stroke={i===0?"#46CAD3":"#2A9498"} strokeOpacity={i===0?1:0.4-i*0.08} strokeWidth={i===0?2.4:1.4} className="c-arc"/>
      ))}
      <circle cx="60" cy="60" r="4.5" fill="#46CAD3" className="c-sig"/>
    </svg>
  );
}

/* ============================================================
   HEADER / FOOTER / MODAL
   ============================================================ */
const NAV_ITEMS = [
  ["platform","Platform"], ["program","ProT‑096"], ["investors","Investors & partners"],
  ["resources","Resources"], ["about","About"],
];

function Header({ page, onRequest }) {
  const [scrolled, setScrolled] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    const fn = () => setScrolled(window.scrollY > 8);
    window.addEventListener("scroll", fn); fn();
    return () => window.removeEventListener("scroll", fn);
  }, []);
  React.useEffect(() => { setOpen(false); }, [page]);
  const nav = (p) => (e) => { e.preventDefault(); go(p); };
  return (
    <header className={"hdr" + (scrolled ? " scrolled" : "")}>
      <div className="wrap row">
        <Logo />
        <nav className="nav">
          {NAV_ITEMS.map(([p,l]) => (
            <a key={p} className={page === p ? "active" : ""} onClick={nav(p)}>{l}</a>
          ))}
          <a className={page === "contact" ? "active" : ""} onClick={nav("contact")}>Contact</a>
          <a className="nav-storylab" href="/storylab.html" title="Internal strategy review — not part of the public site">[ StoryLab ]</a>
        </nav>
        <div className="nav-cta">
          <button className="btn btn-primary btn-sm" onClick={onRequest}>Request briefing <Icon name="arrow" size={16} /></button>
          <button className="menu-btn" onClick={() => setOpen(!open)} aria-label="Menu"><Icon name="menu" size={20} /></button>
        </div>
      </div>
      <div className={"mobile-nav" + (open ? " open" : "")}>
        <div className="wrap">
          {NAV_ITEMS.map(([p,l]) => <a key={p} onClick={nav(p)}>{l}</a>)}
          <a onClick={nav("contact")}>Contact</a>
          <a className="nav-storylab" href="/storylab.html">[ StoryLab ] — internal</a>
        </div>
      </div>
    </header>
  );
}

function Footer({ onRequest }) {
  const nav = (p) => (e) => { e.preventDefault(); go(p); };
  return (
    <footer className="ftr">
      <div className="wrap">
        <div className="frow">
          <div style={{ maxWidth: 320 }}>
            <Logo dark />
            <p style={{ color: "var(--fg-on-dark-2)", fontSize: 15, lineHeight: 1.6, marginTop: 18 }}>
              Targeted Notch activators to restore immune competence — beginning with ProT‑096.
            </p>
            <button className="btn btn-on-dark btn-sm" style={{ marginTop: 20 }} onClick={onRequest}>
              Request a briefing <Icon name="arrow" size={15} />
            </button>
          </div>
          <div>
            <div className="ft-h">Science</div>
            <a onClick={nav("platform")}>Platform</a>
            <a onClick={nav("program")}>ProT‑096</a>
            <a onClick={nav("resources")}>Resources</a>
          </div>
          <div>
            <div className="ft-h">Company</div>
            <a onClick={nav("investors")}>Investors & partners</a>
            <a onClick={nav("about")}>About & team</a>
            <a onClick={nav("contact")}>Contact</a>
          </div>
          <div>
            <div className="ft-h">Connect</div>
            <a onClick={nav("contact")}>Request briefing</a>
            <a onClick={nav("investors")}>Partnering</a>
            <a href="mailto:hello@protgen.bio">hello@protgen.bio</a>
          </div>
        </div>
        <div className="legal">
          <span>© 2026 ProTGen, Inc. · All rights reserved.</span>
          <span>RESTORE IMMUNE COMPETENCE</span>
        </div>
      </div>
    </footer>
  );
}

function RequestModal({ open, onClose, preset }) {
  const [sent, setSent] = React.useState(false);
  const [type, setType] = React.useState(preset || "Investor");
  React.useEffect(() => { if (open) { setSent(false); setType(preset || "Investor"); } }, [open, preset]);
  if (!open) return null;
  return (
    <div className="scrim" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 6 }}>
          <div>
            <Eyebrow>Request a briefing</Eyebrow>
            <h3 style={{ fontSize: 25, fontWeight: 600, margin: "13px 0 0", letterSpacing: "-.01em" }}>
              {sent ? "Request received." : "Start a conversation"}
            </h3>
          </div>
          <button className="x" onClick={onClose} aria-label="Close">×</button>
        </div>
        {sent ? (
          <p style={{ color: "var(--fg-2)", fontSize: 15.5, lineHeight: 1.62, marginTop: 14 }}>
            Thank you. The ProTGen team will follow up with briefing materials tailored to your interest. <span style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--slate)" }}>(Demo form — no data is sent.)</span>
          </p>
        ) : (
          <div className="stack-sm" style={{ marginTop: 18 }}>
            <div className="field"><label>Work email</label><input placeholder="name@firm.com" /></div>
            <div className="field"><label>Organization</label><input placeholder="Firm or company" /></div>
            <div className="field">
              <label>I'm reaching out as a…</label>
              <select value={type} onChange={(e) => setType(e.target.value)}>
                <option>Investor</option><option>Strategic partner</option>
                <option>Scientific collaborator</option><option>Internal champion</option><option>Other</option>
              </select>
            </div>
            <button className="btn btn-primary" style={{ marginTop: 12, width: "100%", justifyContent: "center" }} onClick={() => setSent(true)}>
              Send request <Icon name="arrow" size={16} />
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

/* Section header helper */
function SectionHead({ label, tone, title, lead, center, maxw }) {
  return (
    <div style={{ textAlign: center ? "center" : "left", maxWidth: maxw, margin: center ? "0 auto" : undefined }}>
      <Eyebrow tone={tone}>{label}</Eyebrow>
      {title && <h2 className="sec-h" style={{ marginTop: 24, marginLeft: center ? "auto" : 0, marginRight: center ? "auto" : 0 }}>{title}</h2>}
      {lead && <p className="lead" style={{ margin: center ? "0 auto" : 0 }}>{lead}</p>}
    </div>
  );
}

Object.assign(window, {
  useRoute, go, useReveal, Mark, Logo, Eyebrow, Icon,
  ConstellationMotif, RestorationField, GateMotif, ThymicArch,
  Header, Footer, RequestModal, SectionHead,
});
