/* global React, Tag, Confidence, VideoTile, EmotionTimeline, Transcript, InsightCard, NeroLogo, NeroMark */

// ============================================================
// SHARED BLOG DATA
// ============================================================

const LANES = {
  index:  { label: "Evidence Index", short: "INDEX",  desc: "Frameworks &amp; methodology" },
  report: { label: "Sample Report",  short: "REPORT", desc: "Anonymized real study output" },
  field:  { label: "Field Notes",    short: "FIELD",  desc: "Patterns from many studies" },
  case:   { label: "Case Study",     short: "CASE",   desc: "Customer outcome stories" },
};

const POSTS = [
  {
    id: "evidence-trail",
    lane: "index",
    title: "Stop trusting AI summaries. Start trusting the evidence trail.",
    excerpt: "AI synthesis is fast, but unsupported. Here is the discipline we use to keep every cluster traceable to clips, quotes, and segments.",
    tags: ["methodology", "ai-research", "ethics"],
    industry: "All",
    mode: "All modes",
    publishAt: "2026-05-18",
    date: "May 18, 2026",
    read: "9 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    featured: true,
    viz: "evidence-stack",
  },
  {
    id: "challenger-sportswear",
    lane: "report",
    title: "Sample Report: The challenger's playbook — beating the Giants in sportswear",
    excerpt: "259 buyers, 5 findings, 5 personas. Comfort beats the logo for 92%, and 73% trust peers over big-brand ads — the data, the verbatims, the strategy.",
    tags: ["consumer", "sportswear", "brand"],
    industry: "Consumer / Sportswear",
    mode: "AI-Moderated Interviews",
    publishAt: "2025-10-15",
    date: "Oct 2025",
    read: "14 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "report-meta",
  },
  {
    id: "food-ad-emotion-test",
    lane: "report",
    title: "Sample Report: Two ad creatives, 244 faces — what emotion data said before launch",
    excerpt: "A national food brand A/B-tested two pre-roll cuts with video emotion capture (n=244). The predominant expression was flat — the real signal was a Surprise spike in one cut, invisible in the aggregate.",
    tags: ["concept", "video", "cpg"],
    industry: "CPG",
    mode: "Concept & Video Testing",
    publishAt: "2025-02-18",
    date: "Feb 2025",
    read: "12 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "video-strip",
  },
  {
    id: "retail-investor-attitudes",
    lane: "report",
    title: "Sample Report: What retail investors actually trust (and what they only say they do)",
    excerpt: "n=133 active retail investors. 93% say management quality is critical; about half actually check it. The say-do gaps that move confidence — and the language that closes them.",
    tags: ["brand", "ethics"],
    industry: "All",
    mode: "AI-moderated Interviews",
    publishAt: "2025-05-13",
    date: "May 2025",
    read: "13 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "report-meta",
  },
  {
    id: "consumer-product-reaction",
    lane: "report",
    title: "Sample Report: A consumer-product reaction study, frame by frame",
    excerpt: "n=112. 89 of 112 rated interest 8-plus, but faces stayed mild — and the top blocker was price ambiguity. Where attention held, where it dropped, and the said-versus-felt gap.",
    tags: ["concept", "video", "cpg"],
    industry: "CPG",
    mode: "Concept & Video Testing",
    publishAt: "2025-06-17",
    date: "Jun 2025",
    read: "11 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "video-strip",
  },
  {
    id: "synthetic-vs-human",
    lane: "index",
    title: "Synthetic vs human research: when each one wins",
    excerpt: "Synthetic focus groups are not a replacement for participants. Here is the decision matrix we use across 200+ studies.",
    tags: ["synthetic", "methodology"],
    industry: "All",
    mode: "Synthetic Focus Groups",
    publishAt: "2026-05-06",
    date: "May 6, 2026",
    read: "7 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "matrix",
  },
  {
    id: "hesitation-8s",
    lane: "field",
    title: "Hesitation peaks 8 seconds after the pricing page loads",
    excerpt: "Across 14 SaaS pricing studies, the same micro-pattern: cursor stalls between tiers, then drifts to support.",
    tags: ["pricing", "ux"],
    industry: "B2B SaaS",
    mode: "Agentic UX Test",
    publishAt: "2026-05-04",
    date: "May 4, 2026",
    read: "3 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "sparkline",
  },
  {
    id: "marlow-messaging",
    lane: "case",
    title: "How a DTC brand killed three messaging directions before spending a dollar on the wrong one",
    excerpt: "A consumer brand ran several value-prop and messaging directions through research before launch. Most died on contact with real reactions; one survived and tested strongly.",
    tags: ["brand", "synthetic", "messaging"],
    industry: "Consumer fintech",
    mode: "Synthetic + Concept",
    publishAt: "2025-07-22",
    date: "Jul 2025",
    read: "10 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "case-stack",
  },
  {
    id: "what-counts-evidence",
    lane: "index",
    title: "What counts as evidence? A framework for AI research outputs",
    excerpt: "Source clip, transcript moment, behavioral signal, segment pattern, confidence — five layers that make a recommendation defensible.",
    tags: ["methodology", "ai-research"],
    industry: "All",
    mode: "All modes",
    publishAt: "2026-04-24",
    date: "Apr 24, 2026",
    read: "8 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "layers",
  },
  {
    id: "snack-packaging",
    lane: "report",
    title: "Sample Report: When the survey favorite loses — a licensed-toy concept test (n=220)",
    excerpt: "Parents named the gentler of two screen properties as their favorite. The attention and durability signals — the things that actually drove willingness to pay — overturned the stated ranking.",
    tags: ["concept", "video", "cpg"],
    industry: "CPG",
    mode: "Concept & Video Testing",
    publishAt: "2025-08-19",
    date: "Aug 2025",
    read: "11 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "video-strip",
  },
  {
    id: "five-flavors-friction",
    lane: "index",
    title: "Five flavors of UX friction agents catch before users do",
    excerpt: "Navigational dead-ends, cognitive load spikes, signposting gaps, trust failures, and mismatched expectations — each with a sample diagnostic.",
    tags: ["ux", "agentic"],
    industry: "All",
    mode: "Agentic UX Test",
    publishAt: "2026-04-14",
    date: "Apr 14, 2026",
    read: "6 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "tiles",
  },
  {
    id: "where-do-i-click",
    lane: "field",
    title: "The 'where do I click' moment is more diagnostic than NPS",
    excerpt: "Across 22 product studies, the latency between intent and action predicted churn better than any survey instrument.",
    tags: ["onboarding", "field-notes"],
    industry: "B2B SaaS",
    mode: "AI-moderated Interviews",
    publishAt: "2026-04-08",
    date: "Apr 8, 2026",
    read: "4 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "sparkline",
  },
  {
    id: "onboarding-clarity",
    lane: "field",
    title: "Onboarding clarity: where new users stall, and the gap between activation metrics and felt experience",
    excerpt: "Activation dashboards say onboarding is done. Research shows where users actually stall, recover, or quietly disengage — and how to close the gap between the metric and the felt experience.",
    tags: ["onboarding", "saas"],
    industry: "B2B SaaS",
    mode: "AI-moderated Interviews",
    publishAt: "2026-04-01",
    date: "Apr 1, 2026",
    read: "13 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "report-meta",
  },
  {
    id: "200-mid-market",
    lane: "field",
    title: "What mid-market buyers tell us about 'enterprise tier' anxiety",
    excerpt: "The phrase 'enterprise plan' triggers a specific hesitation in mid-market buyers — opaque pricing, forced sales calls, the fear of being upsold. The recurring language, and what reduces it.",
    tags: ["pricing", "b2b"],
    industry: "B2B SaaS",
    mode: "AI-moderated Interviews",
    publishAt: "2026-03-26",
    date: "Mar 26, 2026",
    read: "5 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "sparkline",
  },
  {
    id: "verra-onboarding",
    lane: "case",
    title: "An onboarding rewrite, traced to the clips that justified it",
    excerpt: "A B2B SaaS team rebuilt onboarding off research evidence — every change tied back to a specific participant clip, transcript, and behavioral signal, so stakeholders could audit the recommendation instead of trusting it.",
    tags: ["case", "saas", "onboarding"],
    industry: "B2B SaaS",
    mode: "AI-moderated Interviews",
    publishAt: "2026-04-30",
    date: "Apr 2026",
    read: "9 min",
    author: { name: "NeroView Research", role: "Evidence Practice" },
    viz: "case-stack",
  },
];

