/* global React */
const { useState, useEffect, useRef, useMemo } = React;

// ============================================================
// HERO
// ============================================================
const HERO_VARIANTS = [
  {
    id: 1,
    parts: [
      { t: "Your website is " },
      { t: "real estate", cls: "em" },
      { t: ". Every page is a " },
      { t: "property", cls: "underline-mark" },
      { t: ". PageSight is the only platform that tells you " },
      { t: "what each one is worth", cls: "em" },
      { t: " — and what to fix this week." },
    ],
    sub: "The Digital Property Intelligence platform for small businesses, marketing agencies, and mid-market teams.",
    cta: "Get started",
  },
  {
    id: 2,
    parts: [
      { t: "274 signals per visitor. " },
      { t: "Twenty times", cls: "em" },
      { t: " more than Google Analytics. Which is why PageSight can tell you things " },
      { t: "no other tool can compute", cls: "underline-mark" },
      { t: "." },
    ],
    sub: "Hardware tier. Connection quality. AI engine referrals. Cohort-segmented Web Vitals. Behavioral biometrics. The signals competitors don't capture are the ones that change the answer.",
    cta: "See the 274 signals",
  },
  {
    id: 3,
    parts: [
      { t: "Stop reading dashboards. Start fixing " },
      { t: "what's broken", cls: "underline-mark" },
      { t: ". PageSight tells you, every morning, the " },
      { t: "three things to look at", cls: "em" },
      { t: " and the three things to do." },
    ],
    sub: "Each recommendation includes a dollar estimate. Each one is automatically verified 14 days later. No mystery scores.",
    cta: "Get tomorrow's three",
  },
  {
    id: 4,
    parts: [
      { t: "One platform replaces " },
      { t: "Semrush, Hotjar, Sentry", cls: "em" },
      { t: ", and AgencyAnalytics. Plus the " },
      { t: "cross-client intelligence", cls: "underline-mark" },
      { t: " those tools were never built to provide." },
    ],
    sub: "When a fix lifts conversion for one client, PageSight auto-identifies the other clients it'll work for. Agency mode is included in the $399/mo tier.",
    cta: "See Agency mode",
  },
  {
    id: 5,
    parts: [
      { t: "The read.", cls: "em" },
      { t: " Your " },
      { t: "/pricing", cls: "underline-mark" },
      { t: " page loses " },
      { t: "78% of mobile visitors", cls: "em" },
      { t: " because the phone field opens an alphabet keyboard. Fix: one HTML attribute." },
    ],
    sub: "Estimated impact: $2,400/month. PageSight is the only platform that finds this — and proves the fix worked 14 days later.",
    cta: "Find ours",
  },
];

function StreamedHeadline({ parts, speed = 14 }) {
  const [n, setN] = useState(0);
  const fullText = useMemo(() => parts.map((p) => p.t).join(""), [parts]);

  useEffect(() => {
    setN(0);
    let i = 0;
    const total = fullText.length;
    const id = setInterval(() => {
      i += 2;
      if (i >= total) {
        setN(total);
        clearInterval(id);
      } else {
        setN(i);
      }
    }, speed);
    return () => clearInterval(id);
  }, [fullText, speed]);

  // Render with character budget
  let budget = n;
  const isDone = n >= fullText.length;
  return (
    <h1 className="hero-headline">
      {parts.map((p, idx) => {
        if (budget <= 0) return null;
        const shown = p.t.slice(0, budget);
        budget -= p.t.length;
        if (p.cls) {
          return (
            <span key={idx} className={p.cls}>
              {shown}
            </span>
          );
        }
        return <span key={idx}>{shown}</span>;
      })}
      {!isDone && <span className="caret"></span>}
    </h1>
  );
}

// ============================================================
// SIGNAL PANEL
// ============================================================
const SIGNAL_LIBRARY = [
  ["geo.country",        "DE"],
  ["geo.city",           "München"],
  ["geo.asn",            "AS3320"],
  ["geo.isp",            "Deutsche Telekom"],
  ["hw.cores",           "8"],
  ["hw.ram",             "16 GB"],
  ["hw.tier",            "high"],
  ["net.ect",            "4g"],
  ["net.rtt",            "82 ms"],
  ["net.downlink",       "23.4 Mbps"],
  ["net.saveData",       "false"],
  ["click.gclid",        "Cj0KCQjwxYS…"],
  ["click.fbclid",       "IwAR2vL…"],
  ["referrer.engine",    "chat.openai.com"],
  ["referrer.type",      "ai_engine"],
  ["biometric.fitts",    "r=0.041"],
  ["biometric.tremor",   "band=4.2hz"],
  ["scroll.velocity",    "1.18 px/ms"],
  ["vitals.lcp.p75",     "1980 ms"],
  ["vitals.inp.p75",     "180 ms"],
  ["vitals.cls.p75",     "0.04"],
  ["dwell.para.h2_3",    "12.4 s"],
  ["dwell.para.cta_1",   "1.1 s"],
  ["form.field.phone",   "abandoned"],
  ["form.kbd.detected",  "alphabet"],
  ["bot.score",          "0.04"],
  ["bot.bayes.joint",    "0.001"],
  ["intent.score",       "0.78"],
  ["intent.delta",       "+0.21"],
  ["dark.social.class",  "whatsapp"],
  ["committee.role",     "engineer"],
  ["committee.size",     "3"],
  ["session.lineage",    "sess_8e2a→2b1f"],
  ["replay.idx",         "07:42–08:11"],
  ["identity.hmac",      "fs_v1·z9Lq…"],
  ["page.value.usd",     "412.80"],
  ["error.weighted.usd", "1,240.00"],
];

