const { useState, useEffect } = React;

/* ============================================================
   THRYVX buyer-first product studio site
   Same lab aesthetic, plain-language content,
   honest + explainable visuals.
   ============================================================ */

/* ---------- Tiny atoms ---------- */

const Mono = ({ children, style }) =>
  <span style={{ fontFamily: "'JetBrains Mono', ui-monospace, monospace", letterSpacing: "-0.01em", ...style }}>{children}</span>;

const Tag = ({ children, tone = "default" }) => {
  const tones = {
    default: { bg: "rgba(19,19,24,0.06)", color: "var(--ink-soft)", border: "1px solid rgba(19,19,24,0.08)" },
    live: { bg: "rgba(201,81,42,0.12)", color: "var(--accent)", border: "1px solid rgba(201,81,42,0.25)" },
    dev: { bg: "rgba(19,19,24,0.04)", color: "var(--ink-soft)", border: "1px dashed rgba(19,19,24,0.30)" }
  };
  return (
    <Mono style={{
      ...tones[tone],
      padding: "4px 10px",
      borderRadius: 999,
      fontSize: 11,
      textTransform: "uppercase",
      letterSpacing: "0.08em",
      display: "inline-flex",
      alignItems: "center",
      gap: 6,
      whiteSpace: "nowrap"
    }}>
      {tone === "live" && <span style={{ width: 6, height: 6, borderRadius: 999, background: "var(--accent)", boxShadow: "0 0 0 3px rgba(201,81,42,0.20)" }} />}
      {children}
    </Mono>);
};

const PrimaryBtn = ({ children, onClick, style }) =>
  <button onClick={onClick} className="primary-btn" style={style}>
    <span>{children}</span>
    <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /></svg>
  </button>;

const GhostBtn = ({ children, onClick, style }) =>
  <button onClick={onClick} className="ghost-btn" style={style}>{children}</button>;

function Logo() {
  return (
    <div className="brand-mark" aria-label="ThryvX Studio">
      <img src="logo.png" alt="" className="brand-logo" />
      <span className="brand-name">Thryv<span>X</span></span>
      <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.16em" }}>Studio</Mono>
    </div>);
}

/* ---------- NAV ---------- */

function Nav({ onContact }) {
  const [scrolled, setScrolled] = useState(false);

  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 72);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  return (
    <nav className={"nav " + (scrolled ? "is-scrolled" : "")}>
      <div className="nav-inner">
        <div className="nav-brand">
          <Logo />
        </div>

        <div className="nav-status">
          <span className="dot" />
          <Mono style={{ fontSize: 11, color: "var(--ink-soft)", textTransform: "uppercase", letterSpacing: "0.12em" }}>
            Taking on 2 new partners this quarter
          </Mono>
        </div>

        <div className="nav-links">
          <a href="#focus">What we do</a>
          <a href="#how">How we work</a>
          <a href="#team">Leadership</a>
          <a href="#work">Our work</a>
          <PrimaryBtn onClick={onContact}>Start a conversation</PrimaryBtn>
        </div>
      </div>
    </nav>);
}

/* ---------- HERO ---------- */

function Hero({ onContact }) {
  return (
    <section className="hero">
      <div className="hero-grid">
        <div className="hero-left">
          <div className="hero-meta">
            <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>
              An independent engineering studio
            </Mono>
            <span className="hero-meta-line" />
            <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>
              Senior-led · enterprise pedigree
            </Mono>
          </div>

          <h1 className="hero-title">
            Put AI to work.
            <br />
            Build software that <em>lasts.</em>
          </h1>

          <p className="hero-sub">
            ThryvX helps businesses use AI in ways that actually pay off, and build the apps and platforms
            their operations depend on, secure from day one, fully owned by you, and engineered to
            outlast the hype.
          </p>

          <div className="hero-cta">
            <PrimaryBtn onClick={onContact}>Start a conversation</PrimaryBtn>
            <GhostBtn onClick={() => document.getElementById("how").scrollIntoView({ behavior: "smooth" })}>
              See how we work
              <Mono style={{ marginLeft: 8, color: "var(--muted)" }}>↓</Mono>
            </GhostBtn>
          </div>

          <div className="hero-paradigms">
            <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.20em" }}>
              What we help with
            </Mono>
            <div className="paradigm-row">
              <span>AI that earns its keep</span>
              <span className="dot-sep">·</span>
              <span>Apps, portals &amp; platforms</span>
              <span className="dot-sep">·</span>
              <span>Security &amp; trust</span>
              <span className="dot-sep">·</span>
              <span>Systems that scale</span>
            </div>
          </div>
        </div>

        <div className="hero-right">
          <LeverageCard />
        </div>
      </div>
    </section>);
}

/* ---------- Leverage visual (honest, explainable) ---------- */

