// blog.jsx — index + post view (state-toggled)

const { useState: useStateB } = React;

const POSTS_FALLBACK = [
  {
    slug: "logo-animation-file-formats-explained",
    title: "Logo animation file formats, explained.",
    excerpt: "MP4, MOV, GIF, Lottie, WebM. When to use which — and why most clients only need two of them.",
    category: "Practical",
    minutes: 6,
    date: "May 04, 2026",
    cover: "fmt",
    body: [
      { kind: "p", text: "When you commission a logo animation, you'll usually get back three or four file types. Most clients have no idea what to do with them. Here's a cheat sheet, sorted by where you'll actually use each one." },
      { kind: "h", text: "MP4 — the workhorse" },
      { kind: "p", text: "This is the file you'll use 80% of the time. It plays everywhere, it's small enough to email, and it looks correct on every screen. If you only have time to grab one file from your delivery folder, grab the MP4." },
      { kind: "pull", text: "If you only have time to grab one file, grab the MP4." },
      { kind: "h", text: "MOV with alpha — for layering" },
      { kind: "p", text: "Comes with a transparent background, which means you can drop your logo over video footage, photographs, anywhere the background isn't a flat colour. Slightly heavier file than MP4. Editors love it." },
      { kind: "h", text: "GIF — only when you must" },
      { kind: "p", text: "GIFs are everywhere, but they're a terrible format for motion graphics. Limited to 256 colours, no transparency that doesn't look like a 1998 web page. The only reason to ship one is if your platform genuinely doesn't accept anything else." },
      { kind: "h", text: "Lottie / JSON — for websites" },
      { kind: "p", text: "If your animation is going to live on a website, ask for Lottie. It's a JSON file that renders animation as vectors, which means infinitely sharp at any size and tiny in filesize. Not all animations export cleanly to Lottie — anything with heavy raster effects won't — but for clean vector marks it's gold." },
    ],
  },
  {
    slug: "premium-vs-cheap-logo-animation",
    title: "Why a $50 logo animation isn't a $50 logo animation.",
    excerpt: "A pricing breakdown of what you're actually paying for — and why the cheapest option ends up costing you more.",
    category: "Opinion",
    minutes: 4,
    date: "Apr 18, 2026",
    cover: "pricing",
    body: [
      { kind: "p", text: "I've animated a lot of logos. I've also seen a lot of clients come to me after spending fifty dollars on a marketplace template and getting something they couldn't use. So let me show you what that fifty dollars actually buys, and what mine does." },
      { kind: "h", text: "What you get for $50" },
      { kind: "p", text: "A template. Someone built a generic logo reveal years ago — maybe a swoop with sparkles, maybe a glitch effect — and uploaded it to a marketplace. The seller swaps in your logo over a base mask and renders it. The same animation has been sold to thousands of other companies. It probably looks fine. It also doesn't say anything about your brand." },
      { kind: "h", text: "What you get for $200" },
      { kind: "p", text: "Custom motion designed for your specific mark. Timing tuned to the proportions of your logo. Sound design built around the animation rather than slapped on top. Files in formats you'll actually use. A brand asset that lasts five years instead of looking dated within six months." },
      { kind: "pull", text: "Cheap motion graphics age in dog years. Custom work doesn't." },
      { kind: "h", text: "The hidden cost of cheap" },
      { kind: "p", text: "Every cheap logo animation I've seen has been redone within eighteen months. The client gets it, uses it once, realises it looks like everyone else's, and commissions a new one. By the time they're three swap-outs in, they've spent more than they would've on a custom job at the start." },
      { kind: "p", text: "Cheap motion graphics age in dog years. Custom work doesn't." },
    ],
  },
  {
    slug: "animated-logo-youtube-channel",
    title: "The two-second logo: making intros for YouTube.",
    excerpt: "If your intro is over three seconds, your viewers are leaving. A breakdown of intros that work in the new attention economy.",
    category: "How-to",
    minutes: 5,
    date: "Apr 02, 2026",
    cover: "youtube",
    body: [
      { kind: "p", text: "Watch any successful YouTube channel from 2019 and you'll see a six-second logo intro. Watch one from 2026 and the intro is two seconds, sometimes less. The data is brutal: every additional second of intro is another percent of viewers who close the tab." },
      { kind: "h", text: "Cut your intro in half. Then cut it again." },
      { kind: "p", text: "Most creators send me a three-second concept and I send back two-second cuts. Almost every time, the shorter one tests better in retention analytics. Your intro is not where you tell viewers who you are — it's where you confirm they're in the right place." },
      { kind: "h", text: "The 'snap' principle" },
      { kind: "p", text: "Good two-second intros land on a single sharp beat. Logo enters, hits a final pose, locks. No drift, no afterthought, no settling. The animation tells the viewer 'this is content, get ready' — and then it gets out of the way." },
      { kind: "pull", text: "Your intro isn't where you tell viewers who you are. It's where you confirm they're in the right place." },
      { kind: "h", text: "Sound matters more than you think" },
      { kind: "p", text: "Half the punch of a great two-second logo is in the sound design. A clean impact, one whoosh, maybe a tonal sting — that's it. If your intro relies on visual fireworks because the sound is silent, you're losing half your impact on every play." },
    ],
  },
];

