/* Enhanced sections for the Marvin Gaye, What's Going On dossier.
   Each component is conditionally rendered by AlbumPage when the matching
   richer data exists on the essay object. */

const { useState: useWState, useEffect: useWEffect, useMemo: useWMemo } = React;

/* -------------------------------------------------
   1. PressingLedger, dynamic collector's corner
   ------------------------------------------------- */
function PressingLedger({ pressings, affiliates }) {
  const [filter, setFilter] = useWState("all"); // all | buy | avoid
  const [activeId, setActiveId] = useWState(pressings[0].id);
  const [sortBy, setSortBy] = useWState("rank"); // rank | sound | rarity | value

  const filtered = useWMemo(() => {
    let list = [...pressings];
    if (filter !== "all") list = list.filter(p => p.verdict === filter);
    if (sortBy === "sound") list.sort((a, b) => b.sound - a.sound);
    if (sortBy === "rarity") list.sort((a, b) => b.rarity - a.rarity);
    if (sortBy === "value") list.sort((a, b) => b.value - a.value);
    return list;
  }, [filter, sortBy, pressings]);

  const active = pressings.find(p => p.id === activeId) || pressings[0];

  return (
    <div className="ledger">
      <div className="ledger-controls">
        <div className="ledger-filters">
          {[
            { k: "all", n: "All pressings" },
            { k: "buy", n: "Worth buying" },
            { k: "avoid", n: "Skip" }
          ].map(f => (
            <button key={f.k} className={filter === f.k ? "lf active" : "lf"} onClick={() => setFilter(f.k)}>{f.n}</button>
          ))}
        </div>
        <div className="ledger-sort">
          <span>Sort by</span>
          {[
            { k: "rank", n: "Grail order" },
            { k: "sound", n: "Sound" },
            { k: "rarity", n: "Rarity" },
            { k: "value", n: "Value/$" }
          ].map(s => (
            <button key={s.k} className={sortBy === s.k ? "ls active" : "ls"} onClick={() => setSortBy(s.k)}>{s.n}</button>
          ))}
        </div>
      </div>

      <div className="ledger-grid">
        <ul className="ledger-list">
          {filtered.map(p => {
            const isActive = p.id === activeId;
            return (
              <li key={p.id} className={"lrow " + (isActive ? "active " : "") + "v-" + p.verdict}
                  onClick={() => setActiveId(p.id)}>
                <div className="lrow-top">
                  <span className="lrow-year">{p.year}</span>
                  <span className="lrow-country">{p.country}</span>
                  <span className={"lrow-verdict v-" + p.verdict}>{p.verdict === "buy" ? "BUY" : "SKIP"}</span>
                  <span className="lrow-rating">{"★".repeat(Math.round(p.rating / 2))}<span className="o">{"★".repeat(5 - Math.round(p.rating / 2))}</span></span>
                </div>
                <div className="lrow-title">{p.title}</div>
                <div className="lrow-rank">{p.rank}</div>
              </li>
            );
          })}
        </ul>

        <div className="ledger-detail">
          <div className="ld-top">
            <div className="ld-rank">{active.rank}</div>
            <h4>{active.title}</h4>
            <div className="ld-meta">
              <span><b>Format</b>{active.format}</span>
              <span><b>Plant</b>{active.plant}</span>
              <span><b>Matrix</b><code>{active.matrix}</code></span>
            </div>
          </div>

          <div className="ld-marks">
            <h5>Identifying marks</h5>
            <ul>{active.marks.map((m, i) => <li key={i}>{m}</li>)}</ul>
          </div>

          <div className="ld-bars">
            <Bar label="Sound quality" pct={active.sound} />
            <Bar label="Rarity" pct={active.rarity} />
            <Bar label="Value for money" pct={active.value} />
          </div>

          <div className="ld-values">
            {Object.entries(active.values).map(([k, v]) => (
              <div className="ldv" key={k}>
                <span className="ldv-k">{k === "vgplus" ? "VG+" : k.toUpperCase()}</span>
                <span className="ldv-v">£{v}</span>
              </div>
            ))}
          </div>

          <p className="ld-note">{active.note}</p>
        </div>
      </div>

      <button
        className="ledger-guide-link"
        onClick={() => window.__nav && window.__nav.goPressingGuide && window.__nav.goPressingGuide()}>
        <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="6" cy="6" r="5"/><path d="M6 5.5v3M6 3.6h.01"/></svg>
        Not sure which pressing you own? Read the identification guide
      </button>
    </div>
  );
}

