/* ===== Pills ===== */
function Pill({ children, onClick, href, variant = "ghost", style }) {
  const cls = "pill pill--" + variant;
  if (href) {
    return (
      <a className={cls} href={href} target="_blank" rel="noopener noreferrer" style={style}>
        {children}
      </a>
    );
  }
  return (
    <button className={cls} onClick={onClick} style={style}>
      {children}
    </button>
  );
}

/* Outlaw-style pill: black circular arrow + label */
function ArrowPill({ children, href, onClick }) {
  const inner = (
    <React.Fragment>
      <span className="arrowpill__dot">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden="true">
          <path d="M5 12h13M13 6l6 6-6 6" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </span>
      <span className="arrowpill__label">{children}</span>
    </React.Fragment>
  );
  if (href) return <a className="arrowpill" href={href} target="_blank" rel="noopener noreferrer">{inner}</a>;
  return <button className="arrowpill" onClick={onClick}>{inner}</button>;
}

function SearchBar({ value, onChange, tinted, placeholder = "Search the directory", onFocus }) {
  return (
    <div className={"searchbar" + (tinted ? " searchbar--tint" : "")}>
      <input value={value} onChange={(e) => onChange(e.target.value)} onFocus={onFocus} placeholder={placeholder} spellCheck={false} />
      <svg width="19" height="19" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <circle cx="11" cy="11" r="7" stroke="currentColor" strokeWidth="2" />
        <path d="M16.5 16.5L21 21" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
      </svg>
    </div>
  );
}

