const { useState, useEffect, useMemo } = React;

// ===== Tiny markdown renderer =====
function escapeHtml(s) {
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
function renderInline(s) {
  s = escapeHtml(s);
  s = s.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img alt="$1" src="$2" />');
  s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noreferrer">$1</a>');
  s = s.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
  s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>");
  s = s.replace(/`([^`]+)`/g, "<code>$1</code>");
  return s;
}
function renderMarkdown(md) {
  if (!md) return "";
  // Handle escaped newlines that sometimes come from JSON
  let text = String(md).replace(/\\r\\n/g, "\n").replace(/\\n/g, "\n").replace(/\r\n/g, "\n");
  if (window.marked) {
    try { window.marked.setOptions({ breaks: true, gfm: true, headerIds: false, mangle: false }); } catch (e) {}
    const parser = window.marked.parse || window.marked;
    let html = parser(text);
    if (window.DOMPurify) {
      html = window.DOMPurify.sanitize(html, { ADD_ATTR: ["target", "align"] });
    }
    return html;
  }
  return escapeHtml(text);
}


const MODRINTH_USER = "NOAM2509_S";

// Map modrinth slug → curseforge slug (manual overrides for mods with different names on CF)
const CF_MAP = {
  hidearmor: "simple-hide-armor",
};

// Try fetching CurseForge data with multiple slug guesses
async function findCurseforge(modrinthSlug, modTitle) {
  // Build candidate slugs to try
  const candidates = [];
  if (CF_MAP[modrinthSlug]) candidates.push(CF_MAP[modrinthSlug]);
  candidates.push(modrinthSlug);
  // title-based slug: "Kill Count" → "kill-count"
  const titleSlug = (modTitle || "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
  if (titleSlug && !candidates.includes(titleSlug)) candidates.push(titleSlug);
  // common prefix variations
  if (titleSlug) {
    candidates.push("simple-" + titleSlug);
    candidates.push(titleSlug + "-mod");
    candidates.push(titleSlug + "-fabric");
  }
  // unique
  const tried = new Set();
  for (const slug of candidates) {
    if (!slug || tried.has(slug)) continue;
    tried.add(slug);
    try {
      const res = await fetch(`https://api.cfwidget.com/minecraft/mc-mods/${slug}`);
      if (!res.ok) continue;
      const data = await res.json();
      if (data && data.urls && data.urls.curseforge && typeof data.downloads?.total === "number") {
        return {
          slug,
          downloads: data.downloads.total,
          url: data.urls.curseforge,
        };
      }
    } catch (e) {}
  }
  return null;
}

// Project type detection: returns array — a project can be BOTH "mod" and "plugin"
function detectTypes(p) {
  const loaders = (p.loaders || []).map(l => l.toLowerCase());
  const types = new Set();
  const pluginLoaders = ["bukkit","spigot","paper","purpur","folia","velocity","bungeecord","waterfall"];
  const modLoaders = ["fabric","forge","neoforge","quilt","liteloader","modloader","rift"];
  if (loaders.some(l => pluginLoaders.includes(l))) types.add("plugin");
  if (loaders.some(l => modLoaders.includes(l))) types.add("mod");
  if (loaders.includes("datapack")) types.add("datapack");
  if (p.project_type === "resourcepack") types.add("resourcepack");
  if (p.project_type === "shader") types.add("shader");
  if (types.size === 0) types.add("mod");
  return Array.from(types);
}

// Gallery images per mod slug (local assets)
const GALLERY_MAP = {
  hidearmor: [
    { src: "assets/screenshot_before.png", label: "Armor visible — default" },
    { src: "assets/screenshot_after.png", label: "Press H — armor hidden" },
  ],
};

// Local icon overrides per mod slug
const ICON_MAP = {
  hidearmor: "assets/hide_armor.png",
};