function Bar({ label, pct }) {
  return (
    <div className="bar-row">
      <span className="bar-label">{label}</span>
      <div className="bar-track"><span style={{ width: pct + "%" }} /></div>
      <span className="bar-pct">{pct}</span>
    </div>
  );
}

/* -------------------------------------------------
   2. ChartsVisual, bar chart + sparkline + timeline
   ------------------------------------------------- */
function ChartsVisual({ peakData, salesMilestones, globalSales, datapoint, released }) {
  const maxShown = 80; // visualize up to #80; anything lower clips but labels show truth

  // Pull the primary "Month YYYY" the album was released, from the (variable)
  // released string, e.g. "August 5, 1966 (UK) · ..." -> "August 1966".
  function enteredMonth(str) {
    if (!str) return null;
    const m = str.match(/(January|February|March|April|May|June|July|August|September|October|November|December)\s+(?:\d{1,2},\s*)?(\d{4})/);
    return m ? (m[1] + " " + m[2]) : null;
  }
  const entered = enteredMonth(released);

  return (
    <div className="charts-visual">
      <div className="cv-bars">
        <div className="cv-bar-head">
          <span>Peak position by market</span>
          {entered && <span className="cv-entered">Released {entered}</span>}
        </div>
        {peakData.map((d, i) => {
          const pct = Math.max(4, 100 - (Math.min(d.peak, maxShown) / maxShown) * 100);
          return (
            <div className="cv-bar" key={i}>
              <span className="cv-flag">{d.country}</span>
              <span className="cv-market">{d.market}</span>
              <div className="cv-bar-track">
                <span className="cv-bar-fill" style={{ width: pct + "%" }} />
                <span className="cv-peak">№{d.peak}</span>
              </div>
              <span className="cv-runline">
                {d.weeks ? <span className="cv-weeks">{d.weeks} wks on chart</span> : null}
                {d.cert ? <span className="cv-cert">{d.cert}</span> : null}
              </span>
            </div>
          );
        })}
      </div>

      <div className="cv-milestones">
        <div className="cv-milestones-head">Commercial arc</div>
        <ol className="cv-ml">
          {salesMilestones.map((m, i) => (
            <li key={i}>
              <span className="ml-year">{m.year}</span>
              <span className="ml-dot" />
              <span className="ml-event">{m.events}</span>
            </li>
          ))}
        </ol>
      </div>

      <div className="cv-footer">
        <div>
          <b>Global sales</b>
          <p>{globalSales}</p>
        </div>
        <div>
          <b>One interesting data point</b>
          <p>{datapoint}</p>
        </div>
      </div>
    </div>
  );
}




/* -------------------------------------------------
   3. SceneGrid, making-of illustrated vignettes
   ------------------------------------------------- */
function SceneGrid({ scenes }) {
  return (
    <div className="scenes">
      {scenes.map((s, i) => (
        <article className={"scene scene-" + s.key} key={s.key}>
          <div className="scene-art">
            {s.imageUrl ? (
              <div className="scene-photo">
                <img
                  src={s.imageUrl}
                  alt={s.headline || ""}
                  loading="lazy"
                  style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
                />
                <span className="ga-ref-flag" aria-label="Reference image, not licensed">REF</span>
                {s.imageCredit && (
                  <span
                    style={{
                      position: "absolute", left: 0, right: 0, bottom: 0,
                      padding: "4px 7px", fontSize: "9px", lineHeight: 1.3,
                      fontFamily: "var(--mono, monospace)", letterSpacing: "0.02em",
                      color: "rgba(255,255,255,0.9)",
                      background: "linear-gradient(to top, rgba(0,0,0,0.62), rgba(0,0,0,0))",
                      pointerEvents: "none",
                    }}
                  >{s.imageCredit}</span>
                )}
              </div>
            ) : (
              <SceneArt kind={s.key} />
            )}
          </div>
          <div className="scene-body">
            <div className="scene-sub">{s.sub}</div>
            <h4>{s.headline}</h4>
            <p>{s.body}</p>
          </div>
        </article>
      ))}
    </div>
  );
}