function getPosts() {
  return window.VM_POSTS || POSTS_FALLBACK;
}

function Blog({ setRoute }) {
  const [post, setPost] = useStateB(null);

  if (post) return <Post post={post} onBack={() => setPost(null)} setPost={setPost} setRoute={setRoute} />;

  return <Index setPost={setPost} setRoute={setRoute} />;
}

/* ─── index ─── */

function Index({ setPost, setRoute }) {
  return (
    <main style={{ paddingTop: 88 }}>
      <section style={{ padding: "60px 0 var(--d-block)" }}>
        <div className="wrap-wide">
          <div className="eyebrow" style={{ marginBottom: 20 }}>—  Studio journal</div>

          <div style={{
            display: "grid",
            gridTemplateColumns: "1.5fr 1fr",
            gap: 64, alignItems: "flex-end",
            marginBottom: 64,
          }}>
            <h1 style={{ fontSize: "clamp(56px, 7.4vw, 136px)" }}>
              <span className="serif">Notes</span>
              <span style={{ color: "var(--ink)" }}> from</span>
              <br/>
              <span style={{ color: "var(--ink)" }}>the </span>
              <span className="serif spectrum">studio.</span>
            </h1>
            <p style={{
              color: "var(--ink-2)", fontSize: 19, lineHeight: 1.5,
              margin: 0, maxWidth: 420,
            }}>
              Thoughts on logo animation, pricing, formats, and what makes motion
              actually land. New posts roughly monthly.
            </p>
          </div>

          {/* featured */}
          <Reveal>
            <button
              onClick={() => setPost(getPosts()[0])}
              style={{
                background: "transparent", border: 0, padding: 0,
                cursor: "pointer", textAlign: "left", width: "100%",
                color: "var(--ink)",
              }}
            >
              <FeaturedCard post={getPosts()[0]} />
            </button>
          </Reveal>

          {/* grid of remaining */}
          <div style={{
            marginTop: 32,
            display: "grid",
            gridTemplateColumns: "repeat(2, 1fr)",
            gap: 20,
          }}>
            {getPosts().slice(1).map((p, i) => (
              <Reveal key={p.slug} delay={i * 100}>
                <PostCard post={p} onClick={() => setPost(p)} />
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <NewsletterStrip setRoute={setRoute} />
    </main>
  );
}

function FeaturedCard({ post }) {
  return (
    <div className="glass" style={{
      borderRadius: "var(--radius-lg)",
      overflow: "hidden",
      display: "grid",
      gridTemplateColumns: "1.1fr 1fr",
      minHeight: 380,
    }}>
      <CoverArt kind={post.cover} featured />
      <div style={{ padding: "40px 44px", display: "flex", flexDirection: "column", gap: 18 }}>
        <div style={{ display: "flex", gap: 8 }}>
          <span className="chip"><span className="chip-dot" style={{ background: "var(--p-cyan)", boxShadow: "0 0 10px var(--p-cyan)" }}/> FEATURED</span>
          <span className="chip">{post.category.toUpperCase()}</span>
        </div>
        <div style={{
          fontFamily: "var(--font-display)", fontSize: 44, fontWeight: 500,
          letterSpacing: "-0.03em", lineHeight: 1,
          textWrap: "pretty",
        }}>
          {post.title}
        </div>
        <p className="serif" style={{
          fontSize: 22, color: "var(--ink-2)", margin: 0,
          lineHeight: 1.35,
          textWrap: "pretty",
        }}>
          {post.excerpt}
        </p>
        <div style={{ flex: 1 }} />
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <div className="mono" style={{ fontSize: 11, letterSpacing: "0.14em", color: "var(--ink-3)" }}>
            {post.date.toUpperCase()} · {post.minutes} MIN READ
          </div>
          <span style={{
            display: "inline-flex", alignItems: "center", gap: 6,
            fontFamily: "var(--font-sans)", fontWeight: 500, fontSize: 14,
            color: "var(--p-cyan)",
          }}>
            Read essay <span className="arr">→</span>
          </span>
        </div>
      </div>
    </div>
  );
}

function PostCard({ post, onClick }) {
  return (
    <button
      onClick={onClick}
      className="glass"
      style={{
        background: "transparent", padding: 0,
        cursor: "pointer", textAlign: "left",
        color: "var(--ink)",
        borderRadius: "var(--radius)",
        overflow: "hidden",
        display: "flex", flexDirection: "column",
        transition: "transform 240ms, border-color 240ms",
      }}
      onMouseEnter={e => { e.currentTarget.style.transform = "translateY(-3px)"; e.currentTarget.style.borderColor = "var(--line-2)"; }}
      onMouseLeave={e => { e.currentTarget.style.transform = "none"; e.currentTarget.style.borderColor = "var(--line)"; }}
    >
      <CoverArt kind={post.cover} />
      <div style={{ padding: "26px 28px 24px", display: "flex", flexDirection: "column", gap: 12, flex: 1 }}>
        <div style={{ display: "flex", gap: 8 }}>
          <span className="chip">{post.category.toUpperCase()}</span>
        </div>
        <div style={{
          fontFamily: "var(--font-display)", fontSize: 28, fontWeight: 500,
          letterSpacing: "-0.025em", lineHeight: 1.1,
        }}>
          {post.title}
        </div>
        <p style={{ color: "var(--ink-2)", fontSize: 14, lineHeight: 1.5, margin: 0 }}>
          {post.excerpt}
        </p>
        <div style={{ flex: 1 }} />
        <div className="mono" style={{ fontSize: 10, letterSpacing: "0.14em", color: "var(--ink-3)" }}>
          {post.date.toUpperCase()} · {post.minutes} MIN
        </div>
      </div>
    </button>
  );
}

/* ─── cover art (no real images — make distinct generative covers) ─── */

function CoverArt({ kind, featured }) {
  return (
    <div style={{
      position: "relative",
      aspectRatio: featured ? "auto" : "16/9",
      minHeight: featured ? "auto" : 200,
      background: "var(--void-2)",
      overflow: "hidden",
    }}>
      {/* atmosphere */}
      <div style={{
        position: "absolute", inset: 0,
        background: kind === "fmt"
          ? "radial-gradient(70% 50% at 20% 20%, rgba(0,212,255,0.45), transparent 70%), radial-gradient(70% 50% at 80% 80%, rgba(138,77,255,0.45), transparent 70%)"
          : kind === "pricing"
            ? "radial-gradient(70% 50% at 30% 70%, rgba(255,179,71,0.4), transparent 70%), radial-gradient(70% 50% at 80% 20%, rgba(255,43,216,0.35), transparent 70%)"
            : "radial-gradient(70% 50% at 20% 80%, rgba(255,90,165,0.4), transparent 70%), radial-gradient(70% 50% at 80% 20%, rgba(74,108,255,0.4), transparent 70%)",
        filter: "blur(28px)",
      }} />

      {/* artwork */}
      <div style={{
        position: "absolute", inset: 0,
        display: "flex", alignItems: "center", justifyContent: "center",
      }}>
        {kind === "fmt" && <FormatGlyph />}
        {kind === "pricing" && <PricingGlyph />}
        {kind === "youtube" && <YouTubeGlyph />}
      </div>

      {/* grain */}
      <div style={{
        position: "absolute", inset: 0,
        background: "url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence baseFrequency='0.9'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.08 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>\")",
        mixBlendMode: "overlay", opacity: 0.6,
      }} />

      <CornerMark pos="tl" />
      <CornerMark pos="tr" />
      <CornerMark pos="bl" />
      <CornerMark pos="br" />
    </div>
  );
}

function CornerMark({ pos }) {
  const map = {
    tl: { top: 16, left: 16, borderTop: "1px solid rgba(255,255,255,0.5)", borderLeft: "1px solid rgba(255,255,255,0.5)" },
    tr: { top: 16, right: 16, borderTop: "1px solid rgba(255,255,255,0.5)", borderRight: "1px solid rgba(255,255,255,0.5)" },
    bl: { bottom: 16, left: 16, borderBottom: "1px solid rgba(255,255,255,0.5)", borderLeft: "1px solid rgba(255,255,255,0.5)" },
    br: { bottom: 16, right: 16, borderBottom: "1px solid rgba(255,255,255,0.5)", borderRight: "1px solid rgba(255,255,255,0.5)" },
  };
  return <div style={{ position: "absolute", width: 14, height: 14, ...map[pos] }} />;
}

function FormatGlyph() {
  return (
    <svg width="280" height="220" viewBox="0 0 280 220" fill="none" style={{ maxWidth: "60%", height: "auto" }}>
      {["MP4","MOV","GIF","JSON"].map((ext, i) => (
        <g key={ext} transform={`translate(${20 + i*60}, ${30 + (i%2)*30})`}>
          <rect width="80" height="100" rx="4" stroke="rgba(255,255,255,0.7)" strokeWidth="1" fill="rgba(255,255,255,0.04)" />
          <path d="M50 0v20h20" stroke="rgba(255,255,255,0.7)" strokeWidth="1" />
          <text x="40" y="60" textAnchor="middle" fill="white" fontFamily="monospace" fontSize="14" fontWeight="600">{ext}</text>
        </g>
      ))}
    </svg>
  );
}

function PricingGlyph() {
  return (
    <svg width="280" height="220" viewBox="0 0 280 220" fill="none" style={{ maxWidth: "55%", height: "auto" }}>
      <text x="60" y="110" fontFamily="serif" fontStyle="italic" fontSize="80" fill="rgba(255,255,255,0.95)" fontWeight="400">$50</text>
      <line x1="50" y1="98" x2="180" y2="98" stroke="rgba(255,90,90,0.95)" strokeWidth="3" />
      <text x="140" y="180" fontFamily="serif" fontStyle="italic" fontSize="56" fill="white" fontWeight="400">$200</text>
    </svg>
  );
}

function YouTubeGlyph() {
  return (
    <svg width="280" height="220" viewBox="0 0 280 220" fill="none" style={{ maxWidth: "55%", height: "auto" }}>
      <rect x="40" y="40" width="200" height="140" rx="10" stroke="rgba(255,255,255,0.7)" strokeWidth="1" />
      <polygon points="125,90 165,110 125,130" fill="white" />
      <rect x="40" y="160" width="50" height="3" fill="rgba(255,90,90,0.9)" />
      <rect x="90" y="160" width="150" height="3" fill="rgba(255,255,255,0.3)" />
    </svg>
  );
}

/* ─── post view ─── */

function Post({ post, onBack, setPost, setRoute }) {
  return (
    <main style={{ paddingTop: 88 }}>
      <article style={{ padding: "60px 0 var(--d-section)" }}>
        <div className="wrap" style={{ maxWidth: 980 }}>
          {/* breadcrumb */}
          <div style={{ display: "flex", gap: 6, alignItems: "center", marginBottom: 40 }}>
            <button onClick={onBack} style={{
              background: "transparent", border: 0, padding: 0,
              color: "var(--ink-3)", cursor: "pointer",
              fontFamily: "var(--font-mono)", fontSize: 11, letterSpacing: "0.14em",
            }}>
              ← JOURNAL
            </button>
            <span className="mono" style={{ fontSize: 11, color: "var(--ink-4)", letterSpacing: "0.14em" }}>
              · {post.category.toUpperCase()}
            </span>
          </div>

          {/* header */}
          <header style={{ marginBottom: 56 }}>
            <h1 style={{
              fontSize: "clamp(40px, 5.6vw, 88px)",
              maxWidth: 880,
              marginBottom: 28,
            }}>
              {post.title}
            </h1>
            <div style={{
              display: "flex", justifyContent: "space-between", alignItems: "center", gap: 24,
              paddingTop: 20, paddingBottom: 20,
              borderTop: "1px solid var(--line)",
              borderBottom: "1px solid var(--line)",
              flexWrap: "wrap",
            }}>
              <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
                <div style={{
                  width: 36, height: 36, borderRadius: "50%",
                  background: "linear-gradient(135deg, var(--p-cyan), var(--p-violet))",
                }} />
                <div>
                  <div style={{ fontSize: 14, fontWeight: 600 }}>Alex Moretti</div>
                  <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.12em" }}>
                    {post.date.toUpperCase()} · {post.minutes} MIN READ
                  </div>
                </div>
              </div>
              <div style={{ display: "flex", gap: 8 }}>
                <button className="btn btn-ghost" style={{ fontSize: 12, padding: "8px 14px" }}>Copy link</button>
                <button className="btn btn-ghost" style={{ fontSize: 12, padding: "8px 14px" }}>Share</button>
              </div>
            </div>
          </header>

          {/* cover */}
          <Reveal>
            <div style={{
              borderRadius: "var(--radius-lg)",
              overflow: "hidden",
              border: "1px solid var(--line)",
              marginBottom: 56,
              aspectRatio: "16/8",
              position: "relative",
            }}>
              <CoverArt kind={post.cover} featured />
            </div>
          </Reveal>

          {/* body */}
          <div style={{ display: "flex", flexDirection: "column", gap: 28 }}>
            {post.body.map((b, i) => {
              if (b.kind === "h") return (
                <h2 key={i} style={{
                  fontSize: "clamp(28px, 3.4vw, 44px)",
                  letterSpacing: "-0.025em",
                  marginTop: 16,
                }}>
                  <span className="serif">{b.text}</span>
                </h2>
              );
              if (b.kind === "pull") return (
                <blockquote key={i} style={{
                  margin: "24px 0",
                  padding: "32px 40px",
                  borderLeft: "2px solid var(--p-cyan)",
                  background: "var(--glass)",
                  borderRadius: "0 var(--radius-sm) var(--radius-sm) 0",
                  fontFamily: "var(--font-serif)", fontStyle: "italic",
                  fontSize: "clamp(24px, 2.6vw, 34px)",
                  lineHeight: 1.3,
                  color: "var(--ink)",
                }}>
                  {b.text}
                </blockquote>
              );
              return (
                <p key={i} style={{
                  fontSize: 19, lineHeight: 1.65,
                  color: "var(--ink-2)",
                  margin: 0,
                  textWrap: "pretty",
                }}>
                  {b.text}
                </p>
              );
            })}
          </div>

          {/* footer / next */}
          <div style={{ marginTop: 80, paddingTop: 40, borderTop: "1px solid var(--line)" }}>
            <div className="eyebrow" style={{ marginBottom: 24 }}>—  Keep reading</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: 16 }}>
              {getPosts().filter(p => p.slug !== post.slug).slice(0, 2).map(p => (
                <PostCard key={p.slug} post={p} onClick={() => { window.scrollTo({ top: 0 }); setPost(p); }} />
              ))}
            </div>
          </div>
        </div>
      </article>
    </main>
  );
}

/* ─── newsletter strip ─── */

function NewsletterStrip({ setRoute }) {
  return (
    <section style={{ padding: "var(--d-block) 0 var(--d-section)" }}>
      <div className="wrap-wide">
        <Reveal>
          <div className="glass glass-hi" style={{
            position: "relative",
            padding: "56px 56px",
            borderRadius: "var(--radius-lg)",
            display: "grid",
            gridTemplateColumns: "1.4fr 1fr",
            gap: 32,
            alignItems: "center",
            overflow: "hidden",
          }}>
            <div style={{
              position: "absolute",
              right: -100, bottom: -120, width: 380, opacity: 0.4,
              filter: "blur(0.5px)",
              animation: "prismFloat 9s ease-in-out infinite",
              pointerEvents: "none",
            }}>
              <img src="assets/prism.png" alt="" style={{ width: "100%", display: "block" }} />
            </div>
            <div style={{ position: "relative" }}>
              <div className="eyebrow" style={{ marginBottom: 14 }}>—  Studio dispatch</div>
              <h2 style={{ fontSize: "clamp(36px, 4.4vw, 56px)", marginBottom: 16 }}>
                One <span className="serif">essay</span> per month.<br/>
                <span className="serif" style={{ color: "var(--ink-2)" }}>Nothing else.</span>
              </h2>
              <p style={{ color: "var(--ink-2)", fontSize: 16, lineHeight: 1.5, margin: 0, maxWidth: 460 }}>
                New posts, occasional behind-the-scenes of recent commissions, and
                first-look at slots when I open them. Unsubscribe with one click.
              </p>
            </div>
            <div style={{ position: "relative", display: "flex", flexDirection: "column", gap: 12 }}>
              <input
                type="email"
                placeholder="you@studio.com"
                style={{
                  ...inputStyleC, fontSize: 16, padding: "16px 18px",
                  background: "rgba(6,6,11,0.55)",
                }}
              />
              <button className="btn btn-primary" style={{ width: "100%", justifyContent: "center", fontSize: 15 }}>
                Subscribe <span className="arr">→</span>
              </button>
              <div className="mono" style={{ fontSize: 11, color: "var(--ink-3)", letterSpacing: "0.14em", textAlign: "center" }}>
                312 SUBSCRIBERS · NO SPAM
              </div>
            </div>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

const inputStyleC = {
  width: "100%",
  padding: "14px 16px",
  borderRadius: "var(--radius-sm)",
  background: "var(--glass)",
  border: "1px solid var(--line-2)",
  color: "var(--ink)",
  fontFamily: "var(--font-sans)",
  fontSize: 15,
  outline: "none",
};

Object.assign(window, { Blog, VM_POSTS: POSTS_FALLBACK });