function useFetchMods() {
  const [mods, setMods] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;

    async function load() {
      try {
        // 1) Get user ID
        const userRes = await fetch(`https://api.modrinth.com/v2/user/${MODRINTH_USER}`);
        if (!userRes.ok) throw new Error("User not found");
        const user = await userRes.json();

        // 2) Get user's projects
        const projRes = await fetch(`https://api.modrinth.com/v2/user/${user.id}/projects`);
        if (!projRes.ok) throw new Error("Projects not found");
        const projects = await projRes.json();

        if (cancelled) return;

        // 3) For each project, also try to get CF downloads
        const modsData = await Promise.all(projects.map(async (p) => {
          const cf = await findCurseforge(p.slug, p.title);
          const cfDownloads = cf ? cf.downloads : 0;

          // Fetch all versions from Modrinth
          let modrinthVersions = [];
          try {
            const vRes = await fetch(`https://api.modrinth.com/v2/project/${p.slug}/version`);
            if (vRes.ok) {
              const vData = await vRes.json();
              modrinthVersions = vData.map(v => ({
                id: v.id,
                name: v.name,
                versionNumber: v.version_number,
                gameVersions: v.game_versions || [],
                loaders: (v.loaders || []).map(l => l.charAt(0).toUpperCase() + l.slice(1)),
                datePublished: v.date_published ? new Date(v.date_published) : null,
                downloads: v.downloads || 0,
                versionType: v.version_type,
                files: v.files || [],
                changelog: v.changelog || "",
                primaryFile: (v.files || []).find(f => f.primary) || (v.files || [])[0] || null,
              }));
            }
          } catch (err) {
            console.warn(`Failed to fetch versions for ${p.slug}:`, err);
          }

          const gameVersions = p.game_versions || [];
          const latestMC = gameVersions[gameVersions.length - 1] || "?";
          const loaders = (p.loaders || []).map(l => l.charAt(0).toUpperCase() + l.slice(1));

          return {
            id: p.slug,
            type: detectTypes(p)[0],
            types: detectTypes(p),
            name: p.title,
            tagline: p.description || "",
            description: p.body || p.description || "",
            category: p.slug,
            loaders,
            versions: [latestMC],
            downloads: (p.downloads || 0) + cfDownloads,
            updated: p.updated ? new Date(p.updated).toLocaleDateString() : "recent",
            created: p.published ? new Date(p.published).toLocaleDateString() : "?",
            license: p.license?.id || p.license?.name || "?",
            modrinthSlug: p.slug,
            curseforgeSlug: cf ? cf.slug : null,
            links: {
              modrinth: `https://modrinth.com/mod/${p.slug}`,
              curseforge: cf ? cf.url : null,
            },
            color: "#7CFC9B",
            image: ICON_MAP[p.slug] || (p.icon_url || null),
            gallery: GALLERY_MAP[p.slug] || (p.gallery || []).map(g => ({ src: g.url, label: g.title || "" })),
            clientSide: p.client_side === "required" || p.client_side === "optional",
            serverSide: p.server_side,
            clientSideRaw: p.client_side,
            categories: p.categories || [],
            additionalCategories: p.additional_categories || [],
            allVersions: gameVersions,
            modrinthVersions,
            followers: p.followers || 0,
            issuesUrl: p.issues_url || null,
            sourceUrl: p.source_url || null,
            wikiUrl: p.wiki_url || null,
            discordUrl: p.discord_url || null,
            donationUrls: p.donation_urls || [],
            status: p.status,
            requestedStatus: p.requested_status,
            projectType: p.project_type,
            slug: p.slug,
          };
        }));

        if (!cancelled) {
          setMods(modsData);
          setLoading(false);
        }
      } catch (e) {
        console.warn("Failed to fetch mods from Modrinth:", e);
        if (!cancelled) setLoading(false);
      }
    }

    load();
    return () => { cancelled = true; };
  }, []);

  return { mods, loading };
}

// ===== Pixel-block icon (no SVG library, just CSS grid) =====
function ModIcon({ mod, size = 64 }) {
  if (mod.image) {
    return (
      <div className="img-icon" style={{ width: size, height: size }}>
        <img src={mod.image} alt={mod.name} />
      </div>
    );
  }
  return <BlockIcon seed={mod.id} color={mod.color} />;
}

function BlockIcon({ seed, color }) {
  // Deterministic 5x5 pattern based on seed
  const cells = useMemo(() => {
    let h = 0;
    for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
    const out = [];
    for (let i = 0; i < 25; i++) {
      h = (h * 1103515245 + 12345) >>> 0;
      out.push((h % 100) > 55);
    }
    // mirror left/right for nicer look
    for (let r = 0; r < 5; r++) {
      out[r * 5 + 4] = out[r * 5 + 0];
      out[r * 5 + 3] = out[r * 5 + 1];
    }
    return out;
  }, [seed]);
  return (
    <div className="block-icon" style={{ "--ic": color }}>
      {cells.map((on, i) => (
        <span key={i} className={on ? "on" : "off"} />
      ))}
    </div>
  );
}