/* Global search with a live preview dropdown across firms / funds / people (max 10). */
function GlobalSearch({ value, onChange, tinted, nav, placeholder }) {
  const [open, setOpen] = React.useState(false);
  const wrapRef = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, []);

  const results = React.useMemo(() => {
    const q = (value || "").trim().toLowerCase();
    if (!q) return [];
    const firms = [], funds = [], people = [];
    for (const f of window.FIRMS || []) {
      if (f.name.toLowerCase().includes(q)) firms.push({ kind: "firm", id: f.id, title: f.name, sub: f.teaser });
    }
    for (const fd of window.FUNDS || []) {
      const fm = window.getFirm(fd.firmId);
      if ((fd.name + " " + (fm ? fm.name : "")).toLowerCase().includes(q))
        funds.push({ kind: "fund", id: fd.id, title: fd.name, sub: (fm ? fm.name : "") + (fd.vintage ? " · " + fd.vintage : "") });
    }
    for (const p of window.PEOPLE || []) {
      const fm = window.getFirm(p.firmId);
      if ((p.name + " " + (p.role || "") + " " + (fm ? fm.name : "")).toLowerCase().includes(q))
        people.push({ kind: "person", id: p.id, title: p.name, sub: (fm ? fm.name : "") + (p.role ? " · " + p.role : "") });
    }
    // Keep a representative mix (firm profile + funds + people), capped at 10 total.
    return [...firms.slice(0, 3), ...funds.slice(0, 5), ...people.slice(0, 5)].slice(0, 10);
  }, [value]);

  const goTo = (r) => { setOpen(false); onChange(""); nav[r.kind](r.id); };

  return (
    <div className="gsearch" ref={wrapRef}>
      <SearchBar value={value} onChange={(v) => { onChange(v); setOpen(true); }} tinted={tinted}
        placeholder={placeholder} onFocus={() => { if ((value || "").trim()) setOpen(true); }} />
      {open && (value || "").trim() && (
        <div className="gsearch__panel">
          {results.length === 0 ? (
            <div className="gsearch__empty">No firms, funds, or people match “{value}”.</div>
          ) : results.map((r) => (
            <button className="gsearch__item" key={r.kind + "-" + r.id} onMouseDown={(e) => { e.preventDefault(); goTo(r); }}>
              <span className={"gsearch__badge gsearch__badge--" + r.kind}>{r.kind}</span>
              <span className="gsearch__text">
                <span className="gsearch__title">{r.title}</span>
                <span className="gsearch__sub">{r.sub}</span>
              </span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

/* Native email capture — posts to /api/subscribe (beehiiv), stays fully on-brand. */
function SubscribeForm({ tinted, compact }) {
  const [email, setEmail] = React.useState("");
  const [state, setState] = React.useState("idle"); // idle | sending | done | error
  const submit = async (e) => {
    e.preventDefault();
    const v = email.trim();
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) { setState("error"); return; }
    setState("sending");
    try {
      const r = await fetch("/api/subscribe", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: v }),
      });
      if (!r.ok) throw new Error("bad");
      setState("done");
    } catch (_) {
      // Static hosting without the function — fall back to the beehiiv page.
      window.open("https://confluencevcweekly.beehiiv.com/subscribe?email=" + encodeURIComponent(v), "_blank", "noopener");
      setState("done");
    }
  };
  if (state === "done") {
    return <p className={"subform__done" + (tinted ? " subform__done--tint" : "")}>You're in. Check your inbox for a welcome note.</p>;
  }
  return (
    <form className={"subform" + (tinted ? " subform--tint" : "") + (compact ? " subform--compact" : "")} onSubmit={submit} noValidate>
      <input
        className="subform__input" type="email" value={email} spellCheck={false}
        placeholder="Email address" aria-label="Email address"
        onChange={(e) => { setEmail(e.target.value); if (state === "error") setState("idle"); }}
      />
      <button className="subform__btn" type="submit" disabled={state === "sending"}>
        {state === "sending" ? "Joining…" : "Join the list"}
      </button>
      {state === "error" && <span className="subform__err">Enter a valid email.</span>}
    </form>
  );
}

/* Upgrade CTA — unlock the complete Firm Architecture Research library (annual members).
   compact renders a slim single-row bar for the top of pages. */
function ResearchUpgrade({ compact }) {
  if (compact) {
    return (
      <aside className="upgrade upgrade--compact">
        <div className="upgrade__body">
          <span className="upgrade__kicker">Members only</span>
          <span className="upgrade__title upgrade__title--sm">Unlock the full research behind this page</span>
        </div>
        <ArrowPill href="https://confluencevcweekly.beehiiv.com/upgrade">Upgrade to unlock</ArrowPill>
      </aside>
    );
  }
  return (
    <aside className="upgrade">
      <div className="upgrade__body">
        <span className="upgrade__kicker">Members only</span>
        <h3 className="upgrade__title">Unlock the full research</h3>
        <p className="upgrade__text">
          Annual members get the complete Firm Architecture Research library — every framework,
          source document, and cross-firm comparison behind these breakdowns, updated as we publish.
        </p>
      </div>
      <ArrowPill href="https://confluencevcweekly.beehiiv.com/upgrade">Upgrade to unlock</ArrowPill>
    </aside>
  );
}

/* Inline newsletter band — a second on-page capture, dropped mid-content. */
function InlineSubscribe({ tinted }) {
  return (
    <aside className={"inlinesub" + (tinted ? " inlinesub--tint" : "")}>
      <h3 className="inlinesub__title">Architecture lessons from the firms that built venture capital. In your inbox, weekly.</h3>
      <SubscribeForm tinted={tinted} compact={true} />
    </aside>
  );
}

/* Scroll-triggered newsletter popup, bottom-right (Acquired-style). Dismissal persists. */
function NewsletterPopup() {
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    if (localStorage.getItem("vp_popup_dismissed")) return;
    const onScroll = () => {
      if (window.scrollY > window.innerHeight * 0.85) {
        setShown(true);
        window.removeEventListener("scroll", onScroll);
      }
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const close = () => { setShown(false); try { localStorage.setItem("vp_popup_dismissed", "1"); } catch (e) {} };
  if (!shown) return null;
  return (
    <div className="npop" role="dialog" aria-label="Subscribe to the newsletter">
      <button className="npop__close" onClick={close} aria-label="Close">×</button>
      <span className="npop__kicker">Never miss a breakdown</span>
      <p className="npop__pitch">The funds, people, and decisions behind the most iconic firms in venture — in your inbox.</p>
      <SubscribeForm />
    </div>
  );
}

/* Welcome letter — a torn-paper note that greets new readers on the home page.
   Emerges gracefully on load; a click anywhere dismisses it (persisted per visitor). */
function WelcomePopup() {
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (localStorage.getItem("vp_welcome_seen")) return;
    const t = setTimeout(() => setOpen(true), 450);
    return () => clearTimeout(t);
  }, []);
  const close = () => { setOpen(false); try { localStorage.setItem("vp_welcome_seen", "1"); } catch (e) {} };
  if (!open) return null;
  return (
    <div className="welcome" onClick={close} role="dialog" aria-label="Welcome to The Vintage Project">
      <div className="welcome__letter" onClick={close}>
        <h2 className="welcome__title">Welcome to The Vintage Project.</h2>
        <div className="welcome__body">
          <p>
            The Vintage Project: Presented by{" "}
            <a href="https://www.outlaw.vc/" target="_blank" rel="noopener noreferrer"><em>Outlaw</em></a>{" "}
            &amp;{" "}
            <a href="https://confluencevcweekly.beehiiv.com/" target="_blank" rel="noopener noreferrer"><em>Confluence</em></a>.
          </p>
          <p>Outlaw is a thesis-driven investment firm designed for out-of-distribution individuals.</p>
          <p>We view ourselves as a talent agency masquerading as an investment firm. This philosophy stems from our belief that all returns from this asset class are ultimately rooted from humans.</p>
          <p>The Vintage Project is the most comprehensive library of historically notable investment firms, funds, and people responsible for their success.</p>
          <p>This project serves as an open-source guide for our own aspiration of building the next great investment firm on this list.</p>
        </div>
        <div className="welcome__signoff">Outlaw</div>
        <div className="welcome__close">Click anywhere to close</div>
      </div>
    </div>
  );
}

/* Shared footer — "A project presented by…" on every page. */
function SiteFooter({ tinted }) {
  return (
    <footer className={"sitefoot" + (tinted ? " sitefoot--tint" : "")}>
      <div className="sitefoot__sub">
        <span className="sitefoot__subpitch">Architecture lessons from the firms that built venture capital. In your inbox, weekly.</span>
        <SubscribeForm tinted={tinted} compact={true} />
      </div>
      <span className="sitefoot__note">A project presented by Outlaw, in partnership with Confluence.</span>
      <div className="sitefoot__links">
        <a className="footlink" href="https://www.outlaw.vc/" target="_blank" rel="noopener noreferrer">
          <span className="footlink__label">Outlaw</span>
          <span className="footlink__url">outlaw.vc <span className="footlink__arrow">↗</span></span>
        </a>
        <a className="footlink" href="https://confluencevcweekly.beehiiv.com/" target="_blank" rel="noopener noreferrer">
          <span className="footlink__label">Confluence</span>
          <span className="footlink__url">confluencevcweekly.beehiiv.com <span className="footlink__arrow">↗</span></span>
        </a>
      </div>
    </footer>
  );
}

/* Carousel wrapper + iconic-fund / iconic-person cards */
function Carousel({ title, sub, onMore, children }) {
  return (
    <section className="carousel">
      <div className="carousel__head">
        <div>
          <h2 className="carousel__title">{title}</h2>
          {sub && <p className="carousel__sub">{sub}</p>}
        </div>
        {onMore && <Pill variant="ghost" onClick={onMore}>More →</Pill>}
      </div>
      <div className="carousel__track">{children}</div>
    </section>
  );
}

function FundCard({ fund, firm, onClick, teaser }) {
  return (
    <button className="fcard" onClick={onClick}>
      <div className="fcard__art" style={{ background: firm.deep }}>
        {firm.logoSrc
          ? <img src={firm.logoSrc} alt={firm.name} />
          : <span className="fcard__logoword" style={{ color: firm.ink }}>{firm.name}</span>}
      </div>
      <div className="fcard__body">
        <span className="fcard__name">{fund.name}</span>
        <span className="fcard__meta">{firm.name}{fund.vintage ? " · " + fund.vintage : ""}</span>
        {teaser && <span className="fcard__teaser">{teaser}</span>}
      </div>
    </button>
  );
}

function PersonCard({ person, firm, onClick, teaser }) {
  return (
    <button className="pcard" onClick={onClick}>
      <span className={"pcard__portrait" + (person.noPhoto ? " pcard__portrait--named" : "")}>
        {person.headshotSrc
          ? <img src={person.headshotSrc} alt={person.name} />
          : (person.noPhoto
            ? <span className="pcard__nameart">{person.name}</span>
            : <span className="pcard__initials">{initialsOf(person.name)}</span>)}
      </span>
      <span className="pcard__name">{person.name}</span>
      <span className="pcard__teaser">{teaser || (firm ? firm.name : "")}</span>
    </button>
  );
}

/* Logo — user-fillable image slot + home button.
   The image-slot holds the user's own logo (persists to a root sidecar);
/* Outlaw logo — real wordmark linking out to outlaw.vc (new tab).
   White art by default (for the dark color-flood pages); inverted to dark on
   the light home canvas via the brand--dark modifier. */
function OutlawLogo({ tinted }) {
  return (
    <a className={"brand" + (tinted ? "" : " brand--dark")} href="https://www.outlaw.vc/" target="_blank" rel="noopener noreferrer" aria-label="Outlaw — outlaw.vc">
      <img className="brand__img" src="assets/outlaw-logo-white.png" alt="Outlaw" />
    </a>
  );
}

function TopBar({ search, setSearch, tinted, inkColor, onMenu, onHome, showSearch = true, nav }) {
  return (
    <header className={"topbar" + (tinted ? " topbar--tint" : "")}>
      <OutlawLogo tinted={tinted} />
      <div className="topbar__search">
        {showSearch && (nav
          ? <GlobalSearch value={search} onChange={setSearch} tinted={tinted} nav={nav} />
          : <SearchBar value={search} onChange={setSearch} tinted={tinted} />)}
      </div>
      <nav className="topbar__nav">
        <Pill variant="ghost" onClick={onMenu}>Menu</Pill>
        <Pill variant="solid" href="https://confluencevcweekly.beehiiv.com/p/introducing-outlaw">More</Pill>
      </nav>
    </header>
  );
}

/* ===== Notion-style colored tags ===== */
const TAG_COLOR = {
  "LP base composition": "green",
  "Fund structure": "purple",
  "Founder relationships / value-add model": "blue",
  "First-time LPs": "blue",
  "New stage": "red",
  "Open-ended structure": "purple",
  "Continuation vehicle": "blue",
  "Equal partnership": "green",
  "Held fund size flat": "gray",
  "New sector mandate": "yellow",
  "Return to early-stage": "green",
  "Founder-friendly terms": "blue",
  "Scaled with concentration": "yellow",
  "Dedicated growth vehicle": "purple",
  "Institutional LPs": "blue",
  "Larger LP base": "blue",
  "Permanent-capital ambition": "red",
  "Founder picking": "brown",
  "Macro / market structure": "gray",
  "Sector specialization": "red",
  "Negotiation / deal structure": "green",
  "Succession mechanism": "yellow",
  "Brand architecture": "blue",
  "Cultural artifacts": "gray",
  "Storytelling / narrative": "purple",
  "Firm building": "brown",
  "Global expansion": "green",
  "Decision discipline": "gray",
};
function tagColor(label) { return TAG_COLOR[label] || "gray"; }

function Tag({ children }) {
  return <span className={"tag tag--" + tagColor(children)}>{children}</span>;
}
function TagRow({ items }) {
  if (!items || !items.length) return null;
  return <div className="tagrow">{items.map((t, i) => <Tag key={i}>{t}</Tag>)}</div>;
}

/* ===== Directory tile (firms / funds) with bottom teaser reveal ===== */
function Tile({ title, tag, teaser, bg, ink, accent, onClick, variant = "logo", initials, logoId, src, fullName }) {
  return (
    <div className="tile">
      <div className="tile__reveal">
        <span className="tile__teaser">{teaser}</span>
      </div>
      <button className="tile__face" onClick={onClick} style={{ background: bg }}>
        {variant === "person" ? (
          <span className={"tile__portrait" + (fullName ? " tile__portrait--named" : "")} style={{ "--c": bg, "--a": accent }}>
            {fullName
              ? <span className="tile__logo" style={{ color: ink }}>{fullName}</span>
              : <span className="tile__initials" style={{ color: ink }}>{initials}</span>}
          </span>
        ) : (
          <span className="tile__logo" style={{ color: ink }}>{title}</span>
        )}
      </button>
      {logoId && (
        <image-slot
          id={logoId}
          class="tile__logoslot"
          shape="rounded"
          radius="12"
          fit="cover"
          src={src || undefined}
          placeholder={variant === "person" ? "Drop headshot" : "Drop logo"}
        ></image-slot>
      )}
      <span className="tile__tag">{tag}</span>
    </div>
  );
}

function DirectoryGrid({ children }) {
  return <div className="grid">{children}</div>;
}

/* ===== Compact firm chip (More firms — logo + name only) ===== */
function FirmChip({ firm, onClick }) {
  return (
    <button className="firmchip" onClick={onClick}>
      <span className="firmchip__swatch" style={{ background: firm.bg, color: firm.ink }}>
        {firm.logoSrc
          ? <img className="firmchip__logo" src={firm.logoSrc} alt="" />
          : firm.name.split(" ").map((w) => w[0]).slice(0, 2).join("")}
      </span>
      <span className="firmchip__name">{firm.name}</span>
    </button>
  );
}

function initialsOf(name) {
  return name.split(" ").map((w) => w[0]).slice(0, 2).join("");
}

/* ===== Breadcrumbs ===== */
/* crumbs: [{ label, onClick? }] — last item is current (no onClick) */
function Breadcrumbs({ crumbs }) {
  if (!crumbs || crumbs.length < 2) return null;
  return (
    <nav className="breadcrumbs" aria-label="Breadcrumb">
      {crumbs.map((c, i) => (
        <React.Fragment key={i}>
          {i > 0 && <span className="breadcrumbs__sep" aria-hidden="true">/</span>}
          {c.onClick ? (
            <button className="breadcrumbs__item breadcrumbs__item--link" onClick={c.onClick}>{c.label}</button>
          ) : (
            <span className="breadcrumbs__item breadcrumbs__item--current">{c.label}</span>
          )}
        </React.Fragment>
      ))}
    </nav>
  );
}

Object.assign(window, {
  Pill, ArrowPill, SearchBar, GlobalSearch, SubscribeForm, InlineSubscribe, NewsletterPopup, WelcomePopup, ResearchUpgrade, SiteFooter, Carousel, FundCard, PersonCard,
  OutlawLogo, TopBar, Tag, TagRow, Tile, DirectoryGrid, FirmChip, initialsOf, Breadcrumbs,
});
