// NekoWebFree — pages: Home, Browse, Category, Product detail, About

const { useState: useStateP, useEffect: useEffectP, useMemo: useMemoP } = React;

// ──────────────────────────────────────────────────────────────────
// HOME
// ──────────────────────────────────────────────────────────────────
function HomePage() {
  const { go } = useRouter();
  const cfg = window.NEKO_CONFIG;
  const data = window.NEKO_DATA;
  const [q, setQ] = useStateP("");

  const featured = ["nekobaseplate", "nekohangoutkit", "nekocustomchat", "nekocarryv1", "nekomorph", "nekoaudioweb", "nekosky", "nekoaurashop"]
    .map(id => data.products.find(p => p.id === id))
    .filter(Boolean);

  const popularByCat = [
    { cat: "kits",     pick: "nekobaseplate" },
    { cat: "chat",     pick: "nekocustomchat" },
    { cat: "carry",    pick: "nekocarryv3lite" },
    { cat: "avatar",   pick: "nekomorph" },
  ].map(({ cat, pick }) => ({ cat: data.cats[cat], product: data.products.find(p => p.id === pick) }));

  return (
    <main>
      {/* HERO */}
      <section style={{ paddingTop: 88, paddingBottom: 56 }}>
        <div className="container" style={{ display: "grid", gridTemplateColumns: "1.15fr 1fr", gap: 80, alignItems: "center" }}>
          <div>
            <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 24, display: "flex", alignItems: "center", gap: 10 }}>
              <span style={{ width: 24, height: 1, background: "var(--muted)" }} />
              Free hub — Roblox systems
            </div>
            <h1 style={{
              fontSize: "clamp(48px, 6vw, 76px)", lineHeight: 1.02, letterSpacing: "-0.035em",
              margin: 0, fontWeight: 600,
            }}>
              NoMen Free
              <br />systems for <em style={{ fontStyle: "normal", color: "var(--accent)" }}>Roblox</em>.
            </h1>
            <p style={{ fontSize: 17, color: "var(--muted)", lineHeight: 1.55, marginTop: 28, maxWidth: 460 }}>
              Base kits, custom chat, carry rigs, NPCs, mounts, admin tools, music players — drop them into your place and ship. No payment, no whitelist.
            </p>
            <div style={{ display: "flex", gap: 12, marginTop: 36, flexWrap: "wrap" }}>
              <Btn primary onClick={() => go("/browse")}>Browse all systems →</Btn>
              <Btn href={cfg.youtubeChannel} external style={{ borderColor: "var(--accent)", color: "var(--accent)" }}>
                <YtGlyph /> Subscribe on YouTube
              </Btn>
            </div>
            <div style={{ display: "flex", gap: 28, marginTop: 56, color: "var(--muted)", fontSize: 12, fontFamily: "var(--mono)", letterSpacing: "0.06em", textTransform: "uppercase", flexWrap: "wrap" }}>
              <span>NoMen systems</span>
              <span style={{ width: 1, background: "var(--border)" }} />
              <span>11 categories</span>
              <span style={{ width: 1, background: "var(--border)" }} />
              <span>Free forever</span>
            </div>
          </div>

          {/* Hero composition — clean stack, no overlap */}
          <div style={{ position: "relative", display: "flex", flexDirection: "column", gap: 18 }}>
            {/* Top card — featured product with real YT thumb */}
            <div onClick={() => go("/p/nekohangoutkit")} style={{
              borderRadius: 6, overflow: "hidden",
              border: "1px solid var(--border)", background: "var(--bg)",
              cursor: "pointer",
              boxShadow: "0 12px 36px rgba(0,0,0,0.06)",
            }}>
              <div style={{ position: "relative", width: "100%", aspectRatio: "16/9", background: "var(--ink)", overflow: "hidden" }}>
                <img src="https://i.ytimg.com/vi/OsBYxC21Rxs/hqdefault.jpg" alt=""
                     style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
                <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", background: "rgba(0,0,0,0.18)" }}>
                  <div style={{ width: 56, height: 56, borderRadius: "50%", background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 6px 20px rgba(0,0,0,0.3)" }}>
                    <svg width="18" height="18" viewBox="0 0 22 22"><polygon points="6,3 6,19 19,11" fill="white" /></svg>
                  </div>
                </div>
                <div style={{
                  position: "absolute", top: 12, left: 12,
                  fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.06em",
                  textTransform: "uppercase", color: "white", background: "var(--accent)",
                  padding: "3px 7px", borderRadius: 2,
                }}>Featured</div>
              </div>
              <div style={{ padding: "16px 18px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>
                    Base Kit · with trailer
                  </div>
                  <div style={{ fontSize: 18, fontWeight: 600, letterSpacing: "-0.015em" }}>NekoHangoutKit</div>
                </div>
                <SourcePill kind="roblox-create" />
              </div>
            </div>

            {/* Bottom strip — dark, slogan + subscribed chip inline (no overlap) */}
            <div style={{
              borderRadius: 6, overflow: "hidden",
              background: "var(--ink)", color: "var(--bg)",
              padding: "20px 22px",
              display: "flex", alignItems: "center", gap: 16,
            }}>
              <NekoMark size={32} />
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", color: "oklch(0.7 0.005 60)", marginBottom: 2 }}>
                  Drop · install · run
                </div>
                <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em" }}>
                  Find, download, drag into Studio.
                </div>
              </div>
              <div style={{
                background: "var(--accent)", color: "white",
                padding: "8px 12px", borderRadius: 999,
                fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.06em",
                display: "flex", alignItems: "center", gap: 6, flexShrink: 0,
              }}>
                <YtGlyph /> SUB ✓
              </div>
            </div>
          </div>
        </div>
      </section>

      {/* SEARCH */}
      <section style={{ paddingTop: 12, paddingBottom: 48 }}>
        <div className="container">
          <div style={{
            display: "flex", alignItems: "center", gap: 14, padding: "16px 22px",
            border: "1px solid var(--border)", borderRadius: 999, background: "var(--bg)",
          }}>
            <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="var(--muted)" strokeWidth="1.5"><circle cx="8" cy="8" r="5.5"/><path d="M12 12l4 4"/></svg>
            <input
              value={q}
              onChange={(e) => setQ(e.target.value)}
              placeholder="Search systems — try 'carry', 'chat', 'morph'…"
              onKeyDown={(e) => { if (e.key === "Enter" && q) go("/browse"); }}
              style={{ flex: 1, border: "none", outline: "none", fontFamily: "var(--sans)", fontSize: 15, background: "transparent", color: "var(--ink)" }}
            />
            <kbd style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", border: "1px solid var(--border)", borderRadius: 4, padding: "3px 7px" }}>↵</kbd>
          </div>
          {q && (
            <div style={{ marginTop: 16, display: "flex", flexDirection: "column", gap: 4 }}>
              {data.products
                .filter(p => p.name.toLowerCase().includes(q.toLowerCase()))
                .slice(0, 6)
                .map(p => (
                  <button key={p.id} onClick={() => go(`/p/${p.id}`)} style={{
                    display: "flex", alignItems: "center", justifyContent: "space-between", gap: 16,
                    padding: "10px 18px", border: "1px solid var(--border)", borderRadius: 999,
                    background: "var(--bg)", cursor: "pointer", textAlign: "left",
                  }}>
                    <span style={{ fontSize: 14, fontWeight: 500 }}>{p.name}</span>
                    <SourcePill kind={p.link.kind} />
                  </button>
                ))}
            </div>
          )}
        </div>
      </section>

      {/* CATEGORIES — 11 of them in 4-col grid (last row 3 wide is fine) */}
      <section style={{ paddingTop: 32, paddingBottom: 24 }}>
        <div className="container">
          <SectionHeading kicker="01 — Categories" title="Browse by what it does." />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 12 }}>
            {Object.values(data.cats).map((c, i) => {
              const count = data.products.filter(p => p.cat === c.id).length;
              return (
                <button key={c.id} onClick={() => go(`/c/${c.id}`)} style={{
                  textAlign: "left", padding: "22px", border: "1px solid var(--border)",
                  borderRadius: 6, background: "var(--bg)", cursor: "pointer", color: "var(--ink)",
                  display: "flex", flexDirection: "column", justifyContent: "space-between",
                  minHeight: 140,
                  transition: "border-color 140ms",
                }}
                onMouseEnter={(e)=>{e.currentTarget.style.borderColor="var(--ink)";}}
                onMouseLeave={(e)=>{e.currentTarget.style.borderColor="var(--border)";}}>
                  <div>
                    <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.08em", marginBottom: 8 }}>
                      {String(i+1).padStart(2,"0")} / {String(Object.values(data.cats).length).padStart(2,"0")}
                    </div>
                    <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: "-0.01em" }}>{c.label}</div>
                    <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 6, lineHeight: 1.45 }}>{c.blurb}</div>
                  </div>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginTop: 16 }}>
                    <span style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)" }}>
                      {count} system{count===1?"":"s"}
                    </span>
                    <span style={{ color: "var(--accent)", fontSize: 18 }}>→</span>
                  </div>
                </button>
              );
            })}
          </div>
        </div>
      </section>

      {/* FEATURED ROW */}
      <section style={{ paddingTop: 80, paddingBottom: 24 }}>
        <div className="container">
          <SectionHeading
            kicker="02 — Featured"
            title="Most-grabbed systems."
            action={<button onClick={() => go("/browse")} style={{ background: "transparent", border: "none", cursor: "pointer", fontSize: 13, color: "var(--muted)", fontFamily: "var(--sans)" }}>View all →</button>}
          />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }}>
            {featured.map(p => <ProductCard key={p.id} product={p} onClick={() => go(`/p/${p.id}`)} />)}
          </div>
        </div>
      </section>

      {/* SOURCE BLOCK — explain mediafire / roblox-create / web */}
      <section style={{ paddingTop: 80, paddingBottom: 24 }}>
        <div className="container">
          <SectionHeading kicker="03 — Where they live" title="Three sources, one workflow." />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 16 }}>
            <SourceCard kind="mediafire" title="MediaFire (.rbxm / .rbxl)"
              body="Direct file download. Save the .rbxm, open Roblox Studio, drag the file into your place." />
            <SourceCard kind="roblox-create" title="Roblox Create Store"
              body="Free asset on Roblox. Click 'Get' on the listing, then find it in your Toolbox → Inventory in Studio." />
            <SourceCard kind="web" title="Web app"
              body="Some tools (like NekoAudioWeb) are hosted web apps you use alongside Studio — no download needed." />
          </div>
        </div>
      </section>

      {/* THANK-YOU CTA */}
      <section style={{ paddingTop: 96, paddingBottom: 24 }}>
        <div className="container">
          <div style={{
            background: "var(--ink)", color: "var(--bg)", borderRadius: 8,
            padding: 56, display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 56, alignItems: "center",
          }}>
            <div>
              <div style={{ fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.1em", textTransform: "uppercase", color: "oklch(0.7 0.005 60)", marginBottom: 16 }}>
                One thing I ask
              </div>
              <h3 style={{ fontSize: 40, fontWeight: 600, letterSpacing: "-0.025em", margin: 0, lineHeight: 1.05 }}>
                Keep the sub — keep the drops.
              </h3>
              <p style={{ fontSize: 15, color: "oklch(0.78 0.005 60)", marginTop: 18, lineHeight: 1.55, marginBottom: 0, maxWidth: 460 }}>
                These systems exist because the channel does. Stay subscribed and new free drops keep landing here.
              </p>
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
              <a href={cfg.youtubeChannel} target="_blank" rel="noreferrer" style={{
                display: "inline-flex", alignItems: "center", gap: 10, justifyContent: "center",
                padding: "16px 24px", borderRadius: 999,
                background: "var(--accent)", color: "white",
                textDecoration: "none", fontFamily: "var(--sans)", fontSize: 15, fontWeight: 600,
              }}>
                <YtGlyph /> Subscribe to {cfg.youtubeHandle} ↗
              </a>
              <a href={cfg.discordInvite} target="_blank" rel="noreferrer" style={{
                display: "inline-flex", alignItems: "center", gap: 10, justifyContent: "center",
                padding: "16px 24px", borderRadius: 999,
                background: "transparent", color: "var(--bg)",
                textDecoration: "none", fontFamily: "var(--sans)", fontSize: 15, fontWeight: 500,
                border: "1px solid oklch(0.4 0.005 60)",
              }}>
                Join Discord — nomenfams ↗
              </a>
            </div>
          </div>
        </div>
      </section>
    </main>
  );
}