// ===== Header =====
function Header({ route, go, search, setSearch }) {
  return (
    <header className="hd">
      <div className="hd-inner">
        <button className="logo" onClick={() => go({ view: "home" })}>
          <img className="site-logo" src="assets/site-logo.png" alt="" />
          <span className="logo-text">
            <span className="logo-name">NOAM2509_S</span>
            <small>Minecraft Mods</small>
          </span>
        </button>

        <nav className="hd-nav">
          <button
            className={"navlink " + (route.view === "home" ? "active" : "")}
            onClick={() => go({ view: "home" })}
          >Mods</button>
          <button
            className={"navlink " + (route.view === "wiki" ? "active" : "")}
            onClick={() => go({ view: "wiki" })}
          >Wiki</button>
          <button className="navlink" onClick={() => go({ view: "about" })}>About</button>
        </nav>

        <div className="hd-search">
          <span className="hd-search-icon">⌕</span>
          <input
            placeholder="Search mods…"
            value={search}
            onChange={(e) => setSearch(e.target.value)}
          />
          <span className="hd-search-kbd">/</span>
        </div>
      </div>
      <div className="hd-stripe" />
    </header>
  );
}

// ===== Sidebar =====
function Sidebar({ activeCat, setCat, route, categories }) {
  return (
    <aside className="side">
      <div className="side-block">
        <div className="side-title">Categories</div>
        <ul className="cat-list">
          {categories.map((c) => (
            <li key={c.id}>
              <button
                className={"cat-item " + (activeCat === c.id ? "active" : "")}
                onClick={() => setCat(c.id)}
              >
                <span className="cat-dot" />
                <span className="cat-label">{c.label}</span>
                <span className="cat-count">{c.count}</span>
              </button>
            </li>
          ))}
          <li>
            <button className="cat-item add" disabled>
              <span className="cat-dot ghost" />
              <span className="cat-label">More coming…</span>
            </button>
          </li>
        </ul>
      </div>

      <div className="side-block">
        <div className="side-title">Loader</div>
        <div className="chip-row">
          <span className="chip chip-on">Fabric</span>
          <span className="chip">Forge</span>
          <span className="chip">NeoForge</span>
          <span className="chip">Quilt</span>
        </div>
      </div>

      <div className="side-block">
        <div className="side-title">MC Version</div>
        <div className="chip-row">
          <span className="chip chip-on">1.21.11</span>
        </div>
      </div>

      <div className="side-block side-cta">
        <div className="cta-pat" />
        <div className="cta-body">
          <div className="cta-title">Need a feature?</div>
          <div className="cta-sub">Drop me a request via the mod page.</div>
        </div>
      </div>
    </aside>
  );
}

// ===== Mod card (home grid) =====
function ModCard({ mod, onOpen, downloads }) {
  const dl = downloads ?? mod.downloads;
  return (
    <article className="card" onClick={onOpen}>
      <div className="card-top">
        <ModIcon mod={mod} size={40} />
        <div className="card-head">
          <h3 className="card-title">{mod.name}</h3>
          <p className="card-sub">{mod.tagline}</p>
          <div className="card-meta">
            <span className="meta-pill">
              <em className="dot" style={{ background: mod.color }} />
              {mod.loaders.join(" · ")}
            </span>
            <span className="meta-pill ghost">MC {mod.versions[0]}</span>
          </div>
        </div>
      </div>
      <div className="card-stats">
        <div className="stat">
          <span className="stat-num">{dl != null ? dl.toLocaleString() : "—"}</span>
          <span className="stat-lbl">downloads</span>
        </div>
        <div className="stat">
          <span className="stat-num">{mod.loaders.join(", ")}</span>
          <span className="stat-lbl">loader</span>
        </div>
        <div className="stat">
          <span className="stat-num">{mod.updated}</span>
          <span className="stat-lbl">updated</span>
        </div>
      </div>
    </article>
  );
}

