// OnCatch shared marketing components.
// Positioning locked: "Built by QA, for QA" — OnPath Testing (founded 2009)
// is the credibility marker, not an upsell. Surface it as part of every value claim.
// Source of truth: docs/positioning-brief.md + docs/future-state-product-spec.md

// basePath is "" for root-level pages, "../" for /blog/* pages, etc.
// Set window.__BASE = "../" before rendering on nested pages.
const __BP = () => (typeof window !== "undefined" && window.__BASE) || "";

const Logo = ({ accent = false, tag = true }) => (
  <a href={`${__BP()}index.html`} className="brand" aria-label="OnCatch home">
    <span className={`brand-mark ${accent ? "accent" : ""}`} aria-hidden="true">O</span>
    <span>OnCatch</span>
    {tag && <span className="brand-tag">by OnPath</span>}
  </a>
);

const Header = ({ active = "home" }) => {
  const bp = __BP();
  const [mobileOpen, setMobileOpen] = React.useState(false);
  // Close the mobile menu if the viewport widens past the breakpoint.
  React.useEffect(() => {
    const handler = () => { if (window.innerWidth > 900) setMobileOpen(false); };
    window.addEventListener("resize", handler);
    return () => window.removeEventListener("resize", handler);
  }, []);
  // Close on Escape
  React.useEffect(() => {
    if (!mobileOpen) return;
    const handler = (e) => { if (e.key === "Escape") setMobileOpen(false); };
    document.addEventListener("keydown", handler);
    return () => document.removeEventListener("keydown", handler);
  }, [mobileOpen]);
  return (
    <>
      <a href="#main-content" className="skip-link sr-only-focusable">Skip to main content</a>
      <header className="site-header" data-mobile-open={mobileOpen ? "true" : "false"}>
        <div className="container row">
          <Logo />
          <button
            className="nav-toggle"
            type="button"
            aria-expanded={mobileOpen}
            aria-controls="primary-nav"
            aria-label={mobileOpen ? "Close menu" : "Open menu"}
            onClick={() => setMobileOpen(!mobileOpen)}
          >
            <span className="bars" aria-hidden="true">
              <span /><span /><span />
            </span>
          </button>
          <nav className="nav" id="primary-nav" aria-label="Primary">
            <a href={`${bp}how-it-works.html`} data-active={active === "how"}>How it works</a>
            <a href={`${bp}admin.html`} data-active={active === "admin"}>Admin</a>
            <a href={`${bp}security.html`} data-active={active === "security"}>Security</a>
            <a href={`${bp}compare.html`} data-active={active === "compare"}>Compare</a>
            <a href={`${bp}pricing.html`} data-active={active === "pricing"}>Pricing</a>
            <a href={`${bp}docs.html`} data-active={active === "docs"}>Docs</a>
            <a href={`${bp}blog/`} data-active={active === "blog"}>Blog</a>
            <a href="https://www.onpathtesting.com" target="_blank" rel="noreferrer">
              OnPath <span aria-hidden="true">↗</span>
              <span className="sr-only">(opens in new tab)</span>
            </a>
            <a href={`${bp}#signup`} className="btn btn-primary btn-arrow">Get early access</a>
          </nav>
        </div>
      </header>
    </>
  );
};

