// =============================================================
// Design Brains — Blog
// =============================================================

const BLOG_POSTS = [
  {
    slug: "ai-agents-that-actually-ship",
    cat: "AI Automation",
    date: "Apr 28, 2026",
    readTime: "8 min",
    title: "AI agents that actually ship: a field guide for product teams",
    excerpt: "Most AI agent demos die on contact with production. Here's the architecture, evals, and guardrails we use to ship agents that survive Monday morning.",
    body: [
      { h: "The demo-to-production gap" },
      { p: "Every AI agent looks great in a demo and feels broken three days into production. The reason is rarely the model — it's the surrounding system. Eval suites, human-in-the-loop fallbacks, retry logic, and tool guardrails are the boring parts that turn a flashy prototype into something your team trusts." },
      { p: "We've shipped agents into customer support, sales ops, and internal documentation workflows. The pattern that survives: small scope, tight evals, generous logging, and a graceful escalation path the moment confidence drops." },
      { h: "What we mean by 'eval suite'" },
      { p: "Before deploying any agent, we build a regression set of 50–200 real examples — labelled by humans on your team — that the agent must clear above an agreed threshold before any change ships. We re-run it on every prompt edit, model swap, or tool change. It's the single highest-leverage thing you can build." },
      { h: "Tool calls, not freestyle text" },
      { p: "Anything the agent can act on goes through structured tool calls. The agent never types an SQL query directly into your database. The agent never decides on its own to issue a refund. Every action is a typed, auditable function call with explicit permissions — and the dangerous ones go through a human." },
      { h: "Cost as a first-class metric" },
      { p: "We instrument every call with token counts, latency, and outcome. We chart cost-per-task next to accuracy. The combination tells you whether to upgrade the model, prune the prompt, or reroute to a cheaper one. Most teams ignore this until the bill hurts. Don't." },
    ],
  },
  {
    slug: "design-systems-small-team",
    cat: "Design",
    date: "Apr 19, 2026",
    readTime: "6 min",
    title: "Design systems for teams of three (and why most are over-engineered)",
    excerpt: "If you're a small team, you don't need a design system. You need 12 components, three colors, and the discipline to use them.",
    body: [
      { h: "The trap: building Material before you ship anything" },
      { p: "We've seen too many small startups try to build a Material-grade design system in their first quarter. They produce 80 tokens, four type ramps, and a 200-page Notion doc — and ship a single page. The customer doesn't care. The design system isn't the product. The product is the product." },
      { h: "What we use instead" },
      { p: "On a 5-person team you need: a primary, two neutrals, an accent. One display font, one body font, optionally one mono. A spacing scale that's just multiples of 8. A button (three variants), a card, a form field, a navigation pattern. That's the entire system. Add to it only when you've shipped enough product to feel its absence." },
      { h: "When to actually build a system" },
      { p: "Build a system when more than one designer is regularly making decisions, when you have a real component in three different colors across three different pages, or when QA is finding inconsistencies you can't keep in your head. Until then, keep it small and ruthless." },
    ],
  },
  {
    slug: "shipping-fast-mobile-apps-bangladesh",
    cat: "App Development",
    date: "Apr 12, 2026",
    readTime: "7 min",
    title: "Shipping fast mobile apps for the Bangladesh market",
    excerpt: "Network constraints, mid-range Android dominance, and bKash integration: what we've learned shipping consumer apps in Bogra.",
    body: [
      { h: "The reality of the mid-range Android" },
      { p: "Most users in Bangladesh are not on flagship phones. They're on a 2–3 year old Android with 3–4GB of RAM and a flaky 4G connection. Your app needs to feel fast on that device, or you're losing the market." },
      { h: "Defaults that pay off" },
      { p: "We default to: aggressive image optimization (WebP, multi-size), JS bundle budgets enforced in CI, offline-first caching, and a perceived-performance pass where every screen has a skeleton state inside 200ms. None of it is glamorous, all of it matters." },
      { h: "Payment integration realities" },
      { p: "bKash, Nagad, and SSLCommerz dominate. We've integrated all three multiple times — the documentation is uneven and the testing environments are flaky, but the patterns are stable. Plan for two weeks per provider, including the reconciliation logic. Your finance team will thank you." },
    ],
  },
  {
    slug: "automation-roi-actually-measure",
    cat: "AI Automation",
    date: "Apr 03, 2026",
    readTime: "5 min",
    title: "How to actually measure the ROI of your automations",
    excerpt: "Hours saved is the wrong metric. Here's the framework we use to prove (or disprove) that an automation is worth keeping.",
    body: [
      { h: "Why 'hours saved' is misleading" },
      { p: "Every automation pitch promises hours saved. But hours saved are useless if (a) you didn't actually redeploy that time, or (b) the automation introduces enough exception-handling that the saved hours are eaten by support work. We've seen both." },
      { h: "A better framework" },
      { p: "Track three numbers per automation: throughput delta (how much more work moves through the team), exception rate (how often a human still has to step in), and cost per outcome (inference + maintenance, divided by tasks completed). If the exception rate climbs above ~15%, the automation is probably costing you more than it saves." },
      { h: "Kill the ones that don't work" },
      { p: "We retire about 1 in 5 automations we ship after the first quarter. Not because they don't work — because the input data drifted, or the team's workflow changed, or the original problem went away. That's healthy. Don't sentimentally maintain dead automations." },
    ],
  },
  {
    slug: "core-web-vitals-2026",
    cat: "Website Development",
    date: "Mar 24, 2026",
    readTime: "9 min",
    title: "Core Web Vitals in 2026: what still matters and what's noise",
    excerpt: "Google's metrics keep evolving. Here's what we still chase, what we've stopped chasing, and what we measure instead.",
    body: [
      { h: "The metrics that still earn their keep" },
      { p: "LCP under 2 seconds, CLS under 0.05, INP under 200ms. These three are still the right ceiling for a marketing site in 2026. Anything above and you're losing rank, conversions, and trust." },
      { h: "What's noise" },
      { p: "Total bundle size, raw Lighthouse scores, server response time in isolation. They're proxies. They sometimes correlate with the real metrics and sometimes don't. We don't optimize for them directly anymore — we optimize for what users feel." },
      { h: "What we measure instead" },
      { p: "Real-user monitoring (RUM) over synthetic. Field data over lab data. Measurement on a mid-range Android over a MacBook on fiber. The story your real users tell is the story that matters." },
    ],
  },
  {
    slug: "ecommerce-checkout-conversion",
    cat: "Website Development",
    date: "Mar 15, 2026",
    readTime: "6 min",
    title: "Six checkout changes that lifted conversions by 18% on average",
    excerpt: "Pulled from 14 e-commerce projects we've shipped: the highest-leverage checkout interventions, ranked by impact.",
    body: [
      { h: "1. Cut the form to its bare minimum" },
      { p: "Every field you don't strictly need is friction. Address autocomplete + email + phone is enough for 90% of stores. Everything else can be inferred or asked later." },
      { h: "2. Show shipping cost on the cart, not at the end" },
      { p: "The number one cart-abandonment reason is surprise shipping cost. Surface it early, even as a 'from $X' estimate. Trust beats hope." },
      { h: "3. Local payment methods, prominently" },
      { p: "If your audience is in Bangladesh, bKash and Nagad get top billing. If your audience is in India, UPI does. Don't bury the payment method your customers actually want." },
      { h: "4–6: order summary always visible, post-purchase upsell, automatic guest checkout" },
      { p: "Each one moved our average AOV or completion rate by 2–4%. They compound. Combined, they're the difference between a storefront that converts and one that doesn't." },
    ],
  },
  {
    slug: "saas-pricing-page-anatomy",
    cat: "Design",
    date: "Mar 02, 2026",
    readTime: "5 min",
    title: "The anatomy of a SaaS pricing page that doesn't lie",
    excerpt: "Pricing pages are the most-read page on most SaaS sites. Here's how to design one that respects the visitor's time.",
    body: [
      { h: "Three columns, sometimes" },
      { p: "Three is the sweet spot when your tiers are genuinely different. Two is right when you're really just selling 'team' and 'enterprise'. Five is almost always too many. Be honest about what you're actually offering." },
      { h: "Show the price. Always." },
      { p: "Hiding the price behind 'contact sales' is a mark of self-doubt. If your customers can't afford to know what something costs without a 30-minute call, your pricing strategy is the problem — not the page." },
      { h: "FAQ underneath, not in a modal" },
      { p: "The questions buyers have at the pricing page are predictable. Answer them inline. Annual discount, taxes, refunds, what counts as a seat. Treat the page like a spec sheet, not a marketing brochure." },
    ],
  },
  {
    slug: "kicking-off-design-engagement",
    cat: "Process",
    date: "Feb 22, 2026",
    readTime: "4 min",
    title: "How we kick off a design engagement (and why the first week matters)",
    excerpt: "The five-day discovery sprint we run on every project, and the documents we ship by Friday.",
    body: [
      { h: "Day 1: Listen" },
      { p: "We sit with the founder, the product lead, and someone who actually talks to customers. We ask about goals, constraints, and what 'good' looks like in 90 days. We don't pitch. We don't propose. We just listen, and we write everything down." },
      { h: "Day 2–3: Look" },
      { p: "We audit your current product, your competitors, and 10 sites in adjacent categories that are doing something interesting. By end of day three we have a 20-slide observation deck with patterns, gaps, and opportunities." },
      { h: "Day 4: Sketch" },
      { p: "Low-fi wireframes for the three screens that matter most. We try two or three directions per screen and pick favorites with you in a working session." },
      { h: "Day 5: Plan" },
      { p: "By Friday you have a 1-page brief, a phased timeline with milestones, a fixed-price quote per phase, and a rough visual direction. You decide on Monday whether to keep going. About 80% of the time we do." },
    ],
  },
  {
    slug: "framer-motion-restraint",
    cat: "Design",
    date: "Feb 11, 2026",
    readTime: "5 min",
    title: "Animation as restraint: motion design for serious products",
    excerpt: "The best animation on a SaaS dashboard is the one you don't notice. Here's how we think about motion in product UI.",
    body: [
      { h: "Three jobs animation does well" },
      { p: "Spatial continuity (this thing came from there). Status feedback (something happened). Focus direction (look here next). If your animation isn't doing one of these, it's costing you attention without paying you back." },
      { h: "What we mostly don't do" },
      { p: "Decorative scroll animations on dashboards. Cute hover effects on buttons that delay the action. Long-form micro-interactions that feel charming once and slow on the 50th use. Restraint pays compounding interest." },
      { h: "The reduced-motion test" },
      { p: "Every animation we ship has a reduced-motion fallback. If your app feels broken with motion off, the motion was load-bearing — and it shouldn't be." },
    ],
  },
  {
    slug: "ai-replacing-jobs-not-yet",
    cat: "AI Automation",
    date: "Jan 30, 2026",
    readTime: "7 min",
    title: "AI isn't replacing your team. It's changing what they do.",
    excerpt: "After deploying AI into 30+ teams over two years, here's what's actually happening — and the role shifts we're seeing.",
    body: [
      { h: "What we observe in practice" },
      { p: "Across the teams we've embedded AI into, almost no one has been laid off. What's happened instead: 30–50% of the rote work is gone, and the people doing that work have moved up the value chain. Customer support agents are doing more proactive outreach. Marketing teams are running more experiments. Ops teams are building the automations themselves." },
      { h: "The roles that grow" },
      { p: "Quality reviewers, not workers. Workflow designers, not workflow operators. People who can read an AI output and tell whether it's right. That last skill — the editorial judgment — is the rarest and highest-leverage capability we're seeing emerge." },
      { h: "What this means for hiring" },
      { p: "Hire for taste and judgment. The AI does the typing. You still need the human who knows what 'good' looks like." },
    ],
  },
  {
    slug: "headless-cms-2026-decision",
    cat: "Website Development",
    date: "Jan 18, 2026",
    readTime: "6 min",
    title: "Headless CMS in 2026: how we choose between Sanity, Payload, and Contentful",
    excerpt: "Three CMS platforms, three different strengths. The decision tree we use on every project.",
    body: [
      { h: "Sanity: when content is structured" },
      { p: "Sanity is our default for editorial sites and product catalogs where content has a real schema. The studio is excellent, the GROQ query language is fantastic, and the editing experience is the best in class. Cost can climb at scale; budget for it." },
      { h: "Payload: when you want to own the stack" },
      { p: "Payload is self-hostable, open source, and TypeScript-native. We reach for it when the client wants no vendor lock-in, or when there's a tight integration with custom backend logic. Worth the slightly higher setup time on the right project." },
      { h: "Contentful: when the org is enterprise" },
      { p: "Contentful wins on enterprise SSO, governance, and team workflows. For startups it's overkill and overpriced. For 500-person marketing teams, it's worth it." },
    ],
  },
  {
    slug: "agency-vs-freelancer-vs-inhouse",
    cat: "Process",
    date: "Jan 06, 2026",
    readTime: "5 min",
    title: "Agency, freelancer, or in-house: when each one is the right answer",
    excerpt: "An honest breakdown of when to hire us, when not to, and what to expect from each path.",
    body: [
      { h: "Freelancer: when scope is small and clear" },
      { p: "If you need a 5-page site in 10 days, hire a great freelancer. The coordination overhead of an agency isn't worth it." },
      { h: "In-house: when work is continuous" },
      { p: "If you'll need design and engineering work every week for the next two years, build an in-house team. No agency can be as fast as a co-located team that knows your product." },
      { h: "Agency: when you need senior + multidisciplinary, fast" },
      { p: "Where we add value: when you need design + engineering + AI together, when you need senior judgment without a senior salary, and when speed matters more than long-term build-up. Don't hire us for what a freelancer or an in-house team would do better." },
    ],
  },
];

