<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Verses &mdash; Poetry Showcase</title>
<meta name="description" content="Verses is a curated digital showcase for poetry, sher, and shayari. Read, explore, and share beautiful pieces of literature where emotions become words.">
<meta name="keywords" content="poetry, sher, shayari, poems, literature, verses, digital showcase, writing, poets">
<meta property="og:title" content="Verses &mdash; Poetry Showcase">
<meta property="og:description" content="Discover curated poetry, sher, and shayari. A minimal digital space where emotions become words.">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Verses &mdash; Poetry Showcase">
<meta name="twitter:description" content="Discover curated poetry, sher, and shayari. A minimal digital space where emotions become words.">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%230f0e0c'/%3E%3Ctext x='32' y='44' font-family='Georgia,serif' font-style='italic' font-size='34' fill='%23f4ede2' text-anchor='middle'%3EV%3C/text%3E%3C/svg%3E">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,600;1,400&family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=Jost:wght@400;500&display=swap" rel="stylesheet">
<style>
  body { margin: 0; background: #0f0e0c; color: #f4ede2; }
  * { box-sizing: border-box; }
  ::selection { background: #f4ede2; color: #0f0e0c; }
  @keyframes riseIn { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: translateY(0); } }
  .poem-body, .reader-scroll { scrollbar-width: none; }
  .poem-body::-webkit-scrollbar, .reader-scroll::-webkit-scrollbar { display: none; }
</style>
</head>
<body>

<div id="splash-screen" style="position:fixed; inset:0; z-index:9999; background:#0f0e0c; display:flex; flex-direction:column; align-items:center; justify-content:center; transition:opacity 0.8s ease, visibility 0.8s ease; opacity:1; visibility:visible;">
  <div style="font-family:'Playfair Display',serif; font-size:clamp(32px,5vw,56px); font-weight:600; font-style:italic; color:#f4ede2; margin-bottom:12px; letter-spacing:0.02em;">Verses</div>
  <div style="font-family:'Cormorant Garamond',serif; font-size:clamp(16px,2vw,22px); opacity:0.8; font-style:italic; color:#f4ede2;">where emotions become words</div>
</div>

<div style="position:relative; width:100%; height:100vh; overflow:hidden; font-family:'Jost',sans-serif;">

  <div id="bg-back" style="position:absolute; inset:0; background:linear-gradient(160deg, #1a1e2b 0%, #0f0e0c 70%); transition:opacity 0.05s;"></div>
  <div id="bg-front" style="position:absolute; inset:0; background:linear-gradient(160deg, #1a1e2b 0%, #0f0e0c 70%); opacity:0; transition:opacity 1.4s ease;"></div>
  <div style="position:absolute; inset:0; background:radial-gradient(ellipse at 50% 30%, rgba(0,0,0,0) 0%, rgba(0,0,0,0.45) 100%);"></div>

  <header style="position:fixed; top:0; left:0; right:0; z-index:20; display:flex; align-items:center; gap:clamp(10px,2vw,28px); padding:clamp(12px,2vw,20px) clamp(16px,3vw,40px); backdrop-filter:blur(10px); background:linear-gradient(to bottom, rgba(15,14,12,0.65), rgba(15,14,12,0)); flex-wrap:wrap;">
    <div style="display:flex; flex-direction:column; justify-content:center;">
      <a href="/verses" style="font-family:'Playfair Display',serif; font-size:clamp(18px,2.2vw,24px); letter-spacing:0.02em; white-space:nowrap; line-height:1.2; color:inherit; text-decoration:none; cursor:pointer;">Verses</a>
      <div style="font-family:'Cormorant Garamond',serif; font-size:clamp(13px,1.4vw,15px); opacity:0.8; font-style:italic; white-space:nowrap; line-height:1.2;">where emotions become words</div>
    </div>

    <div style="flex:1; display:flex; align-items:center; gap:10px; min-width:200px; max-width:380px;">
      <div style="flex:1; display:flex; align-items:center; gap:8px; background:rgba(244,237,226,0.08); border:1px solid rgba(244,237,226,0.18); border-radius:999px; padding:8px 16px;">
        <span style="opacity:0.6; font-size:14px;">&#9906;</span>
        <input id="search-input" placeholder="search title, poet, or a line" style="background:transparent; border:none; outline:none; color:#f4ede2; font-family:'Jost',sans-serif; font-size:clamp(13px,1.4vw,14px); width:100%;" />
      </div>
      <a href="/verses/admin/login" title="Admin sign in" aria-label="Admin sign in" style="flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:999px; border:1px solid rgba(244,237,226,0.18); background:rgba(244,237,226,0.06); color:#f4ede2; text-decoration:none; transition:background 0.2s;">
        <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
      </a>
    </div>

    <div style="display:flex; gap:6px; flex:1 1 auto; align-items:flex-start;">
      <div id="tag-list" style="display:flex; gap:6px; flex-wrap:wrap; padding:2px 0; overflow:hidden; max-height:40px; transition:max-height 0.3s ease;"></div>
      <button id="tag-expand-btn" style="display:none; flex:0 0 auto; background:none; border:none; color:rgba(244,237,226,0.8); padding:7px 6px; cursor:pointer; display:inline-flex; align-items:center; justify-content:center;" aria-label="Expand tags">
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
      </button>
    </div>
  </header>

  <div id="scroll-container" style="position:relative; z-index:10; width:100%; height:100%; overflow-y:auto; overflow-x:hidden; scroll-snap-type:y mandatory; scroll-behavior:smooth;">
    <div id="poems-container"></div>

    <section id="empty-state" style="display:none; width:100%; height:100vh; align-items:center; justify-content:center; text-align:center; padding:0 20px;">
      <div style="font-family:'Cormorant Garamond',serif; font-size:clamp(18px,2.4vw,24px); opacity:0.7; font-style:italic;">No poems found &mdash; try another word or theme.</div>
    </section>
  </div>

  <!-- compact scroll indicator: just "more above" / "more below" chevrons -->
  <div id="scroll-indicator" style="position:fixed; right:clamp(10px,2vw,22px); top:50%; transform:translateY(-50%); z-index:20; display:flex; flex-direction:column; align-items:center; gap:10px;"></div>

  <div id="reader" style="display:none; position:fixed; inset:0; z-index:40; animation:riseIn 0.4s ease;">
    <div id="reader-bg" style="position:absolute; inset:0; background:linear-gradient(160deg, #1a1e2b 0%, #0f0e0c 70%);"></div>
    <div style="position:absolute; inset:0; background:radial-gradient(ellipse at 50% 30%, rgba(0,0,0,0) 0%, rgba(0,0,0,0.5) 100%);"></div>
    <button id="reader-close" title="Close" aria-label="Close" style="position:absolute; top:22px; right:26px; z-index:3; background:rgba(244,237,226,0.1); border:1px solid rgba(244,237,226,0.3); color:#f4ede2; width:40px; height:40px; border-radius:50%; font-size:17px; cursor:pointer;">&times;</button>
    <div class="reader-scroll" style="position:relative; z-index:2; height:100%; overflow-y:auto; padding:clamp(70px,10vh,110px) clamp(20px,6vw,60px) clamp(60px,10vh,110px);">
      <article style="width:min(680px,92vw); margin:0 auto;">
        <h1 id="reader-title" style="font-family:'Playfair Display',serif; font-weight:600; font-style:italic; font-size:clamp(28px,4.4vw,46px); line-height:1.15; margin:0 0 14px 0; color:#f4ede2;"></h1>
        <div id="reader-author" style="font-family:'Jost',sans-serif; font-size:clamp(13px,1.5vw,15px); letter-spacing:0.04em; opacity:0.65; margin-bottom:40px; color:#f4ede2;"></div>
        <div id="reader-body" style="font-family:'Cormorant Garamond',serif; font-size:clamp(18px,2.2vw,23px); line-height:1.8; white-space:pre-line; color:rgba(244,237,226,0.94);"></div>
      </article>
    </div>
  </div>

</div>

<script>
(function () {
  var THEME_GRAD = {
    nature: "linear-gradient(160deg, #1c2b1f 0%, #0f0e0c 70%)",
    hope:   "linear-gradient(160deg, #2b2412 0%, #0f0e0c 70%)",
    loss:   "linear-gradient(160deg, #1a1e2b 0%, #0f0e0c 70%)",
    joy:    "linear-gradient(160deg, #2b1c1a 0%, #0f0e0c 70%)",
    love:   "linear-gradient(160deg, #2b141e 0%, #0f0e0c 70%)",
    dreams: "linear-gradient(160deg, #1a1a2b 0%, #0f0e0c 70%)",
    seasons: "linear-gradient(160deg, #1a1e2b 0%, #0f0e0c 70%)",
    solitude: "linear-gradient(160deg, #1a1a2b 0%, #0f0e0c 70%)"
  };
  var DEFAULT_GRAD = THEME_GRAD.loss;
  var ALL_TAGS = [];            // loaded from /verses/api/v1/tags
  var PAGE_SIZE = 10;           // poems fetched per page
  var MAX_WINDOW = 20;          // hard cap of poems kept in the DOM at once

  // --- mode: bare showcase (paginated feed) vs a poet's page (loads all) ---
  function poetSlugFromUrl() {
    var parts = location.pathname.split("/").filter(Boolean); // ["verses", "<slug>"?]
    return (parts.length >= 2 && parts[0] === "verses") ? decodeURIComponent(parts[1]) : null;
  }
  var slug = poetSlugFromUrl();
  var mode = slug ? "poet" : "feed";

  // feed paging state
  var firstPage = 0, lastPage = -1, hasPrev = false, hasNext = false;
  var loadingNext = false, loadingPrev = false;

  // poet-mode state (full list, filtered client-side)
  var currentEditor = null;
  var poetPoems = [];

  var activeTag = null;
  var query = "";
  var currentBg = null;
  var showingFront = false;

  var poemsContainer = document.getElementById("poems-container");
  var headerEl = document.querySelector("header");
  if (headerEl) {
    new ResizeObserver(function (entries) {
      document.documentElement.style.setProperty("--header-height", entries[0].contentRect.height + "px");
    }).observe(headerEl);
  }
  var emptyState = document.getElementById("empty-state");
  var indicatorEl = document.getElementById("scroll-indicator");
  var tagListEl = document.getElementById("tag-list");
  var searchInput = document.getElementById("search-input");
  var scrollContainer = document.getElementById("scroll-container");
  var bgBack = document.getElementById("bg-back");
  var bgFront = document.getElementById("bg-front");
  var reader = document.getElementById("reader");
  var readerBg = document.getElementById("reader-bg");
  var readerTitle = document.getElementById("reader-title");
  var readerAuthor = document.getElementById("reader-author");
  var readerBody = document.getElementById("reader-body");

  var observer = null;

  // ---------- reader ----------
  function openReader(poem) {
    if (poem.title) { readerTitle.textContent = poem.title; readerTitle.style.display = "block"; }
    else { readerTitle.style.display = "none"; }
    readerAuthor.textContent = poem.author || "";
    readerBody.textContent = poem.body || "";
    readerBg.style.background = bgFor(poem);
    reader.style.display = "block";
  }
  function closeReader() { reader.style.display = "none"; }

  // ---------- background ----------
  function gradFor(theme) { return THEME_GRAD[theme] || DEFAULT_GRAD; }
  function bgFor(poem) {
    if (poem.coverImageUrl) {
      var safe = String(poem.coverImageUrl).replace(/["'()\\\s]/g, "");
      if (safe) return "#0f0e0c url('" + safe + "') center center / cover no-repeat";
    }
    return gradFor(poem.theme);
  }
  function crossfadeTo(bg) {
    if (!bg || bg === currentBg) return;
    currentBg = bg;
    if (showingFront) {
      bgBack.style.background = bg; bgFront.style.opacity = "0"; showingFront = false;
    } else {
      bgFront.style.background = bg; void bgFront.offsetWidth; bgFront.style.opacity = "1"; showingFront = true;
    }
  }

  // ---------- tags (chips + overflow expand) ----------
  function renderTags() {
    tagListEl.innerHTML = "";
    ALL_TAGS.forEach(function (tag) {
      var isActive = activeTag === tag;
      var btn = document.createElement("button");
      btn.textContent = tag;
      btn.style.cssText = "flex:0 0 auto; padding:7px 15px; border-radius:999px; font-family:'Jost',sans-serif; font-size:12.5px; letter-spacing:0.03em; cursor:pointer; transition:all 0.2s; border:1px solid " +
        (isActive ? "#f4ede2" : "rgba(244,237,226,0.25)") + "; background:" +
        (isActive ? "#f4ede2" : "transparent") + "; color:" +
        (isActive ? "#0f0e0c" : "rgba(244,237,226,0.75)") + ";";
      btn.addEventListener("click", function () {
        activeTag = activeTag === tag ? null : tag;
        renderTags();
        onFilterChanged();
      });
      tagListEl.appendChild(btn);
    });
    setTimeout(checkTagOverflow, 50);
  }

  var tagExpandBtn = document.getElementById("tag-expand-btn");
  var tagsExpanded = false;
  var expandDownSvg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>';
  var expandUpSvg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>';
  function checkTagOverflow() {
    if (tagListEl.scrollHeight > 46) {
      tagExpandBtn.style.display = "inline-flex";
    } else {
      tagExpandBtn.style.display = "none";
      tagsExpanded = false;
      tagListEl.style.maxHeight = "40px";
      tagExpandBtn.innerHTML = expandDownSvg;
    }
  }
  tagExpandBtn.addEventListener("click", function () {
    tagsExpanded = !tagsExpanded;
    if (tagsExpanded) { tagListEl.style.maxHeight = tagListEl.scrollHeight + "px"; tagExpandBtn.innerHTML = expandUpSvg; }
    else { tagListEl.style.maxHeight = "40px"; tagExpandBtn.innerHTML = expandDownSvg; }
  });
  window.addEventListener("resize", checkTagOverflow);

  // ---------- poem view + section builders ----------
  function toPoemView(p) {
    return {
      title: p.title,
      contentType: p.contentType,
      author: p.authorName,
      authorSlug: p.authorSlug,
      body: p.body,
      tags: p.tags || [],
      theme: p.theme,
      coverImageUrl: p.coverImageUrl
    };
  }
  function bodySizeFor(body) {
    return (body || "").length > 260 ? "clamp(16px,2vw,20px)" : "clamp(19px,2.6vw,26px)";
  }

  var SECTION_CSS = "scroll-snap-align:start; scroll-snap-stop:always; width:100%; min-height:100vh; display:flex; align-items:center; justify-content:center; padding:calc(var(--header-height, 100px) + 40px) clamp(20px,6vw,60px) clamp(40px,6vh,60px);";

  function buildPoemSection(poem) {
    var section = document.createElement("section");
    section.className = "poem-section";
    section.setAttribute("data-bg", bgFor(poem));
    section.style.cssText = SECTION_CSS;

    var article = document.createElement("article");
    article.className = "poem-body";
    article.style.cssText = "width:min(680px,92vw); max-height:calc(100vh - var(--header-height, 100px) - 80px); overflow:hidden; opacity:0; transform:translateY(28px); transition:opacity 0.9s ease, transform 0.9s ease;";

    var chipsWrap = document.createElement("div");
    chipsWrap.style.cssText = "display:flex; gap:8px; margin-bottom:18px; flex-wrap:wrap;";
    (poem.tags || []).forEach(function (t) {
      var chip = document.createElement("span");
      chip.textContent = t;
      chip.style.cssText = "font-size:11px; letter-spacing:0.08em; text-transform:uppercase; padding:4px 11px; border-radius:999px; border:1px solid rgba(244,237,226,0.3); color:rgba(244,237,226,0.75);";
      chipsWrap.appendChild(chip);
    });

    var h1 = document.createElement("h1");
    h1.style.cssText = "font-family:'Playfair Display',serif; font-weight:600; font-style:italic; font-size:clamp(28px,4.4vw,46px); line-height:1.15; margin:0 0 14px 0; text-wrap:pretty;";
    if (poem.title) h1.textContent = poem.title; else h1.style.display = "none";

    var authorEl;
    if (poem.authorSlug) {
      authorEl = document.createElement("a");
      authorEl.href = "/verses/" + poem.authorSlug;
      authorEl.style.cssText = "font-family:'Jost',sans-serif; font-size:clamp(13px,1.5vw,15px); letter-spacing:0.04em; opacity:0.65; margin-bottom:36px; display:inline-block; color:#f4ede2; text-decoration:none; transition:opacity 0.2s;";
      authorEl.addEventListener("mouseover", function () { this.style.opacity = "0.85"; });
      authorEl.addEventListener("mouseout", function () { this.style.opacity = "0.65"; });
    } else {
      authorEl = document.createElement("div");
      authorEl.style.cssText = "font-family:'Jost',sans-serif; font-size:clamp(13px,1.5vw,15px); letter-spacing:0.04em; opacity:0.65; margin-bottom:36px;";
    }
    authorEl.textContent = poem.author || "";

    var lines = (poem.body || "").split("\n");
    var isLong = lines.length > 14;
    var shownBody = isLong ? lines.slice(0, 10).join("\n") : (poem.body || "");
    var bodyMask = isLong ? "linear-gradient(to bottom, black 55%, transparent 100%)" : "none";

    var bodyEl = document.createElement("div");
    bodyEl.style.cssText = "font-family:'Cormorant Garamond',serif; font-size:" + bodySizeFor(poem.body) + "; line-height:1.75; white-space:pre-line; color:rgba(244,237,226,0.94); text-wrap:pretty; mask-image:" + bodyMask + "; -webkit-mask-image:" + bodyMask + ";";
    bodyEl.textContent = shownBody;

    article.appendChild(chipsWrap);
    article.appendChild(h1);
    article.appendChild(authorEl);
    article.appendChild(bodyEl);
    if (isLong) {
      var moreBtn = document.createElement("button");
      moreBtn.innerHTML = "Continue reading &rarr;";
      moreBtn.style.cssText = "margin-top:22px; padding:11px 24px; border-radius:999px; background:rgba(244,237,226,0.08); border:1px solid rgba(244,237,226,0.3); color:#f4ede2; font-family:'Jost',sans-serif; font-size:13px; letter-spacing:0.04em; cursor:pointer;";
      moreBtn.addEventListener("click", (function (pm) { return function () { openReader(pm); }; })(poem));
      article.appendChild(moreBtn);
    }
    section.appendChild(article);
    return section;
  }

  function buildEditorSection(editor) {
    var section = document.createElement("section");
    section.className = "editor-section";
    section.setAttribute("data-bg", THEME_GRAD.solitude);
    section.style.cssText = SECTION_CSS + " text-align:center;";

    var article = document.createElement("article");
    article.className = "poem-body";
    article.style.cssText = "width:min(680px,92vw); max-height:calc(100vh - var(--header-height, 100px) - 80px); overflow-y:auto; opacity:0; transform:translateY(28px); transition:opacity 0.9s ease, transform 0.9s ease; text-align:center; padding-right:14px;";

    var edH1 = document.createElement("h1");
    edH1.style.cssText = "font-family:'Playfair Display',serif; font-weight:600; font-style:italic; font-size:clamp(32px,5vw,50px); line-height:1.15; margin:0 0 16px 0; color:#f4ede2;";
    edH1.textContent = editor.displayName || "Poet";
    article.appendChild(edH1);

    if (editor.bio) {
      var edBio = document.createElement("div");
      edBio.style.cssText = "font-family:'Jost',sans-serif; font-size:clamp(15px,1.8vw,18px); opacity:0.8; margin-bottom:28px; line-height:1.6; max-width:500px; margin-left:auto; margin-right:auto;";
      edBio.textContent = editor.bio;
      article.appendChild(edBio);
    }
    if (editor.socialLinks) {
      var socialWrap = document.createElement("div");
      socialWrap.style.cssText = "display:flex; gap:12px; justify-content:center; flex-wrap:wrap;";
      var icons = { INSTAGRAM: "Instagram", YOUTUBE: "YouTube", TWITTER: "Twitter/X", FACEBOOK: "Facebook", WEBSITE: "Website" };
      for (var k in editor.socialLinks) {
        if (editor.socialLinks[k]) {
          var a = document.createElement("a");
          a.href = editor.socialLinks[k];
          a.target = "_blank";
          a.style.cssText = "display:inline-flex; align-items:center; padding:8px 16px; border-radius:999px; border:1px solid rgba(244,237,226,0.3); color:#f4ede2; text-decoration:none; font-size:13.5px; opacity:0.85; transition:all 0.2s;";
          a.textContent = icons[k] || k;
          socialWrap.appendChild(a);
        }
      }
      if (socialWrap.children.length > 0) article.appendChild(socialWrap);
    }
    section.appendChild(article);
    return section;
  }

  function buildMessageSection(text) {
    var section = document.createElement("section");
    section.className = "poem-section";
    section.setAttribute("data-bg", THEME_GRAD.solitude);
    section.style.cssText = SECTION_CSS + " text-align:center;";
    var article = document.createElement("article");
    article.className = "poem-body";
    article.style.cssText = "width:min(680px,92vw); opacity:0; transform:translateY(28px); transition:opacity 0.9s ease, transform 0.9s ease;";
    var msg = document.createElement("div");
    msg.style.cssText = "font-family:'Cormorant Garamond',serif; font-size:clamp(18px,2.4vw,24px); opacity:0.7; font-style:italic;";
    msg.textContent = text;
    article.appendChild(msg);
    section.appendChild(article);
    return section;
  }

  // ---------- compact scroll indicator (▲ more above / ▼ more below) ----------
  var upBtn, downBtn;
  function chevron(dirUp) {
    var b = document.createElement("button");
    b.style.cssText = "background:none; border:none; color:#f4ede2; cursor:pointer; padding:2px; display:inline-flex; opacity:0.25; transition:opacity 0.3s;";
    b.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="' + (dirUp ? "m18 15-6-6-6 6" : "m6 9 6 6 6-6") + '"/></svg>';
    b.addEventListener("click", function () { scrollByPoem(dirUp ? -1 : 1); });
    return b;
  }
  function buildIndicator() {
    indicatorEl.innerHTML = "";
    upBtn = chevron(true);
    downBtn = chevron(false);
    indicatorEl.appendChild(upBtn);
    indicatorEl.appendChild(downBtn);
  }
  function sectionH() {
    var s = poemsContainer.querySelector("section");
    return (s && s.offsetHeight) ? s.offsetHeight : scrollContainer.clientHeight;
  }
  function currentSectionIndex() {
    return Math.round(scrollContainer.scrollTop / sectionH());
  }
  function scrollByPoem(dir) {
    var sections = poemsContainer.querySelectorAll("section");
    var target = Math.max(0, Math.min(sections.length - 1, currentSectionIndex() + dir));
    if (sections[target]) sections[target].scrollIntoView({ behavior: "smooth", block: "start" });
  }
  function updateIndicator() {
    if (!upBtn) return;
    var count = poemsContainer.querySelectorAll("section").length;
    var cur = currentSectionIndex();
    var above = cur > 0 || (mode === "feed" && hasPrev);
    var below = cur < count - 1 || (mode === "feed" && hasNext);
    upBtn.style.opacity = above ? "0.9" : "0.15";
    upBtn.style.pointerEvents = above ? "auto" : "none";
    downBtn.style.opacity = below ? "0.9" : "0.15";
    downBtn.style.pointerEvents = below ? "auto" : "none";
  }

  // ---------- intersection observer (reveal + crossfade + prefetch) ----------
  function observeAll() {
    if (observer) observer.disconnect();
    observer = new IntersectionObserver(function (entries) {
      entries.forEach(function (entry) {
        if (!entry.isIntersecting) return;
        var article = entry.target.querySelector("article");
        if (article) { article.style.opacity = "1"; article.style.transform = "translateY(0)"; }
        crossfadeTo(entry.target.getAttribute("data-bg"));
        onSectionVisible(entry.target);
      });
    }, { root: scrollContainer, threshold: 0.35 });
    poemsContainer.querySelectorAll("section").forEach(function (s) { observer.observe(s); });
  }
  function onSectionVisible(sec) {
    updateIndicator();
    var sections = Array.prototype.slice.call(poemsContainer.querySelectorAll("section"));
    var pos = sections.indexOf(sec);
    if (pos === -1) return;
    // prefetch when 3rd from an edge (both feed and poet mode paginate)
    if (pos >= sections.length - 3 && hasNext && !loadingNext) loadNext();
    if (pos <= 2 && hasPrev && !loadingPrev) loadPrev();
  }

  // ---------- window mutations with scroll anchoring ----------
  // Sections are uniform height, so adding/removing above the viewport is
  // corrected by shifting scrollTop by (count * sectionHeight). Snap is paused
  // during the mutation so it doesn't fight the programmatic adjustment.
  function withSnapPaused(fn) {
    var prev = scrollContainer.style.scrollSnapType;
    scrollContainer.style.scrollSnapType = "none";
    fn();
    requestAnimationFrame(function () { scrollContainer.style.scrollSnapType = prev || "y mandatory"; });
  }
  function appendPoems(poems) {
    var frag = document.createDocumentFragment();
    poems.forEach(function (p) { frag.appendChild(buildPoemSection(p)); });
    poemsContainer.appendChild(frag);
    observeAll();
  }
  function firstPoemSection() { return poemsContainer.querySelector(".poem-section"); }
  function lastPoemSection() {
    var list = poemsContainer.querySelectorAll(".poem-section");
    return list.length ? list[list.length - 1] : null;
  }
  // Drop the oldest page (10 poems) from the front. If page 0 is leaving, its
  // editor hero (poet mode) goes with it. scrollTop is shifted down by the exact
  // height removed so the poem you're reading stays put.
  function trimFrontPage() {
    var H = sectionH();
    withSnapPaused(function () {
      var removed = 0;
      if (firstPage === 0) {
        var hero = poemsContainer.querySelector(".editor-section");
        if (hero) { poemsContainer.removeChild(hero); removed++; }
      }
      for (var i = 0; i < PAGE_SIZE; i++) { var s = firstPoemSection(); if (!s) break; poemsContainer.removeChild(s); removed++; }
      scrollContainer.scrollTop -= removed * H;
    });
    firstPage++;
    hasPrev = true;
    observeAll();
  }
  // Drop the newest page from the end (below the viewport — no scroll shift).
  function trimBackPage() {
    for (var i = 0; i < PAGE_SIZE; i++) { var s = lastPoemSection(); if (!s) break; poemsContainer.removeChild(s); }
    lastPage--;
    hasNext = true;
    observeAll();
  }

  // ---------- unified paginated sliding window (feed + poet) ----------
  function fetchPage(page) {
    var base = (mode === "poet") ? "/verses/api/v1/poets/" + encodeURIComponent(slug) : "/verses/api/v1/poems";
    var url = base + "?page=" + page + "&size=" + PAGE_SIZE;
    if (activeTag) url += "&tag=" + encodeURIComponent(activeTag);
    if (query) url += "&q=" + encodeURIComponent(query);
    return fetch(url, { headers: { "Content-Type": "application/json" } })
      .then(function (r) { return r.status === 404 ? null : r.json(); });
  }

  function resetWindow() {
    firstPage = 0; lastPage = -1; hasPrev = false; hasNext = false;
    loadingNext = false; loadingPrev = false;
    poemsContainer.innerHTML = "";
    loadingNext = true;
    return fetchPage(0).then(function (env) {
      loadingNext = false;
      var d = (env && env.success) ? env.data : null;
      if (mode === "poet" && d && d.editor) currentEditor = d.editor;
      var poems = (d && Array.isArray(d.poems)) ? d.poems.map(toPoemView) : [];
      var hasPoet = (mode === "poet" && currentEditor);
      if (!poems.length && !hasPoet) { showEmpty(); updateIndicator(); return; }
      hideEmpty();
      firstPage = 0; lastPage = 0;
      hasNext = !!(d && d.hasNext); hasPrev = false;
      var frag = document.createDocumentFragment();
      if (hasPoet) frag.appendChild(buildEditorSection(currentEditor)); // hero = page 0's header
      poems.forEach(function (p) { frag.appendChild(buildPoemSection(p)); });
      if (!poems.length && hasPoet) {
        frag.appendChild(buildMessageSection((query || activeTag) ? "No matching pieces." : "This poet hasn't published any pieces yet."));
      }
      poemsContainer.appendChild(frag);
      observeAll();
      initFirstVisible();
    }).catch(function () { loadingNext = false; showEmpty(); });
  }

  function loadNext() {
    loadingNext = true;
    fetchPage(lastPage + 1).then(function (env) {
      var d = (env && env.success) ? env.data : null;
      var poems = (d && Array.isArray(d.poems)) ? d.poems.map(toPoemView) : [];
      if (poems.length) {
        lastPage++;
        hasNext = !!(d && d.hasNext);
        appendPoems(poems);
        if (lastPage - firstPage + 1 > 2) trimFrontPage();   // keep ~2 pages (≤20) in the DOM
      } else {
        hasNext = false;
      }
      loadingNext = false;
      updateIndicator();
    }).catch(function () { loadingNext = false; });
  }

  function loadPrev() {
    loadingPrev = true;
    fetchPage(firstPage - 1).then(function (env) {
      var d = (env && env.success) ? env.data : null;
      var poems = (d && Array.isArray(d.poems)) ? d.poems.map(toPoemView) : [];
      if (poems.length) {
        var newFirst = firstPage - 1;
        var H = sectionH();
        var frag = document.createDocumentFragment();
        if (mode === "poet" && newFirst === 0 && currentEditor) frag.appendChild(buildEditorSection(currentEditor));
        poems.forEach(function (p) { frag.appendChild(buildPoemSection(p)); });
        var added = frag.childNodes.length;
        withSnapPaused(function () {
          poemsContainer.insertBefore(frag, poemsContainer.firstChild);
          scrollContainer.scrollTop += added * H;   // keep the current poem in place
        });
        observeAll();
        firstPage = newFirst;
        hasPrev = firstPage > 0;
        if (lastPage - firstPage + 1 > 2) trimBackPage();
      } else {
        hasPrev = false;
      }
      loadingPrev = false;
      updateIndicator();
    }).catch(function () { loadingPrev = false; });
  }

  // ---------- shared helpers ----------
  function showEmpty() {
    emptyState.style.display = "flex";
    var m = emptyState.querySelector("div");
    if (m) m.textContent = (query || activeTag) ? "No poems found — try another word or theme." : "No poems have been published yet.";
  }
  function hideEmpty() { emptyState.style.display = "none"; }
  function initFirstVisible() {
    var first = poemsContainer.querySelector("section");
    if (!first) return;
    showingFront = false;
    currentBg = first.getAttribute("data-bg");
    bgBack.style.background = currentBg;
    bgFront.style.opacity = "0";
    var a = first.querySelector("article");
    if (a) { a.style.opacity = "1"; a.style.transform = "translateY(0)"; }
    updateIndicator();
  }
  function onFilterChanged() {
    // filters change the result set -> rebuild the window from the top
    scrollContainer.scrollTop = 0;
    resetWindow();
  }

  // ---------- search (debounced) ----------
  var searchTimer;
  searchInput.addEventListener("input", function (e) {
    query = e.target.value || "";
    clearTimeout(searchTimer);
    searchTimer = setTimeout(onFilterChanged, 300);
  });

  document.getElementById("reader-close").addEventListener("click", closeReader);
  document.addEventListener("keydown", function (e) { if (e.key === "Escape") closeReader(); });

  // ---------- tags load ----------
  function loadTags() {
    fetch("/verses/api/v1/tags", { headers: { "Content-Type": "application/json" } })
      .then(function (r) { return r.json(); })
      .then(function (env) {
        if (env && env.success && Array.isArray(env.data)) ALL_TAGS = env.data;
        renderTags();
      }).catch(function () { renderTags(); });
  }

  // ---------- splash ----------
  function hideSplash() {
    var splash = document.getElementById("splash-screen");
    if (splash) { splash.style.opacity = "0"; setTimeout(function () { splash.style.visibility = "hidden"; }, 800); }
  }

  // ---------- bootstrap ----------
  buildIndicator();
  loadTags();
  var minWait = new Promise(function (resolve) { setTimeout(resolve, 1000); });
  var firstLoad = resetWindow();
  Promise.all([firstLoad, minWait]).then(hideSplash);
  setTimeout(hideSplash, 6000); // safety net so the splash never sticks
})();
</script>

</body>
</html>