const Footer = () => {
  const bp = __BP();
  return (
    <footer className="site-footer">
      <div className="container">
        <div className="footer-grid">
          <div>
            <Logo />
            <p style={{ marginTop: 16 }}>
              Built by OnPath Testing — 17 years of QA work, condensed into a
              forensic bug-capture widget. Founded 2009. Boulder, CO.
            </p>
          </div>
          <div>
            <h4>Product</h4>
            <ul>
              <li><a href={`${bp}how-it-works.html`}>How it works</a></li>
              <li><a href={`${bp}admin.html`}>Admin (triage view)</a></li>
              <li><a href={`${bp}integrations.html`}>Integrations</a></li>
              <li><a href={`${bp}ai-loop.html`}>AI loop (MCP)</a></li>
              <li><a href={`${bp}pricing.html`}>Pricing</a></li>
              <li><a href={`${bp}docs.html`}>Docs</a></li>
              <li><a href={`${bp}changelog.html`}>Changelog</a></li>
            </ul>
          </div>
          <div>
            <h4>Compare</h4>
            <ul>
              <li><a href={`${bp}compare.html`}>Feature matrix</a></li>
              <li><a href={`${bp}blog/`}>All comparisons</a></li>
              <li><a href={`${bp}blog/oncatch-vs-userback.html`}>vs. Userback</a></li>
              <li><a href={`${bp}blog/oncatch-vs-marker-io.html`}>vs. Marker.io</a></li>
              <li><a href={`${bp}blog/oncatch-vs-bugherd.html`}>vs. BugHerd</a></li>
              <li><a href={`${bp}blog/oncatch-vs-bird-eats-bug.html`}>vs. Bird Eats Bug</a></li>
              <li><a href={`${bp}blog/oncatch-vs-jam-dev.html`}>vs. Jam.dev</a></li>
              <li><a href={`${bp}blog/oncatch-vs-gleap.html`}>vs. Gleap</a></li>
              <li><a href={`${bp}blog/oncatch-vs-feedbear.html`}>vs. FeedBear</a></li>
            </ul>
          </div>
          <div>
            <h4>Company</h4>
            <ul>
              <li><a href={`${bp}about.html`}>About OnPath</a></li>
              <li><a href={`${bp}onpath-qa-bundle.html`}>OnPath SDLC bundle</a></li>
              <li><a href={`${bp}security.html`}>Security &amp; compliance</a></li>
              <li><a href={`${bp}legal-dpa.html`}>DPA</a></li>
              <li><a href={`${bp}legal-subprocessors.html`}>Subprocessors</a></li>
              <li><a href={`${bp}legal-privacy.html`}>Privacy</a></li>
              <li><a href={`${bp}legal-terms.html`}>Terms</a></li>
              <li><a href="https://www.onpathtesting.com" target="_blank" rel="noreferrer">OnPath Testing <span aria-hidden="true">↗</span></a></li>
            </ul>
          </div>
          <div>
            <h4>Get in touch</h4>
            <ul>
              <li><a href="mailto:hello@oncatch.app">hello@oncatch.app</a></li>
              <li><a href={`${bp}#signup`}>Request access</a></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 OnPath Testing · OnCatch is a registered product of OnPath Testing</span>
          <span>v1.0</span>
        </div>
      </div>
    </footer>
  );
};

// Faithful, non-functional mock of the widget UI.
const WidgetMock = ({ state = "form", referenceCode = "OC-7F2K-91" }) => {
  if (state === "success") {
    return (
      <div className="widget-mock" role="presentation">
        <div className="wm-head">
          <div className="wm-title">Thanks — we got it</div>
        </div>
        <div className="wm-body" style={{ alignItems: "center", textAlign: "center" }}>
          <div style={{ fontSize: 13, color: "var(--muted)" }}>
            Your report has been sent to the team.
          </div>
          <div style={{
            fontFamily: "var(--font-mono)", fontWeight: 600,
            background: "var(--accent-soft)", color: "var(--accent-deep)",
            padding: "6px 12px", borderRadius: 4, fontSize: 13,
          }}>{referenceCode}</div>
          <button className="wm-submit" style={{ marginTop: 4 }}>Report another</button>
        </div>
        <div className="wm-foot">Powered by OnCatch</div>
      </div>
    );
  }
  return (
    <div className="widget-mock" role="presentation">
      <div className="wm-head">
        <div className="wm-title">Report a problem</div>
        <div className="wm-x">×</div>
      </div>
      <div className="wm-body">
        <div>
          <label className="wm-label">Title *</label>
          <input className="wm-input" defaultValue="Checkout fails when promo code is applied" />
        </div>
        <div>
          <label className="wm-label">Description *</label>
          <textarea className="wm-input" defaultValue="Click 'Apply', spinner runs forever. I'm on Chrome on a Mac." />
        </div>
        <div>
          <label className="wm-label">Your name</label>
          <input className="wm-input" defaultValue="Ada Lovelace" />
        </div>
        <div>
          <label className="wm-label">Your email</label>
          <input className="wm-input" defaultValue="ada@acme.io" />
        </div>
        <div className="wm-attach">
          <span className="wm-attach-icon mono">+</span>
          <span>Attach screenshot or video <span className="muted">(optional)</span></span>
        </div>
        <button className="wm-submit">Send report</button>
      </div>
      <div className="wm-foot">Powered by OnCatch</div>
    </div>
  );
};