function BlogIndex() {
  const [activeCat, setActiveCat] = useState("All");
  const cats = ["All", ...Array.from(new Set(BLOG_POSTS.map(p => p.cat)))];
  const posts = activeCat === "All" ? BLOG_POSTS : BLOG_POSTS.filter(p => p.cat === activeCat);
  const featured = posts[0];
  const rest = posts.slice(1);

  return (
    <div className="page-enter">
      <PageHero
        eyebrow="Field notes"
        title={<>Writing on design,<br/>code, and <span style={{ color: "var(--brand)" }}>AI</span> in practice.</>}
        subtitle="Things we've learned shipping real work for real teams. No SEO bait, no listicles — just notes from the studio."
      />

      <section style={{ paddingTop: 0 }}>
        <div className="container">
          {/* Category filter */}
          <Reveal>
            <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 64, paddingBottom: 32, borderBottom: "1px solid var(--border)" }}>
              {cats.map((c) => (
                <button
                  key={c}
                  onClick={() => setActiveCat(c)}
                  style={{
                    background: activeCat === c ? "var(--fg)" : "transparent",
                    color: activeCat === c ? "var(--bg)" : "var(--fg-2)",
                    border: "1px solid " + (activeCat === c ? "var(--fg)" : "var(--border-strong)"),
                    borderRadius: 3,
                    padding: "8px 16px",
                    fontSize: 13,
                    fontWeight: 500,
                    cursor: "pointer",
                    fontFamily: "inherit",
                    transition: "all 0.2s var(--ease)",
                  }}
                >
                  {c}
                </button>
              ))}
            </div>
          </Reveal>

          {/* Featured */}
          {featured && (
            <Reveal>
              <Link to={`/blog/${featured.slug}`}>
                <article style={{
                  display: "grid",
                  gridTemplateColumns: "1.1fr 1fr",
                  gap: 0,
                  border: "1px solid var(--border)",
                  marginBottom: 64,
                  background: "var(--card)",
                  transition: "all 0.3s var(--ease)",
                }} className="blog-feature card-glow">
                  <div style={{
                    minHeight: 420,
                    background: "linear-gradient(135deg, var(--brand-faint), var(--bg-2))",
                    position: "relative",
                    display: "grid",
                    placeItems: "center",
                    borderRight: "1px solid var(--border)",
                  }}>
                    <BlogGraphic i={0} large/>
                    <div className="chip" style={{ position: "absolute", top: 24, left: 24 }}>{featured.cat}</div>
                  </div>
                  <div style={{ padding: "48px 48px 40px", display: "flex", flexDirection: "column", justifyContent: "space-between" }}>
                    <div>
                      <div className="font-mono" style={{ fontSize: 12, color: "var(--muted)", letterSpacing: "0.12em", marginBottom: 16 }}>FEATURED · {featured.date} · {featured.readTime} read</div>
                      <h2 className="font-display" style={{ fontSize: "clamp(28px, 3vw, 40px)", lineHeight: 1.1, letterSpacing: "-0.025em", marginBottom: 20 }}>{featured.title}</h2>
                      <p style={{ color: "var(--fg-2)", fontSize: 17, lineHeight: 1.55 }}>{featured.excerpt}</p>
                    </div>
                    <div style={{ marginTop: 32, color: "var(--brand)", fontSize: 15, fontWeight: 600 }}>Read article →</div>
                  </div>
                </article>
              </Link>
            </Reveal>
          )}

          {/* Grid */}
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 0 }} className="blog-grid">
            {rest.map((p, i) => (
              <Reveal key={p.slug} delay={i * 50}>
                <Link to={`/blog/${p.slug}`}>
                  <article className="card card-glow" style={{
                    height: "100%",
                    minHeight: 420,
                    display: "flex",
                    flexDirection: "column",
                    padding: 0,
                    marginLeft: i % 3 === 0 ? 0 : -1,
                    marginTop: i >= 3 ? -1 : 0,
                  }}>
                    <div style={{
                      height: 180,
                      background: `linear-gradient(${135 + (i * 30) % 360}deg, var(--brand-faint), var(--bg-2))`,
                      borderBottom: "1px solid var(--border)",
                      display: "grid",
                      placeItems: "center",
                      position: "relative",
                    }}>
                      <BlogGraphic i={i + 1}/>
                      <div className="chip" style={{ position: "absolute", top: 16, left: 16 }}>{p.cat}</div>
                    </div>
                    <div style={{ padding: 28, display: "flex", flexDirection: "column", flex: 1 }}>
                      <div className="font-mono" style={{ fontSize: 11, color: "var(--muted)", letterSpacing: "0.12em", marginBottom: 12 }}>{p.date} · {p.readTime}</div>
                      <h3 className="font-display" style={{ fontSize: 20, fontWeight: 600, letterSpacing: "-0.02em", lineHeight: 1.2, marginBottom: 12 }}>{p.title}</h3>
                      <p style={{ color: "var(--fg-2)", fontSize: 14, lineHeight: 1.5, flex: 1, margin: 0 }}>{p.excerpt}</p>
                      <div style={{ marginTop: 20, color: "var(--brand)", fontSize: 13, fontWeight: 600 }}>Read →</div>
                    </div>
                  </article>
                </Link>
              </Reveal>
            ))}
          </div>
        </div>
        <style>{`
          @media (max-width: 880px) {
            .blog-feature { grid-template-columns: 1fr !important; }
            .blog-grid { grid-template-columns: 1fr !important; }
            .blog-grid > div > a > article { margin-left: 0 !important; margin-top: -1px !important; }
          }
          @media (min-width: 881px) and (max-width: 1100px) {
            .blog-grid { grid-template-columns: repeat(2, 1fr) !important; }
          }
        `}</style>
      </section>
    </div>
  );
}