const ALL_TOPICS = ["methodology", "ai-research", "ux", "ethics", "synthetic", "concept", "video", "pricing", "onboarding", "brand", "ecommerce", "saas", "cpg", "agentic"];
const ALL_INDUSTRIES = ["All", "B2B SaaS", "Consumer fintech", "E-commerce", "CPG"];
const ALL_MODES = ["All modes", "AI-moderated Interviews", "Agentic UX Test", "Concept & Video Testing", "Synthetic Focus Groups"];

// ============================================================
// SHARED NAV (with Blog active state)
// ============================================================

function BlogNav({ active }) {
  const [open, setOpen] = React.useState(false);
  const close = () => setOpen(false);
  const signin = () => window.open("https://platform.neroview.com/sign-in", "_blank", "noopener");
  const links = [
    { href: "/#platform", label: "Method" },
    { href: "/#modes", label: "Study Types" },
    { href: "/#report", label: "Sample Report" },
    { href: "/blog", label: "Resources", current: active === "blog" },
  ];
  return (
    <nav className="nav">
      <div className="nav__inner">
        <a href="/" style={{ textDecoration: "none", color: "inherit" }}>
          <NeroLogo />
        </a>
        <div className="nav__links">
          {links.map((l) => <a key={l.href} className="nav__link" href={l.href} style={l.current ? { color: "var(--fg)" } : null}>{l.label}</a>)}
        </div>
        <div className="nav__cta">
          <button className="btn btn--ghost" onClick={signin}>Sign in</button>
          <a className="btn btn--primary" href="/#demo">Get a demo</a>
        </div>
        <button className="nav__burger" aria-label="Menu" aria-expanded={open} onClick={() => setOpen(!open)}>
          <span /><span /><span />
        </button>
      </div>
      {open && (
        <div className="nav__mobile">
          {links.map((l) => <a key={l.href} className="nav__link" href={l.href} onClick={close} style={l.current ? { color: "var(--fg)" } : null}>{l.label}</a>)}
          <div className="nav__mobile-cta">
            <button className="btn btn--ghost" onClick={() => { close(); signin(); }}>Sign in</button>
            <a className="btn btn--primary" href="/#demo" onClick={close}>Get a demo</a>
          </div>
        </div>
      )}
    </nav>
  );
}