const FlowDiagram = () => (
  <div className="flow">
    <div className="flow-step">
      <div className="flow-num mono">01</div>
      <div className="flow-label">A user spots a bug</div>
      <div className="flow-sub">They open the widget and submit a title, description, and an optional screenshot or video.</div>
    </div>
    <div className="flow-line" />
    <div className="flow-step">
      <div className="flow-num mono">02</div>
      <div className="flow-label">You triage in one place</div>
      <div className="flow-sub">All submissions land on your admin dashboard. Review, prioritise, dismiss, or merge duplicates.</div>
    </div>
    <div className="flow-line" />
    <div className="flow-step">
      <div className="flow-num mono">03</div>
      <div className="flow-label">It lands in your tracker</div>
      <div className="flow-sub">Push to Jira, Linear, GitHub, Slack — or out via webhook with the details intact.</div>
    </div>
  </div>
);

const SectionEyebrow = ({ children }) => (
  <div className="eyebrow"><span className="dot" />{children}</div>
);

const Stat = ({ num, label }) => (
  <div>
    <div className="stat-num">{num}</div>
    <div className="stat-label">{label}</div>
  </div>
);

// Award band — third-party recognition for OnPath Testing.
// 4 Clutch awards (all 2024) + 4 Mercury 100 awards (2015, 2021, 2022, 2024).
// All images have transparent backgrounds. Real OnPath client logos defer to
// Wave 2 (gated on per-client name-use permission confirmation).
const LogoRow = () => {
  const bp = __BP();
  const clutch = [
    { src: "clutch-top-software-testing-2024.png", alt: "Clutch — Top Software Testing Company 2024", label: "Top Software Testing" },
    { src: "clutch-top-software-testing-us-2024.png", alt: "Clutch — Top Software Testing Company, United States 2024", label: "Top in United States" },
    { src: "clutch-top-software-testing-colorado-2024.png", alt: "Clutch — Top Software Testing Company, Colorado 2024", label: "Top in Colorado" },
    { src: "clutch-global-spring-2024.png", alt: "Clutch — Global Award, Spring 2024", label: "Global Award · Spring" },
  ];
  const mercury = [
    { src: "mercury-100-2015.png", alt: "Mercury 100 Boulder Valley — 2015", label: "2015" },
    { src: "mercury-100-2021.png", alt: "Mercury 100 Boulder Valley — 2021", label: "2021" },
    { src: "mercury-100-2022.png", alt: "Mercury 100 Boulder Valley — 2022", label: "2022" },
    { src: "mercury-100-2024.png", alt: "Mercury 100 Boulder Valley — 2024", label: "2024" },
  ];
  return (
    <div className="awards-band" aria-label="OnPath Testing — awards and recognition">
      <div className="awards-group">
        <div className="awards-group-head">
          <span className="awards-group-label">Clutch</span>
          <span className="awards-group-sub">Top Software Testing Company · 2024</span>
        </div>
        <div className="awards-grid" role="list">
          {clutch.map((a) => (
            <a
              key={a.src}
              className="award-cell"
              role="listitem"
              href="https://clutch.co/profile/onpath-testing"
              target="_blank"
              rel="noreferrer"
              title={a.alt}
            >
              <img src={`${bp}uploads/${a.src}`} alt={a.alt} loading="lazy" />
              <span className="award-cell-caption">{a.label}</span>
            </a>
          ))}
        </div>
      </div>
      <div className="awards-group">
        <div className="awards-group-head">
          <span className="awards-group-label">Mercury 100</span>
          <span className="awards-group-sub">Boulder Valley Fastest-Growing Private Companies · 6× honoree</span>
        </div>
        <div className="awards-grid" role="list">
          {mercury.map((a) => (
            <a
              key={a.src}
              className="award-cell"
              role="listitem"
              href="https://www.bcbr.com/awards/mercury-100/"
              target="_blank"
              rel="noreferrer"
              title={a.alt}
            >
              <img src={`${bp}uploads/${a.src}`} alt={a.alt} loading="lazy" />
              <span className="award-cell-caption">{a.label}</span>
            </a>
          ))}
        </div>
      </div>
    </div>
  );
};