function BlogPostPage({ slug }) {
  const post = BLOG_POSTS.find((p) => p.slug === slug);
  const idx = BLOG_POSTS.findIndex((p) => p.slug === slug);
  if (!post) {
    return (
      <div className="page-enter">
        <PageHero eyebrow="404" title="Post not found." subtitle="That article might have moved or been retired."/>
        <div className="container" style={{ paddingBottom: 80 }}>
          <Button to="/blog">← All posts</Button>
        </div>
      </div>
    );
  }
  const next = BLOG_POSTS[(idx + 1) % BLOG_POSTS.length];
  const prev = BLOG_POSTS[(idx - 1 + BLOG_POSTS.length) % BLOG_POSTS.length];
  const related = BLOG_POSTS.filter(p => p.cat === post.cat && p.slug !== post.slug).slice(0, 3);

  return (
    <div className="page-enter">
      <section style={{ paddingTop: 160, paddingBottom: 40, position: "relative", overflow: "hidden" }}>
        <div aria-hidden style={{ position: "absolute", inset: 0, pointerEvents: "none", overflow: "hidden" }}>
          <svg width="600" height="600" viewBox="0 0 600 600" style={{ position: "absolute", right: "-200px", top: "-100px", opacity: 0.4 }} className="page-hero-disc">
            <path d="M 300 50 A 250 250 0 0 1 300 550 L 300 50 Z" fill="var(--brand)" opacity="0.10"/>
          </svg>
        </div>
        <div className="container" style={{ position: "relative", zIndex: 2, maxWidth: 880 }}>
          <Reveal>
            <Link to="/blog" style={{ color: "var(--muted)", fontSize: 14, fontFamily: "JetBrains Mono, monospace", marginBottom: 32, display: "inline-block" }}>← All articles</Link>
          </Reveal>
          <Reveal delay={60}>
            <div style={{ display: "flex", gap: 12, alignItems: "center", marginBottom: 24, flexWrap: "wrap" }}>
              <span className="chip" style={{ background: "var(--brand-faint)", borderColor: "rgba(108,191,0,0.3)", color: "var(--brand)" }}>{post.cat}</span>
              <span className="font-mono" style={{ fontSize: 12, color: "var(--muted)" }}>{post.date} · {post.readTime} read</span>
            </div>
          </Reveal>
          <Reveal delay={120}>
            <h1 className="font-display" style={{ fontSize: "clamp(36px, 5vw, 60px)", lineHeight: 1.05, letterSpacing: "-0.03em", marginBottom: 28 }}>
              {post.title}
            </h1>
          </Reveal>
          <Reveal delay={180}>
            <p className="lead" style={{ fontSize: 21, lineHeight: 1.5, maxWidth: 720, marginBottom: 0 }}>{post.excerpt}</p>
          </Reveal>
        </div>
      </section>

      {/* Cover */}
      <section style={{ paddingTop: 0, paddingBottom: 40 }}>
        <div className="container" style={{ maxWidth: 1100 }}>
          <Reveal>
            <div style={{
              minHeight: 360,
              background: "linear-gradient(135deg, var(--brand-faint), var(--bg-2))",
              border: "1px solid var(--border)",
              display: "grid",
              placeItems: "center",
            }}>
              <BlogGraphic i={idx} large/>
            </div>
          </Reveal>
        </div>
      </section>

      {/* Body */}
      <section style={{ paddingTop: 40, paddingBottom: 100 }}>
        <div className="container" style={{ maxWidth: 720 }}>
          {post.body.map((b, i) => (
            <Reveal key={i} delay={i * 40}>
              {b.h ? (
                <h2 className="font-display" style={{ fontSize: 28, fontWeight: 600, letterSpacing: "-0.025em", marginTop: 56, marginBottom: 20 }}>{b.h}</h2>
              ) : (
                <p style={{ fontSize: 18, lineHeight: 1.7, color: "var(--fg-2)", marginBottom: 24, textWrap: "pretty" }}>{b.p}</p>
              )}
            </Reveal>
          ))}

          {/* Author / CTA */}
          <Reveal>
            <div style={{ marginTop: 80, padding: "40px 0", borderTop: "1px solid var(--border)", borderBottom: "1px solid var(--border)", display: "flex", gap: 20, alignItems: "center", flexWrap: "wrap" }}>
              <div style={{
                width: 56, height: 56,
                background: "linear-gradient(135deg, var(--brand), var(--brand-deep))",
                display: "grid", placeItems: "center",
                color: "#0A0C09", fontWeight: 700, fontSize: 18,
                fontFamily: "DM Sans, sans-serif",
              }}>DB</div>
              <div style={{ flex: 1, minWidth: 200 }}>
                <div style={{ fontWeight: 600, fontSize: 15 }}>The Design Brains team</div>
                <div style={{ fontSize: 14, color: "var(--muted)" }}>Writing from Bogra, Bangladesh</div>
              </div>
              <Button variant="primary" to="/quote">Work with us →</Button>
            </div>
          </Reveal>
        </div>
      </section>

      {/* Prev / Next */}
      <section style={{ paddingTop: 0, paddingBottom: 80, borderTop: "1px solid var(--border)" }}>
        <div className="container">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0 }} className="blog-pn">
            <Link to={`/blog/${prev.slug}`}>
              <div className="card" style={{ height: "100%", minHeight: 160, marginRight: -1 }}>
                <div className="font-mono" style={{ fontSize: 12, color: "var(--muted)", letterSpacing: "0.12em", marginBottom: 8 }}>← PREVIOUS</div>
                <div className="font-display" style={{ fontSize: 20, fontWeight: 600 }}>{prev.title}</div>
              </div>
            </Link>
            <Link to={`/blog/${next.slug}`}>
              <div className="card" style={{ height: "100%", minHeight: 160, textAlign: "right" }}>
                <div className="font-mono" style={{ fontSize: 12, color: "var(--muted)", letterSpacing: "0.12em", marginBottom: 8 }}>NEXT →</div>
                <div className="font-display" style={{ fontSize: 20, fontWeight: 600 }}>{next.title}</div>
              </div>
            </Link>
          </div>
        </div>
        <style>{`
          @media (max-width: 720px) {
            .blog-pn { grid-template-columns: 1fr !important; }
            .blog-pn > div:first-child > a > div { margin-right: 0 !important; }
          }
        `}</style>
      </section>

      {/* Related */}
      {related.length > 0 && (
        <section style={{ paddingTop: 60, paddingBottom: 100, background: "var(--bg-2)", borderTop: "1px solid var(--border)" }}>
          <div className="container">
            <SectionHeader eyebrow="Related" title={`More on ${post.cat.toLowerCase()}.`}/>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 0 }} className="blog-grid">
              {related.map((p, i) => (
                <Reveal key={p.slug} delay={i * 50}>
                  <Link to={`/blog/${p.slug}`}>
                    <article className="card card-glow" style={{ height: "100%", minHeight: 280, padding: 28, marginLeft: i === 0 ? 0 : -1 }}>
                      <div className="font-mono" style={{ fontSize: 11, color: "var(--muted)", letterSpacing: "0.12em", marginBottom: 12 }}>{p.date} · {p.readTime}</div>
                      <h3 className="font-display" style={{ fontSize: 19, fontWeight: 600, letterSpacing: "-0.02em", lineHeight: 1.2, marginBottom: 12 }}>{p.title}</h3>
                      <p style={{ color: "var(--fg-2)", fontSize: 14, lineHeight: 1.5, margin: 0 }}>{p.excerpt}</p>
                    </article>
                  </Link>
                </Reveal>
              ))}
            </div>
          </div>
          <style>{`@media (max-width: 880px) { .blog-grid { grid-template-columns: 1fr !important; } }`}</style>
        </section>
      )}
    </div>
  );
}