function SourceCard({ kind, title, body }) {
  return (
    <div style={{
      padding: 28, border: "1px solid var(--border)", borderRadius: 6, background: "var(--bg)",
      display: "flex", flexDirection: "column", gap: 16,
    }}>
      <SourcePill kind={kind} />
      <div style={{ fontSize: 17, fontWeight: 600, letterSpacing: "-0.01em" }}>{title}</div>
      <p style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.6, margin: 0 }}>{body}</p>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────
// BROWSE — all products with filters
// ──────────────────────────────────────────────────────────────────
function BrowsePage({ initialCat }) {
  const { go } = useRouter();
  const [cat, setCat] = useStateP(initialCat || "all");
  const [src, setSrc] = useStateP("all");
  const [sort, setSort] = useStateP("default");
  const [q, setQ] = useStateP("");
  const data = window.NEKO_DATA;

  const filtered = useMemoP(() => {
    let r = data.products;
    if (cat !== "all") r = r.filter(p => p.cat === cat);
    if (src !== "all") r = r.filter(p => p.link.kind === src);
    if (q) r = r.filter(p => p.name.toLowerCase().includes(q.toLowerCase()));
    if (sort === "name") r = [...r].sort((a,b) => a.name.localeCompare(b.name));
    if (sort === "cat")  r = [...r].sort((a,b) => a.cat.localeCompare(b.cat));
    return r;
  }, [cat, src, sort, q]);

  const catObj = cat === "all" ? null : data.cats[cat];

  return (
    <main className="container" style={{ paddingTop: 56, paddingBottom: 32 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 28 }}>
        <a href="#/" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/");}}>NekoWebFree</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <span style={{ color: "var(--ink)" }}>{catObj ? catObj.label : "All systems"}</span>
      </div>

      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 32, gap: 24, flexWrap: "wrap" }}>
        <div>
          <h1 style={{ fontSize: 44, fontWeight: 600, letterSpacing: "-0.025em", margin: 0, lineHeight: 1.05 }}>
            {catObj ? catObj.label : "All free systems"}
          </h1>
          <p style={{ fontSize: 15, color: "var(--muted)", marginTop: 12, marginBottom: 0, maxWidth: 560 }}>
            {catObj ? catObj.blurb : "NoMen Free systems for Roblox. Filter by category or source, grab whatever you need."}
          </p>
        </div>
        <div style={{ fontFamily: "var(--mono)", fontSize: 12, color: "var(--muted)" }}>{filtered.length} results</div>
      </div>

      {/* Controls */}
      <div style={{ display: "flex", flexDirection: "column", gap: 14, marginBottom: 28, paddingBottom: 24, borderBottom: "1px solid var(--border)" }}>
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
          <FilterChip active={cat==="all"} onClick={() => setCat("all")}>All ({data.products.length})</FilterChip>
          {Object.values(data.cats).map(c => {
            const n = data.products.filter(p => p.cat === c.id).length;
            return (
              <FilterChip key={c.id} active={cat===c.id} onClick={() => setCat(c.id)}>
                {c.label} ({n})
              </FilterChip>
            );
          })}
        </div>
        <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap" }}>
          <span style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.08em", textTransform: "uppercase" }}>Source</span>
          <div style={{ display: "flex", gap: 6 }}>
            <FilterChip active={src==="all"} onClick={() => setSrc("all")}>All</FilterChip>
            <FilterChip active={src==="mediafire"} onClick={() => setSrc("mediafire")}>MediaFire</FilterChip>
            <FilterChip active={src==="roblox-create"} onClick={() => setSrc("roblox-create")}>Roblox Store</FilterChip>
            <FilterChip active={src==="web"} onClick={() => setSrc("web")}>Web App</FilterChip>
          </div>
          <div style={{ flex: 1 }} />
          <input
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Search…"
            style={{
              padding: "8px 14px", border: "1px solid var(--border)", borderRadius: 999,
              fontFamily: "var(--sans)", fontSize: 13, outline: "none",
              background: "var(--bg)", color: "var(--ink)", width: 200,
            }}
          />
          <select value={sort} onChange={(e) => setSort(e.target.value)} style={{
            padding: "8px 14px", border: "1px solid var(--border)", borderRadius: 999,
            fontFamily: "var(--sans)", fontSize: 13, background: "var(--bg)", color: "var(--ink)", cursor: "pointer",
          }}>
            <option value="default">Default order</option>
            <option value="cat">By category</option>
            <option value="name">Name A–Z</option>
          </select>
        </div>
      </div>

      {filtered.length === 0 ? (
        <div style={{ padding: 80, textAlign: "center", color: "var(--muted)", fontFamily: "var(--mono)", fontSize: 12 }}>
          NOTHING MATCHES — try clearing filters.
        </div>
      ) : (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }}>
          {filtered.map(p => <ProductCard key={p.id} product={p} onClick={() => go(`/p/${p.id}`)} />)}
        </div>
      )}
    </main>
  );
}