// ===== Home view =====
function HomeView({ go, activeCat, search, mods, loading }) {
  const filtered = mods.filter((m) => {
    if (activeCat !== "all" && !(m.types || [m.type]).includes(activeCat)) return false;
    if (search && !m.name.toLowerCase().includes(search.toLowerCase())) return false;
    return true;
  });

  const categories = useMemo(() => {
    const counts = { mod: 0, plugin: 0, datapack: 0, resourcepack: 0, shader: 0 };
    mods.forEach((m) => {
      const ts = m.types || [m.type];
      ts.forEach(t => { if (counts[t] != null) counts[t]++; });
    });
    const cats = [{ id: "all", label: "All Projects", count: mods.length }];
    const labels = { mod: "Mods", plugin: "Plugins", datapack: "Datapacks", resourcepack: "Resource Packs", shader: "Shaders" };
    Object.entries(counts).forEach(([k, v]) => {
      if (v > 0) cats.push({ id: k, label: labels[k], count: v });
    });
    return cats;
  }, [mods]);
  const cat = categories.find((c) => c.id === activeCat) || categories[0];

  return (
    <div className="home">
      <div className="hero">
        <div className="hero-tag">// minecraft mods · client-side · open source</div>
        <h1 className="hero-title">
          Small mods.<br />
          <span className="hero-accent">Big quality of life.</span>
        </h1>
        <p className="hero-sub">
          A workshop of lightweight Fabric mods built for clean visuals and zero hassle.
          Browse by category, grab from Modrinth or CurseForge.
        </p>
        <div className="hero-stats">
          <div><b>{mods.length}</b> mod{mods.length !== 1 ? "s" : ""} published</div>
          <div className="dotsep">·</div>
          <div><b>Fabric</b> loader</div>
          <div className="dotsep">·</div>
          <div><b>Auto-synced</b> from Modrinth</div>
        </div>
      </div>

      <div className="results-bar">
        <div className="results-left">
          Showing <b>{filtered.length}</b> mod{filtered.length !== 1 ? "s" : ""} in{" "}
          <span className="cat-tag">{cat.label}</span>
        </div>
        <div className="results-right">
          <label>Sort</label>
          <select defaultValue="updated">
            <option value="updated">Recently updated</option>
            <option>Most downloaded</option>
            <option>Newest</option>
          </select>
        </div>
      </div>

      <div className="grid">
        {loading && (
          <div className="empty">
            <div className="empty-mark">⟳</div>
            <div>Loading mods from Modrinth…</div>
          </div>
        )}
        {!loading && filtered.map((m) => (
          <ModCard key={m.id} mod={m} downloads={m.downloads} onOpen={() => go({ view: "mod", id: m.id })} />
        ))}
        {!loading && filtered.length === 0 && (
          <div className="empty">
            <div className="empty-mark">∅</div>
            <div>No mods match this filter (yet).</div>
          </div>
        )}
      </div>
    </div>
  );
}