const EmbedSnippet = () => (
  <pre className="code-block" aria-label="Embed snippet">
{`<`}<span className="k">script</span>{` `}
  <span className="a">src</span>=<span className="s">"https://cdn.oncatch.app/w-1.0.0.js"</span>{`
  `}<span className="a">data-workspace</span>=<span className="s">"ws_acme_8x2k"</span>{`
  async defer></`}<span className="k">script</span>{`>`}
{`\n`}<span className="c">{`// One tag. Works on any site, any framework.`}</span>
  </pre>
);

// 4-card value-prop grid. Pain → Receipt structure: each card names a real
// QA-buyer pain, then shows the concrete capability that addresses it.
const ReasonsToBelieve = () => (
  <div className="rtb-grid rtb-grid-4">
    {[
      {
        kicker: "01",
        title: "Captures evidence under hostile tech",
        pain: "Screenshots without console logs leave triage stuck. Reproduction blocked.",
        receipt: "WebGL. Strict CSP. Service workers. RTL. Streaming AI. Large DOMs. Cross-origin iframes. WordPress plugin chaos. The tech stacks where naive widgets quietly fail — we tested every one.",
      },
      {
        kicker: "02",
        title: "Built for QA workflows that already exist",
        pain: "\"Cannot reproduce\" tickets eat engineering bandwidth. Defect reports scattered across email, Slack, and support threads.",
        receipt: "Two-way sync with Jira, Linear, and GitHub at Team $49. MCP server so Claude Code, Cursor, and Windsurf read your queue at Solo $19. Zapier for everything else. Webhook v1 is additive-only forever — CI blocks breaking changes.",
      },
      {
        kicker: "03",
        title: "Western-launch compliance, built in",
        pain: "SSO gated to 25 seats (Userback). SSO custom-quoted (Marker.io). SOC 2 Type 2 gated to $999/mo enterprise (Gleap).",
        receipt: "GPC universal opt-out honored end-to-end. Breach notification matrix across 4 jurisdictions + 72-hour clock workflow + 4 templates. US state privacy disclosures across 20 states. WCAG 2.1 AA verified. 7 locales. All at the Free tier.",
      },
      {
        kicker: "04",
        title: "Engineered by a 17-year QA company",
        pain: "81% of enterprise tech leaders report increased production issues from AI-generated code. 66% of devs cite \"AI solutions that are almost right, but not quite\" as their biggest frustration. Your QA team is the line of defense.",
        receipt: "OnPath Testing has been doing QA since 2009 — for ClearCaptions, BioTRUST Nutrition, GoodParty.org, Wrapmate, RAIR, ClearWellness, English 360, and dozens more. Clutch Top Software Testing Companies 2022. Mercury 100 6× honoree.",
      },
    ].map(({ kicker, title, pain, receipt }) => (
      <div className="rtb-cell" key={kicker}>
        <div className="rtb-kicker mono">{kicker}</div>
        <div className="rtb-title h-3">{title}</div>
        <div className="rtb-pain">
          <span className="rtb-label mono">Pain</span>
          <span>{pain}</span>
        </div>
        <div className="rtb-receipt">
          <span className="rtb-label mono">Receipt</span>
          <span>{receipt}</span>
        </div>
      </div>
    ))}
  </div>
);