function SignalPanel() {
  const [tick, setTick] = useState(0);
  const [hotIdx, setHotIdx] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setTick((t) => t + 1);
      setHotIdx((h) => (h + 1) % 11);
    }, 700);
    return () => clearInterval(id);
  }, []);

  const items = useMemo(() => {
    const arr = [];
    for (let i = 0; i < 11; i++) {
      const lib = SIGNAL_LIBRARY[(tick + i) % SIGNAL_LIBRARY.length];
      const tStamp = ((tick * 73 + i * 31) % 1000) / 10;
      arr.push({
        t: `t+${tStamp.toFixed(1)}s`,
        k: lib[0],
        v: lib[1],
        hot: i === hotIdx,
      });
    }
    return arr;
  }, [tick, hotIdx]);

  const captured = 162 + (tick % 113);

  return (
    <div className="signal-panel">
      <div className="signal-head">
        <span className="light"></span>
        <span className="signal-head-label">SESSION · LIVE CAPTURE</span>
        <span className="signal-head-id">sess_8e2a · visitor_2b1f</span>
      </div>

      <div className="signal-body">
        <div className="signal-feed">
          {items.map((row, i) => (
            <div key={i} className={`signal-row ${row.hot ? "hot" : ""}`}>
              <span className="t">{row.t}</span>
              <span className="k">{row.k}</span>
              <span className="v">{row.v}</span>
            </div>
          ))}
        </div>
      </div>

      <div className="signal-foot">
        <div>
          <b>{captured} / 274</b>
          signals captured
        </div>
        <div>
          <b>0.04</b>
          bot score · human
        </div>
        <div>
          <b style={{ color: "#f3a87f" }}>0.78</b>
          intent score · high
        </div>
      </div>
    </div>
  );
}