// ===== Mod Detail view =====
function ModView({ mod, go }) {
  const [tab, setTab] = useState("description");
  const dl = mod.downloads;
  return (
    <div className="modview">
      <div className="crumbs">
        <button onClick={() => go({ view: "home" })}>Mods</button>
        <span>/</span>
        <span className="crumbs-cur">{mod.name}</span>
      </div>

      <div className="mod-hero">
        <div className="mod-hero-body">
          <div className="mod-hero-tag">{mod.loaders.join(" · ")} · {mod.clientSide ? "CLIENT-SIDE" : ""}</div>
          <div className="mod-hero-title-row">
            {mod.image && <img className="mod-hero-logo" src={mod.image} alt="" />}
            <h1>{mod.name}</h1>
          </div>
          <p className="mod-hero-sub">{mod.tagline}</p>
          <div className="mod-hero-meta">
            <span className="meta-pill">
              <em className="dot" style={{ background: mod.color }} />
              {mod.loaders.join(" · ")}
            </span>
            <span className="meta-pill ghost">MC {mod.versions[0]}</span>
            <span className="meta-pill ghost">License: {mod.license}</span>
            <span className="meta-pill ghost">Updated {mod.updated}</span>
          </div>
        </div>
        <div className="mod-hero-cta">
          <a className="btn btn-primary" href={mod.links.modrinth} target="_blank" rel="noreferrer">
            <span className="btn-mark mr">M</span>
            Get on Modrinth
            <span className="btn-arrow">↗</span>
          </a>
          {mod.links.curseforge && (
            <a className="btn btn-ghost" href={mod.links.curseforge} target="_blank" rel="noreferrer">
              <span className="btn-mark cf">C</span>
              CurseForge
              <span className="btn-arrow">↗</span>
            </a>
          )}
        </div>
      </div>

      <div className="mod-tabs">
        {["description", "versions", "gallery", "source"].map((t) => (
          <button
            key={t}
            className={"tab " + (tab === t ? "active" : "")}
            onClick={() => setTab(t)}
          >
            {t}
          </button>
        ))}
      </div>

      <div className="mod-grid">
        <div className="mod-main">
          {tab === "description" && (
            <div className="prose">
              <h2>About</h2>
              <div className="md-body" dangerouslySetInnerHTML={{ __html: renderMarkdown(mod.description || mod.tagline) }} />

              <h2>Project info</h2>
              <table className="meta-table">
                <tbody>
                  <tr><th>Loaders</th><td>{mod.loaders.join(", ") || "—"}</td></tr>
                  <tr><th>MC versions</th><td>{(mod.allVersions && mod.allVersions.length) ? mod.allVersions.join(", ") : "—"}</td></tr>
                </tbody>
              </table>

              {(mod.sourceUrl || mod.issuesUrl || mod.wikiUrl || mod.discordUrl || (mod.donationUrls && mod.donationUrls.length)) && (
                <>
                  <h2>External links</h2>
                  <ul>
                    {mod.sourceUrl && <li><a href={mod.sourceUrl} target="_blank" rel="noreferrer">Source code</a></li>}
                    {mod.issuesUrl && <li><a href={mod.issuesUrl} target="_blank" rel="noreferrer">Issue tracker</a></li>}
                    {mod.wikiUrl && <li><a href={mod.wikiUrl} target="_blank" rel="noreferrer">Wiki</a></li>}
                    {mod.discordUrl && <li><a href={mod.discordUrl} target="_blank" rel="noreferrer">Discord</a></li>}
                    {(mod.donationUrls || []).map((d, i) => (
                      <li key={i}><a href={d.url} target="_blank" rel="noreferrer">Donate ({d.platform})</a></li>
                    ))}
                  </ul>
                </>
              )}

              <h2>Get it</h2>
              <div className="dl-row">
                <a className="dl-card mr" href={mod.links.modrinth} target="_blank" rel="noreferrer">
                  <span className="dl-mark">M</span>
                  <span className="dl-body">
                    <b>Modrinth</b>
                    <small>Primary distribution</small>
                  </span>
                  <span className="dl-arr">→</span>
                </a>
                {mod.links.curseforge && (
                  <a className="dl-card cf" href={mod.links.curseforge} target="_blank" rel="noreferrer">
                    <span className="dl-mark">C</span>
                    <span className="dl-body">
                      <b>CurseForge</b>
                      <small>Mirror</small>
                    </span>
                    <span className="dl-arr">→</span>
                  </a>
                )}
              </div>
            </div>
          )}
          {tab === "versions" && (
            <div className="versions">
              <div className="ver-row ver-head">
                <span>Version</span><span>MC</span><span>Loader</span><span>Date</span><span></span>
              </div>
              {(mod.modrinthVersions && mod.modrinthVersions.length > 0) ? (
                mod.modrinthVersions.map((v) => (
                  <div className="ver-row" key={v.id}>
                    <span>
                      <b>{v.versionNumber}</b>
                      {v.versionType && v.versionType !== "release" && (
                        <span className={`ver-tag ver-${v.versionType}`}> {v.versionType}</span>
                      )}
                    </span>
                    <span>{v.gameVersions.join(", ") || "—"}</span>
                    <span>{v.loaders.join(", ") || "—"}</span>
                    <span>{v.datePublished ? v.datePublished.toLocaleDateString() : "—"}</span>
                    {v.primaryFile ? (
                      <a className="ver-dl" href={v.primaryFile.url} target="_blank" rel="noreferrer" download>
                        Download
                      </a>
                    ) : (
                      <a className="ver-dl" href={`${mod.links.modrinth}/version/${v.id}`} target="_blank" rel="noreferrer">
                        View
                      </a>
                    )}
                  </div>
                ))
              ) : (
                <div className="ver-empty">No versions published yet.</div>
              )}
            </div>
          )}
          {tab === "gallery" && (
            <div className="gallery">
              {(mod.gallery && mod.gallery.length > 0) ? mod.gallery.map((g, i) => (
                <figure key={i} className="shot real">
                  <img src={g.src || g.url} alt={g.label || g.title || ""} />
                  {(g.label || g.title) && <figcaption>{g.label || g.title}</figcaption>}
                </figure>
              )) : (
                <div className="empty">
                  <div className="empty-mark">🖼</div>
                  <div>No gallery images yet.</div>
                </div>
              )}
            </div>
          )}
          {tab === "source" && (
            <div className="source">
              <div className="source-empty">
                <div className="empty-mark">{ }</div>
                <div>Source repo not linked yet.</div>
                <small>Add a GitHub URL in your mod settings later.</small>
              </div>
            </div>
          )}
        </div>

        <aside className="mod-side">
          <div className="side-block">
            <div className="side-title">Stats</div>
            <div className="kv"><span>Downloads</span><b>{dl != null ? dl.toLocaleString() : "—"}</b></div>
            <div className="kv"><span>Created</span><b>{mod.created}</b></div>
            <div className="kv"><span>Updated</span><b>{mod.updated}</b></div>
          </div>
          <div className="side-block">
            <div className="side-title">Compatibility</div>
            <div className="compat">
              <div className="compat-row"><span>Loader</span><b>{mod.loaders.join(", ") || "—"}</b></div>
              <div className="compat-row"><span>MC</span><b>{(mod.allVersions && mod.allVersions.length) ? `${mod.allVersions[0]}${mod.allVersions.length > 1 ? `–${mod.allVersions[mod.allVersions.length-1]}` : ""}` : "—"}</b></div>
              <div className="compat-row"><span>Side</span><b>{mod.clientSideRaw === "required" && mod.serverSide === "required" ? "Client + Server" : mod.clientSideRaw === "required" ? "Client" : mod.serverSide === "required" ? "Server" : "Both"}</b></div>
            </div>
          </div>
          <div className="side-block">
            <div className="side-title">Author</div>
            <div className="author">
              <div className="author-av"><img className="site-logo" src="assets/site-logo.png" alt="" style={{width:'36px',height:'36px',borderRadius:'6px'}} /></div>
              <div>
                <b>NOAM2509_S</b>
                <small>solo dev · Fabric</small>
              </div>
            </div>
          </div>
        </aside>
      </div>
    </div>
  );
}