const VsTable = () => (
  <table className="vs-table">
    <caption className="sr-only">OnCatch versus competitors — quick comparison</caption>
    <thead>
      <tr className="vs-row vs-head">
        <th scope="col" className="vs-cell" aria-label="Feature"></th>
        <th scope="col" className="vs-cell vs-us">
          <div className="brand">
            <span className="brand-mark accent" aria-hidden="true">O</span>
            <span>OnCatch</span>
          </div>
        </th>
        <th scope="col" className="vs-cell"><span className="muted">Marker.io</span></th>
        <th scope="col" className="vs-cell"><span className="muted">Userback</span></th>
        <th scope="col" className="vs-cell"><span className="muted">Jam.dev</span></th>
      </tr>
    </thead>
    <tbody>
      {[
        ["Installs cleanly under strict CSP", "yes", "unknown", "unknown", "extension only"],
        ["Console + network logs at lowest paid tier", "Solo $19 flat", "$149/mo Team", "$15-19/seat", "free"],
        ["Two-way Jira / Linear / GitHub sync", "Team $49", "Jira only $", "paid tiers", "—"],
        ["MCP server (Claude / Cursor / Windsurf)", "Solo $19", "banner only", "banner only", "free"],
        ["Custom domain + white-label", "Team $49", "$$", "$$ Business", "—"],
        ["Compliance built in (GDPR + CCPA + UK + EU + AU + CA)", "Free tier", "enterprise", "25-seat min", "—"],
        ["17-year QA company built it (OnPath, 2009)", "yes", "no", "no", "no"],
      ].map(([label, us, mkr, uba, jam]) => (
        <tr className="vs-row" key={label}>
          <th scope="row" className="vs-cell vs-label">{label}</th>
          <Cell val={us} highlight />
          <Cell val={mkr} />
          <Cell val={uba} />
          <Cell val={jam} />
        </tr>
      ))}
    </tbody>
    <tfoot>
      <tr>
        <td colSpan="5" className="vs-foot">
          <a href="compare.html" style={{ color: "var(--accent)", fontWeight: 600 }}>
            See the full matrix · 7 direct competitors + 4 adjacent tools · 60+ features →
          </a>
        </td>
      </tr>
    </tfoot>
  </table>
);
const Cell = ({ val, highlight }) => {
  const isYes = val === "yes";
  const isNo = val === "no" || val === "—";
  return (
    <td className={`vs-cell ${highlight ? "vs-us" : ""}`}>
      {isYes && <span className="vs-yes" aria-label="Yes">✓</span>}
      {isNo && <span className="vs-no" aria-label="No">—</span>}
      {!isYes && !isNo && <span className={highlight ? "" : "muted"}>{val}</span>}
    </td>
  );
};

const Integrations = () => (
  <div className="integ-grid">
    {[
      { name: "Slack", mark: "SL", desc: "Per-workspace OAuth + channel selection. Posts new defect reports with title, reporter, screenshot thumbnail, link to the triage view. Optional notifications on triage state changes." },
      { name: "GitHub Issues", mark: "GH", desc: "Two-way sync at Team $49. One-click \"Send to GitHub\" creates an issue with screenshot embedded and console + network logs as fenced code blocks. Issue state syncs back to your triage view." },
      { name: "Linear", mark: "LI", desc: "Two-way sync at Team $49. Per-workspace OAuth + team picker. Same content shape. Issue state syncs both directions." },
      { name: "Jira Cloud", mark: "JR", desc: "Two-way sync at Team $49. Full description, attachments, and reporter context preserved on every push. State syncs both ways." },
      { name: "MCP server", mark: "AI", desc: "Available at Solo $19. Claude Code, Cursor, and Windsurf read your bug queue directly via the Model Context Protocol. The same AI that wrote the bug can read the report. npm: @oncatch/mcp-server." },
      { name: "Zapier", mark: "ZP", desc: "4 triggers + 4 actions at Team $49. Connect to Asana, ClickUp, Trello, Notion, Microsoft Teams, or any custom internal tool." },
      { name: "Webhook v1", mark: "WH", desc: "POST every report to your endpoint. Additive-only forever, CI-enforced. HMAC-SHA256 signing. pg_cron retries. SSRF defense blocks private URLs." },
      { name: "Email", mark: "EM", desc: "Reporter auto-reply with the reference code. Admin notify per-domain. Daily digest of new defect reports for the workspace, configurable per-user." },
    ].map(({ name, mark, desc }) => (
      <div className="integ-cell" key={name}>
        <div className="integ-mark mono">{mark}</div>
        <div>
          <div className="h-3">{name}</div>
          <div className="muted" style={{ fontSize: 14 }}>{desc}</div>
        </div>
      </div>
    ))}
  </div>
);

