(() => { // ---- Adaptive ambient quality. // A lightweight requestAnimationFrame probe samples fps in the background. // If the scene drops below target for a couple of windows, it peels back the // cheapest-to-lose space effects first (shooting stars, then star count, then // twinkle) and retires once there is nothing else to cut. ---- function createAutoPerf(opts = {}) { const prefersReducedMotion = opts.prefersReducedMotion; const actions = []; // { order, label, run, done } let started = false; // order = cut priority (lowest first); cheaper-to-lose / most-expensive-to-run // effects get the lowest numbers so they go first. function register(order, label, run) { actions.push({ order, label, run, done: false }); } function degradeOnce() { actions.sort((a, b) => a.order - b.order); const next = actions.find((a) => !a.done); if (!next) return false; next.done = true; try { next.run(); } catch (e) { /* a single failed cut shouldn't stall the rest of the ladder */ } document.documentElement.dataset.perfCuts = String(actions.filter((a) => a.done).length); return actions.some((a) => !a.done); // any cuts left? } function start(cfg = {}) { if (started) return; started = true; // A page that respects reduced-motion isn't running the heavy ambient // animation in the first place, so there's nothing to claw back — skip the // probe entirely rather than burn battery sampling. if (prefersReducedMotion) return; const THRESHOLD = cfg.threshold ?? 30; // fps floor we try to hold const SAMPLE_MS = cfg.sampleMs ?? 1000; // measurement window length const PATIENCE = cfg.patience ?? 2; // bad windows in a row before cutting const GRACE_MS = cfg.graceMs ?? 1500; // ignore initial load/layout jank const COOLDOWN = cfg.cooldownMs ?? 1200; // settle time after each cut const MAX_LIFE = cfg.maxLifeMs ?? 60000; // stop probing once smooth this long let frames = 0; let windowStart = 0; let holdUntil = 0; let bornAt = 0; let lowStreak = 0; const tick = (now) => { frames++; const elapsed = now - windowStart; if (elapsed >= SAMPLE_MS) { const fps = (frames * 1000) / elapsed; frames = 0; windowStart = now; // A window that spans a tab switch / long stall (rAF pauses while the // tab is hidden) reports a meaningless fps — never cut on it. const stalled = elapsed > SAMPLE_MS * 2.5; if (!stalled && now >= holdUntil) { if (fps < THRESHOLD) { if (++lowStreak >= PATIENCE) { lowStreak = 0; const more = degradeOnce(); holdUntil = now + COOLDOWN; // let the cut land before re-judging if (!more) return; // exhausted the ladder — let the probe die } } else { lowStreak = 0; if (now - bornAt > MAX_LIFE) return; // smooth long enough — retire } } } requestAnimationFrame(tick); }; // Only begin sampling after the grace period so first-paint jank doesn't // trip an instant downgrade. setTimeout(() => { bornAt = windowStart = holdUntil = performance.now(); requestAnimationFrame(tick); }, GRACE_MS); } return { register, start, degradeOnce, actions }; } document.addEventListener("DOMContentLoaded", () => { const bg = document.querySelector(".background"); // ---- Detect mobile / low-power devices ---- const isMobile = window.innerWidth < 768 || /Android|iPhone|iPad|iPod|webOS|BlackBerry|Opera Mini/i.test(navigator.userAgent); const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; // Governs the adaptive downgrade ladder below. The space background // registers individual cuts as it builds; autoPerf.start() begins watching // the frame rate once it's up. const autoPerf = createAutoPerf({ prefersReducedMotion }); window.__autoPerf = autoPerf; // handy for debugging / manual autoPerf.degradeOnce() // ---- Members-style space background: gradient + lightweight star field ---- if (bg && !bg.querySelector(".star")) { bg.setAttribute("aria-hidden", "true"); // Cheap: absolutely-positioned dots with opacity-only twinkle. // This mirrors the Members Space background while keeping the blog fast. const starCount = isMobile ? 60 : 140; const frag = document.createDocumentFragment(); for (let i = 0; i < starCount; i++) { const s = document.createElement("span"); s.className = "star" + (Math.random() < 0.15 ? " big" : ""); s.style.left = Math.random() * 100 + "%"; s.style.top = Math.random() * 100 + "%"; s.style.setProperty("--dur", (3 + Math.random() * 5).toFixed(2) + "s"); s.style.setProperty("--delay", (-Math.random() * 6).toFixed(2) + "s"); s.style.setProperty("--min-op", (0.08 + Math.random() * 0.15).toFixed(2)); s.style.setProperty("--max-op", (0.6 + Math.random() * 0.4).toFixed(2)); frag.appendChild(s); } bg.appendChild(frag); // A few slow shooting stars, skipped on mobile / reduced motion. if (!isMobile && !prefersReducedMotion) { for (let i = 0; i < 3; i++) { const sh = document.createElement("span"); sh.className = "shooting-star"; sh.style.setProperty("--top", (5 + Math.random() * 40) + "%"); sh.style.setProperty("--delay", (i * 7 + Math.random() * 5).toFixed(2) + "s"); bg.appendChild(sh); } } autoPerf.register(10, "space: remove shooting stars", () => { bg.querySelectorAll(".shooting-star").forEach((s) => s.remove()); }); autoPerf.register(30, "space: thin stars", () => { bg.querySelectorAll(".star").forEach((s, i) => { if (i % 2 === 0) s.remove(); }); }); autoPerf.register(60, "space: freeze twinkle", () => { bg.querySelectorAll(".star").forEach((s) => (s.style.animation = "none")); }); } // Everything ambient is built and registered — start watching the frame rate. autoPerf.start(); // ---- Copy buttons on
 ----
    document.querySelectorAll("pre").forEach((pre) => {
      if (pre.querySelector(".copy-btn")) return;
      const code = pre.querySelector("code") || pre;
      const btn = document.createElement("button");
      btn.className = "copy-btn";
      btn.textContent = "Copy";
      btn.addEventListener("click", async () => {
        try {
          await navigator.clipboard.writeText(code.innerText);
          btn.textContent = "Copied!";
          setTimeout(() => (btn.textContent = "Copy"), 1200);
        } catch {
          btn.textContent = "Error";
          setTimeout(() => (btn.textContent = "Copy"), 1200);
        }
      });
      pre.appendChild(btn);
    });

    // ---- Share button: copies the canonical ?post= URL for a post ----
    const SITE_BASE = "https://huggingface.co/spaces/bench-labs/blog";
    const SHARE_ICON =
      '' +
      '' +
      '' +
      "";
    const CHECK_ICON =
      '' +
      '';

    // A single share menu is open at a time; clicking elsewhere or Escape closes it.
    let activeShareMenu = null;
    function closeShareMenu() {
      if (activeShareMenu) { activeShareMenu.remove(); activeShareMenu = null; }
    }
    document.addEventListener("click", closeShareMenu);
    document.addEventListener("keydown", (e) => { if (e.key === "Escape") closeShareMenu(); });
    window.addEventListener("resize", closeShareMenu);
    window.addEventListener("scroll", closeShareMenu, true);

    function flashCopied(btn, ok) {
      btn.classList.remove("copied", "error");
      btn.classList.add(ok ? "copied" : "error");
      if (ok) btn.innerHTML = CHECK_ICON;
      btn.dataset.tooltip = ok ? "Copied!" : "Couldn't copy";
      setTimeout(() => {
        btn.classList.remove("copied", "error");
        btn.innerHTML = SHARE_ICON;
        btn.dataset.tooltip = "Share";
      }, 1400);
    }

    // Build the three shareable representations of a post.
    function shareFormats(filename, meta) {
      const url = `${SITE_BASE}?post=${filename}`;
      const title = (meta.title || filename).replace(/\s+/g, " ").trim();
      const desc = (meta.description || "").replace(/\s+/g, " ").trim();
      return [
        { label: "Link", sub: "URL only", text: url },
        { label: "Markdown", sub: "[title](link)", text: `[${title}](${url})` },
        { label: "Markdown + description", sub: "title, link & summary",
          text: desc ? `[${title}](${url})\n\n${desc}` : `[${title}](${url})` },
      ];
    }

    function openShareMenu(btn, filename, getMeta) {
      closeShareMenu();
      const menu = document.createElement("div");
      menu.className = "share-menu";
      menu.setAttribute("role", "menu");
      const heading = document.createElement("div");
      heading.className = "share-menu-title";
      heading.textContent = "Copy as";
      menu.appendChild(heading);

      shareFormats(filename, getMeta()).forEach((fmt) => {
        const item = document.createElement("button");
        item.type = "button";
        item.className = "share-menu-item";
        item.setAttribute("role", "menuitem");
        item.innerHTML = `${fmt.label}${fmt.sub}`;
        item.addEventListener("click", async (e) => {
          e.preventDefault();
          e.stopPropagation();
          closeShareMenu();
          try {
            await navigator.clipboard.writeText(fmt.text);
            flashCopied(btn, true);
          } catch {
            flashCopied(btn, false);
          }
        });
        menu.appendChild(item);
      });

      // Position under the button, right-aligned, clamped to the viewport.
      document.body.appendChild(menu);
      const r = btn.getBoundingClientRect();
      const mw = menu.offsetWidth, mh = menu.offsetHeight, gap = 8, pad = 8;
      let left = Math.min(r.right - mw, window.innerWidth - mw - pad);
      left = Math.max(pad, left);
      let top = r.bottom + gap;
      if (top + mh > window.innerHeight - pad) top = Math.max(pad, r.top - gap - mh);
      menu.style.left = `${Math.round(left)}px`;
      menu.style.top = `${Math.round(top)}px`;
      menu.addEventListener("click", (e) => e.stopPropagation());
      activeShareMenu = menu;
    }

    function makeShareBtn(filename, getMeta) {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "share-btn tooltip";
      btn.setAttribute("aria-label", "Share");
      btn.setAttribute("aria-haspopup", "menu");
      btn.dataset.tooltip = "Share";
      btn.innerHTML = SHARE_ICON;
      btn.addEventListener("click", (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (activeShareMenu) { closeShareMenu(); return; }
        openShareMenu(btn, filename, getMeta || (() => ({ title: filename, description: "" })));
      });
      return btn;
    }

    // One share button per post card on the index page
    document.querySelectorAll(".post-card").forEach((card) => {
      if (card.querySelector(".share-btn")) return;
      const link = card.querySelector('a[href^="posts/"]');
      if (!link) return;
      const filename = link.getAttribute("href").split("/").pop();
      card.appendChild(makeShareBtn(filename, () => ({
        title: card.querySelector("h2")?.textContent || filename,
        description: card.querySelector("p:not(.post-card-snippet)")?.textContent || "",
      })));
    });

    // ---- Search bar (index page only): basic title/description filter,
    // deep search across fetched post content, and a date-sort filter popup ----
    const postsSection = document.querySelector(".posts");
    if (postsSection) {
      const form = document.getElementById("search-form");
      const input = document.getElementById("search-input");
      const deepBtn = document.getElementById("deep-search-btn");
      const filterBtn = document.getElementById("filter-btn");
      const filterPopup = document.getElementById("filter-popup");
      const statusEl = document.getElementById("deep-search-status");
      const cardData = Array.from(postsSection.querySelectorAll(".post-card")).map((card) => {
        const link = card.querySelector('a[href^="posts/"]');
        return {
          card,
          file: link ? link.getAttribute("href").split("/").pop() : null,
          title: (card.querySelector("h2")?.textContent || "").trim(),
          desc: (card.querySelector("p")?.textContent || "").trim(),
          dateText: (card.querySelector(".date")?.textContent || "").trim(),
        };
      });

      const deepTextCache = new Map(); // filename -> plain text content of that post

      function clearSnippets() {
        cardData.forEach(({ card }) => card.querySelector(".post-card-snippet")?.remove());
      }

      function showAll() {
        clearSnippets();
        cardData.forEach(({ card }) => (card.style.display = ""));
      }

      function runBasicSearch(query) {
        const q = query.trim().toLowerCase();
        clearSnippets();
        if (!q) {
          cardData.forEach(({ card }) => (card.style.display = ""));
          return;
        }
        cardData.forEach(({ card, title, desc }) => {
          const match = title.toLowerCase().includes(q) || desc.toLowerCase().includes(q);
          card.style.display = match ? "" : "none";
        });
      }

      async function fetchPostText(file) {
        if (deepTextCache.has(file)) return deepTextCache.get(file);
        const res = await fetch("posts/" + file);
        const html = await res.text();
        const doc = new DOMParser().parseFromString(html, "text/html");
        const text = (doc.querySelector(".article") || doc.body).textContent.replace(/\s+/g, " ").trim();
        deepTextCache.set(file, text);
        return text;
      }

      function escapeHtml(s) {
        return s.replace(/&/g, "&").replace(//g, ">");
      }

      function makeSnippet(text, q) {
        const idx = text.toLowerCase().indexOf(q);
        if (idx === -1) return null;
        const start = Math.max(0, idx - 50);
        const end = Math.min(text.length, idx + q.length + 70);
        let snippet = text.slice(start, end);
        if (start > 0) snippet = "…" + snippet;
        if (end < text.length) snippet += "…";
        return snippet;
      }

      function highlightSnippet(snippet, q) {
        const idx = snippet.toLowerCase().indexOf(q.toLowerCase());
        if (idx === -1) return escapeHtml(snippet);
        return (
          escapeHtml(snippet.slice(0, idx)) +
          "" + escapeHtml(snippet.slice(idx, idx + q.length)) + "" +
          escapeHtml(snippet.slice(idx + q.length))
        );
      }

      async function runDeepSearch(query) {
        const q = query.trim().toLowerCase();
        clearSnippets();
        if (!q) {
          showAll();
          return;
        }

        deepBtn.classList.add("is-busy");
        deepBtn.disabled = true;
        statusEl.hidden = false;
        statusEl.textContent = "Searching " + cardData.length + " posts…";

        try {
          await Promise.all(
            cardData.map(async ({ card, title, desc, file }) => {
              const titleHit = title.toLowerCase().includes(q);
              const descHit = desc.toLowerCase().includes(q);
              let snippet = null;

              if (file) {
                try {
                  const text = await fetchPostText(file);
                  snippet = makeSnippet(text, q);
                } catch {
                  // couldn't fetch this post's content — fall back to title/description match only
                }
              }

              const match = titleHit || descHit || snippet;
              card.style.display = match ? "" : "none";

              if (match && snippet) {
                const snippetEl = document.createElement("p");
                snippetEl.className = "post-card-snippet";
                snippetEl.innerHTML = "Found in post: " + highlightSnippet(snippet, q);
                const readLink = card.querySelector('a[href^="posts/"]');
                if (readLink) readLink.insertAdjacentElement("beforebegin", snippetEl);
                else card.appendChild(snippetEl);
              }
            })
          );
        } finally {
          deepBtn.classList.remove("is-busy");
          deepBtn.disabled = false;
          statusEl.hidden = true;
        }
      }

      form?.addEventListener("submit", (e) => {
        e.preventDefault();
        runBasicSearch(input.value);
      });

      deepBtn?.addEventListener("click", () => {
        runDeepSearch(input.value);
      });

      // ---- Filter popup: sort by date ----
      function parseCardDate(dateText) {
        const m = dateText.match(/^(\w+)\s+(\d{4})$/);
        if (!m) return null;
        const t = new Date(`${m[1]} 1, ${m[2]}`).getTime();
        return Number.isNaN(t) ? null : t;
      }

      function applySort(direction) {
        const withDate = cardData.filter((c) => parseCardDate(c.dateText) !== null);
        const withoutDate = cardData.filter((c) => parseCardDate(c.dateText) === null);
        withDate.sort((a, b) => {
          const ta = parseCardDate(a.dateText);
          const tb = parseCardDate(b.dateText);
          return direction === "new" ? tb - ta : ta - tb;
        });
        [...withDate, ...withoutDate].forEach(({ card }) => postsSection.appendChild(card));
      }

      filterBtn?.addEventListener("click", () => {
        const opening = filterPopup.hidden;
        filterPopup.hidden = !opening;
        filterBtn.setAttribute("aria-expanded", String(opening));
      });

      filterPopup?.querySelectorAll(".filter-choice").forEach((btn) => {
        btn.addEventListener("click", () => {
          filterPopup.querySelectorAll(".filter-choice").forEach((b) => b.classList.remove("active"));
          btn.classList.add("active");
          applySort(btn.dataset.sort);
        });
      });

      document.addEventListener("click", (e) => {
        if (filterPopup && !filterPopup.hidden && !filterPopup.contains(e.target) && e.target !== filterBtn && !filterBtn.contains(e.target)) {
          filterPopup.hidden = true;
          filterBtn.setAttribute("aria-expanded", "false");
        }
      });
    }

    // ---- Article-only features: sidebar + collapsible code ----
    const article = document.querySelector(".article");
    if (!article) return;

    // ---- Wrap tables so very wide ones scroll horizontally instead of
    // overflowing the page. The table stays width:100% (a real table, so its
    // columns distribute across the full container); the wrapper owns the
    // overflow-x scrolling. ----
    article.querySelectorAll("table").forEach((table) => {
      if (table.parentElement.classList.contains("table-scroll")) return;
      const wrap = document.createElement("div");
      wrap.className = "table-scroll";
      table.parentNode.insertBefore(wrap, table);
      wrap.appendChild(table);
    });

    // ---- Share button for the current post ----
    {
      const filename = location.pathname.split("/").pop();
      if (filename && /\.html?$/i.test(filename) && !article.querySelector(".share-btn")) {
        const shareBtn = makeShareBtn(filename, () => ({
          title: article.querySelector("h1")?.textContent || document.title,
          description: article.querySelector("h1 ~ p")?.textContent
            || article.querySelector("p")?.textContent || "",
        }));
        shareBtn.classList.add("share-btn-article");
        const back = article.querySelector(".back");
        if (back) back.insertAdjacentElement("afterend", shareBtn);
        else article.prepend(shareBtn);
      }
    }

    // ---- External links: open in a new tab + hover preview card with fetched metadata ----
    {
      const linkMetaCache = new Map();

      function faviconlessTitle(href) {
        try {
          const u = new URL(href);
          const parts = u.pathname.split("/").filter(Boolean);
          if (u.hostname === "huggingface.co" && parts[0] === "spaces" && parts.length >= 3) {
            return `${parts[1]}/${parts[2]} · Hugging Face Space`;
          }
          if (u.hostname === "huggingface.co" && parts.length >= 2) {
            return `${parts[0]}/${parts[1]} · Hugging Face Hub`;
          }
          return u.hostname;
        } catch {
          return href;
        }
      }

      async function loadLinkMeta(href) {
        if (linkMetaCache.has(href)) return linkMetaCache.get(href);
        const metaPromise = (async () => {
          try {
            const res = await fetch(href, { mode: "cors" });
            if (!res.ok) throw new Error("bad status");
            const html = await res.text();
            const doc = new DOMParser().parseFromString(html, "text/html");
            const title = doc.querySelector("title")?.textContent?.trim() || null;
            const desc =
              doc.querySelector('meta[name="description"]')?.getAttribute("content")?.trim() ||
              doc.querySelector('meta[property="og:description"]')?.getAttribute("content")?.trim() ||
              null;
            return { title, desc };
          } catch {
            return { title: null, desc: null };
          }
        })();
        linkMetaCache.set(href, metaPromise);
        return metaPromise;
      }

      article.querySelectorAll('a[href^="http"]').forEach((link) => {
        link.addEventListener("click", (e) => {
          e.preventDefault();
          window.open(link.href, "_blank", "noopener,noreferrer");
        });

        let card = null;
        let hideTimer = null;

        link.addEventListener("mouseenter", () => {
          clearTimeout(hideTimer);
          card = document.createElement("div");
          card.className = "link-preview-card";
          const urlEl = document.createElement("div");
          urlEl.className = "link-preview-url";
          urlEl.textContent = link.href;
          const titleEl = document.createElement("div");
          titleEl.className = "link-preview-title";
          titleEl.textContent = faviconlessTitle(link.href);
          card.appendChild(titleEl);
          card.appendChild(urlEl);
          document.body.appendChild(card);

          const rect = link.getBoundingClientRect();
          card.style.top = rect.bottom + window.scrollY + 8 + "px";
          const maxLeft = window.scrollX + document.documentElement.clientWidth - card.offsetWidth - 12;
          card.style.left = Math.min(rect.left + window.scrollX, Math.max(12, maxLeft)) + "px";
          requestAnimationFrame(() => card?.classList.add("visible"));

          loadLinkMeta(link.href).then((meta) => {
            if (!card || !card.isConnected) return;
            if (meta.title) titleEl.textContent = meta.title;
            if (meta.desc) {
              const descEl = document.createElement("div");
              descEl.className = "link-preview-desc";
              descEl.textContent = meta.desc;
              card.appendChild(descEl);
            }
          });
        });

        link.addEventListener("mouseleave", () => {
          const toRemove = card;
          card = null;
          if (!toRemove) return;
          toRemove.classList.remove("visible");
          hideTimer = setTimeout(() => toRemove.remove(), 180);
        });
      });
    }

    const headings = article.querySelectorAll("h1, h2, h3");
    const codeBlocks = article.querySelectorAll("pre");

    const codeFiles = [];
    codeBlocks.forEach((pre, idx) => {
      let name = null;
      let el = pre.previousElementSibling;
      while (el) {
        if (el.tagName === "H2" || el.tagName === "H3") {
          const text = el.textContent.trim();
          if (text.match(/\.\w{1,5}$/) || text.match(/\.\w{1,5}\s/)) {
            name = text.replace(/\s*\(.*\)/, "").trim();
          }
          break;
        }
        el = el.previousElementSibling;
      }
      if (!name) name = `code-block-${idx + 1}.txt`;
      codeFiles.push({ name, pre });
    });

    // Make code blocks collapsible
    codeBlocks.forEach((pre, idx) => {
      const wrapper = document.createElement("div");
      wrapper.className = "code-collapsible";

      const toggle = document.createElement("button");
      toggle.className = "code-toggle";
      const fileName = codeFiles[idx].name;
      toggle.innerHTML = ` ${fileName}`;
      toggle.setAttribute("aria-expanded", "false");

      pre.parentNode.insertBefore(wrapper, pre);
      wrapper.appendChild(toggle);
      wrapper.appendChild(pre);
      pre.classList.add("collapsed");

      toggle.addEventListener("click", () => {
        const expanded = pre.classList.toggle("collapsed");
        toggle.setAttribute("aria-expanded", !expanded);
        toggle.querySelector(".code-toggle-arrow").textContent = expanded ? "▶" : "▼";
      });
    });

    // ---- Build sidebar ----
    // MOBILE: sidebar becomes a collapsible drawer at the top
    const sidebar = document.createElement("nav");
    sidebar.className = "article-sidebar";

    if (isMobile) {
      sidebar.classList.add("sidebar-mobile");

      const drawerToggle = document.createElement("button");
      drawerToggle.className = "sidebar-drawer-toggle";
      drawerToggle.textContent = "☰ Contents & Files";
      drawerToggle.setAttribute("aria-expanded", "false");

      const drawerBody = document.createElement("div");
      drawerBody.className = "sidebar-drawer-body";
      drawerBody.style.display = "none";

      drawerToggle.addEventListener("click", () => {
        const open = drawerBody.style.display === "none";
        drawerBody.style.display = open ? "block" : "none";
        drawerToggle.setAttribute("aria-expanded", open);
      });

      sidebar.appendChild(drawerToggle);
      sidebar.appendChild(drawerBody);

      // We'll append TOC / files into drawerBody instead
      sidebar._container = drawerBody;
    } else {
      sidebar._container = sidebar;
    }

    const container = sidebar._container;

    // TOC section
    const tocTitle = document.createElement("div");
    tocTitle.className = "sidebar-title";
    tocTitle.textContent = "Contents";
    container.appendChild(tocTitle);

    const tocList = document.createElement("ul");
    tocList.className = "sidebar-toc";

    headings.forEach((h, i) => {
      if (!h.id) h.id = "heading-" + i;
      const li = document.createElement("li");
      li.className = "toc-" + h.tagName.toLowerCase();
      const a = document.createElement("a");
      a.href = "#" + h.id;
      a.textContent = h.textContent;
      a.addEventListener("click", (e) => {
        e.preventDefault();
        h.scrollIntoView({ behavior: "smooth", block: "start" });
        // MOBILE: close drawer after tap
        if (isMobile) {
          const body = sidebar.querySelector(".sidebar-drawer-body");
          if (body) body.style.display = "none";
          const tog = sidebar.querySelector(".sidebar-drawer-toggle");
          if (tog) tog.setAttribute("aria-expanded", "false");
        }
      });
      li.appendChild(a);
      tocList.appendChild(li);
    });
    container.appendChild(tocList);

    // Files section
    if (codeFiles.length > 0) {
      const filesTitle = document.createElement("div");
      filesTitle.className = "sidebar-title";
      filesTitle.textContent = "Files";
      container.appendChild(filesTitle);

      const fileList = document.createElement("ul");
      fileList.className = "sidebar-files";

      codeFiles.forEach(({ name, pre }) => {
        const li = document.createElement("li");
        const btn = document.createElement("button");
        btn.className = "sidebar-file-btn";
        btn.innerHTML = ` ${name}`;

        btn.addEventListener("click", () => {
          const code = pre.querySelector("code") || pre;
          const text = code.innerText;
          const blob = new Blob([text], { type: "text/plain" });
          const url = URL.createObjectURL(blob);
          const a = document.createElement("a");
          a.href = url;
          a.download = name;
          a.click();
          URL.revokeObjectURL(url);
        });

        li.appendChild(btn);
        fileList.appendChild(li);
      });
      container.appendChild(fileList);
    }

    document.body.appendChild(sidebar);

    // Highlight active TOC item on scroll
    // MOBILE: use a throttled observer with larger margins for perf
    const tocLinks = tocList.querySelectorAll("a");
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            tocLinks.forEach((l) => l.classList.remove("active"));
            const link = tocList.querySelector(`a[href="#${entry.target.id}"]`);
            if (link) link.classList.add("active");
          }
        });
      },
      {
        rootMargin: isMobile ? "-60px 0px -60% 0px" : "-80px 0px -70% 0px",
        // MOBILE: lower threshold = fewer callbacks
        threshold: isMobile ? 0 : 0,
      }
    );
    headings.forEach((h) => observer.observe(h));
  });
})();