<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Playlists · AI Creator Digest</title>
<meta name="description" content="Every collection I've curated — each one a consensus guide synthesized across every video inside it.">
<meta property="og:site_name" content="AI Creator Digest">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<link rel="alternate" type="application/rss+xml" title="AI Creator Digest" href="/feed.xml">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='4' fill='%23B5602B'/%3E%3Ctext x='16' y='22' font-family='Arial,sans-serif' font-weight='700' font-size='17' fill='white' text-anchor='middle'%3ED%3C/text%3E%3C/svg%3E">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/web/assets/style.css">
<script defer src="/web/assets/app.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NJGKWXP83R"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-NJGKWXP83R');
</script>

<!-- X conversion tracking base code -->
<script>
!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
twq('config','oft1f');
</script>
<!-- End X conversion tracking base code -->
</head>
<body>
<a class="skip" href="#main">Skip to content</a>

<div class="wrap">
  <header class="site-header">
    <a href="/" class="wordmark" style="text-decoration:none">
      <span class="title">AI Creator Digest</span>
      <span class="tag">aicreatordigest.com — youtube, read instead of watched</span>
    </a>
    <nav class="site-nav" aria-label="Primary">
      <a href="/">home</a>
      <a href="/playlists">playlists</a>
      <a href="/digests">digests</a>
      <a href="/creators">creators</a>
      <a href="/about">about</a>
      <span class="spacer"></span>
      <span class="issue" id="issue-marker"></span>
    </nav>
    <hr class="divider">
  </header>

  <main id="main">
    <div class="page-head">
      <div class="eyebrow">Playlists</div>
      <h1>Every collection</h1>
      <p class="dek">Each one opens with a consensus guide I synthesize across every video inside it.</p>
    </div>

    <section class="filters" id="filters" hidden>
      <div class="filter-row">
        <input type="search" id="q" class="filter-search" placeholder="Search collections…" autocomplete="off" aria-label="Search collections">
        <label class="filter-sort">
          <span>Sort</span>
          <select id="sort" aria-label="Sort collections">
            <option value="name">A–Z</option>
            <option value="digests">Most digests</option>
            <option value="creators">Most creators</option>
          </select>
        </label>
      </div>
      <p class="filter-count" id="filter-count" aria-live="polite"></p>
    </section>

    <section class="band" style="padding-top:0">
      <div class="grid-2" id="playlists"><p class="loading">Loading…</p></div>
    </section>
  </main>

  <footer class="site-footer">
    <span>AI Creator Digest © 2026</span>
    <span class="spacer"></span>
    <a href="/how-we-synthesize">how i synthesize</a>
    <a href="/how-this-works">how this works</a>
    <a href="https://github.com/crgallego/aicreatordigest/issues/new?title=Channel%20suggestion" target="_blank" rel="noopener">submit a channel</a>
    <a href="/feed.xml">rss</a>
  </footer>
</div>

<script>
document.addEventListener("DOMContentLoaded", async () => {
  const A = window.ACD;
  const index = await A.loadIndex();
  A.renderIssueMarker(index);

  const all = Object.values(index.playlists);
  const gridEl = document.getElementById("playlists");
  const countEl = document.getElementById("filter-count");

  A.setMeta({
    title: "Playlists",
    description: "Every collection on AI Creator Digest, each opening with a consensus guide synthesized across its videos.",
    path: "/playlists",
  });

  if (!all.length) {
    gridEl.innerHTML = A.emptyState(
      "No collections yet",
      "Collections appear here as soon as the first digest in one is published."
    );
    return;
  }

  const showFilters = all.length >= 5;
  document.getElementById("filters").hidden = !showFilters;
  const state = { q: "", sort: "name" };

  if (showFilters) {
    document.getElementById("q").addEventListener("input", (e) => {
      state.q = e.target.value.trim().toLowerCase();
      draw();
    });
    document.getElementById("sort").addEventListener("change", (e) => {
      state.sort = e.target.value;
      draw();
    });
  }

  draw();

  function draw() {
    const matched = all
      .filter((p) => {
        if (!state.q) return true;
        return [p.name, p.consensusSummary]
          .filter(Boolean)
          .join(" ")
          .toLowerCase()
          .includes(state.q);
      })
      .sort((a, b) => {
        const byName = () => (a.name || "").localeCompare(b.name || "");
        if (state.sort === "digests") return (b.videoCount || 0) - (a.videoCount || 0) || byName();
        if (state.sort === "creators") return (b.creatorCount || 0) - (a.creatorCount || 0) || byName();
        return byName();
      });

    gridEl.innerHTML = matched.length
      ? matched
          .map((p) => {
            const pVideos = index.videos.filter((v) => v.playlistSlug === p.slug);
            return A.playlistCell(
              {
                slug: p.slug,
                name: p.name,
                topic: A.groupTopic(pVideos),
                blurb: p.consensusSummary || "",
                updatedAt: p.consensusUpdatedAt,
              },
              p.videoCount ?? pVideos.length
            );
          })
          .join("")
      : A.emptyState("Nothing matched", "Try a different search.");

    if (showFilters) {
      countEl.textContent = `${matched.length} of ${all.length} collection${all.length === 1 ? "" : "s"}`;
    }
  }
});
</script>
</body>
</html>