// ===== Wiki view =====
function WikiView() {
  return (
    <div className="wiki">
      <div className="wiki-hero">
        <div className="wiki-tag">// docs</div>
        <h1>Wiki</h1>
        <p>A home for guides, configuration references, and FAQ. Empty for now — content coming as the mods grow.</p>
      </div>
      <div className="wiki-empty">
        <div className="wiki-empty-art">
          <div className="we-row"><span /><span className="on" /><span /><span /><span /></div>
          <div className="we-row"><span /><span className="on" /><span className="on" /><span /><span /></div>
          <div className="we-row"><span /><span className="on" /><span /><span className="on" /><span /></div>
          <div className="we-row"><span /><span className="on" /><span /><span /><span className="on" /></div>
          <div className="we-row"><span /><span /><span /><span /><span /></div>
        </div>
        <h2>Nothing here yet</h2>
        <p>Once a mod has enough config to document, articles will land here. Expect:</p>
        <ul>
          <li>Installation walkthroughs (Fabric / loaders)</li>
          <li>Per-mod configuration references</li>
          <li>FAQ &amp; troubleshooting</li>
          <li>Changelogs</li>
        </ul>
      </div>
    </div>
  );
}

// ===== About view =====
function AboutView() {
  return (
    <div className="wiki">
      <div className="wiki-hero">
        <div className="wiki-tag">// who</div>
        <h1>About</h1>
        <p>NOAM2509_S — solo modder making small client-side Fabric mods for Minecraft 1.21.x.</p>
      </div>
      <div className="wiki-empty">
        <h2>Philosophy</h2>
        <ul>
          <li>Tiny, single-purpose mods</li>
          <li>Client-side first — friendly to any server</li>
          <li>Open source by default</li>
        </ul>
      </div>
    </div>
  );
}