// ============================================================
// POST CARD
// ============================================================

// Resolve a post's page URL. The two original posts keep their bespoke pages;
// every new post lives at /blog/<id>.html (generic engine).
function postHref(post) {
  if (post.id === "evidence-trail") return "/blog/post.html";
  if (post.id === "challenger-sportswear") return "/blog/sample-report.html";
  return `/blog/${post.id}.html`;
}

function PostCard({ post, index }) {
  const lane = LANES[post.lane];
  let cls = "postcard";
  if (post.lane === "report") cls += " postcard--report";

  const exhibitNum = String((index ?? 0) + 1).padStart(2, "0");

  return (
    <a className={cls} href={postHref(post)}>
      <div className="postcard__lane">
        <span><b>EXHIBIT {exhibitNum} · {lane.short}</b> · {post.read}</span>
        <span>{post.date.split(",")[0]}</span>
      </div>
      <h3 className="postcard__title">{post.title}</h3>
      <p className="postcard__excerpt">{post.excerpt}</p>
      <PostViz kind={post.viz} />
      <div className="postcard__tags">
        {post.tags.slice(0, 3).map((t) => <Tag key={t}>#{t}</Tag>)}
      </div>
      <div className="postcard__meta">
        <strong>{post.author.name}</strong>
        <span>{post.industry}</span>
      </div>
    </a>
  );
}

// ============================================================
// PostViz — tiny visual marker per card style
// ============================================================

function PostViz({ kind }) {
  if (kind === "sparkline") {
    const heights = [40, 60, 35, 72, 50, 88, 64, 78, 52, 96, 70, 84];
    return (
      <div className="postcard__viz">
        <div className="postcard__sparkline">
          {heights.map((h, i) => <i key={i} style={{ height: `${h}%`, opacity: 0.4 + (i % 4) * 0.15 }} />)}
        </div>
      </div>
    );
  }
  if (kind === "report-meta") {
    return (
      <div className="postcard__viz" style={{ display: "flex", justifyContent: "space-between", fontFamily: "var(--mono)", fontSize: 10.5, color: "var(--fg-muted)", letterSpacing: "0.08em" }}>
        <span>n=48</span><span>3 SEGMENTS</span><span>21 CLIPS</span><span>87% CONF.</span>
      </div>
    );
  }
  if (kind === "matrix") {
    return (
      <div className="postcard__viz" style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 4, fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.1em", color: "var(--fg-muted)" }}>
        <div style={{ padding: "4px 8px", border: "1px solid var(--border)", borderRadius: 4 }}>SYNTHETIC ◦ EXPLORE</div>
        <div style={{ padding: "4px 8px", border: "1px solid var(--accent)", color: "var(--fg)", borderRadius: 4 }}>HUMAN ◦ VALIDATE</div>
      </div>
    );
  }
  if (kind === "layers") {
    return (
      <div className="postcard__viz">
        <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
          {[55, 70, 85, 65, 90].map((w, i) => (
            <div key={i} style={{ height: 4, width: `${w}%`, background: i === 4 ? "var(--accent)" : "color-mix(in oklab, var(--fg-muted) 50%, transparent)", borderRadius: 1 }} />
          ))}
        </div>
      </div>
    );
  }
  if (kind === "video-strip") {
    return (
      <div className="postcard__viz" style={{ display: "flex", gap: 4 }}>
        {[0, 1, 2, 3, 4, 5].map((i) => (
          <div key={i} style={{ flex: 1, aspectRatio: "1/1.2", background: i === 2 ? "color-mix(in oklab, var(--accent) 22%, var(--bg-elev-2))" : "var(--bg-elev-2)", border: "1px solid var(--border)", borderRadius: 3 }} />
        ))}
      </div>
    );
  }
  if (kind === "tiles") {
    return (
      <div className="postcard__viz" style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 3 }}>
        {[1, 2, 3, 4, 5].map((i) => (
          <div key={i} style={{ aspectRatio: "1", background: "var(--bg-elev-2)", border: "1px solid var(--border)", borderRadius: 2, display: "grid", placeItems: "center", fontFamily: "var(--mono)", fontSize: 9, color: "var(--fg-dim)" }}>{i.toString().padStart(2, "0")}</div>
        ))}
      </div>
    );
  }
  if (kind === "case-stack") {
    return (
      <div className="postcard__viz" style={{ display: "flex", gap: 4 }}>
        <div style={{ width: 60, height: 28, background: "var(--bg-elev-2)", border: "1px solid var(--border)", borderRadius: 4 }} />
        <div style={{ width: 28, alignSelf: "center", fontFamily: "var(--mono)", fontSize: 12, color: "var(--fg-dim)", textAlign: "center" }}>→</div>
        <div style={{ width: 60, height: 28, background: "color-mix(in oklab, var(--accent) 30%, var(--bg-elev-2))", border: "1px solid color-mix(in oklab, var(--accent) 50%, var(--border))", borderRadius: 4 }} />
      </div>
    );
  }
  if (kind === "evidence-stack") {
    return (
      <div className="postcard__viz" style={{ display: "flex", flexDirection: "column", gap: 3 }}>
        <div style={{ height: 8, width: "80%", background: "var(--bg-elev)", border: "1px solid var(--border)", borderRadius: 2 }} />
        <div style={{ height: 8, width: "100%", background: "var(--bg-elev)", border: "1px solid var(--border)", borderRadius: 2 }} />
        <div style={{ height: 8, width: "60%", background: "color-mix(in oklab, var(--accent) 30%, var(--bg-elev))", border: "1px solid var(--accent)", borderRadius: 2 }} />
      </div>
    );
  }
  return null;
}