// ──────────────────────────────────────────────────────────────────
// PRODUCT DETAIL
// ──────────────────────────────────────────────────────────────────
function ProductPage({ id }) {
  const { go } = useRouter();
  const data = window.NEKO_DATA;
  const p = data.products.find(x => x.id === id);

  if (!p) {
    return (
      <main className="container" style={{ paddingTop: 96, paddingBottom: 96, textAlign: "center" }}>
        <h1 style={{ fontSize: 32, fontWeight: 600 }}>Not found</h1>
        <p style={{ color: "var(--muted)" }}>That system doesn't exist. Try browsing.</p>
        <Btn onClick={() => go("/browse")}>← Back to browse</Btn>
      </main>
    );
  }
  const cat = data.cats[p.cat];
  const hue = data.hue[p.cat];
  const related = data.products.filter(x => x.cat === p.cat && x.id !== p.id).slice(0, 4);

  const sourceLabel = {
    "mediafire": "MediaFire",
    "roblox-create": "Roblox Create Store",
    "web": "Hosted web app",
  }[p.link.kind] || "Download";

  const howSteps = p.link.kind === "mediafire"
    ? [
        ["A", "Click Download", <>Press the orange button below — it opens the MediaFire page in a new tab.</>],
        ["B", "Save the file", <>On MediaFire, click <strong>Download</strong>. Save the <code style={codeStyle}>.rbxm</code> / <code style={codeStyle}>.rbxl</code> to your computer.</>],
        ["C", "Drag into Studio", <>Open Roblox Studio. Drag the file directly into the viewport (or right-click in Explorer → Insert From File).</>],
      ]
    : p.link.kind === "roblox-create"
    ? [
        ["A", "Open the listing", <>Click below to open the asset's page on the Roblox Create Store.</>],
        ["B", "Get the asset", <>Press the <strong>Get</strong> button on Roblox. It's added to your inventory for free.</>],
        ["C", "Insert in Studio", <>In Studio, open Toolbox → Inventory. Find the asset, right-click → <strong>Insert</strong>.</>],
      ]
    : [
        ["A", "Open the web app", <>Click below to launch the hosted tool in a new tab.</>],
        ["B", "Use alongside Studio", <>The app runs in your browser — keep it open next to Roblox Studio while you work.</>],
        ["C", "No install needed", <>Nothing to drag, nothing to insert. Just bookmark it.</>],
      ];

  return (
    <main className="container" style={{ paddingTop: 40, paddingBottom: 32 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 28 }}>
        <a href="#/" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/");}}>NekoWebFree</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <a href={`#/c/${cat.id}`} style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go(`/c/${cat.id}`);}}>{cat.label}</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <span style={{ color: "var(--ink)" }}>{p.name}</span>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 56 }}>
        {/* Media + how-to */}
        <div>
          {p.video ? (
            <VideoPlayer
              key={p.id+"-vid"}
              videoId={p.video}
              label={`${p.name} — trailer`}
            />
          ) : (
            <StripedPlaceholder hue={hue} label={`${p.id} — no preview yet`} badge={p.badge} aspect="16/9" />
          )}

          <section style={{ marginTop: 48 }}>
            <h2 style={{ fontSize: 20, fontWeight: 600, letterSpacing: "-0.015em", marginBottom: 14 }}>About this system</h2>
            <p style={{ fontSize: 15, color: "var(--muted)", lineHeight: 1.65, margin: 0 }}>
              {p.tagline} {p.note ? <span style={{ color: "var(--ink)" }}>· {p.note}.</span> : null}
            </p>

            <h3 style={{ fontSize: 14, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--muted)", marginTop: 36, marginBottom: 18, fontFamily: "var(--mono)" }}>
              How to install
            </h3>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12 }}>
              {howSteps.map(([n, title, body]) => (
                <div key={n} style={{ padding: 20, border: "1px solid var(--border)", borderRadius: 6 }}>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--accent)", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 8 }}>Step {n}</div>
                  <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "-0.01em", marginBottom: 6 }}>{title}</div>
                  <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.55 }}>{body}</div>
                </div>
              ))}
            </div>

            <h3 style={{ fontSize: 14, fontWeight: 600, letterSpacing: "0.04em", textTransform: "uppercase", color: "var(--muted)", marginTop: 36, marginBottom: 18, fontFamily: "var(--mono)" }}>
              Specs
            </h3>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 12 }}>
              {[
                ["Source", sourceLabel],
                ["Category", cat.label],
                ["Price", "Free"],
                ["License", "Personal use — credit appreciated"],
                ["Engine", "Roblox 2025+"],
                ["Updated", p.updated],
              ].map(([k,v]) => (
                <div key={k} style={{ borderTop: "1px solid var(--border)", paddingTop: 10 }}>
                  <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase" }}>{k}</div>
                  <div style={{ fontSize: 14, marginTop: 4 }}>{v}</div>
                </div>
              ))}
            </div>
          </section>
        </div>

        {/* Buy panel (now just Download panel) */}
        <aside style={{ position: "sticky", top: 96, alignSelf: "flex-start", height: "fit-content" }}>
          {p.badge && (
            <div style={{ display: "inline-block", fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.08em", textTransform: "uppercase", padding: "4px 9px", background: "var(--accent)", color: "white", borderRadius: 2, marginBottom: 16 }}>
              {p.badge}
            </div>
          )}
          <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 10 }}>
            {cat.label}
          </div>
          <h1 style={{ fontSize: 32, fontWeight: 600, letterSpacing: "-0.025em", margin: 0, lineHeight: 1.15 }}>{p.name}</h1>
          <p style={{ fontSize: 15, color: "var(--muted)", lineHeight: 1.55, marginTop: 14, marginBottom: 0 }}>{p.tagline}</p>

          <div style={{ marginTop: 28, padding: 24, border: "1px solid var(--border)", borderRadius: 6 }}>
            <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginBottom: 18 }}>
              <div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 10, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 4 }}>
                  Price
                </div>
                <div style={{ fontFamily: "var(--mono)", fontSize: 30, fontWeight: 600, color: "var(--accent)" }}>
                  FREE
                </div>
              </div>
              <SourcePill kind={p.link.kind} />
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
              <a href={p.link.url} target="_blank" rel="noreferrer" style={{
                display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 10,
                padding: "14px 22px", borderRadius: 999,
                background: "var(--ink)", color: "var(--bg)",
                textDecoration: "none", fontFamily: "var(--sans)", fontSize: 15, fontWeight: 600,
              }}>
                <DownloadIcon /> Download from {sourceLabel} ↗
              </a>
              <button onClick={() => { navigator.clipboard?.writeText(p.link.url); }} style={{
                padding: "12px 22px", borderRadius: 999, border: "1px solid var(--border)", background: "var(--bg)",
                fontFamily: "var(--sans)", fontSize: 13, fontWeight: 500, cursor: "pointer", color: "var(--ink)",
              }}>
                Copy link
              </button>
            </div>
            <div style={{ marginTop: 18, paddingTop: 18, borderTop: "1px solid var(--border)", fontSize: 12, color: "var(--muted)", lineHeight: 1.6 }}>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
                <span>Source</span><span style={{ color: "var(--ink)" }}>{sourceLabel}</span>
              </div>
              <div style={{ display: "flex", justifyContent: "space-between", marginBottom: 4 }}>
                <span>License</span><span style={{ color: "var(--ink)" }}>Free · personal use</span>
              </div>
              <div style={{ display: "flex", justifyContent: "space-between" }}>
                <span>Creator</span><span style={{ color: "var(--ink)" }}>{window.NEKO_CONFIG.creator}</span>
              </div>
            </div>
          </div>

          <div style={{ display: "flex", alignItems: "center", gap: 10, marginTop: 20, fontSize: 12, color: "var(--muted)" }}>
            <NekoMark size={20} />
            <span>By <span style={{ color: "var(--ink)" }}>{window.NEKO_CONFIG.creator}</span> · updated {p.updated}</span>
          </div>

          <div style={{
            marginTop: 20, padding: "12px 14px",
            background: "var(--accent-soft)", borderRadius: 6,
            fontSize: 12, color: "var(--ink)", lineHeight: 1.5,
          }}>
            ♥ If you use it, please stay subscribed to {window.NEKO_CONFIG.youtubeHandle} — that's the only cost.
          </div>
        </aside>
      </div>

      {related.length > 0 && (
        <section style={{ marginTop: 96 }}>
          <SectionHeading kicker="More from this category" title={`Other ${cat.label.toLowerCase()}.`} />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 16 }}>
            {related.map(r => <ProductCard key={r.id} product={r} onClick={() => go(`/p/${r.id}`)} />)}
          </div>
        </section>
      )}
    </main>
  );
}
const codeStyle = {
  fontFamily: "var(--mono)", fontSize: 11, padding: "1px 5px",
  background: "var(--accent-soft)", color: "var(--accent)", borderRadius: 3,
};