function LeverageCard() {
  const W = 320, H = 200, P = 28;
  // Two illustrative paths. "Add people": value rises in proportion to cost (straight line).
  // "Build leverage": a small team + good systems keeps producing more over time (gentle curve).
  const linear = `M ${P} ${H - P} L ${W - P} ${P + 36}`;
  const pts = [];
  for (let i = 0; i <= 40; i++) {
    const t = i / 40;
    const x = P + t * (W - P * 2);
    // ease-out style curve that overtakes the straight line
    const y = (H - P) - Math.pow(t, 0.62) * (H - P * 2);
    pts.push([x, y]);
  }
  const lever = "M " + pts.map(p => `${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(" L ");

  return (
    <div className="accel-card">
      <div className="accel-head">
        <Mono style={{ fontSize: 10, textTransform: "uppercase", letterSpacing: "0.18em", color: "var(--muted)" }}>
          Two ways to grow
        </Mono>
      </div>

      <div className="accel-title">
        <span style={{ fontFamily: "'Bricolage Grotesque', sans-serif", fontWeight: 600, fontSize: 20, letterSpacing: "-0.02em" }}>
          Leverage, not headcount
        </span>
      </div>

      <svg viewBox={`0 0 ${W} ${H}`} className="accel-svg">
        {/* axes */}
        <line x1={P} y1={P} x2={P} y2={H - P} stroke="var(--ink)" strokeWidth="1" opacity="0.35" />
        <line x1={P} y1={H - P} x2={W - P} y2={H - P} stroke="var(--ink)" strokeWidth="1" opacity="0.35" />

        {/* "add people" straight line */}
        <path d={linear} stroke="var(--ink)" strokeWidth="1.5" strokeDasharray="4 4" opacity="0.55" fill="none" />

        {/* "build leverage" curve */}
        <path d={`${lever} L ${W - P} ${H - P} L ${P} ${H - P} Z`} fill="var(--accent)" opacity="0.10" />
        <path d={lever} stroke="var(--accent)" strokeWidth="2.5" fill="none" />
        <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r="4.5" fill="var(--accent)" />

        {/* axis labels */}
        <text x={P} y={P - 8} fontSize="9" fill="var(--muted)" fontFamily="'JetBrains Mono'">WHAT YOU GET</text>
        <text x={W - P - 24} y={H - 6} fontSize="9" fill="var(--muted)" fontFamily="'JetBrains Mono'">TIME →</text>
      </svg>

      <div className="accel-legend">
        <div>
          <span className="legend-swatch" style={{ background: "var(--accent)" }} />
          <Mono style={{ fontSize: 11, color: "var(--ink-soft)" }}>A studio that builds leverage</Mono>
        </div>
        <div>
          <span className="legend-swatch" style={{ background: "var(--ink)", opacity: 0.55 }} />
          <Mono style={{ fontSize: 11, color: "var(--muted)" }}>A shop that adds people</Mono>
        </div>
      </div>

      <p className="accel-caption">
        <strong>What this shows:</strong> a typical agency grows your output by billing more hours. Cost
        and results rise together, forever. We build systems that do the heavy lifting, so a senior-led team
        keeps delivering more without you paying for an ever-growing crew.
      </p>
      <span className="accel-note">Illustrative concept</span>
    </div>);
}

/* ---------- CHASM BAND ---------- */

function ChasmBand() {
  return (
    <section className="chasm">
      <p className="chasm-text">
        There's a chasm between wanting AI and actually running on it, between a promising prototype and a
        platform you can trust. Crossing it is the hard part. <em>That's the part we do.</em>
      </p>
    </section>);
}

/* ---------- WHAT WE DO ---------- */

const FOCUS = [
  {
    code: "01",
    sub: "AI Enablement",
    problem: "“Everyone says we need AI. Where does it actually help us?”",
    title: "Put AI to work",
    body: "We find the places in your business where AI genuinely saves time or money, build it into the tools your team already uses, and make sure it keeps earning its keep. No science projects, no buzzwords.",
    bullets: ["Find the use cases worth doing", "Build it into real workflows", "Prove it actually helps"],
    glyph:
      <svg viewBox="0 0 80 80" width="100%" height="100%">
        <circle cx="40" cy="40" r="6" fill="var(--accent)" />
        {[0, 60, 120, 180, 240, 300].map((a, i) => {
          const r = 26;
          const x = 40 + Math.cos(a * Math.PI / 180) * r;
          const y = 40 + Math.sin(a * Math.PI / 180) * r;
          return <g key={i}><line x1="40" y1="40" x2={x} y2={y} stroke="var(--ink)" strokeWidth="0.8" opacity="0.5" /><circle cx={x} cy={y} r="3" fill="var(--ink)" /></g>;
        })}
      </svg>
  },
  {
    code: "02",
    sub: "Apps, Portals & Platforms",
    problem: "“We need real software built, and we want to own it.”",
    title: "Build the platform you run on",
    body: "A customer portal, an internal tool, or the core platform your whole business runs on. We design and build software that fits how you actually work, replaces brittle manual processes, and is yours to keep.",
    bullets: ["Customer & internal platforms", "Replace manual, error-prone steps", "Built to grow with you"],
    glyph:
      <svg viewBox="0 0 80 80" width="100%" height="100%">
        <rect x="14" y="14" width="22" height="22" stroke="var(--ink)" fill="none" />
        <rect x="44" y="14" width="22" height="22" stroke="var(--ink)" fill="none" strokeDasharray="2 2" />
        <rect x="14" y="44" width="22" height="22" stroke="var(--ink)" fill="none" strokeDasharray="2 2" />
        <rect x="44" y="44" width="22" height="22" stroke="var(--accent)" fill="var(--accent)" fillOpacity="0.15" />
        <line x1="36" y1="25" x2="44" y2="25" stroke="var(--accent)" strokeWidth="1" />
        <line x1="55" y1="36" x2="55" y2="44" stroke="var(--accent)" strokeWidth="1" />
      </svg>
  },
  {
    code: "03",
    sub: "Security & Reliability",
    problem: "“It has to be secure, and it has to keep working as we grow.”",
    title: "Make it secure and built to last",
    body: "Software that handles real data and real money has to be trustworthy. We build with security and reliability from the first line, and clean up the aging systems that slow you down, so what you ship today still stands in five years.",
    bullets: ["Security built in from day one", "Reliable as you scale up", "No lock-in, no decay"],
    glyph:
      <svg viewBox="0 0 80 80" width="100%" height="100%">
        <path d="M40 12 L62 22 V40 C62 54 52 64 40 68 C28 64 18 54 18 40 V22 Z" stroke="var(--ink)" fill="none" strokeWidth="1.2" />
        <path d="M31 40 L37 46 L50 31" stroke="var(--accent)" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
  }
];

function FocusSection() {
  return (
    <section className="focus" id="focus">
      <div className="section-head">
        <div>
          <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>What we do</Mono>
          <h2 className="section-title">Three problems we're built to solve.</h2>
          <p className="section-sub">
            Most people come to us with one of these. We start from your problem, not our tech.
          </p>
        </div>
      </div>

      <div className="focus-grid">
        {FOCUS.map((f) =>
          <article key={f.code} className="focus-card">
            <div className="focus-card-head">
              <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>{f.code}</Mono>
              <div className="focus-glyph">{f.glyph}</div>
            </div>

            <div className="focus-card-body">
              <Mono style={{ fontSize: 11, color: "var(--accent)", textTransform: "uppercase", letterSpacing: "0.12em" }}>{f.sub}</Mono>
              <p className="focus-problem">{f.problem}</p>
              <h3 className="focus-title">{f.title}</h3>
              <p dangerouslySetInnerHTML={{ __html: f.body }} />
            </div>

            <ul className="focus-bullets">
              {f.bullets.map((b) =>
                <li key={b}>
                  <span className="bullet-tick">+</span>
                  <Mono style={{ fontSize: 12, color: "var(--ink-soft)" }}>{b}</Mono>
                </li>
              )}
            </ul>
          </article>
        )}
      </div>
    </section>);
}

/* ---------- HOW WE'RE DIFFERENT ---------- */

const HOW = [
  {
    num: "01",
    title: "Senior people, on your project",
    body: "The people you talk to are the people who build. No handing your work to a rotating cast of junior contractors after the pitch."
  },
  {
    num: "02",
    title: "You own everything",
    body: "The code, the infrastructure, the decisions, all yours. We build so you're never trapped depending on us. If you want to take it in-house later, you can."
  },
  {
    num: "03",
    title: "We're honest about fit",
    body: "If we're not the right team for your problem, we'll say so and point you to someone who is. We'd rather do one thing excellently than ten things halfway."
  }
];

function HowSection() {
  return (
    <section className="how" id="how">
      <div className="how-head">
        <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>How we work</Mono>
        <h2 className="section-title">We grow your capability, <em>not our headcount.</em></h2>
        <p className="section-sub">
          Most software shops make money by putting more people on your project. That's not us. We're a
          senior-led team that builds the systems that do the heavy lifting, so a lot gets done without an
          army of contractors, and without you depending on us forever.
        </p>
      </div>

      <div className="how-grid">
        {HOW.map((h) =>
          <div key={h.num} className="how-card">
            <div className="how-card-num">{h.num}</div>
            <h3>{h.title}</h3>
            <p>{h.body}</p>
          </div>
        )}
      </div>
    </section>);
}

/* ---------- LEADERSHIP ---------- */

const LEADERS = [
  {
    role: "Founder & Chief Architect",
    name: "Elamukil Tamil",
    line: "Two decades building software that isn't allowed to fail.",
    body: "Tamil has spent nearly twenty years where mistakes are expensive. At Citi, he was part of the engineering team that rebuilt the bank's US mobile app in native code, and contributed to its Apple Watch app that shipped for the watch's launch day. The kind of work where millions of people notice if anything breaks. At SAP he led a global architecture team solving the same problem at a different scale. Along the way, “make it solid, make it secure, make it last” stopped being a standard and became an instinct, and it's the instinct behind everything ThryvX builds.",
    pedigree: ["Citi · AVP, Digital Banking", "SAP Labs · Principal Architect", "MBA, IIM Kozhikode", "MS, NIT Trichy"]
  },
  {
    role: "Co-founder, Product & Delivery",
    name: "Arul Arasu Velusamy",
    line: "Lives in the gap between a good idea and a thing that actually works.",
    body: "Arul has spent his career turning tangled, high-stakes requirements into product that ships. In banking and fintech, where one overlooked detail becomes a fraud case or a compliance breach, he's led product and delivery across dozens of parallel engineering streams, from identity and fraud prevention to AI-powered platforms. Earlier, he spent six years rebuilding how a major lender ran its operations, shoulder-to-shoulder with risk and compliance. He's the one who makes sure the technology is solving the real problem, not an interesting version of it.",
    pedigree: ["Fintech & digital banking", "TVS Credit · 6 years", "MBA, IIM Kozhikode"]
  }
];

function LeadershipSection() {
  return (
    <section className="lead" id="team">
      <div className="lead-head">
        <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Leadership</Mono>
        <h2 className="section-title">Built and led by people who've done this <em>at scale.</em></h2>
      </div>

      <div className="lead-story">
        <p className="lead-story-lead">ThryvX began as a disagreement at IIM Kozhikode.</p>
        <p>
          Tamil, twenty years into building software for banks, argued that most products fail because the
          business never really understood what was being built. Arul, who'd spent his career shipping regulated
          fintech, argued the opposite: that engineers build elegant things nobody asked for. They were
          both right.
        </p>
        <p>
          The gap between those two failures is where most projects die, and neither of them could fill it alone.
          So they stopped arguing and built the thing that could. That's ThryvX: deep engineering and real product
          judgment, in the same room, on purpose. Now growing a senior team to do the same for you.
        </p>
      </div>

      <div className="lead-grid">
        {LEADERS.map((p) =>
          <article key={p.name} className="lead-card">
            <div className="lead-avatar" aria-hidden="true">
              {p.name.split(" ").slice(0, 2).map((n) => n[0]).join("")}
            </div>
            <Mono style={{ fontSize: 11, color: "var(--accent)", textTransform: "uppercase", letterSpacing: "0.12em" }}>{p.role}</Mono>
            <h3 className="lead-name">{p.name}</h3>
            <p className="lead-line">{p.line}</p>
            <p className="lead-body">{p.body}</p>
            <ul className="lead-pedigree">
              {p.pedigree.map((b) => <li key={b}>{b}</li>)}
            </ul>
          </article>
        )}
      </div>
    </section>);
}

/* ---------- OUR WORK ---------- */

const VENTURES = [
  {
    id: "verveli",
    name: "Verveli",
    code: "01",
    paradigm: "Networks you can trust",
    status: "production",
    statusLabel: "Live",
    href: "https://verveli.app",
    short: "A trusted network for alumni communities.",
    long: "Turns static alumni directories into living, verified networks, so institutions and their members can actually find each other and trust who's who.",
    metrics: [
      { k: "stage", v: "Live" },
      { k: "area", v: "Alumni networks" },
      { k: "built by", v: "ThryvX" }
    ],
    visual: "graph",
    caption: "A living trust graph for alumni, companies, and opportunity"
  },
  {
    id: "identity-link",
    name: "Identity-Link",
    code: "02",
    paradigm: "Documents you can verify",
    status: "dev",
    statusLabel: "In development",
    short: "Offer letters and onboarding documents you can verify are real.",
    long: "Closes the gap between hiring systems and onboarding with documents that can be independently checked as genuine, cutting fraud and the manual back-and-forth of verifying paperwork.",
    metrics: [
      { k: "stage", v: "In development" },
      { k: "area", v: "Hiring & HR" },
      { k: "with", v: "Early partners" }
    ],
    visual: "doc",
    caption: "A document you can verify"
  },
  {
    id: "proof-of-work",
    name: "Proof-of-Work",
    code: "03",
    paradigm: "Proof you can check",
    status: "dev",
    statusLabel: "Early",
    short: "Verifiable proof of real work for early-career talent.",
    long: "Helps students and early founders show real, checkable evidence of what they've actually built and done, instead of resumes nobody can verify.",
    metrics: [
      { k: "stage", v: "Early" },
      { k: "area", v: "Hiring & credentials" },
      { k: "status", v: "Building" }
    ],
    visual: "blocks",
    caption: "A chain of checkable records"
  }
];

function VenturesSection() {
  const [active, setActive] = useState("verveli");
  const v = VENTURES.find((x) => x.id === active);
  const activateFromKey = (e, id) => {
    if (e.key !== "Enter" && e.key !== " ") return;
    e.preventDefault();
    setActive(id);
  };

  return (
    <section className="ventures" id="work">
      <div className="section-head">
        <div>
          <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Our work</Mono>
          <h2 className="section-title">Our own products are our <em>R&amp;D.</em></h2>
          <p className="section-sub">
            Building for ourselves is how we stay ahead of the tools and patterns most teams are still reading
            about. That edge goes straight into the work we do for you.
          </p>
        </div>
        <div className="ventures-counter">
          <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.14em" }}>
            {VENTURES.findIndex((x) => x.id === active) + 1} / {VENTURES.length}
          </Mono>
        </div>
      </div>

      <div className="ventures-table">
        <div className="ventures-table-head">
          <Mono>Product</Mono>
          <Mono>What it does</Mono>
          <Mono>Status</Mono>
          <Mono></Mono>
        </div>
        {VENTURES.map((x) =>
          <div
            key={x.id}
            role="button"
            tabIndex="0"
            onClick={() => setActive(x.id)}
            onKeyDown={(e) => activateFromKey(e, x.id)}
            className={"ventures-row " + (active === x.id ? "is-active" : "")}>
            <div className="vt-name">
              <Mono style={{ color: "var(--muted)", fontSize: 11, marginRight: 12 }}>{x.code}</Mono>
              <span>{x.name}</span>
              {x.href &&
                <a className="vt-external" href={x.href} target="_blank" rel="noreferrer"
                  aria-label={`Open ${x.name} in a new tab`}
                  onClick={(e) => e.stopPropagation()}>
                  <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
                    <path d="M5 3.2H3.4a1.4 1.4 0 0 0-1.4 1.4v6a1.4 1.4 0 0 0 1.4 1.4h6a1.4 1.4 0 0 0 1.4-1.4V9" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
                    <path d="M8 2h4v4M7 7l4.6-4.6" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </a>
              }
            </div>
            <div className="vt-paradigm">{x.paradigm}</div>
            <div>
              {x.href ?
                <a className="status-link" href={x.href} target="_blank" rel="noreferrer"
                  aria-label={`Open ${x.name}`}
                  onClick={(e) => e.stopPropagation()}>
                  <Tag tone={x.status === "production" ? "live" : "dev"}>{x.statusLabel}</Tag>
                </a> :
                <Tag tone={x.status === "production" ? "live" : "dev"}>{x.statusLabel}</Tag>
              }
            </div>
            <div className="vt-arrow">
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" /></svg>
            </div>
          </div>
        )}
      </div>

      <div className="venture-detail">
        <div className="vd-left">
          <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.16em" }}>
            {v.code} · {v.paradigm}
          </Mono>
          <h3 className="vd-name">
            {v.name}
            {v.href &&
              <a className="vd-external" href={v.href} target="_blank" rel="noreferrer" aria-label={`Open ${v.name} in a new tab`}>
                <svg width="18" height="18" viewBox="0 0 14 14" fill="none">
                  <path d="M5 3.2H3.4a1.4 1.4 0 0 0-1.4 1.4v6a1.4 1.4 0 0 0 1.4 1.4h6a1.4 1.4 0 0 0 1.4-1.4V9" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
                  <path d="M8 2h4v4M7 7l4.6-4.6" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
              </a>
            }
          </h3>
          <p className="vd-short">{v.short}</p>
          <p className="vd-long">{v.long}</p>

          <div className="vd-metrics">
            {v.metrics.map((m) =>
              <div key={m.k}>
                <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.14em" }}>{m.k}</Mono>
                <div className="vd-metric-v">{m.v}</div>
              </div>
            )}
          </div>

          <div className="vd-actions">
            {v.href ?
              <a className="status-link" href={v.href} target="_blank" rel="noreferrer" aria-label={`Open ${v.name}`}>
                <Tag tone={v.status === "production" ? "live" : "dev"}>{v.statusLabel}</Tag>
              </a> :
              <Tag tone={v.status === "production" ? "live" : "dev"}>{v.statusLabel}</Tag>
            }
          </div>
        </div>

        <div className="vd-right">
          <VentureVisual kind={v.visual} />
          <div className="vv-caption">{v.caption}</div>
        </div>
      </div>
    </section>);
}

function VentureVisual({ kind }) {
  if (kind === "graph") return <GraphVisual />;
  if (kind === "doc") return <DocVisual />;
  return <BlocksVisual />;
}

function GraphVisual() {
  const svgRef = React.useRef(null);
  const dragRef = React.useRef(null);
  const simRef = React.useRef(null);
  const [, setTick] = React.useState(0);
  const GR = 36, CX = 50, CY = 50;
  const ENABLE_GLOBE_EVENTS = true;

  if (!simRef.current) {
    const C = 16, PER = 5, BG = 340;
    const coColors = ['#C9512A','#5F6472','#7E725F','#6D7583','#8B6658','#565F68'];
    const nodes = [];
    const edges = [];

    // 0: Hub — pinned at center always
    nodes.push({ hub: true, r: 4 });

    // 1..C: Companies at modest latitudes so the action stays on the readable front face.
    for (let i = 0; i < C; i++) {
      const lon = (i / C) * Math.PI * 2;
      const lat = Math.sin(i * 1.7) * 0.58;
      nodes.push({ lon, lat, company: true, color: coColors[i % coColors.length], r: 1.9, pulse: i === 0 ? 1 : 0 });
    }

    // C+1..C+C*PER: People clustered around companies
    for (let i = 0; i < C; i++) {
      const co = nodes[i + 1];
      for (let j = 0; j < PER; j++) {
        const pidx = nodes.length;
        nodes.push({
          lon: co.lon + (Math.random() - 0.5) * 1.15,
          lat: Math.max(-0.92, Math.min(0.92, co.lat + (Math.random() - 0.5) * 0.58)),
          member: true, r: 0.78 + Math.random() * 0.34,
          dlon: (Math.random() - 0.5) * 0.00022, dlat: (Math.random() - 0.5) * 0.00016
        });
      }
    }

    const structuredEnd = nodes.length; // boundary before BG nodes

    // BG: uniform sphere dots — these define the globe shape, including light polar caps.
    for (let i = 0; i < BG; i++) {
      const cap = Math.random() < 0.22;
      const u = cap
        ? (Math.random() < 0.5 ? -1 : 1) * (0.76 + Math.random() * 0.2)
        : (Math.random() * 2 - 1) * 0.92;
      nodes.push({
        lon: Math.random() * Math.PI * 2,
        lat: Math.asin(u),
        r: cap ? 0.24 + Math.random() * 0.34 : 0.32 + Math.random() * 0.48,
        bg: true,
        cap,
        dlon: (Math.random() - 0.5) * 0.00012,
        morphAlpha: 0,
        morphTimer: 260 + Math.floor(Math.random() * 900)
      });
    }

    // Connected sphere mesh: every dot participates through its nearest neighbors.
    const meshPairs = new Set();
    for (let a = 1; a < nodes.length; a++) {
      const near = [];
      for (let b = 1; b < nodes.length; b++) {
        if (a === b) continue;
        const rawLon = Math.abs((nodes[a].lon || 0) - (nodes[b].lon || 0));
        const dl = Math.min(rawLon, Math.PI * 2 - rawLon);
        const dt = Math.abs((nodes[a].lat || 0) - (nodes[b].lat || 0));
        const d = Math.sqrt(dl * dl + dt * dt);
        if (d > 0.04 && d < 0.34) near.push({ b, d });
      }
      near.sort((m, n) => m.d - n.d);
      for (const n of near.slice(0, 4)) {
        const key = a < n.b ? `${a}-${n.b}` : `${n.b}-${a}`;
        if (!meshPairs.has(key)) {
          meshPairs.add(key);
          edges.push({ a, b: n.b, alpha: 0.58, fixed: true, kind: "mesh" });
        }
      }
    }

    simRef.current = { nodes, edges, frame: 0, C, PER, structuredEnd, rotOffset: 0 };
  }

  React.useEffect(() => {
    let raf;
    const step = () => {
      const SIM = simRef.current;
      const { nodes } = SIM;
      const frame = SIM.frame++;

      // Drift non-hub, non-company nodes on sphere
      for (let i = 1; i < nodes.length; i++) {
        const nd = nodes[i];
        if (nd.company) continue;
        if (nd.dlon) nd.lon += nd.dlon;
        if (nd.dlat) nd.lat = Math.max(-1.26, Math.min(1.26, nd.lat + nd.dlat));
        if (nd.pulse) nd.pulse = Math.max(0, nd.pulse - 0.018);
      }

      const rotForEvents = SIM.frame * 0.003 + SIM.rotOffset;
      const frontZ = (nd, rot = rotForEvents) => Math.cos(nd.lat || 0) * Math.sin((nd.lon || 0) + rot);
      const sphereDist = (a, b) => {
        const rawLon = Math.abs((a.lon || 0) - (b.lon || 0));
        const dl = Math.min(rawLon, Math.PI * 2 - rawLon);
        const dt = Math.abs((a.lat || 0) - (b.lat || 0));
        return Math.sqrt(dl * dl + dt * dt);
      };
      const addEventLinks = (idx, count = 3) => {
        const source = nodes[idx];
        if (!source) return;
        const nearby = nodes
          .map((nd, i) => ({ nd, i, d: sphereDist(source, nd), z: frontZ(nd) }))
          .filter(({ nd, i, d, z }) => i !== idx && !nd.hub && z > 0.08 && d > 0.04 && d < 0.42)
          .sort((a, b) => a.d - b.d);
        for (const target of nearby.slice(0, count)) {
          const exists = SIM.edges.some(e =>
            !e.fixed && !e.shrinking &&
            ((e.a === idx && e.b === target.i) || (e.a === target.i && e.b === idx))
          );
          if (exists) continue;
          SIM.edges.push({ a: idx, b: target.i, alpha: 0, growing: true, fixed: false, age: 0, kind: "pulse" });
          target.nd.pulse = Math.max(target.nd.pulse || 0, 0.45);
        }
      };

      if (ENABLE_GLOBE_EVENTS && frame > 30 && frame % 18 === 0) {
        const candidates = nodes
          .map((nd, i) => ({ nd, i }))
          .filter(({ nd }) => nd.bg && !nd.morphing && !nd.morphed && !nd.reverting && frontZ(nd) > 0.18)
          .map(item => {
            const crowd = nodes.reduce((sum, other) => {
              if (!other.eventKind || other.reverting) return sum;
              return sum + (sphereDist(item.nd, other) < 0.42 ? 1 : 0);
            }, 0);
            return { ...item, score: crowd + Math.random() * 0.8 };
          })
          .sort((a, b) => a.score - b.score);
        const pick = candidates[0];
        if (pick) {
          const nd = pick.nd;
          const isCompany = Math.random() < 0.18;
          nd.r = isCompany ? 0.82 + Math.random() * 0.32 : 0.58 + Math.random() * 0.28;
          nd.morphing = true;
          nd.morphAlpha = 0;
          nd.eventKind = isCompany ? "company" : "member";
          nd.pulse = 1;
          addEventLinks(pick.i, isCompany ? 5 : 4);
        }
      }

      if (ENABLE_GLOBE_EVENTS) {
        // Morph only front-facing BG dots so new member/company activity happens where it can be seen.
        for (const nd of nodes) {
          if (!nd.bg) continue;
          const z = Math.cos(nd.lat || 0) * Math.sin((nd.lon || 0) + rotForEvents);
          if (!nd.morphing && !nd.morphed && !nd.reverting) {
            nd.morphTimer = (nd.morphTimer || 0) - 1;
            if (nd.morphTimer <= 0 && z > 0.12) {
              nd.morphTimer = 400 + Math.floor(Math.random() * 900);
            }
          }
          if (nd.morphing) {
            nd.morphAlpha = Math.min(1, nd.morphAlpha + 0.025);
            if (nd.morphAlpha >= 1) { nd.morphing = false; nd.morphed = true; nd.morphTimer = 120 + Math.floor(Math.random() * 150); }
          }
          if (nd.morphed) {
            nd.morphTimer--;
            if (nd.morphTimer <= 0 || z < -0.02) { nd.morphed = false; nd.reverting = true; }
          }
          if (nd.reverting) {
            nd.morphAlpha = Math.max(0, nd.morphAlpha - 0.025);
            if (nd.morphAlpha <= 0) { nd.reverting = false; nd.morphAlpha = 0; nd.eventKind = null; nd.morphTimer = 520 + Math.floor(Math.random() * 900); }
          }
        }
        for (const e of SIM.edges) {
          if (!e.fixed && !e.shrinking && (nodes[e.a]?.reverting || nodes[e.b]?.reverting)) e.shrinking = true;
        }
      }

      // Dynamic edge lifecycle — rotation-driven connections
      if (ENABLE_GLOBE_EVENTS && frame > 40) {
        // Add or refresh local relationships around currently visible event dots.
        if (frame % 14 === 0) {
          const rot = SIM.frame * 0.003 + SIM.rotOffset;
          const active = nodes
            .map((nd, i) => ({ nd, i, z: frontZ(nd, rot) }))
            .filter(({ nd, z }) => nd.eventKind && !nd.reverting && z > 0.14);
          const pick = active[Math.floor(Math.random() * active.length)];
          if (pick) addEventLinks(pick.i, pick.nd.eventKind === "company" ? 3 : 2);
          const liveEdges = SIM.edges.filter(e => !e.fixed).length;
          if (liveEdges > 62) {
            const old = SIM.edges.find(e => !e.fixed && !e.growing && !e.shrinking);
            if (old) old.shrinking = true;
          }
        }

        // Age non-fixed edges — dissolve when old or endpoints drift to back face
        const rot = SIM.frame * 0.003 + SIM.rotOffset;
        for (const e of SIM.edges) {
          if (e.fixed || e.shrinking || e.growing) continue;
          e.age = (e.age || 0) + 1;
          const za = Math.cos(nodes[e.a].lat||0) * Math.sin((nodes[e.a].lon||0) + rot);
          const zb = Math.cos(nodes[e.b].lat||0) * Math.sin((nodes[e.b].lon||0) + rot);
          const bothBack = za < -0.04 || zb < -0.04;
          const maxAge = e.kind === "bridge" ? 320 : e.kind === "hub" ? 260 : 160;
          const tooOld = e.age > maxAge + Math.floor(Math.random() * 180);
          if (bothBack || tooOld) e.shrinking = true;
        }
      }
      for (let ei = SIM.edges.length - 1; ei >= 0; ei--) {
        const e = SIM.edges[ei];
        if (e.growing) { e.alpha = Math.min(1, e.alpha + 0.022); if (e.alpha >= 1) { e.growing = false; e.age = 0; } }
        if (e.shrinking) { e.alpha = Math.max(0, e.alpha - 0.012); if (e.alpha <= 0) SIM.edges.splice(ei, 1); }
      }

      setTick((t) => (t + 1) % 1000000);
      raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, []);

  const onGlobeDown = (e) => { dragRef.current = { x: e.clientX, rot: simRef.current.rotOffset }; };
  const onGlobeMove = (e) => {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.x;
    const w = svgRef.current ? svgRef.current.getBoundingClientRect().width : 300;
    simRef.current.rotOffset = dragRef.current.rot + (dx / w) * Math.PI * 2;
  };
  const onGlobeUp = () => { dragRef.current = null; };

  const SIM = simRef.current;
  const rotY = SIM.frame * 0.003 + SIM.rotOffset;
  const project = (lon, lat) => {
    const x3 = GR * Math.cos(lat) * Math.cos(lon + rotY);
    const y3 = GR * Math.sin(lat);
    const z3 = GR * Math.cos(lat) * Math.sin(lon + rotY);
    return { x: CX + x3, y: CY - y3, z: z3, depth: (z3 + GR) / (2 * GR) };
  };

  const { nodes, edges } = SIM;
  // Hub pinned at center, rendered behind all dots (very low z so it sorts to back)
  const proj = nodes.map((nd, i) => i === 0 ? { x: CX, y: CY, z: -GR * 99, depth: 1 } : project(nd.lon || 0, nd.lat || 0));
  const sorted = nodes.map((_, i) => i).sort((a, b) => proj[a].z - proj[b].z);
  const renderHub = () => {
    const hr = 5.1;
    const hrot = SIM.frame * 0.006 + SIM.rotOffset;
    return (
      <g>
        <circle cx={CX} cy={CY} r={hr + 5.6} fill="rgba(201,81,42,0.04)" filter="url(#hub-soft-glow)" />
        <circle cx={CX} cy={CY} r={hr + 3.2} fill="none" stroke="rgba(201,81,42,0.22)" strokeWidth="0.35" />
        <circle cx={CX} cy={CY} r={hr + 1.4} fill="rgba(245,242,234,0.72)" />
        <circle cx={CX} cy={CY} r={hr} fill="rgba(28,28,58,0.58)" filter="url(#hub-soften)" />
        <g clipPath="url(#hub-gc)" opacity="0.62">
          {[0, Math.PI / 3, 2 * Math.PI / 3].map((base, k) => {
            const a = base + hrot;
            const rx = Math.abs(Math.cos(a)) * hr;
            return rx > 0.12 ? (
              <ellipse key={k} cx={CX} cy={CY} rx={rx} ry={hr}
                fill="none" stroke="rgba(245,242,234,0.32)" strokeWidth="0.38"
                strokeDasharray={Math.cos(a) < 0 ? "0.6 1.2" : undefined} />
            ) : null;
          })}
          {[-2.8, 0, 2.8].map((yOff, k) => {
            const rx = Math.sqrt(Math.max(0, hr * hr - yOff * yOff));
            return <ellipse key={k} cx={CX} cy={CY + yOff} rx={rx} ry={0.85}
              fill="none" stroke="rgba(245,242,234,0.22)" strokeWidth="0.32" />;
          })}
        </g>
        <circle cx={CX} cy={CY} r={hr} fill="rgba(245,242,234,0.08)" />
        <circle cx={CX} cy={CY} r={hr} fill="none" stroke="rgba(245,242,234,0.28)" strokeWidth="0.45" />
        <rect x={CX - 9.8} y={CY + hr + 2.3} width="19.6" height="4.2" rx="2.1"
          fill="rgba(245,242,234,0.72)" stroke="rgba(19,19,24,0.1)" strokeWidth="0.22" />
        <text x={CX} y={CY + hr + 5.25} fontSize="1.65" fill="var(--ink)"
          fontFamily="'JetBrains Mono'" textAnchor="middle" fontWeight="600"
          opacity="0.72" letterSpacing="0.08em" pointerEvents="none">INSTITUTE</text>
      </g>
    );
  };

  return (
    <svg ref={svgRef} viewBox="0 0 100 100" className="vv-svg vv-graph"
      style={{ touchAction: "none", cursor: "grab" }}
      onPointerDown={onGlobeDown} onPointerMove={onGlobeMove}
      onPointerUp={onGlobeUp} onPointerLeave={onGlobeUp}>

      <defs>
        <clipPath id="hub-gc"><circle cx={CX} cy={CY} r={5.5} /></clipPath>
        <filter id="hub-soften" x="-30%" y="-30%" width="160%" height="160%">
          <feGaussianBlur stdDeviation="0.55" />
        </filter>
        <filter id="hub-soft-glow" x="-60%" y="-60%" width="220%" height="220%">
          <feGaussianBlur stdDeviation="1.4" />
        </filter>
      </defs>

      {/* Institute globe sits below the network so connections can cross over it. */}
      {renderHub()}

      {/* Edges */}
      {edges.map((e, i) => {
        const pa = proj[e.a], pb = proj[e.b];
        const avgZ = (pa.z + pb.z) / 2;
        if (avgZ < -GR * 0.05) return null;
        const lineLen = Math.sqrt((pb.x - pa.x) ** 2 + (pb.y - pa.y) ** 2);
        if (lineLen > 18) return null;
        const depthOp = Math.max(0, (avgZ + GR * 0.05) / (GR * 1.05));
        const kindBoost = e.kind === "mesh" ? 0.5 : e.kind === "pulse" || e.kind === "pulseAccent" ? 0.78 : e.kind === "bridge" ? 0.58 : 0.42;
        const op = (e.alpha ?? 1) * depthOp * kindBoost * 0.78;
        if (op <= 0.02) return null;
        const stroke = e.kind === "pulseAccent" || e.accent ? "rgba(201,81,42,0.42)" : e.kind === "bridge" ? "rgba(19,19,24,0.26)" : "rgba(19,19,24,0.28)";
        const width = e.kind === "pulse" || e.kind === "pulseAccent" ? 0.32 : e.kind === "mesh" ? 0.24 : 0.26;
        return <line key={i} x1={pa.x} y1={pa.y} x2={pb.x} y2={pb.y}
          stroke={stroke} strokeWidth={width} opacity={op}
          strokeDasharray={e.kind === "pulseAccent" ? "1.2 1.8" : undefined}
          strokeLinecap="round" />;
      })}

      {/* Nodes back-to-front */}
      {sorted.map(i => {
        const nd = nodes[i], p = proj[i];

        if (nd.hub) return null;

        if (p.z < -GR * 0.06) return null;
        const vis = Math.max(0, (p.z + GR * 0.06) / (GR * 1.06));
        const nodeAlpha = (nd.alpha ?? 1) * Math.max(0.02, vis);
        const r = nd.r * (0.45 + 0.55 * p.depth);
        const { x, y } = p;
        const ringOp = Math.max(0, Math.min(1, nd.pulse || 0)) * Math.max(0, p.z / GR);

        if (nd.bg) {
          const ma = nd.morphAlpha || 0;
          const dotOp = nodeAlpha * 0.72 * (1 - ma);
          const personOp = nodeAlpha * ma;
          const pr = Math.max(r, nd.eventKind === "company" ? 1.55 : 1.25);
          return (
            <g key={i}>
              {dotOp > 0.01 && <circle cx={x} cy={y} r={r} fill="var(--ink)" opacity={dotOp} />}
              {personOp > 0.01 && (
                <g opacity={personOp}>
                  {ringOp > 0.02 && <circle cx={x} cy={y} r={pr + 1.8 + (1 - ringOp) * 1.7}
                    fill="none" stroke="rgba(201,81,42,0.56)" strokeWidth="0.28" opacity={ringOp} />}
                  <circle cx={x} cy={y} r={pr + 0.7} fill="rgba(245,242,234,0.86)" />
                  <circle cx={x} cy={y} r={pr} fill={nd.eventKind === "company" ? "var(--accent)" : "var(--ink)"} opacity="0.78" />
                  {nd.eventKind === "company" && <circle cx={x} cy={y} r={pr + 0.9} fill="none" stroke="var(--accent)" strokeWidth="0.34" opacity="0.78" />}
                  {ringOp > 0.12 && (
                    <g transform={`translate(${x},${y}) scale(${pr / 6})`} fill="none"
                      stroke="var(--card)" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" pointerEvents="none">
                      {nd.eventKind === "company" ? (
                        <>
                          <rect x={-2.3} y={-1.9} width={4.6} height={4.6} rx={0.45} />
                          <path d="M -1 -1.9 V -3 H 1 V -1.9 M -0.9 0 H -0.85 M 0.85 0 H 0.9 M -0.9 1.6 H -0.85 M 0.85 1.6 H 0.9" />
                        </>
                      ) : (
                        <>
                          <circle cx={0} cy={-1.2} r={1.2} />
                          <path d="M -2.4 2.4 C -1.8 0.8 1.8 0.8 2.4 2.4" />
                        </>
                      )}
                    </g>
                  )}
                </g>
              )}
            </g>
          );
        }
        if (nd.company) {
          const sc = r / 9;
          return (
            <g key={i} opacity={nodeAlpha}>
              {ringOp > 0.02 && <circle cx={x} cy={y} r={r + 3 + (1 - ringOp) * 2}
                fill="none" stroke="rgba(201,81,42,0.5)" strokeWidth="0.36" opacity={ringOp} />}
              <circle cx={x} cy={y} r={r + 0.95} fill="none" stroke={nd.color} strokeWidth="0.32" opacity="0.72" />
              <circle cx={x} cy={y} r={Math.max(0.9, r * 0.68)} fill={nd.color} opacity="0.82" />
              {nodeAlpha > 0.42 && (
                <g transform={`translate(${x},${y}) scale(${Math.max(0.9, r) / 6})`} fill="none"
                  stroke="var(--card)" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" pointerEvents="none" opacity="0.9">
                  <rect x={-2.3} y={-1.9} width={4.6} height={4.6} rx={0.45} />
                  <path d="M -1 -1.9 V -3 H 1 V -1.9" />
                </g>
              )}
            </g>
          );
        }
        return (
          <g key={i} opacity={nodeAlpha}>
            {ringOp > 0.02 && <circle cx={x} cy={y} r={r + 2.4 + (1 - ringOp) * 2}
              fill="none" stroke="rgba(201,81,42,0.42)" strokeWidth="0.3" opacity={ringOp} />}
            <circle cx={x} cy={y} r={r} fill="var(--ink)" opacity="0.72" />
          </g>
        );
      })}
    </svg>
  );
}

function DocVisual() {
  return (
    <svg viewBox="0 0 100 100" className="vv-svg">
      <g transform="translate(8, 18) rotate(-4)">
        <rect width="44" height="58" fill="var(--card-alt)" stroke="var(--ink)" strokeWidth="0.4" opacity="0.6" />
      </g>
      <g transform="translate(15, 14) rotate(-1)">
        <rect width="44" height="58" fill="var(--card-alt)" stroke="var(--ink)" strokeWidth="0.4" opacity="0.85" />
      </g>
      <g transform="translate(22, 10)">
        <rect width="44" height="58" fill="var(--card)" stroke="var(--ink)" strokeWidth="0.5" />
        <line x1="6" y1="10" x2="38" y2="10" stroke="var(--ink)" strokeWidth="0.4" />
        <line x1="6" y1="16" x2="30" y2="16" stroke="var(--ink)" strokeWidth="0.3" opacity="0.5" />
        <line x1="6" y1="20" x2="34" y2="20" stroke="var(--ink)" strokeWidth="0.3" opacity="0.5" />
        <line x1="6" y1="24" x2="32" y2="24" stroke="var(--ink)" strokeWidth="0.3" opacity="0.5" />
        <line x1="6" y1="28" x2="36" y2="28" stroke="var(--ink)" strokeWidth="0.3" opacity="0.5" />
      </g>
      <g transform="translate(72, 56)">
        <circle r="10" fill="var(--accent)" opacity="0.12" />
        <circle r="7" fill="none" stroke="var(--accent)" strokeWidth="0.5" />
        <path d="M -3 0 L -1 2 L 3 -2" stroke="var(--accent)" strokeWidth="1" fill="none" strokeLinecap="round" />
      </g>
    </svg>);
}

function BlocksVisual() {
  return (
    <svg viewBox="0 0 100 100" className="vv-svg">
      {[0, 1, 2, 3].map((i) => {
        const x = 8 + i * 22;
        const isLast = i === 3;
        return (
          <g key={i}>
            <rect x={x} y={36} width="18" height="28" fill={isLast ? "var(--accent)" : "var(--card-alt)"} fillOpacity={isLast ? 0.15 : 1} stroke={isLast ? "var(--accent)" : "var(--ink)"} strokeWidth="0.5" />
            <line x1={x + 3} y1={48} x2={x + 15} y2={48} stroke={isLast ? "var(--accent)" : "var(--ink)"} strokeWidth="0.3" opacity="0.5" />
            <line x1={x + 3} y1={51} x2={x + 12} y2={51} stroke={isLast ? "var(--accent)" : "var(--ink)"} strokeWidth="0.3" opacity="0.5" />
            <path d="M 31 40 L 35 44 L 42 36" transform={`translate(${x - 6}, 6)`} stroke={isLast ? "var(--accent)" : "var(--ink)"} strokeWidth="0.6" fill="none" strokeLinecap="round" strokeLinejoin="round" opacity="0.7" />
            {i < 3 && <path d={`M ${x + 18} 50 L ${x + 22} 50`} stroke="var(--ink)" strokeWidth="0.5" opacity="0.6" />}
          </g>);
      })}
    </svg>);
}

/* ---------- ENGAGE ---------- */

function EngageSection({ onContact }) {
  return (
    <section className="engage" id="engage">
      <div className="engage-inner">
        <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Work with us</Mono>

        <h2 className="engage-title">
          Tell us what you're <em>trying to do.</em>
        </h2>

        <div className="engage-body">
          <p>
            No long sales process. Start with a short note about what you're working on and what you'd like to
            change. A senior engineer, not a salesperson, reads every one and replies within a few days.
          </p>
        </div>

        <div className="engage-criteria">
          <Mono style={{ fontSize: 10, color: "var(--accent-soft)", textTransform: "uppercase", letterSpacing: "0.18em" }}>We're a strong fit when</Mono>
          <ul>
            <li>You want a real partner, not just extra pairs of hands</li>
            <li>You care about owning what gets built</li>
            <li>You'd rather do one thing excellently than ten halfway</li>
          </ul>
        </div>

        <PrimaryBtn onClick={onContact} style={{ marginTop: 36, padding: "16px 24px", fontSize: 14 }}>
          Start a conversation
        </PrimaryBtn>
      </div>
    </section>);
}

/* ---------- CONTACT MODAL ---------- */

function ContactModal({ open, onClose }) {
  const [step, setStep] = useState(1);
  const [company, setCompany] = useState("");
  const [goals, setGoals] = useState([]);
  const [success, setSuccess] = useState("");
  const [contact, setContact] = useState("");
  const [submitted, setSubmitted] = useState(false);

  useEffect(() => {
    if (open) { setStep(1); setSubmitted(false); }
  }, [open]);

  const toggleGoal = (g) => {
    setGoals((gs) => gs.includes(g) ? gs.filter((x) => x !== g) : [...gs, g]);
  };

  const goalOpts = [
    { id: "ai", label: "Use AI somewhere in our business", code: "01" },
    { id: "build", label: "Build a new app, portal, or platform", code: "02" },
    { id: "modernize", label: "Fix or modernize something we have", code: "03" },
    { id: "secure", label: "Make our systems more secure & reliable", code: "04" }
  ];

  if (!open) return null;

  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal-sheet" onClick={(e) => e.stopPropagation()}>
        <div className="modal-header">
          <div>
            <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>
              Start a conversation
            </Mono>
            <h3 className="modal-title">Let's talk</h3>
          </div>
          <button className="modal-close" onClick={onClose} aria-label="Close">
            <svg width="16" height="16" viewBox="0 0 16 16"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /></svg>
          </button>
        </div>

        {!submitted ?
          <>
            <p className="modal-intro">
              A few quick questions so we can come prepared. No commitment, and a real engineer reads every one.
            </p>

            <div className="modal-progress">
              {[1, 2, 3, 4].map((n) =>
                <div key={n} className={"mp-pip " + (n <= step ? "is-on" : "")}>
                  <Mono>{String(n).padStart(2, "0")}</Mono>
                </div>
              )}
            </div>

            <div className="modal-form">
              {step === 1 &&
                <div className="form-step">
                  <label>
                    <span className="form-label">01 · Your company</span>
                    <input
                      type="text"
                      value={company}
                      onChange={(e) => setCompany(e.target.value)}
                      placeholder="e.g. Acme Logistics, regional freight company"
                      autoFocus />
                  </label>
                </div>
              }

              {step === 2 &&
                <div className="form-step">
                  <span className="form-label">02 · What are you trying to do?</span>
                  <p className="form-hint">Pick whatever fits. You can choose more than one.</p>
                  <div className="bottleneck-grid">
                    {goalOpts.map((b) => {
                      const on = goals.includes(b.id);
                      return (
                        <button key={b.id} type="button" onClick={() => toggleGoal(b.id)} className={"bottleneck-card " + (on ? "is-on" : "")}>
                          <div className="bn-head">
                            <Mono style={{ fontSize: 10, color: "var(--muted)" }}>{b.code}</Mono>
                            <div className={"bn-check " + (on ? "is-on" : "")}>
                              {on && <svg width="10" height="10" viewBox="0 0 10 10"><path d="M2 5l2 2 4-5" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" /></svg>}
                            </div>
                          </div>
                          <span className="bn-label">{b.label}</span>
                        </button>);
                    })}
                  </div>
                </div>
              }

              {step === 3 &&
                <div className="form-step">
                  <label>
                    <span className="form-label">03 · What would success look like?</span>
                    <p className="form-hint">In plain words: what would be better if this worked?</p>
                    <textarea
                      value={success}
                      onChange={(e) => setSuccess(e.target.value)}
                      placeholder="e.g. Our team stops spending hours on manual data entry · We launch a customer portal · We stop worrying about security"
                      rows="4"
                      autoFocus />
                  </label>
                </div>
              }

              {step === 4 &&
                <div className="form-step">
                  <label>
                    <span className="form-label">04 · How can we reach you?</span>
                    <p className="form-hint">An email is perfect. A senior engineer will reply personally.</p>
                    <input
                      type="text"
                      value={contact}
                      onChange={(e) => setContact(e.target.value)}
                      placeholder="you@company.com"
                      autoFocus />
                  </label>
                </div>
              }
            </div>

            <div className="modal-footer">
              <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.14em" }}>
                Step {step} / 4
              </Mono>
              <div style={{ display: "flex", gap: 10 }}>
                {step > 1 && <GhostBtn onClick={() => setStep((s) => s - 1)}>← Back</GhostBtn>}
                {step < 4 && <PrimaryBtn onClick={() => setStep((s) => s + 1)}>Continue</PrimaryBtn>}
                {step === 4 && <PrimaryBtn onClick={() => setSubmitted(true)}>Send it over</PrimaryBtn>}
              </div>
            </div>
          </> :

          <div className="modal-done">
            <div className="md-seal">
              <svg viewBox="0 0 60 60" width="60" height="60">
                <circle cx="30" cy="30" r="29" stroke="var(--accent)" strokeWidth="1" fill="none" opacity="0.4" />
                <circle cx="30" cy="30" r="22" stroke="var(--accent)" strokeWidth="1" fill="none" />
                <path d="M 20 30 L 27 37 L 41 22" stroke="var(--accent)" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </div>
            <h3 className="modal-title" style={{ marginTop: 8 }}>Thanks, we'll be in touch.</h3>
            <p className="modal-intro" style={{ marginTop: 8 }}>
              A senior engineer will read this and reply within 3 business days. No autoresponders, no sales sequences.
            </p>
            <PrimaryBtn onClick={onClose} style={{ marginTop: 20 }}>Back to site</PrimaryBtn>
          </div>
        }
      </div>
    </div>);
}

/* ---------- FOOTER ---------- */

function Footer({ onContact }) {
  return (
    <footer className="footer">
      <div className="footer-grid">
        <div className="footer-brand">
          <Logo />
          <Mono style={{ fontSize: 11, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.16em", marginTop: 12, display: "block" }}>
            Build. Prove. Thrive.
          </Mono>
        </div>

        <div className="footer-col">
          <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Studio</Mono>
          <a href="#focus">What we do</a>
          <a href="#how">How we work</a>
          <a href="#team">Leadership</a>
          <a href="#work">Our work</a>
        </div>

        <div className="footer-col">
          <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Get in touch</Mono>
          <a href="#engage">Work with us</a>
          <a href="#" onClick={(e) => { e.preventDefault(); onContact(); }}>Start a conversation</a>
          <a href="mailto:hello@thryvx.io">hello@thryvx.io</a>
        </div>

        <div className="footer-col">
          <Mono style={{ fontSize: 10, color: "var(--muted)", textTransform: "uppercase", letterSpacing: "0.18em" }}>Status</Mono>
          <span className="status-row">
            <span className="dot" /> Taking on 2 new partners
          </span>
          <Mono style={{ fontSize: 11, color: "var(--muted)" }}>This quarter</Mono>
        </div>
      </div>

      <div className="footer-base">
        <Mono style={{ fontSize: 11, color: "var(--muted)" }}>© 2026 ThryvX. All rights reserved.</Mono>
        <div className="footer-base-links">
          <a href="#">Terms</a>
          <span>·</span>
          <a href="#">Privacy</a>
        </div>
      </div>
    </footer>);
}

/* ---------- APP ---------- */

function App() {
  const [contactOpen, setContactOpen] = useState(false);
  const openContact = () => setContactOpen(true);

  return (
    <div className="app">
      <Nav onContact={openContact} />
      <Hero onContact={openContact} />
      <ChasmBand />
      <FocusSection />
      <HowSection />
      <LeadershipSection />
      <VenturesSection />
      <EngageSection onContact={openContact} />
      <Footer onContact={openContact} />
      <ContactModal open={contactOpen} onClose={() => setContactOpen(false)} />
    </div>);
}

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