// ============================================================
// NEWSLETTER STRIP
// ============================================================

function Newsletter() {
  const [email, setEmail] = React.useState("");
  const [status, setStatus] = React.useState("idle"); // idle | loading | done | error
  const [errMsg, setErrMsg] = React.useState("");

  const submit = async (e) => {
    e.preventDefault();
    if (status === "loading" || status === "done") return;
    setStatus("loading");
    setErrMsg("");
    try {
      const res = await fetch("/api/subscribe", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email, source: "newsletter" }),
      });
      if (!res.ok) {
        const err = await res.json().catch(() => ({}));
        throw new Error(err.error === "rate_limited" ? "Too many requests — try again in a few minutes." : err.error || "Couldn't subscribe");
      }
      setStatus("done");
    } catch (err) {
      setStatus("error");
      setErrMsg(err.message || "Something went wrong");
    }
  };

  const btnLabel = status === "loading" ? "Subscribing…" : status === "done" ? "Subscribed ✓" : "Subscribe";

  return (
    <section className="newsletter">
      <div>
        <div className="eyebrow">FIELD NOTES · WEEKLY</div>
        <h3 className="newsletter__title">Patterns from real studies, every Friday.</h3>
        <p className="newsletter__desc">
          One short piece a week — a pattern, a clip-grounded observation, or a methodology note. Curated from active NeroView studies. Unsubscribe anytime, no marketing.
        </p>
      </div>
      <div>
        <form className="newsletter__form" onSubmit={submit}>
          <input type="email" placeholder="you@team.com" value={email} onChange={(e) => setEmail(e.target.value)} required disabled={status === "loading" || status === "done"} />
          <button className="btn btn--primary" type="submit" disabled={status === "loading" || status === "done"}>{btnLabel}</button>
        </form>
        <div className="newsletter__cadence">
          {status === "error" ? errMsg.toUpperCase() : "EVERY FRIDAY · ~4 MIN READ"}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, {
  LANES, POSTS, ALL_TOPICS, ALL_INDUSTRIES, ALL_MODES,
  BlogNav, PostCard, PostViz, Newsletter, postHref,
});