const Trust = () => (
  <div className="trust-grid">
    {[
      ["Closed shadow DOM", "The widget can't be styled or scraped from your page's JavaScript. No CSS leaks in either direction."],
      ["Cloudflare Turnstile", "Every submission is challenge-checked. Invisible by default — no CAPTCHA images, no third-party tracking."],
      ["Workspace isolation", "Every database table is workspace-scoped with row-level security. CI tests block merges that could leak across workspaces."],
      ["PII-aware by default", "Tokens, emails, and credit-card patterns are redacted from any captured context before submission leaves the page."],
      ["Stable webhook contract", "Webhook v1 is additive-only forever. Your downstream pipeline keeps working through every release."],
      ["Engineered by a 17-year QA company", "OnPath Testing has been doing QA since 2009 — for ClearCaptions, BioTRUST, GoodParty.org, Wrapmate, and dozens more. Clutch Top Software Testing Companies 2022. Mercury 100 fastest-growing private companies (Boulder Valley)."],
    ].map(([t, b]) => (
      <div className="trust-cell" key={t}>
        <div className="h-3">{t}</div>
        <div className="muted" style={{ fontSize: 14, marginTop: 6 }}>{b}</div>
      </div>
    ))}
  </div>
);

const FAQ = () => {
  const items = [
    ["Is it really one script tag?", "Yes. Add the embed, set data-workspace, the widget mounts itself in a closed shadow DOM. No CSS leaks, no globals, no framework conflicts. Tested under strict CSP, WebGL, service workers, RTL, streaming AI, WordPress plugin chaos, and cross-origin iframes."],
    ["Will it impact my application?", "No. The widget is entirely separate from your application's DOM. It loads asynchronously, mounts in a closed shadow DOM, and leaves no globals on your page. Shadow root keystroke isolation is explicit per event type — host shortcuts still work. If our service ever went down, your app keeps running."],
    ["What does a defect report include?", "Title, description, reporter context (name + email + everything you pass through OnCatch.identify(): userId, plan, custom metadata). Capture: whole-page or region screenshot, annotations (draw / arrow / text), screen recording with Safari fallback, pin-to-element. Auto-attached: last 100 console entries, failed network requests, full browser metadata. Privacy masking runs client-side before anything leaves the page."],
    ["Where do reports go after triage?", "Two-way sync with Jira, Linear, and GitHub at Team $49 — full description + attachments + reporter context preserved on every push. Slack channel notifications. Zapier for Asana / ClickUp / Trello / Notion / Microsoft Teams. MCP server at Solo $19 for Claude Code, Cursor, Windsurf. Webhook v1 — additive-only forever, CI-enforced."],
    ["Why is OnPath behind this widget?", "We've spent 17 years testing other people's software, finding bugs across every framework and hostile tech stack you can name. We built OnCatch because we were tired of feedback widgets designed for designers and generic teams. This is the widget our own QA engineers wanted — and we're making it embeddable for yours. OnPath Testing has been doing QA since 2009, for ClearCaptions, BioTRUST, GoodParty.org, Wrapmate, and dozens more."],
    ["Can OnPath run triage for me?", "Yes. The OnPath SDLC bundle puts real OnPath QA engineers on your queue — annual contract, dedicated CSM, custom integrations. The OnCatch widget is yours either way; the OnPath service is optional."],
  ];
  const [open, setOpen] = React.useState(0);
  return (
    <div className="faq">
      {items.map(([q, a], i) => {
        const isOpen = open === i;
        const panelId = `faq-panel-${i}`;
        const buttonId = `faq-button-${i}`;
        return (
          <div className={`faq-item ${isOpen ? "open" : ""}`} key={q}>
            <button
              id={buttonId}
              className="faq-q"
              type="button"
              onClick={() => setOpen(isOpen ? -1 : i)}
              aria-expanded={isOpen}
              aria-controls={panelId}
            >
              <span>{q}</span>
              <span className="faq-icon mono" aria-hidden="true">{isOpen ? "−" : "+"}</span>
            </button>
            <div
              id={panelId}
              className="faq-a"
              role="region"
              aria-labelledby={buttonId}
              hidden={!isOpen}
            >
              <div>{a}</div>
            </div>
          </div>
        );
      })}
    </div>
  );
};