function BlogGraphic({ i, large = false }) {
  const s = large ? 240 : 140;
  const variants = [
    <svg key="0" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <circle cx="100" cy="100" r="70" stroke="var(--brand)" strokeWidth="1.5" opacity="0.5"/>
      <circle cx="100" cy="100" r="40" fill="var(--brand)" opacity="0.6"/>
      <circle cx="160" cy="60" r="8" fill="var(--brand)"/>
    </svg>,
    <svg key="1" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <path d="M30 100 Q 65 30 100 100 T 170 100" stroke="var(--brand)" strokeWidth="2" fill="none"/>
      <circle cx="100" cy="100" r="6" fill="var(--brand)"/>
    </svg>,
    <svg key="2" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <rect x="40" y="40" width="120" height="120" stroke="var(--brand)" strokeWidth="1.5" opacity="0.4"/>
      <rect x="60" y="60" width="80" height="80" stroke="var(--brand)" strokeWidth="1.5" opacity="0.6"/>
      <rect x="80" y="80" width="40" height="40" fill="var(--brand)"/>
    </svg>,
    <svg key="3" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <path d="M40 160 L 80 60 L 120 110 L 160 40" stroke="var(--brand)" strokeWidth="2" fill="none"/>
      <circle cx="40" cy="160" r="5" fill="var(--brand)"/>
      <circle cx="80" cy="60" r="5" fill="var(--brand)"/>
      <circle cx="120" cy="110" r="5" fill="var(--brand)"/>
      <circle cx="160" cy="40" r="5" fill="var(--brand)"/>
    </svg>,
    <svg key="4" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <path d="M30 30 L 30 170 M 30 170 L 170 170" stroke="var(--brand)" strokeWidth="1.5" opacity="0.5"/>
      <rect x="50" y="120" width="20" height="50" fill="var(--brand)" opacity="0.4"/>
      <rect x="80" y="80" width="20" height="90" fill="var(--brand)" opacity="0.6"/>
      <rect x="110" y="100" width="20" height="70" fill="var(--brand)" opacity="0.5"/>
      <rect x="140" y="60" width="20" height="110" fill="var(--brand)" opacity="0.8"/>
    </svg>,
    <svg key="5" width={s} height={s} viewBox="0 0 200 200" fill="none">
      <circle cx="60" cy="100" r="40" stroke="var(--brand)" strokeWidth="1.5" opacity="0.5"/>
      <circle cx="140" cy="100" r="40" stroke="var(--brand)" strokeWidth="1.5" opacity="0.5"/>
      <circle cx="100" cy="100" r="20" fill="var(--brand)"/>
    </svg>,
  ];
  return variants[i % variants.length];
}

window.BlogIndex = BlogIndex;
window.BlogPostPage = BlogPostPage;
window.BLOG_POSTS = BLOG_POSTS;