// ============================================================
// HERO COMPONENT
// ============================================================
function Hero({ variant, setVariant }) {
  const v = HERO_VARIANTS[variant];

  return (
    <section className="hero">
      <div className="wrap">
        <div className="hero-grid">
          <div className="hero-left">
            <div className="hero-eyebrow-row">
              <span className="chip live">
                <span className="dot"></span>DIGITAL PROPERTY INTELLIGENCE
              </span>
              <span className="variant-dots">
                <span className="mono" style={{ marginRight: 6 }}>HERO</span>
                {HERO_VARIANTS.map((_, i) => (
                  <span
                    key={i}
                    className={`dot ${i === variant ? "on" : ""}`}
                    onClick={() => setVariant(i)}
                  ></span>
                ))}
              </span>
            </div>

            <StreamedHeadline parts={v.parts} key={v.id} />

            <p className="hero-sub">{v.sub}</p>

            <div className="hero-actions">
              <a className="btn btn-primary" href="get-started.html">
                {v.cta}<span className="arrow"></span>
              </a>
              <a className="btn btn-ghost" href="#">Book a 15-min demo</a>
            </div>

            <div className="hero-meta">
              <span><b>274</b> signals per visitor</span>
              <span><b>89</b> validated detectors</span>
              <span><b>0</b> third-party trackers</span>
              <span><b>14-day</b> verification on every fix</span>
            </div>
          </div>

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

// ============================================================
// TICKER BAND
// ============================================================
function TickerBand() {
  const signals = [
    "cf.colo", "cf.country", "cf.asn", "cf.continent",
    "hw.cpuCores", "hw.deviceMem", "hw.gpu.tier",
    "net.ect", "net.rtt", "net.downlink", "net.saveData",
    "click.gclid", "click.fbclid", "click.msclkid", "click.ttclid", "click.li_fat_id",
    "ai.engine.detected", "ai.referrer.chatgpt", "ai.referrer.perplexity",
    "bio.fitts.residual", "bio.tremor.spectral", "bio.scroll.signature",
    "id.hmac.v1", "id.lineage.sess",
    "vitals.lcp.p75.cohort", "vitals.inp.p75.cohort", "vitals.cls.p75.cohort", "vitals.fcp.p75.cohort", "vitals.ttfb.p75.cohort",
    "para.dwell.h2_3", "para.dwell.cta_1", "para.scanpath.idx",
    "replay.metadata", "replay.frame.ms",
    "form.field.abandon.cause", "form.kbd.detected",
    "bot.bayes.joint", "bot.signal.class",
    "intent.score.live", "intent.delta.rolling",
    "exit.pattern.class",
    "dark.social.class",
    "committee.role", "committee.size",
  ];
  const block = signals.map((s, i) => (
    <span key={i}>{s}<span className="sep"> · </span></span>
  ));
  return (
    <div className="ticker">
      <div className="ticker-track">
        {block}{block}
      </div>
    </div>
  );
}

// ============================================================
// THE READ
// ============================================================
const READS = [
  {
    page: "/pricing",
    quote: ["Your ", { hl: "/pricing" }, " page loses ", { hl: "78% of mobile visitors" }, " because the phone field opens an alphabet keyboard."],
    fix: "Set inputmode=\"tel\" on the phone input.",
    impactLabel: "ESTIMATED MONTHLY IMPACT",
    impact: "+$2,400",
    stages: [
      { name: "Detected by Friction × Form pillar", val: "2 days ago", done: true },
      { name: "Cause inferred (kbd × field type mismatch)", val: "confidence 0.94", done: true },
      { name: "Cohort revenue at risk computed", val: "$78/day", done: true },
      { name: "Fix queued · awaiting your approval", val: "pending", done: false },
    ],
  },
  {
    page: "/blog/scaling-rails",
    quote: ["Readers ", { hl: "stop dwelling" }, " at paragraph 7 (\"… now for the gotchas\") — and ", { hl: "63%" }, " of them exit within 4 seconds."],
    fix: "Move the inline diagram up. Add a TL;DR strip.",
    impactLabel: "ESTIMATED MONTHLY IMPACT",
    impact: "+$1,180",
    stages: [
      { name: "Paragraph-level dwell on every <p>", val: "5,418 visitors", done: true },
      { name: "Drop-off paragraph identified", val: "para[7] · -84% retention", done: true },
      { name: "Substack & Ghost cannot compute this", val: "verified", done: true },
      { name: "Substitute layout drafted by Beacon", val: "ready", done: false },
    ],
  },
  {
    page: "/checkout (B2B)",
    quote: ["Three visitors from the same ASN viewed pricing this week — an ", { hl: "engineer, a procurement role, and an executive" }, ". A ", { hl: "buying committee" }, " is forming."],
    fix: "Auto-route to AE; trigger Engage > Accounts playbook.",
    impactLabel: "ESTIMATED PIPELINE",
    impact: "$48,000",
    stages: [
      { name: "Identity resolved · Fellegi-Sunter", val: "p > 0.92", done: true },
      { name: "Role detection (3 / 3 distinct)", val: "Eng · Proc · Exec", done: true },
      { name: "Committee assembled · ACME Corp", val: "ASN-13335", done: true },
      { name: "AE alerted in Slack · playbook open", val: "live now", done: false },
    ],
  },
];

function TheRead() {
  const [idx, setIdx] = useState(0);
  const cur = READS[idx];

  // animate impact number
  const [animKey, setAnimKey] = useState(0);
  useEffect(() => { setAnimKey((k) => k + 1); }, [idx]);

  return (
    <section className="section read-section" id="read">
      <div className="wrap">
        <div className="flex between" style={{ alignItems: "baseline", marginBottom: 28 }}>
          <div>
            <div className="eyebrow">THE EDITORIAL LAYER</div>
            <h2 className="h-2" style={{ marginTop: 12, marginBottom: 0 }}>
              Every morning, <span className="serif-italic" style={{color:"var(--accent)"}}>the read.</span>
            </h2>
            <p className="lede" style={{ marginTop: 14 }}>
              A magazine-quality summary of what changed yesterday, written by the same engine that decides what to put in front of you. Three to look at. Three to fix. Each with a dollar number.
            </p>
          </div>
        </div>

        <div className="iri">
          <div className="iri-inner" style={{ padding: 0 }}>
            <div className="read-card">
              <div className="read-left">
                <div className="eyebrow">
                  THE READ · ISSUE #214 · <span style={{color:"var(--accent)"}}>{cur.page}</span>
                </div>
                <p className="read-quote" style={{ marginTop: 18 }}>
                  {cur.quote.map((q, i) =>
                    typeof q === "string"
                      ? <span key={i}>{q}</span>
                      : <span key={i} className="hl">{q.hl}</span>
                  )}
                </p>
                <div style={{ marginTop: 24 }}>
                  <div className="eyebrow">THE FIX</div>
                  <div className="mono" style={{ fontSize: 13.5, color: "var(--ink-2)", marginTop: 8, lineHeight: 1.5 }}>
                    {cur.fix}
                  </div>
                </div>

                <div className="read-pager">
                  <span className="num">{String(idx + 1).padStart(2,"0")} / {String(READS.length).padStart(2,"0")}</span>
                  <span style={{ flex: 1 }}></span>
                  <button className="btn-page" onClick={() => setIdx((i) => (i - 1 + READS.length) % READS.length)}>‹</button>
                  <button className="btn-page" onClick={() => setIdx((i) => (i + 1) % READS.length)}>›</button>
                </div>
              </div>

              <div className="read-right">
                <div className="eyebrow">{cur.impactLabel}</div>
                <div key={animKey} className="read-impact-num" style={{ marginTop: 8 }}>
                  {cur.impact}
                </div>

                <div className="read-stages">
                  {cur.stages.map((s, i) => (
                    <div key={i} className={`read-stage ${s.done ? "done" : ""}`}>
                      <span className="stage-num">{String(i+1).padStart(2,"0")}</span>
                      <span className="stage-name">{s.name}</span>
                      <span className="stage-val">{s.val}</span>
                    </div>
                  ))}
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// HUBS
// ============================================================
const HUBS = [
  {
    id: "protect",
    color: "#8b3a3a",
    eyebrow: "PROTECT",
    q: "What's bleeding?",
    title: "Watches your site from a real visitor's seat.",
    pillars: ["Vitality", "Integrity", "Velocity", "Authenticity", "Friction"],
    metricLabel: "REVENUE AT RISK / DAY",
    metricValue: "$1,240",
    replaces: "Sentry · UptimeRobot · SpeedCurve · Hotjar (part)",
  },
  {
    id: "convert",
    color: "#c4623d",
    eyebrow: "CONVERT",
    q: "What could lift?",
    title: "Identifies the highest-leverage interventions right now.",
    pillars: ["Intent", "Attention", "Experiments", "Persuasion", "Attribution"],
    metricLabel: "OPPORTUNITY / MONTH",
    metricValue: "$18,400",
    replaces: "OptinMonster · VWO · Optimizely · Hotjar (part)",
  },
  {
    id: "reach",
    color: "#b8860b",
    eyebrow: "REACH",
    q: "Who isn't here yet?",
    title: "Finds the unmet audience and the underused channels.",
    pillars: ["Discovery", "Content", "Outbound", "Word of Mouth", "Earned"],
    metricLabel: "UNTAPPED POTENTIAL / MONTH",
    metricValue: "$32,100",
    replaces: "Ahrefs · Semrush · Profound · Mailchimp · Buffer",
  },
  {
    id: "engage",
    color: "#3d6b3d",
    eyebrow: "ENGAGE",
    q: "Who's already here, and what's next?",
    title: "Manages the relationships with entities, accounts, and customers.",
    pillars: ["Identity", "Conversations", "Accounts", "Advocacy", "Lifecycle"],
    metricLabel: "PIPELINE · AT-RISK REVENUE",
    metricValue: "$94,200",
    replaces: "Intercom (part) · Clearbit · RB2B · Customer.io (part)",
  },
];

function HubBlob({ color, seed = 0 }) {
  // animated SVG blob
  const [t, setT] = useState(0);
  useEffect(() => {
    let raf;
    const start = performance.now();
    const tick = (now) => {
      setT((now - start) / 1000);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  const cx = 48, cy = 48, r = 30;
  const N = 8;
  const pts = [];
  for (let i = 0; i < N; i++) {
    const ang = (i / N) * Math.PI * 2;
    const wob = 1 + 0.08 * Math.sin(t * 0.7 + i * 1.13 + seed) + 0.05 * Math.cos(t * 0.5 + i * 0.7 + seed * 2);
    pts.push([cx + Math.cos(ang) * r * wob, cy + Math.sin(ang) * r * wob]);
  }
  // close smooth path via cubic
  let d = "";
  for (let i = 0; i < N; i++) {
    const p0 = pts[(i - 1 + N) % N];
    const p1 = pts[i];
    const p2 = pts[(i + 1) % N];
    const p3 = pts[(i + 2) % N];
    if (i === 0) d += `M ${p1[0].toFixed(2)} ${p1[1].toFixed(2)} `;
    const c1x = p1[0] + (p2[0] - p0[0]) / 6;
    const c1y = p1[1] + (p2[1] - p0[1]) / 6;
    const c2x = p2[0] - (p3[0] - p1[0]) / 6;
    const c2y = p2[1] - (p3[1] - p1[1]) / 6;
    d += `C ${c1x.toFixed(2)} ${c1y.toFixed(2)}, ${c2x.toFixed(2)} ${c2y.toFixed(2)}, ${p2[0].toFixed(2)} ${p2[1].toFixed(2)} `;
  }
  d += "Z";

  // inner crosshair
  return (
    <svg viewBox="0 0 96 96" className="hub-blob">
      <defs>
        <radialGradient id={`g-${seed}`} cx="50%" cy="40%" r="60%">
          <stop offset="0%" stopColor={color} stopOpacity="0.55" />
          <stop offset="60%" stopColor={color} stopOpacity="0.15" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </radialGradient>
      </defs>
      <path d={d} fill={`url(#g-${seed})`} stroke={color} strokeOpacity="0.45" strokeWidth="1.2" />
      <circle cx={cx} cy={cy} r="3" fill={color} />
      <circle cx={cx} cy={cy} r="14" fill="none" stroke={color} strokeOpacity="0.25" strokeWidth="1" />
      <circle cx={cx} cy={cy} r="22" fill="none" stroke={color} strokeOpacity="0.15" strokeWidth="1" />
    </svg>
  );
}

function Hubs() {
  return (
    <section className="section" id="hubs">
      <div className="wrap">
        <div className="eyebrow">THE FOUR HUBS · PLATFORM SURFACE</div>
        <h2 className="h-1" style={{ marginTop: 16, maxWidth: "18ch" }}>
          Four lenses. <span className="serif-italic" style={{color:"var(--accent)"}}>One question</span> each. Twenty pillars. All deterministic.
        </h2>
        <p className="lede" style={{ marginTop: 18, maxWidth: "60ch" }}>
          Each hub answers a distinct business question. Each ships with detectors validated against statistical, behavioral, and physiological theory — not vibes-based AI. AI shows up later, to interpret.
        </p>

        <div className="hubs-grid">
          {HUBS.map((h, i) => (
            <div key={h.id} className="hub-card" style={{ "--hub-color": h.color }}>
              <div className="hub-icon-wrap">
                <HubBlob color={h.color} seed={i + 1} />
              </div>
              <div className="hub-eyebrow"><span className="swatch"></span>{h.eyebrow}</div>
              <p className="hub-q">“{h.q}”</p>
              <h3 className="hub-title">{h.title}</h3>
              <div className="hub-pillars">
                {h.pillars.map((p) => <span key={p} className="hub-pillar">{p}</span>)}
              </div>
              <div className="hub-metric">
                <span className="hub-metric-label">{h.metricLabel}</span>
                <span className="hub-metric-val">{h.metricValue}</span>
              </div>
              <div className="hub-replaces">REPLACES · <strong>{h.replaces}</strong></div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================================
// STACK COLLAPSE
// ============================================================
const STACK_TILES = [
  { name: "Sentry Team", price: "$26 / mo" },
  { name: "UptimeRobot Pro", price: "$7 / mo" },
  { name: "Hotjar Plus", price: "$39 / mo" },
  { name: "Typeform Basic", price: "$25 / mo" },
  { name: "Mailchimp Standard", price: "$20 / mo" },
  { name: "Hootsuite Pro", price: "$99 / mo" },
  { name: "Ahrefs Lite", price: "$129 / mo" },
  { name: "Intercom Essential", price: "$74 / mo" },
  { name: "Optimizely Web", price: "$3,000 / mo" },
];

function StackCollapse() {
  const [collapsed, setCollapsed] = useState(false);
  const wrapRef = useRef(null);

  // toggle on view + on hover
  useEffect(() => {
    if (!wrapRef.current) return;
    const obs = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          setTimeout(() => setCollapsed(true), 700);
        }
      });
    }, { threshold: 0.35 });
    obs.observe(wrapRef.current);
    return () => obs.disconnect();
  }, []);

  return (
    <section className="section stack-section" id="stack">
      <div className="wrap">
        <div className="stack-wrap">
          <div className="stack-text">
            <div className="eyebrow">THE STACK · COLLAPSED</div>
            <h2 className="h-1" style={{ marginTop: 16, maxWidth: "16ch" }}>
              A typical SMB-growth stack costs <span className="serif-italic accent-text">$2,078</span> a month.
            </h2>
            <p className="lede" style={{ marginTop: 20 }}>
              Sentry · BetterStack · Hotjar Plus · Typeform · Webflow Business · Mailchimp Standard · Hootsuite · Ahrefs · Intercom Advanced · HubSpot Marketing Pro. PageSight Pro is <b style={{color:"var(--ink)"}}>$149</b>. The math is brutal.
            </p>
            <div className="hero-actions">
              <a className="btn btn-accent" href="pricing.html">See pricing<span className="arrow"></span></a>
              <button className="btn btn-ghost" onClick={() => setCollapsed((c) => !c)}>
                {collapsed ? "Expand the stack" : "Collapse the stack"}
              </button>
            </div>
            <div className="hero-meta" style={{marginTop: 32}}>
              <span><b>– 87%</b> tooling cost</span>
              <span><b>– 9</b> contracts</span>
              <span><b>– 4</b> dashboards to learn</span>
            </div>
          </div>

          <div className="stack-viz" ref={wrapRef}>
            {STACK_TILES.map((t, i) => {
              const idx = i;
              const total = STACK_TILES.length;
              // expanded positions: arrange in a loose grid offset
              const angle = ((idx / total) - 0.5) * 0.6; // radians
              const baseY = -180 + idx * 36;
              const baseX = -160 + (idx % 3) * 10;
              const expStyle = {
                transform: `translate(-50%, -50%) translate(${baseX}px, ${baseY}px) rotate(${angle * 12}deg) translateZ(0)`,
              };
              const colStyle = {
                transform: `translate(-50%, -50%) translateY(${(idx - total/2) * -2}px) translateZ(0) scale(0.96)`,
                opacity: 0,
                pointerEvents: "none",
              };
              return (
                <div
                  key={t.name}
                  className="stack-tile"
                  style={{
                    ...((collapsed) ? colStyle : expStyle),
                    transition: "transform 1.2s cubic-bezier(.7,0,.2,1), opacity 0.6s",
                    transitionDelay: `${idx * 0.04}s`,
                    zIndex: 10 - i,
                  }}
                >
                  <span>{t.name}</span>
                  <span className="price">{t.price}</span>
                </div>
              );
            })}
            {/* PageSight tile */}
            <div
              className="stack-tile you"
              style={{
                transform: collapsed
                  ? "translate(-50%, -50%) translateY(0) scale(1.08)"
                  : "translate(-50%, -50%) translateY(180px) scale(0.7)",
                opacity: collapsed ? 1 : 0,
                transition: "transform 1.2s cubic-bezier(.7,0,.2,1), opacity 0.8s 0.4s",
                zIndex: 100,
              }}
            >
              <span><span className="wordmark" style={{fontSize: 16, color: "var(--paper)"}}><span className="mark"></span>PageSight</span> Pro</span>
              <span className="price">$149 / mo</span>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// AUDIENCE TABS
// ============================================================
const AUDIENCES = [
  {
    id: "sarah",
    name: "Sarah",
    who: "SMB OWNER · WEBFLOW + SHOPIFY",
    plan: "Starter · $49/mo",
    quote: "You built a website. PageSight tells you whether it's working — and what to fix this week. Every morning we send you three things to look at and three things to do.",
    stackBefore: [
      ["Google Analytics 4", "free"],
      ["Hotjar Plus", "$39"],
      ["Sentry Team", "$26"],
      ["UptimeRobot Pro", "$7"],
    ],
    before: 72,
    after: 49,
  },
  {
    id: "marcus",
    name: "Marcus",
    who: "AGENCY OWNER · 12 CLIENT SITES",
    plan: "Agency · $399/mo · 50 sites",
    quote: "When a fix lifts conversion for one client, PageSight tells you which other clients are candidates for the same fix. Show your clients exactly what you did, what worked, and what's next — with verified dollar impact.",
    stackBefore: [
      ["Semrush Pro × 1", "$129"],
      ["AgencyAnalytics × 1", "$79"],
      ["Hotjar Business × 12", "$960"],
      ["Sentry Team × 12", "$312"],
      ["Per-client misc", "≈ $300"],
    ],
    before: 1780,
    after: 399,
  },
  {
    id: "priya",
    name: "Priya",
    who: "MID-MARKET MARKETING DIRECTOR · SaaS",
    plan: "Pro · $149/mo + Enterprise pilot",
    quote: "GA4 shows you traffic. PageSight shows you what's actually happening to your conversions, your accounts, and your revenue — across 274 signals per visitor. Detect buying committees forming at target accounts.",
    stackBefore: [
      ["Heap Pro", "$1,200"],
      ["6sense Lite", "$4,200"],
      ["Hotjar Business", "$80"],
      ["Custom dashboards", "≈ $1,500"],
    ],
    before: 6980,
    after: 149,
  },
];

function AudienceTabs({ audience, setAudience }) {
  const cur = AUDIENCES.find((a) => a.id === audience) || AUDIENCES[0];
  const saved = cur.before - cur.after;
  const pct = Math.round((saved / cur.before) * 100);

  return (
    <section className="section audience-section" id="audiences">
      <div className="wrap">
        <div className="eyebrow">THREE WAYS TO READ IT</div>
        <h2 className="h-1" style={{marginTop: 16, marginBottom: 24, maxWidth: "20ch"}}>
          Same engine. <span className="serif-italic accent-text">Different sentence</span> for each kind of operator.
        </h2>

        <div className="audience-tabs">
          {AUDIENCES.map((a) => (
            <div
              key={a.id}
              className={`audience-tab ${audience === a.id ? "active" : ""}`}
              onClick={() => setAudience(a.id)}
            >
              <span>{a.name}</span>
              <span className="who">{a.who}</span>
            </div>
          ))}
        </div>

        <div className="audience-card" key={cur.id}>
          <div>
            <p className="audience-quote">{cur.quote}</p>
            <div style={{marginTop: 32, display: "flex", gap: 12, flexWrap: "wrap"}}>
              <span className="chip"><span className="dot"></span>RECOMMENDED · {cur.plan}</span>
            </div>
            <div className="hero-actions">
              <a className="btn btn-primary" href="get-started.html">Start as {cur.name.toLowerCase()}<span className="arrow"></span></a>
              <a className="btn btn-link" href="#">Read {cur.name}'s walkthrough</a>
            </div>
          </div>
          <div className="audience-stack">
            <h4>Before · {cur.name}'s stack today</h4>
            {cur.stackBefore.map(([name, price]) => (
              <div key={name} className="stack-line">
                <span className="strike">{name}</span>
                <span className="price">{price}/mo</span>
              </div>
            ))}
            <div className="stack-total">
              <span className="stack-total-label">After · PageSight</span>
              <span className="stack-total-val">
                <span style={{color:"var(--ink-mute)", textDecoration:"line-through", marginRight: 8, fontSize: 16, fontWeight: 400}}>${cur.before.toLocaleString()}</span>
                <span className="now">${cur.after.toLocaleString()}</span>
                <span className="mono" style={{fontSize: 12, color: "var(--ink-mute)", marginLeft: 8, fontWeight: 400}}>– {pct}%</span>
              </span>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// PROOF
// ============================================================
function Proof() {
  return (
    <section className="section proof-section" id="proof">
      <div className="wrap">
        <div className="eyebrow">THE PROOF DISCIPLINE</div>
        <h2 className="h-1" style={{marginTop: 16, color: "var(--paper)", maxWidth: "20ch"}}>
          89 detectors. <span className="serif-italic" style={{color:"#e2bd5e"}}>Six lenses</span> of validation. Zero claims we couldn't prove.
        </h2>
        <p className="lede" style={{marginTop: 18, color: "rgba(246,242,234,0.7)", maxWidth: "60ch"}}>
          Every detector that ships is tested against mathematical boundaries, peer-reviewed literature, synthetic data, competitor cross-checks, first-principles derivations, and known-weakness probes. We publish the results — including the caveats — for every claim on this site.
        </p>

        <div className="proof-grid">
          <div className="proof-cell">
            <div className="proof-num strong">55</div>
            <div className="proof-label">STRONG</div>
            <div className="proof-sub">Passes every validation lens. Ship-ready. Used in production scoring.</div>
          </div>
          <div className="proof-cell">
            <div className="proof-num caveat">34</div>
            <div className="proof-label">CAVEATED</div>
            <div className="proof-sub">Works within stated limits. Limits published. Confidence states surfaced in-product.</div>
          </div>
          <div className="proof-cell">
            <div className="proof-num weak">0</div>
            <div className="proof-label">WEAK</div>
            <div className="proof-sub">Doesn't survive the lens. Never ships. Sent back to math.</div>
          </div>
          <div className="proof-cell">
            <div className="proof-num fatal">0</div>
            <div className="proof-label">FATAL</div>
            <div className="proof-sub">Theoretically broken. We've never written one. We won't.</div>
          </div>
        </div>

        <div style={{marginTop: 56, display: "flex", gap: 12, flexWrap: "wrap"}}>
          <a className="btn btn-accent" href="#">Read the validation report<span className="arrow"></span></a>
          <a className="btn btn-ghost" href="#" style={{borderColor: "rgba(246,242,234,0.25)", color: "var(--paper)"}}>See all 89 detectors</a>
        </div>
      </div>
    </section>
  );
}

// ============================================================
// TWELVE
// ============================================================
const TWELVE = [
  ["AI Citation Closed Loop", "Track ChatGPT, Perplexity, Claude, and Gemini citations end-to-end: bot fetch → synthetic prompt monitoring → real visitor detection → conversion attribution.", "Profound / Otterly · one layer each"],
  ["Cohort-segmented heatmaps", "Toggle between 'iPhone fiber returning visitor' and 'Android 3G first-time' and watch the heatmap change entirely.", "Hotjar · no hardware tier captured"],
  ["Buying Committee Detector", "RB2B and 6sense detect people. PageSight reconstructs the committee — engineer + procurement + executive — from anonymous traffic on shared ASNs.", "RB2B / 6sense · individuals only"],
  ["Bot ad-spend waste quantification", "Every UTM source × bot percentage × CPC = exact dollars wasted. Then strip bot conversions from your Meta/Google CAPI feeds automatically.", "Every CRO / analytics tool · doesn't compute"],
  ["Save-Data aware velocity penalties", "Visitors with saveData=true or 2G connections convert 30–60% lower because nothing adapts to them. PageSight surfaces the gap with cohort-specific revenue at risk.", "SpeedCurve / GA4 · no Save-Data field"],
  ["Paragraph-level engagement", "IntersectionObserver on every <p> tells you which paragraphs readers dwell on, which they skip, which AI engines quote.", "Substack / Ghost · pageviews only"],
  ["Dark social sub-classification", "Break 'direct traffic' into WhatsApp, Slack, email forward, iMessage — via in-app browser fingerprint × arrival pattern recognition.", "All analytics tools · 'direct' is a wastebasket"],
  ["Revenue-weighted error ranking", "Sentry shows you error frequency. PageSight shows you the dollar revenue blocked by each error, propensity-matched to cohorts where it didn't fire.", "Sentry · frequency, not dollars"],
  ["Cross-client pattern detection", "When a fix lifts conversion for one client, PageSight auto-identifies the other clients it'll work for. Agency mode = operational leverage, not reports.", "AgencyAnalytics · reporting only"],
  ["Edge experiments × cohort winners", "Tests find 'Variant B wins for mobile 3G but loses on desktop fiber — auto-serve each cohort its winner.'", "Optimizely · $3,000/mo · no auto-serve"],
  ["Verification loop on every action", "Every fix or experiment is automatically re-measured 14 days later. Before / after, in dollars. No platform else does this.", "No competitor · zero verification"],
  ["Bayesian joint-distribution bot scoring", "30+ orthogonal signal classes including Fitts residual and tremor band spectral power — physiology bots cannot fake without modeling neuromuscular noise.", "DataDome / Cloudflare BM · 5–8 signals"],
];

function Twelve() {
  return (
    <section className="section" id="twelve">
      <div className="wrap">
        <div className="eyebrow">THE WEDGE</div>
        <h2 className="h-1" style={{marginTop: 16, maxWidth: "20ch"}}>
          <span className="serif-italic accent-text">Twelve things</span> competitors literally cannot compute.
        </h2>
        <p className="lede" style={{marginTop: 18, maxWidth: "62ch"}}>
          Most analytics tools capture 10–50 signals per visitor. PageSight captures 274. When the inputs are missing, the math is impossible. These are the answers nobody else has — because nobody else has the inputs.
        </p>

        <div className="twelve-grid" style={{marginTop: 48}}>
          {TWELVE.map(([title, body, vs], i) => (
            <div key={title} className="twelve-cell">
              <div className="twelve-num">{String(i+1).padStart(2,"0")}</div>
              <div className="twelve-title">{title}</div>
              <div className="twelve-body">{body}</div>
              <div className="twelve-vs">VS · <b>{vs}</b></div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================================
// TRUST
// ============================================================
function Trust() {
  const rows = [
    ["COMPLIANCE", ["GDPR", "CCPA / CPRA", "LGPD", "PIPEDA", "DPA available"]],
    ["SECURITY",   ["SOC 2 Type II (in progress)", "2FA required", "SSO · Google / MSFT / Okta / OneLogin / SAML"]],
    ["PRIVACY",    ["Email / phone / IP hashed on capture", "No third-party trackers", "First-party data only", "Opt-in for raw PII"]],
    ["REVERSIBILITY", ["One-click rollback on every edge deploy", "Data export anytime", "30-day data preservation post-cancel"]],
    ["TRANSPARENCY", ["All 274 fields documented · with limits", "Live event stream visible to customers", "Open competitive pricing comparison"]],
  ];
  return (
    <section className="section" id="trust" style={{background: "var(--paper-deep)"}}>
      <div className="wrap">
        <div className="eyebrow">PRIVACY · SECURITY · REVERSIBILITY</div>
        <h2 className="h-1" style={{marginTop: 16, maxWidth: "20ch"}}>
          The boring answers, <span className="serif-italic accent-text">on the surface</span>.
        </h2>

        <div style={{marginTop: 48, display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))", gap: 1, background: "var(--rule)"}}>
          {rows.map(([label, items]) => (
            <div key={label} style={{background: "var(--card)", padding: 24}}>
              <div className="eyebrow" style={{marginBottom: 14}}>{label}</div>
              {items.map((x) => (
                <div key={x} style={{fontSize: 13.5, color: "var(--ink-2)", padding: "6px 0", borderBottom: "1px dashed var(--rule)"}}>{x}</div>
              ))}
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ============================================================
// CTA
// ============================================================
function CtaBlock() {
  return (
    <section className="section cta-section">
      <div className="wrap" style={{textAlign: "center"}}>
        <div className="eyebrow" style={{justifyContent: "center", display: "flex"}}>FREE FOR 50K VISITORS / MO · NO CREDIT CARD</div>
        <h2 className="h-display" style={{margin: "24px auto 0", maxWidth: "18ch"}}>
          Find <span className="serif-italic accent-text">your</span> $2,400-a-month bug. By Friday.
        </h2>
        <p className="lede" style={{margin: "28px auto 0", maxWidth: "52ch"}}>
          Drop the beacon on your site. The first read lands in your inbox tomorrow morning. The first fix is measured 14 days later. The math will be brutal.
        </p>
        <div className="hero-actions" style={{justifyContent: "center", marginTop: 36}}>
          <a className="btn btn-accent" href="get-started.html" style={{padding: "14px 22px", fontSize: 15}}>Get started<span className="arrow"></span></a>
          <a className="btn btn-ghost" href="#" style={{padding: "14px 22px", fontSize: 15}}>Book the demo</a>
        </div>
        <p className="mono" style={{marginTop: 28, fontSize: 11, color: "var(--ink-mute)", letterSpacing: "0.04em"}}>
          50K visitors/mo · 1 site · 150 Beacon messages · forever · no card · cancel by ignoring us
        </p>
      </div>
    </section>
  );
}

// Export to window so home-app.jsx can use them
Object.assign(window, {
  Hero, TickerBand, TheRead, Hubs, StackCollapse, AudienceTabs, Proof, Twelve, Trust, CtaBlock,
  AUDIENCES, HERO_VARIANTS,
});