const SignupForm = () => {
  const [submitted, setSubmitted] = React.useState(false);
  const [email, setEmail] = React.useState("");
  const successRef = React.useRef(null);
  // Move focus to the success message when the form transitions to submitted state.
  React.useEffect(() => {
    if (submitted && successRef.current) {
      successRef.current.focus();
    }
  }, [submitted]);
  return (
    <div className="signup">
      {!submitted ? (
        <form className="signup-form" onSubmit={(e) => { e.preventDefault(); setSubmitted(true); }}>
          <label htmlFor="signup-email" className="sr-only">Your work email</label>
          <input
            id="signup-email"
            className="signup-input"
            type="email"
            required
            autoComplete="email"
            placeholder="you@yourcompany.com"
            aria-describedby="signup-help"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <button type="submit" className="btn btn-primary btn-arrow">Start your free workspace</button>
        </form>
      ) : (
        <div
          ref={successRef}
          tabIndex={-1}
          className="signup-thanks"
          role="status"
          aria-live="polite"
        >
          <div className="h-3">You're on the list.</div>
          <div className="muted">We'll send a workspace invite within a day. Reference: <span className="mono">OC-WAIT-{Math.random().toString(36).slice(2, 7).toUpperCase()}</span></div>
        </div>
      )}
      <div id="signup-help" className="muted" style={{ fontSize: 13, marginTop: 12 }}>
        Free during pilot. No credit card. We'll never share your email.
      </div>
    </div>
  );
};

// About OnPath — replaces the prior ServicesUpsell band which apologized for
// OnPath as an "optional upsell." Positioning brief: don't hide OnPath. The
// 17-year QA history IS the credibility marker. Surface it as part of every
// value claim, not as a soft cross-sell.
const ServicesUpsell = () => (
  <div className="services-card">
    <div className="services-grid">
      <div>
        <SectionEyebrow>Why OnPath is behind this widget</SectionEyebrow>
        <h2 className="h-1" style={{ marginTop: 16, maxWidth: "22ch" }}>
          We've been doing <em>QA</em> since 2009.
        </h2>
      </div>
      <div className="services-body">
        <p className="lede" style={{ margin: 0 }}>
          OnCatch is built by OnPath Testing — a software QA consulting company
          founded in 2009 in Boulder, CO. We've spent 17 years testing other
          people's software, finding bugs across every framework, browser, and
          hostile tech stack you can name. We built OnCatch because we were
          tired of feedback widgets designed for designers and generic teams.
          This is the widget our own QA engineers wanted: forensic evidence
          capture, real environment data, repro-step output that drops into
          your tracker without rework.
        </p>
        <div className="services-bullets">
          <div className="services-bullet"><span className="mono">+</span><div><b>17 years of QA work.</b> Founded 2009 · Boulder, CO. Clutch Top Software Testing Companies 2022. Mercury 100 6× honoree.</div></div>
          <div className="services-bullet"><span className="mono">+</span><div><b>Clients (with permission):</b> ClearCaptions · Wrapmate · BioTRUST Nutrition · GoodParty.org · RAIR · ClearWellness · English 360.</div></div>
          <div className="services-bullet"><span className="mono">+</span><div><b>Services breadth:</b> Manual + automation + performance + security + accessibility + QAOps testing across web / desktop / mobile / IoT.</div></div>
          <div className="services-bullet"><span className="mono">+</span><div><b>Need a managed QA partner?</b> The OnPath SDLC bundle adds real OnPath QA engineers to your triage queue — annual contract, dedicated CSM, custom integrations.</div></div>
        </div>
        <div style={{ display: "flex", gap: 12, flexWrap: "wrap", marginTop: 8 }}>
          <a href="https://www.onpathtesting.com" target="_blank" rel="noreferrer" className="btn btn-secondary btn-arrow">
            Visit OnPath Testing <span className="sr-only">(opens in new tab)</span>
          </a>
          <a href="mailto:hello@oncatch.app?subject=Talk%20to%20a%20QA%20engineer" className="btn btn-ghost btn-arrow">Talk to a QA engineer</a>
        </div>
      </div>
    </div>
  </div>
);