// ──────────────────────────────────────────────────────────────────
// ABOUT
// ──────────────────────────────────────────────────────────────────
function AboutPage() {
  const { go } = useRouter();
  const { reset } = useGate();
  const cfg = window.NEKO_CONFIG;
  return (
    <main className="container" style={{ paddingTop: 56, paddingBottom: 32, maxWidth: 880 }}>
      <div style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--muted)", letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 28 }}>
        <a href="#/" style={{ color: "var(--muted)", textDecoration: "none" }} onClick={(e)=>{e.preventDefault();go("/");}}>NekoWebFree</a>
        <span style={{ margin: "0 8px" }}>/</span>
        <span style={{ color: "var(--ink)" }}>About</span>
      </div>

      <h1 style={{ fontSize: 44, fontWeight: 600, letterSpacing: "-0.025em", margin: 0 }}>About this hub.</h1>
      <p style={{ fontSize: 17, color: "var(--muted)", marginTop: 18, lineHeight: 1.6, maxWidth: 640 }}>
        NekoWebFree is a free spin-off of the main NekoShop catalog. Every system listed here is genuinely free — pulled from MediaFire links, my Roblox Create Store, and a couple of hosted web apps.
      </p>

      <section style={{ marginTop: 48 }}>
        <h2 style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-0.015em", margin: 0, marginBottom: 14 }}>The trade</h2>
        <p style={{ fontSize: 15, color: "var(--muted)", lineHeight: 1.65 }}>
          You get the systems. I get a subscriber. That's the deal. Click <a href={cfg.youtubeChannel} target="_blank" rel="noreferrer" style={{ color: "var(--accent)", textDecoration: "none", fontWeight: 500 }}>subscribe</a> on the channel and the whole catalog opens up.
        </p>
      </section>

      <section style={{ marginTop: 36 }}>
        <h2 style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-0.015em", margin: 0, marginBottom: 14 }}>What's the catch?</h2>
        <ul style={{ paddingLeft: 18, marginTop: 0, lineHeight: 1.8, fontSize: 15, color: "var(--muted)" }}>
          <li>No payment. No QRIS, no PayPal, no Robux.</li>
          <li>No whitelist on these — they're the genuinely free ones.</li>
          <li>Personal &amp; community use. Don't re-upload as your own.</li>
          <li>Some builds are tagged <strong style={{ color: "var(--ink)" }}>Protect</strong> — they have my licence headers, please keep them.</li>
        </ul>
      </section>

      <section style={{ marginTop: 36 }}>
        <h2 style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-0.015em", margin: 0, marginBottom: 14 }}>Need help installing?</h2>
        <p style={{ fontSize: 15, color: "var(--muted)", lineHeight: 1.65 }}>
          Join the Discord — most setup questions are answered there. Each system also has a video on the YouTube channel.
        </p>
        <div style={{ display: "flex", gap: 10, marginTop: 14, flexWrap: "wrap" }}>
          <Btn href={cfg.discordInvite} external primary>Open Discord ↗</Btn>
          <Btn href={cfg.youtubeChannel} external>YouTube tutorials ↗</Btn>
        </div>
      </section>

      <section style={{ marginTop: 48, padding: 24, border: "1px dashed var(--border)", borderRadius: 6 }}>
        <div style={{ fontFamily: "var(--mono)", fontSize: 11, letterSpacing: "0.08em", textTransform: "uppercase", color: "var(--muted)", marginBottom: 6 }}>
          Reset
        </div>
        <p style={{ fontSize: 14, color: "var(--ink)", margin: 0, lineHeight: 1.55 }}>
          Want to see the subscribe gate again (e.g. to share the page with a friend)? Click here:
        </p>
        <div style={{ marginTop: 12 }}>
          <button onClick={reset} style={{
            padding: "10px 18px", borderRadius: 999, border: "1px solid var(--border)",
            background: "var(--bg)", color: "var(--ink)",
            fontFamily: "var(--sans)", fontSize: 13, fontWeight: 500, cursor: "pointer",
          }}>
            Reset subscribe gate
          </button>
        </div>
      </section>
    </main>
  );
}

Object.assign(window, { HomePage, BrowsePage, ProductPage, AboutPage });