// ===== App root =====
function App() {
  const tweaks = window.useTweaks
    ? window.useTweaks(window.__TWEAK_DEFAULTS__ || {})
    : [{}, () => {}];
  const [t, setT] = tweaks;

  const [route, setRoute] = useState({ view: "home" });
  const [activeCat, setActiveCat] = useState("all");
  const [search, setSearch] = useState("");

  const { mods, loading } = useFetchMods();

  const go = (r) => {
    setRoute(r);
    window.scrollTo({ top: 0, behavior: "instant" });
  };

  const setCat = (id) => {
    setActiveCat(id);
    if (route.view !== "home") go({ view: "home" });
  };

  const mod = route.view === "mod" ? mods.find((m) => m.id === route.id) : null;

  const categories = useMemo(() => {
    const counts = { mod: 0, plugin: 0, datapack: 0, resourcepack: 0, shader: 0 };
    mods.forEach((m) => {
      const ts = m.types || [m.type];
      ts.forEach(t => { if (counts[t] != null) counts[t]++; });
    });
    const cats = [{ id: "all", label: "All Projects", count: mods.length }];
    const labels = { mod: "Mods", plugin: "Plugins", datapack: "Datapacks", resourcepack: "Resource Packs", shader: "Shaders" };
    Object.entries(counts).forEach(([k, v]) => {
      if (v > 0) cats.push({ id: k, label: labels[k], count: v });
    });
    return cats;
  }, [mods]);

  // Apply tweaks via CSS vars
  const themeStyle = {
    "--accent": t.accent || "#7CFC9B",
    "--accent-2": t.accent2 || "#4DA8FF",
    "--bg": t.dark === false ? "#F5F4EE" : "#0E1014",
    "--surface": t.dark === false ? "#FFFFFF" : "#15181F",
    "--surface-2": t.dark === false ? "#EDEBE3" : "#1B1F28",
    "--ink": t.dark === false ? "#0E1014" : "#E9ECEE",
    "--ink-soft": t.dark === false ? "#5A5E66" : "#9097A1",
    "--line": t.dark === false ? "#D9D6CB" : "#262C36",
    "--font-display": t.font === "mono" ? "'JetBrains Mono', monospace" : "'Space Grotesk', system-ui, sans-serif",
  };

  return (
    <div className="app" style={themeStyle} data-dark={t.dark !== false}>
      <Header route={route} go={go} search={search} setSearch={setSearch} />
      <div className="layout">
        {(route.view === "home" || route.view === "mod") && (
          <Sidebar activeCat={activeCat} setCat={setCat} route={route} categories={categories} />
        )}
        <main className="main">
          {route.view === "home" && (
            <HomeView go={go} activeCat={activeCat} search={search} mods={mods} loading={loading} />
          )}
          {route.view === "mod" && mod && <ModView mod={mod} go={go} />}
          {route.view === "wiki" && <WikiView />}
          {route.view === "about" && <AboutView />}
        </main>
      </div>

      <footer className="site-footer">
        <div className="site-footer-inner">
          <div className="site-footer-brand">
            <img src="assets/site-logo.png" alt="" />
            <div>
              <b>NOAM2509_S</b>
              <small>Minecraft Mods Workshop</small>
            </div>
          </div>
          <div className="site-footer-links">
            <a href="index.html">Mods</a>
            <a href="privacy.html">Privacy</a>
            <a href="privacy.html">Terms</a>
            <a href="accessibility.html">Accessibility</a>
            <a href="https://modrinth.com/user/NOAM2509_S" target="_blank" rel="noreferrer noopener">Modrinth</a>
            <a href="https://discord.gg/xwnnybrGuG" target="_blank" rel="noreferrer noopener">Contact</a>
          </div>
        </div>
        <div className="site-footer-bottom">
          <span>© 2026 NOAM2509_S · Hosted on Vercel</span>
          <span className="disclaimer">Not affiliated with Mojang AB or Microsoft. "Minecraft" is a trademark of Mojang AB.</span>
        </div>
      </footer>

      {/* Tweaks panel */}
      {window.TweaksPanel && (
        <window.TweaksPanel title="Tweaks">
          <window.TweakSection title="Theme">
            <window.TweakToggle label="Dark mode" value={t.dark !== false} onChange={(v) => setT({ dark: v })} />
            <window.TweakColor label="Accent" value={t.accent || "#7CFC9B"} onChange={(v) => setT({ accent: v })} />
            <window.TweakColor label="Accent 2" value={t.accent2 || "#4DA8FF"} onChange={(v) => setT({ accent2: v })} />
          </window.TweakSection>
          <window.TweakSection title="Type">
            <window.TweakRadio
              label="Display font"
              value={t.font || "grotesk"}
              options={[{ value: "grotesk", label: "Grotesk" }, { value: "mono", label: "Mono" }]}
              onChange={(v) => setT({ font: v })}
            />
          </window.TweakSection>
        </window.TweaksPanel>
      )}
    </div>
  );
}

window.App = App;