// Roadmap teaser — what's already in flight + what's intentionally on the
// roadmap. Grounded in concrete capabilities (no "smarter capture" hype). Each
// surface either ships now or names what it'll be when it ships.
const RoadmapTeaser = () => (
  <div className="roadmap-grid">
    {[
      {
        tag: "Surface 1 · Capture",
        title: "Forensic capture, shipping today",
        body: "Whole-page + region screenshot, annotations (draw / arrow / text), screen recording with Safari fallback, pin-to-element bug reporting with overlay on the screenshot, reporter identification SDK, console + network log auto-capture, privacy masking client-side, widget state preservation across close / reopen, shadow root keystroke isolation.",
        bullets: [
          "All shipped — see /admin for the tour",
          "Voice-to-bug + multi-language capture on the roadmap",
          "Reporter intent classifier (bug vs feature vs confusion) on the roadmap",
        ],
      },
      {
        tag: "Surface 2 · Triage",
        title: "Triage view built for QA volume",
        body: "Saved views, bulk actions (transactional audit log), priority sort, custom workspace statuses, console-fingerprint duplicate detection, AI triage shadow mode (severity scoring + auto-summary), multi-admin with ownership transfer, soft-delete + 30-day recovery, centralized error message translation.",
        bullets: [
          "Shipped at Solo and Team tiers — see /admin",
          "Cross-submission pattern detection on the roadmap",
          "Daily digest in plain English on the roadmap",
        ],
      },
      {
        tag: "Surface 3 · OnPath SDLC bundle",
        title: "Real QA engineers in the loop",
        body: "When the queue overflows or the team's short-staffed, real OnPath QA engineers triage your queue on the SDLC bundle. 17 years of QA expertise on call — annual contract, dedicated CSM, custom integrations. No competitor has this; none of them can build it without acquiring an agency.",
        bullets: [
          "Available on the OnPath SDLC bundle today",
          "AI-proposed code change (\"Fix it for me\") on the roadmap",
          "Auto-generated regression tests on the roadmap",
        ],
      },
      {
        tag: "Surface 4 · Where we're going",
        title: "Where the platform's heading",
        body: "Cross-customer signal — what bugs your customers are seeing now, what bug patterns matter at scale, where production health is degrading before users complain. Honestly on the roadmap, not shipping today.",
        bullets: [
          "Self-healing websites (detect → fix → A/B → roll out) — roadmap",
          "Live experience score per URL — roadmap",
          "Cross-customer pattern intelligence, anonymized — roadmap",
        ],
      },
    ].map(({ tag, title, body, bullets }) => (
      <div className="roadmap-cell" key={title}>
        <div className="tag accent" style={{ alignSelf: "flex-start" }}>{tag}</div>
        <div className="h-3">{title}</div>
        <div className="muted" style={{ fontSize: 14.5 }}>{body}</div>
        <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 8, marginTop: 4 }}>
          {bullets.map((b) => (
            <li key={b} style={{ display: "flex", gap: 10, fontSize: 13.5, color: "var(--ink-2)", lineHeight: 1.45 }}>
              <span className="mono" style={{ color: "var(--accent)", flexShrink: 0, fontWeight: 600 }}>+</span>
              <span>{b}</span>
            </li>
          ))}
        </ul>
      </div>
    ))}
  </div>
);

Object.assign(window, {
  Logo, Header, Footer, WidgetMock, FlowDiagram, SectionEyebrow,
  Stat, LogoRow, EmbedSnippet, ReasonsToBelieve, VsTable, Integrations,
  Trust, FAQ, SignupForm, ServicesUpsell, RoadmapTeaser,
});