// CSS/SVG illustrations, editorial-style, no external images
function SceneArt({ kind }) {
  if (kind === "hitsville") {
    return (
      <svg viewBox="0 0 400 260" preserveAspectRatio="xMidYMid slice">
        <rect width="400" height="260" fill="#e9d6aa" />
        {/* floor line */}
        <rect x="0" y="200" width="400" height="60" fill="#3a322a" />
        {/* house */}
        <rect x="90" y="80" width="220" height="130" fill="#f3ede1" stroke="#17130f" strokeWidth="2" />
        <polygon points="90,80 200,30 310,80" fill="#7a2424" stroke="#17130f" strokeWidth="2" />
        {/* sign */}
        <rect x="130" y="100" width="140" height="34" fill="#17130f" />
        <text x="200" y="122" fontSize="13" fontFamily="Inter" fontWeight="800" fill="#f3ede1" textAnchor="middle" letterSpacing="2">HITSVILLE U.S.A.</text>
        {/* windows */}
        <rect x="120" y="150" width="36" height="48" fill="#f9e6b4" stroke="#17130f" strokeWidth="1.5" />
        <rect x="244" y="150" width="36" height="48" fill="#f9e6b4" stroke="#17130f" strokeWidth="1.5" />
        {/* door */}
        <rect x="182" y="158" width="36" height="52" fill="#17130f" />
        <circle cx="210" cy="185" r="1.5" fill="#d97757" />
        {/* lamppost */}
        <line x1="340" y1="210" x2="340" y2="130" stroke="#17130f" strokeWidth="2" />
        <circle cx="340" cy="126" r="6" fill="#f9e6b4" stroke="#17130f" strokeWidth="1.5" />
        {/* night glow */}
        <circle cx="340" cy="126" r="24" fill="#f9e6b4" opacity="0.3" />
      </svg>
    );
  }
  if (kind === "triplevocal") {
    return (
      <svg viewBox="0 0 400 260" preserveAspectRatio="xMidYMid slice">
        <rect width="400" height="260" fill="#17130f" />
        {/* three stacked waveforms */}
        {[70, 130, 190].map((y, i) => (
          <g key={i} transform={`translate(40, ${y})`}>
            <line x1="0" y1="0" x2="320" y2="0" stroke="#3a322a" strokeWidth="1" />
            <path
              d={Array.from({ length: 60 }).map((_, j) => {
                const x = (j / 59) * 320;
                const h = Math.sin((j + i * 7) * 0.6) * (8 + (i * 3)) + Math.cos(j * 1.3 + i) * 6;
                return `M${x} ${-Math.abs(h)} L${x} ${Math.abs(h)}`;
              }).join(" ")}
              stroke={i === 0 ? "#d97757" : i === 1 ? "#f3ede1" : "#b39c6e"}
              strokeWidth="2"
              opacity={i === 1 ? 1 : 0.85}
            />
            <text x="-6" y="4" fontSize="9" fontFamily="JetBrains Mono" fill="#8a8072" textAnchor="end" letterSpacing="2">TR {3 + i * 2}</text>
          </g>
        ))}
        <text x="40" y="235" fontSize="10" fontFamily="JetBrains Mono" fill="#8a8072" letterSpacing="3">MARVIN × 3 · AMPEX 8-TRACK</text>
      </svg>
    );
  }
  if (kind === "floor-bass") {
    return (
      <svg viewBox="0 0 400 260" preserveAspectRatio="xMidYMid slice">
        <rect width="400" height="260" fill="#2d3a4a" />
        {/* floor tiles */}
        <g opacity="0.25">
          {Array.from({ length: 10 }).map((_, i) => (
            <line key={i} x1="0" y1={40 + i * 24} x2="400" y2={40 + i * 24} stroke="#f3ede1" strokeWidth="0.5" />
          ))}
        </g>
        {/* bass guitar laid flat */}
        <g transform="translate(60, 140) rotate(-8)">
          <rect x="0" y="0" width="240" height="30" rx="4" fill="#3a2a1a" stroke="#17130f" strokeWidth="1.5" />
          <rect x="0" y="-4" width="24" height="38" rx="2" fill="#6b4a2a" stroke="#17130f" strokeWidth="1.5" />
          <circle cx="170" cy="15" r="10" fill="#17130f" />
          {/* strings */}
          {[8, 13, 18, 22].map((y, i) => (
            <line key={i} x1="24" y1={y} x2="230" y2={y} stroke="#b39c6e" strokeWidth="0.8" />
          ))}
        </g>
        {/* ghost figure lying */}
        <g transform="translate(100, 100)" opacity="0.6">
          <ellipse cx="80" cy="10" rx="20" ry="14" fill="none" stroke="#f3ede1" strokeWidth="1.5" strokeDasharray="3 3" />
          <path d="M60 10 L40 60 L0 80" fill="none" stroke="#f3ede1" strokeWidth="1.5" strokeDasharray="3 3" />
          <path d="M100 10 L130 50" fill="none" stroke="#f3ede1" strokeWidth="1.5" strokeDasharray="3 3" />
        </g>
        <text x="200" y="245" fontSize="10" fontFamily="JetBrains Mono" fill="#f3ede1" opacity="0.6" textAnchor="middle" letterSpacing="3">TAKE 1 · ONE TAKE ONLY</text>
      </svg>
    );
  }
  if (kind === "gordy-no") {
    return (
      <svg viewBox="0 0 400 260" preserveAspectRatio="xMidYMid slice">
        <rect width="400" height="260" fill="#f3ede1" />
        {/* phone cord */}
        <path d="M80 40 Q120 90 90 130 T110 200" stroke="#17130f" strokeWidth="1.5" fill="none" />
        {/* receiver top */}
        <g transform="translate(50, 30) rotate(-15)">
          <rect x="0" y="0" width="60" height="28" rx="10" fill="#17130f" />
          <circle cx="12" cy="14" r="4" fill="#d97757" />
          <circle cx="48" cy="14" r="4" fill="#d97757" />
        </g>
        {/* speech bubble */}
        <g transform="translate(150, 60)">
          <path d="M0 20 Q0 0 20 0 L210 0 Q230 0 230 20 L230 100 Q230 120 210 120 L60 120 L30 155 L40 120 L20 120 Q0 120 0 100 Z"
                fill="#17130f" />
          <text x="115" y="44" fontSize="15" fontFamily="DM Serif Display" fontStyle="italic" fill="#f3ede1" textAnchor="middle">&ldquo;The worst record</text>
          <text x="115" y="66" fontSize="15" fontFamily="DM Serif Display" fontStyle="italic" fill="#f3ede1" textAnchor="middle">I've ever heard</text>
          <text x="115" y="88" fontSize="15" fontFamily="DM Serif Display" fontStyle="italic" fill="#f3ede1" textAnchor="middle">in my life.&rdquo;</text>
          <text x="115" y="108" fontSize="9" fontFamily="JetBrains Mono" fill="#d97757" textAnchor="middle" letterSpacing="3">, BERRY GORDY</text>
        </g>
      </svg>
    );
  }
  // Generic neutral placeholder for scene keys without a custom illustration
  // (used when an essay has scenes but no imageUrl has been wired yet).
  return (
    <svg viewBox="0 0 400 260" preserveAspectRatio="xMidYMid slice" aria-label="Reference image pending">
      <rect width="400" height="260" fill="#1d1812"/>
      <g opacity="0.18" stroke="#f3ede1" strokeWidth="1">
        <line x1="0" y1="0" x2="400" y2="260"/>
        <line x1="400" y1="0" x2="0" y2="260"/>
        <rect x="0.5" y="0.5" width="399" height="259" fill="none"/>
      </g>
      <text x="200" y="135" fontSize="11" fontFamily="JetBrains Mono" fill="#8a8072" textAnchor="middle" letterSpacing="3">REFERENCE PENDING</text>
    </svg>
  );
}
function CommentsSection({ slug }) {
  const [comments, setComments] = useWState([]);
  const [votes, setVotes] = useWState({});       // { comment_id: netScore }
  const [myVotes, setMyVotes] = useWState({});   // { comment_id: 1 | -1 }
  const [loading, setLoading] = useWState(true);
  const [me, setMe] = useWState(null);
  const [draftBody, setDraftBody] = useWState("");
  const [draftRating, setDraftRating] = useWState(5);
  const [posting, setPosting] = useWState(false);
  const [replyTo, setReplyTo] = useWState(null);   // comment id being replied to
  const [replyBody, setReplyBody] = useWState("");
  const cfBodyRef = React.useRef(null);

  useWEffect(() => {
    let active = true;
    if (window.BBR_auth && window.BBR_auth.current) {
      window.BBR_auth.current().then((u) => { if (active) setMe(u || null); });
    }
    const off = window.BBR_auth && window.BBR_auth.onChange
      ? window.BBR_auth.onChange((u) => setMe(u || null))
      : null;
    return () => { active = false; if (typeof off === "function") off(); };
  }, []);

  async function loadAll() {
    if (!window.BBR_supabase) { setLoading(false); return; }
    try {
      const { data: cData, error: cErr } = await window.BBR_supabase
        .from("comments").select("*").eq("slug", slug)
        .order("created_at", { ascending: true });
      if (cErr) throw cErr;
      const list = cData || [];
      setComments(list);

      // Votes for these comments
      const ids = list.map(c => c.id);
      const tally = {};
      const mine = {};
      if (ids.length) {
        const { data: vData } = await window.BBR_supabase
          .from("comment_votes").select("comment_id, value, user_id").in("comment_id", ids);
        (vData || []).forEach(v => {
          tally[v.comment_id] = (tally[v.comment_id] || 0) + v.value;
          if (me && v.user_id === me.id) mine[v.comment_id] = v.value;
        });
      }
      setVotes(tally);
      setMyVotes(mine);
    } catch (e) {
      console.error("[BBR] load comments failed", e);
    } finally {
      setLoading(false);
    }
  }
  useWEffect(() => { loadAll(); }, [slug, me && me.id]);

  useWEffect(() => {
    const h = (ev) => {
      const t = ev && ev.detail && ev.detail.text;
      if (!t) return;
      setDraftBody(prev => (prev && prev.trim()) ? prev : ("On the question, “" + t + "”\n\n"));
      setTimeout(() => { if (cfBodyRef.current) cfBodyRef.current.focus(); }, 80);
    };
    window.addEventListener("bbr-prefill-comment", h);
    return () => window.removeEventListener("bbr-prefill-comment", h);
  }, []);

  // Ratings only count top-level comments (replies don't carry a rating).
  const topLevel = comments.filter(c => !c.parent_id);
  const repliesByParent = {};
  comments.filter(c => c.parent_id).forEach(r => {
    (repliesByParent[r.parent_id] = repliesByParent[r.parent_id] || []).push(r);
  });

  const allRatings = topLevel.map(c => c.rating).filter(r => typeof r === "number" && r > 0);
  const avg = allRatings.length > 0 ? (allRatings.reduce((a, b) => a + b, 0) / allRatings.length) : 0;
  const distribution = [5, 4, 3, 2, 1].map(n => ({ stars: n, count: allRatings.filter(r => r === n).length }));
  const total = allRatings.length;

  function requireMe() {
    if (!me) { if (window.__bbrRequireAuth) window.__bbrRequireAuth("comment"); return false; }
    return true;
  }

  async function submitComment() {
    const body = draftBody.trim();
    if (!body) return;
    if (!requireMe()) return;
    setPosting(true);
    try {
      const row = { slug, user_id: me.id, name: me.name || "Member", body, rating: draftRating, parent_id: null };
      const { data, error } = await window.BBR_supabase.from("comments").insert(row).select().single();
      if (error) throw error;
      setComments(prev => [...prev, data]);
      setDraftBody(""); setDraftRating(5);
    } catch (e) { console.error("[BBR] post failed", e); }
    finally { setPosting(false); }
  }

  async function submitReply(parentId) {
    const body = replyBody.trim();
    if (!body) return;
    if (!requireMe()) return;
    setPosting(true);
    try {
      const row = { slug, user_id: me.id, name: me.name || "Member", body, rating: null, parent_id: parentId };
      const { data, error } = await window.BBR_supabase.from("comments").insert(row).select().single();
      if (error) throw error;
      setComments(prev => [...prev, data]);
      setReplyBody(""); setReplyTo(null);
    } catch (e) { console.error("[BBR] reply failed", e); }
    finally { setPosting(false); }
  }

  async function vote(commentId, value) {
    if (!requireMe()) return;
    const existing = myVotes[commentId] || 0;
    const next = existing === value ? 0 : value; // tapping the same arrow clears it
    // optimistic update
    setMyVotes(prev => ({ ...prev, [commentId]: next }));
    setVotes(prev => ({ ...prev, [commentId]: (prev[commentId] || 0) - existing + next }));
    try {
      if (next === 0) {
        await window.BBR_supabase.from("comment_votes")
          .delete().eq("comment_id", commentId).eq("user_id", me.id);
      } else {
        await window.BBR_supabase.from("comment_votes")
          .upsert({ comment_id: commentId, user_id: me.id, value: next }, { onConflict: "comment_id,user_id" });
      }
    } catch (e) {
      console.error("[BBR] vote failed", e);
      loadAll(); // resync on failure
    }
  }

  function whenLabel(ts) {
    if (!ts) return "just now";
    const d = new Date(ts);
    const diff = (Date.now() - d.getTime()) / 1000;
    if (diff < 60) return "just now";
    if (diff < 3600) return Math.floor(diff / 60) + "m ago";
    if (diff < 86400) return Math.floor(diff / 3600) + "h ago";
    if (diff < 604800) return Math.floor(diff / 86400) + "d ago";
    return d.toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" });
  }

  function VoteBar({ c }) {
    const score = votes[c.id] || 0;
    const mv = myVotes[c.id] || 0;
    return (
      <span className="c-votes">
        <button className={"c-vote up" + (mv === 1 ? " on" : "")} onClick={() => vote(c.id, 1)} aria-label="Thumbs up">
          <svg width="14" height="14" viewBox="0 0 24 24" fill={mv === 1 ? "currentColor" : "none"} stroke="currentColor" strokeWidth="2"><path d="M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3zm0 0l5-7a2 2 0 0 1 2 2v3h5a2 2 0 0 1 2 2.4l-1.5 7A2 2 0 0 1 16.5 21H7"/></svg>
        </button>
        <span className={"c-score" + (score > 0 ? " pos" : score < 0 ? " neg" : "")}>{score}</span>
        <button className={"c-vote down" + (mv === -1 ? " on" : "")} onClick={() => vote(c.id, -1)} aria-label="Thumbs down">
          <svg width="14" height="14" viewBox="0 0 24 24" fill={mv === -1 ? "currentColor" : "none"} stroke="currentColor" strokeWidth="2"><path d="M17 14V3h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-3zm0 0l-5 7a2 2 0 0 1-2-2v-3H5a2 2 0 0 1-2-2.4l1.5-7A2 2 0 0 1 7.5 3H17"/></svg>
        </button>
      </span>
    );
  }

  function CommentItem({ c, isReply }) {
    return (
      <li className={isReply ? "c c-reply-item" : "c"}>
        <div className="c-avatar" style={{ background: avatarColor(c.name || "Member") }}>{initials(c.name || "Member")}</div>
        <div className="c-body">
          <div className="c-top">
            <span className="c-name">{c.name || "Member"}</span>
            {typeof c.rating === "number" && c.rating > 0 && (
              <span className="c-stars">{"★".repeat(c.rating)}<span className="o">{"★".repeat(5 - c.rating)}</span></span>
            )}
            <span className="c-when">{whenLabel(c.created_at)}</span>
          </div>
          <p>{c.body}</p>
          <div className="c-actions">
            <VoteBar c={c} />
            {!isReply && (
              <button className="c-replybtn" onClick={() => { if (!requireMe()) return; setReplyTo(replyTo === c.id ? null : c.id); setReplyBody(""); }}>
                Reply
              </button>
            )}
          </div>
          {!isReply && replyTo === c.id && (
            <div className="c-replybox">
              <textarea placeholder={"Reply to " + (c.name || "Member") + "…"} value={replyBody}
                        onChange={(e) => setReplyBody(e.target.value)} rows="3" />
              <div className="c-replybox-actions">
                <button className="c-replycancel" onClick={() => { setReplyTo(null); setReplyBody(""); }}>Cancel</button>
                <button className="cf-submit" onClick={() => submitReply(c.id)} disabled={!replyBody.trim() || posting}>
                  {posting ? "Posting…" : "Post reply"}
                </button>
              </div>
            </div>
          )}
          {!isReply && (repliesByParent[c.id] || []).length > 0 && (
            <ul className="c-replies">
              {repliesByParent[c.id].map(r => <CommentItem key={r.id} c={r} isReply={true} />)}
            </ul>
          )}
        </div>
      </li>
    );
  }

  // Newest top-level first
  const ordered = [...topLevel].reverse();

  return (
    <div className="comments">
      <div className="comments-top">
        <div className="rating-aggregate">
          <div className="ra-big">{total ? avg.toFixed(1) : "—"}</div>
          <div className="ra-side">
            <div className="ra-stars">
              {[1,2,3,4,5].map(n => (
                <span key={n} className={avg >= n - 0.25 ? "ra-star on" : (avg >= n - 0.75 ? "ra-star half" : "ra-star")}>★</span>
              ))}
            </div>
            <div className="ra-meta">Based on {total} reader rating{total === 1 ? "" : "s"}</div>
          </div>
        </div>
        <div className="rating-dist">
          {distribution.map(d => {
            const pct = total ? (d.count / total) * 100 : 0;
            return (
              <div className="rd-row" key={d.stars}>
                <span className="rd-stars">{d.stars}★</span>
                <div className="rd-bar"><span style={{ width: pct + "%" }} /></div>
                <span className="rd-count">{d.count}</span>
              </div>
            );
          })}
        </div>
      </div>

      <div className="comments-head" id="comments-list-head">
        <h4>Reader discussion</h4>
        <span>{loading ? "loading…" : (topLevel.length + " comment" + (topLevel.length === 1 ? "" : "s") + " · newest first")}</span>
      </div>

      {!loading && topLevel.length === 0 && (
        <div className="comments-empty"><p>No comments yet. Be the first to make the case.</p></div>
      )}

      <ul className="comments-list">
        {ordered.map((c) => <CommentItem key={c.id} c={c} isReply={false} />)}
      </ul>

      <div className="comment-form" id="comment-form">
        <h5>Add to the conversation</h5>
        {me ? (
          <>
            <div className="cf-asyou">Posting as <b>{me.name}</b></div>
            <div className="cf-rating">
              <span>Your rating:</span>
              {[1,2,3,4,5].map(n => (
                <button key={n} onClick={() => setDraftRating(n)} className={n <= draftRating ? "cf-star on" : "cf-star"}>★</button>
              ))}
            </div>
            <textarea placeholder="Say something thoughtful. Be specific, cite timestamps, disagree well."
                      ref={cfBodyRef} value={draftBody} onChange={(e) => setDraftBody(e.target.value)}
                      className="cf-body" rows="4" />
            <div className="cf-actions">
              <span className="cf-note">Be specific. Cite timestamps. Disagree well.</span>
              <button className="cf-submit" onClick={submitComment} disabled={!draftBody.trim() || posting}>
                {posting ? "Posting…" : "Post comment"}
              </button>
            </div>
          </>
        ) : (
          <div className="cf-signin">
            <p>Sign in to join the discussion. Comments are tied to your account.</p>
            <button className="cf-submit" onClick={() => window.__bbrRequireAuth && window.__bbrRequireAuth("comment")}>
              Sign in to comment
            </button>
          </div>
        )}
      </div>
    </div>
  );
}


function avatarColor(name) {
  const colors = ["#d97757", "#2d5d3a", "#7a2424", "#2d3a4a", "#a56300", "#3a322a"];
  let h = 0;
  for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
  return colors[h % colors.length];
}
function initials(name) {
  return name.split(/\s+/).slice(0, 2).map(s => s[0] || "").join("").toUpperCase();
}

// Export to window
Object.assign(window, {
  PressingLedger, ChartsVisual, SceneGrid, CommentsSection
});
