// ===========================================================
// My Collection, main page.
// Owns the logged-out marketing surface, the empty state, and the
// populated dashboard (hero value, Collection/Wishlist tabs, stats,
// sort/filter, add-record flow). The prototype state switcher lives
// here too. Auth screens + signup gate are rendered by App.
// ===========================================================
const { useState: useMcState, useMemo: useMcMemo, useEffect: useMcEffect, useRef: useMcRef } = React;

function gbp(n) { return "£" + Math.round(n).toLocaleString("en-GB"); }
function condClass(code) { return code === "VG+" ? "VGplus" : (["M", "NM", "VG"].includes(code) ? code : "low"); }

// ---- resolve a stored record into a fully-priced display object ------------
function resolveRecord(rec, albums) {
  const album = (rec.slug
    ? albums.find(a => a.slug === rec.slug)
    : { ...rec.manual, rank: -1, slug: null }) || { ...(rec.manual || {}), rank: -1, slug: rec.slug || null };
  let pressing = rec.pressing;
  if (!pressing && rec.slug) {
    const list = window.BBR_PRESSINGS[rec.slug] || [];
    pressing = list.find(p => p.id === rec.pressingId) || list[0];
  }
  // Fallback so a record with no known pressing never crashes the page.
  if (!pressing) {
    pressing = {
      id: rec.pressingId || "unknown",
      label: (album && album.label) || "Unknown pressing",
      country: "—", year: (album && album.year) || "—",
      catno: "—", format: ["LP"], notes: "", baseNM: 0, lastSold: "—",
      have: 0, want: 0,
    };
  }
  // Guard every field the cards read downstream.
  if (!Array.isArray(pressing.format)) pressing.format = pressing.format ? [pressing.format] : ["LP"];
  if (pressing.baseNM == null) pressing.baseNM = 0;
  if (!pressing.label) pressing.label = "Unknown pressing";
  if (!pressing.lastSold) pressing.lastSold = "—";

  const value = window.BBR_valueOf(pressing.baseNM, rec.condition || "NM");
  // Final safety net: guarantee fields the cards read, even for odd records.
  const trend = rec.trend && Array.isArray(rec.trend.spark)
    ? rec.trend
    : { pct: 0, dir: "flat", spark: Array(12).fill(value) };
  const dateAdded = rec.dateAdded || "";
  const unknownPressing = !!(rec.pressingUnknown || (rec.pressing && rec.pressing.unknown));
  return {
    ...rec, album, pressing, value: unknownPressing ? 0 : value, trend, dateAdded, unknownPressing,
    low: unknownPressing ? 0 : Math.round(value * 0.8), high: unknownPressing ? 0 : Math.round(value * 1.4),
    decade: window.BBR_decadeOf((album && album.year) || 0),
    genre: (album && album.genre) || "Other",
  };
}

// ---- tiny charts -----------------------------------------------------------
function Sparkline({ data, dir, w = 60, h = 22 }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data), max = Math.max(...data), span = (max - min) || 1;
  const pts = data.map((v, i) => [(i / (data.length - 1)) * w, h - 3 - ((v - min) / span) * (h - 6)]);
  const d = "M" + pts.map(p => p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" L");
  const color = dir === "down" ? "oklch(0.55 0.16 28)" : dir === "up" ? "oklch(0.52 0.13 150)" : "var(--ink-mute)";
  const last = pts[pts.length - 1];
  return (
    <svg width={w} height={h} className="spk">
      <path d={d} fill="none" stroke={color} strokeWidth="1.4" strokeLinejoin="round" strokeLinecap="round" />
      <circle cx={last[0]} cy={last[1]} r="2" fill={color} />
    </svg>
  );
}

function Delta({ pct, dir }) {
  const arrow = dir === "down" ? "▼" : dir === "up" ? "▲" : "→";
  const sign = pct > 0 ? "+" : "";
  return <span className={"mc-delta " + dir}><span className="mc-arrow">{arrow}</span>{sign}{pct.toFixed(1)}%</span>;
}

function TrendChart({ history, current }) {
  const data = history.map((p, i) => i === history.length - 1 ? { ...p, v: current } : p);
  const W = 600, H = 190, pad = 10;
  const vals = data.map(d => d.v);
  const min = Math.min(...vals) * 0.9, max = Math.max(...vals) * 1.05, span = (max - min) || 1;
  const x = i => pad + (i / (data.length - 1)) * (W - pad * 2);
  const y = v => H - pad - ((v - min) / span) * (H - pad * 2);
  const line = data.map((d, i) => (i ? "L" : "M") + x(i).toFixed(1) + "," + y(d.v).toFixed(1)).join(" ");
  const area = line + ` L${x(data.length - 1).toFixed(1)},${H - pad} L${x(0).toFixed(1)},${H - pad} Z`;
  const lastX = x(data.length - 1), lastY = y(current);
  return (
    <div className="mc-trend">
      <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ height: 190 }}>
        <defs>
          <linearGradient id="mcArea" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.18" />
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
          </linearGradient>
        </defs>
        {[0.25, 0.5, 0.75].map(f => <line key={f} x1={pad} x2={W - pad} y1={pad + f * (H - pad * 2)} y2={pad + f * (H - pad * 2)} stroke="var(--rule)" strokeWidth="1" />)}
        <path d={area} fill="url(#mcArea)" />
        <path d={line} fill="none" stroke="var(--accent)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" />
        <circle cx={lastX} cy={lastY} r="4" fill="var(--accent)" stroke="var(--paper)" strokeWidth="1.5" />
      </svg>
      <div className="mc-trend-foot">
        {data.filter((_, i) => i % 2 === 0 || i === data.length - 1).map((d, i) => <span key={i}>{d.m}</span>)}
      </div>
    </div>
  );
}

function Bars({ rows, accentIndex }) {
  const max = Math.max(...rows.map(r => r.v), 1);
  return (
    <div className="mc-break">
      {rows.map((r, i) => (
        <div className="mc-bk-row" key={r.k}>
          <div className="mc-bk-label">{r.k}</div>
          <div className="mc-bk-track"><div className={"mc-bk-fill" + (i === accentIndex ? " accent" : "")} style={{ width: (r.v / max * 100) + "%" }} /></div>
          <div className="mc-bk-val">{gbp(r.v)}</div>
        </div>
      ))}
    </div>
  );
}

function AnimatedMoney({ value }) {
  const [disp, setDisp] = useMcState(value);
  const prev = useMcRef(value);
  const raf = useMcRef(0);
  useMcEffect(() => {
    cancelAnimationFrame(raf.current);
    const from = prev.current, to = value, t0 = performance.now(), dur = 650;
    const tick = (t) => {
      const p = Math.min(1, (t - t0) / dur);
      const e = 1 - Math.pow(1 - p, 3);
      setDisp(Math.round(from + (to - from) * e));
      if (p < 1) raf.current = requestAnimationFrame(tick); else prev.current = to;
    };
    raf.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf.current);
  }, [value]);
  return <>{disp.toLocaleString("en-GB")}</>;
}

// ---- record card -----------------------------------------------------------
function RecordCard({ r, onRemove, onIdentify }) {
  return (
    <div className={"mc-card" + (r.unknownPressing ? " mc-card-needspressing" : "")}>
      <div className="mc-card-actions">
        <button className="mc-iconbtn danger" title="Remove" onClick={() => onRemove(r.id)}>
          <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 3.5h10M5 3.5V2h4v1.5M3.5 3.5l.6 8.5h5.8l.6-8.5"/></svg>
        </button>
      </div>
      <div className="mc-card-cover"><Sleeve album={r.album} size={76} /></div>
      <div className="mc-card-body">
        <div className="mc-card-top">
          <div>
            <div className="mc-card-title">{r.album.title}</div>
            <div className="mc-card-artist">{r.album.artist}</div>
          </div>
          {r.unknownPressing
            ? <span className="mc-cond mc-cond-unknown">No pressing</span>
            : <span className={"mc-cond " + condClass(r.condition)}>{r.condition}</span>}
        </div>
        {r.unknownPressing ? (
          <div className="mc-needspressing">
            <div className="mc-needspressing-msg">
              <b>Pressing not identified.</b> Tell us which pressing you own and we'll value it and add it to your collection total.
            </div>
            <div className="mc-needspressing-actions">
              <button className="mc-np-btn" onClick={() => onIdentify && onIdentify(r)}>Add pressing to value it</button>
              <button className="mc-np-link" onClick={() => window.__nav && window.__nav.goPressingGuide && window.__nav.goPressingGuide()}>How to identify it →</button>
            </div>
          </div>
        ) : (
          <>
            <div className="mc-card-meta">
              <b>{r.pressing.label}</b> · {r.pressing.country} · {r.pressing.year}<br />
              {r.pressing.format.join(" · ")}{r.pressing.catno && r.pressing.catno !== "—" ? " · " + r.pressing.catno : ""}
            </div>
            <div className="mc-card-foot">
              <div>
                <div className="mc-card-val"><span className="cur">£</span><AnimatedMoney value={r.value} /></div>
                {r.pressing.fromDiscogs
                  ? <div className="mc-card-range">Market low · {typeof r.pressing.numForSale === "number" && r.pressing.numForSale > 0 ? r.pressing.numForSale.toLocaleString("en-GB") + " for sale" : "via Discogs"}</div>
                  : <div className="mc-card-range">Estimated value</div>}
              </div>
              {r.pressing.fromDiscogs && (typeof r.pressing.have === "number" || typeof r.pressing.want === "number") && (r.pressing.have || r.pressing.want) ? (
                <div className="mc-card-community">
                  <div className="mc-cc-stat"><span className="mc-cc-num">{(r.pressing.have || 0).toLocaleString("en-GB")}</span><span className="mc-cc-label">own it</span></div>
                  <div className="mc-cc-stat"><span className="mc-cc-num">{(r.pressing.want || 0).toLocaleString("en-GB")}</span><span className="mc-cc-label">wishlist</span></div>
                </div>
              ) : null}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function WishCard({ r, onBuy, onMove, onRemove, liveDelta, moved }) {
  // Grail Watch (Part 7): prefer the real VG+ valuation when the repricer has
  // written one; otherwise fall back to the static estimate (no regression).
  const wishVal = (typeof r.est_value === "number") ? r.est_value : r.value;
  const hasReal = typeof r.est_value === "number";
  // Buy link + the retailer it actually drives to, so the "via …" line matches
  // the destination (Rough Trade / Amazon) rather than the item's static vendor.
  const buyHref = (window.BBR_buyUrl && window.BBR_buyUrl(r.album.title + " " + r.album.artist, r.album.slug)) || "#";
  const buyVia = (window.BBR_retailerFor && window.BBR_retailerFor(buyHref)) || r.vendor || "Amazon UK";
  return (
    <div className={"mc-card wish" + (moved ? " wish-moved" : "")}>
      <div className="mc-card-actions">
        <button className="mc-iconbtn danger" title="Remove" onClick={() => onRemove(r.id)}>
          <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 3.5h10M5 3.5V2h4v1.5M3.5 3.5l.6 8.5h5.8l.6-8.5"/></svg>
        </button>
      </div>
      <div className="mc-card-cover"><Sleeve album={r.album} size={76} /></div>
      <div className="mc-card-body">
        <div className="mc-card-top">
          <div>
            <div className="mc-card-title">{r.album.title}</div>
            <div className="mc-card-artist">{r.album.artist} · {r.album.year}</div>
          </div>
          {r.album.rank > 0 && <span className="mc-chip">№ {String(r.album.rank).padStart(3, "0")}</span>}
        </div>
        <div className="mc-card-meta">
          <b>{r.pressing.label}</b> · {r.pressing.country} · {r.pressing.year} · {r.pressing.format.join(" · ")}
          {moved && <span className="wish-moved-tag">Moved this week</span>}
        </div>
        <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", marginTop: 12 }}>
          <div>
            <div className="mc-card-val"><span className="cur">£</span><AnimatedMoney value={wishVal} /></div>
            {hasReal
              ? <div className="mc-card-range">est. value at VG+</div>
              : r.pressing.fromDiscogs
                ? <div className="mc-card-range">Market low{typeof r.pressing.numForSale === "number" && r.pressing.numForSale > 0 ? " · " + r.pressing.numForSale.toLocaleString("en-GB") + " for sale" : ""}</div>
                : <div className="mc-card-range">Estimated value</div>}
            {typeof liveDelta === "number" && (
              <div style={{ marginTop: 6 }}>
                {Math.abs(liveDelta) < 1
                  ? <span className="mc-delta flat" title="From live valuations over the last 30 days">No change</span>
                  : <span className={"mc-delta " + (liveDelta > 0 ? "up" : "down")} title="From live valuations over the last 30 days">
                      <span className="mc-arrow">{liveDelta > 0 ? "▲" : "▼"}</span>£{Math.abs(liveDelta).toLocaleString("en-GB")}
                    </span>}
              </div>
            )}
          </div>
          {r.pressing.fromDiscogs && (r.pressing.have || r.pressing.want) ? (
            <div className="mc-card-community">
              <div className="mc-cc-stat"><span className="mc-cc-num">{(r.pressing.have || 0).toLocaleString("en-GB")}</span><span className="mc-cc-label">own it</span></div>
              <div className="mc-cc-stat"><span className="mc-cc-num">{(r.pressing.want || 0).toLocaleString("en-GB")}</span><span className="mc-cc-label">wishlist</span></div>
            </div>
          ) : null}
        </div>
        <div className="mc-card-foot">
          <div className="mc-buy-row">
            {/* Wishlist card. "Got it" is the honest half of this row and it stays;
                the retailer link is hidden while commerce is off. See
                window.BBR_COMMERCE_ENABLED in data/collection_sample.js. */}
            {window.BBR_COMMERCE_ENABLED ? (
              <a className="mc-buy" href={buyHref} target="_blank" rel="noopener" onClick={() => window.BBR_trackClick && window.BBR_trackClick(r.album.slug, "collection")}>
                Buy on vinyl <span className="ar">→</span>
              </a>
            ) : null}
            <button className="mc-move" onClick={() => onMove(r)} title="Mark as purchased">Got it</button>
          </div>
          <div className="mc-vendor">via {buyVia} · prices via Discogs</div>
        </div>
      </div>
    </div>
  );
}

// ===========================================================
//  POPULATED / EMPTY APP (re-seeds when dataMode changes via key)
// ===========================================================
function CollectionApp({ albums, dataMode, onRequireAuth, openAddRef }) {
  const store = window.BBR_store;
  const [, forceMc] = useMcState(0);
  useMcEffect(() => store.subscribe(() => forceMc((n) => n + 1)), []);
  const records = store.collection;
  const wishlist = store.wishlist;
  const [tab, setTab] = useMcState("collection");
  const [search, setSearch] = useMcState("");
  const [sortKey, setSortKey] = useMcState("value");
  const [filterGenre, setFilterGenre] = useMcState("all");
  const [add, setAdd] = useMcState(null); // {intent, preset, fromWishlistId}

  // expose opener so the marketing/empty CTAs + nav can trigger it
  useMcEffect(() => { if (openAddRef) openAddRef.current = (intent) => setAdd({ intent: intent || "collection" }); }, []);

  const resolved = useMcMemo(() => records.map(r => resolveRecord(r, albums)), [records, albums]);
  const resolvedWish = useMcMemo(() => wishlist.map(r => resolveRecord(r, albums)), [wishlist, albums]);

  const total = resolved.reduce((s, r) => s + r.value, 0);
  const needsPressingCount = resolved.filter(r => r.unknownPressing).length;
  const wishTotal = resolvedWish.reduce((s, r) => s + r.value, 0);
  const mostValuable = resolved.reduce((m, r) => (!m || r.value > m.value ? r : m), null);
  const avg = resolved.length ? Math.round(total / resolved.length) : 0;
  const latest = resolved.reduce((m, r) => (!m || r.dateAdded > m.dateAdded ? r : m), null);

  const genres = useMcMemo(() => Array.from(new Set(resolved.map(r => r.genre))).sort(), [resolved]);

  const view = useMcMemo(() => {
    let list = resolved.filter(r => {
      if (filterGenre !== "all" && r.genre !== filterGenre) return false;
      if (search && !((r.album.title + " " + r.album.artist).toLowerCase().includes(search.toLowerCase()))) return false;
      return true;
    });
    const cmp = {
      value: (a, b) => b.value - a.value,
      artist: (a, b) => a.album.artist.localeCompare(b.album.artist),
      year: (a, b) => a.album.year - b.album.year,
      added: (a, b) => b.dateAdded.localeCompare(a.dateAdded),
      condition: (a, b) => window.BBR_CONDITIONS.findIndex(c => c.code === a.condition) - window.BBR_CONDITIONS.findIndex(c => c.code === b.condition),
    }[sortKey];
    return list.slice().sort(cmp);
  }, [resolved, filterGenre, search, sortKey]);

  const wishView = useMcMemo(() => {
    const cmp = {
      value: (a, b) => b.value - a.value,
      added: (a, b) => b.dateAdded.localeCompare(a.dateAdded),
      ranking: (a, b) => (a.album.rank > 0 ? a.album.rank : 999) - (b.album.rank > 0 ? b.album.rank : 999),
    }[sortKey === "artist" || sortKey === "year" || sortKey === "condition" ? "value" : sortKey] || ((a, b) => b.value - a.value);
    return resolvedWish.slice().sort(cmp);
  }, [resolvedWish, sortKey]);

  // breakdowns
  const byDecade = useMcMemo(() => {
    const m = {};
    resolved.forEach(r => { m[r.decade] = (m[r.decade] || 0) + r.value; });
    return Object.entries(m).map(([k, v]) => ({ k, v })).sort((a, b) => a.k.localeCompare(b.k));
  }, [resolved]);
  const byGenre = useMcMemo(() => {
    const m = {};
    resolved.forEach(r => { m[r.genre] = (m[r.genre] || 0) + r.value; });
    return Object.entries(m).map(([k, v]) => ({ k, v })).sort((a, b) => b.v - a.v);
  }, [resolved]);

  function handleAdd(rec) {
    if (add.intent === "wishlist") {
      store.addToWishlist(rec);
    } else {
      store.addToCollection(rec);
      if (add.fromWishlistId) store.removeFromWishlist(add.fromWishlistId);
      if (add.replaceId) store.removeFromCollection(add.replaceId);
    }
    setAdd(null);
  }
  function moveToCollection(wr) {
    setAdd({ intent: "move", fromWishlistId: wr.id, preset: { album: wr.album, pressing: wr.pressing } });
  }
  // Re-open the add flow for a record whose pressing is unknown, so the user can
  // choose one. On finish we remove the old unidentified record and add the new.
  function identifyPressing(r) {
    setAdd({ intent: "collection", replaceId: r.id, preset: { album: r.album, pressing: null } });
  }

  const empty = resolved.length === 0 && tab === "collection";
  const wishEmpty = resolvedWish.length === 0 && tab === "wishlist";

  return (
    <div className="mc">
      {/* HERO */}
      <div className="mc-hero">
        <div>
          <div className="mc-hero-label">
            <span className="mc-eyebrow">Estimated collection value</span>
          </div>
          <div className="mc-hero-total"><span className="cur">£</span><AnimatedMoney value={total} /></div>
          {total > 0 ? (
            <>
              <div className="mc-hero-sub">
                <span className="mc-chip">{resolved.length} records</span>
                <span className="mc-chip">Avg {gbp(avg)}</span>
                <span className="mc-chip">+{gbp(43)} this month</span>
              </div>
              <div className="mc-hero-milestone">
                <div className="mc-milestone-bar"><div className="mc-milestone-fill" style={{ width: Math.min(100, total / 1500 * 100) + "%" }} /></div>
                <div className="mc-milestone-cap"><span>Next milestone</span><span>£1,500</span></div>
              </div>
            </>
          ) : (
            <div className="mc-hero-sub"><span className="mc-chip">Add your first record to begin</span></div>
          )}
        </div>
        {total > 0 && (
          <div className="mc-hero-stats">
            <div className="mc-stat">
              <div className="mc-stat-k">Most valuable</div>
              <div className="mc-stat-v">{gbp(mostValuable.value)}<small>{mostValuable.album.title}</small></div>
            </div>
            <div className="mc-stat">
              <div className="mc-stat-k">Latest addition</div>
              <div className="mc-stat-v" style={{ fontStyle: "italic" }}>{latest.album.title}<small style={{ fontStyle: "normal" }}>{latest.album.artist}</small></div>
            </div>
            <div className="mc-stat">
              <div className="mc-stat-k">Records</div>
              <div className="mc-stat-v">{resolved.length}<small>across {byGenre.length} genres</small></div>
            </div>
            <div className="mc-stat">
              <div className="mc-stat-k">Wishlist</div>
              <div className="mc-stat-v">{gbp(wishTotal)}<small>{resolvedWish.length} records wanted</small></div>
            </div>
          </div>
        )}
      </div>

      {/* TABS + CONTROLS */}
      <div className="mc-bar">
        <div className="mc-tabs">
          <button className={"mc-tab " + (tab === "collection" ? "active" : "")} onClick={() => setTab("collection")}>Collection<span className="ct">{resolved.length}</span></button>
          <button className={"mc-tab " + (tab === "wishlist" ? "active" : "")} onClick={() => setTab("wishlist")}>Wishlist<span className="ct">{resolvedWish.length}</span></button>
        </div>
        <div className="mc-controls">
          {tab === "collection" && (
            <div className="mc-search">
              <svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="7" cy="7" r="5" /><path d="M11 11l3.5 3.5" /></svg>
              <input placeholder="Search your collection…" value={search} onChange={e => setSearch(e.target.value)} />
            </div>
          )}
          {tab === "collection" && (
            <div className="mc-select">
              <select value={filterGenre} onChange={e => setFilterGenre(e.target.value)}>
                <option value="all">All genres</option>
                {genres.map(g => <option key={g} value={g}>{g}</option>)}
              </select>
            </div>
          )}
          <div className="mc-select">
            <select value={sortKey} onChange={e => setSortKey(e.target.value)}>
              {tab === "collection" ? (
                <>
                  <option value="value">Sort: Value</option>
                  <option value="artist">Sort: Artist</option>
                  <option value="year">Sort: Year</option>
                  <option value="added">Sort: Date added</option>
                  <option value="condition">Sort: Condition</option>
                </>
              ) : (
                <>
                  <option value="value">Sort: Price</option>
                  <option value="added">Sort: Date added</option>
                  <option value="ranking">Sort: Ranking</option>
                </>
              )}
            </select>
          </div>
          <button className="mc-add" onClick={() => setAdd({ intent: tab === "wishlist" ? "wishlist" : "collection" })}>
            <span className="plus">+</span> {tab === "wishlist" ? "Add to wishlist" : "Add record"}
          </button>
        </div>
      </div>

      {/* WISHLIST TOTAL */}
      {tab === "wishlist" && resolvedWish.length > 0 && (
        <div style={{ display: "flex", alignItems: "baseline", gap: 14, marginBottom: 18 }}>
          <span className="mc-eyebrow">Estimated to complete</span>
          <span className="mc-money" style={{ fontSize: 26 }}>{gbp(wishTotal)}</span>
        </div>
      )}

      {/* GRID */}
      {empty && (
        <div className="mc-empty">
          <div className="mc-empty-stack">
            {[1, 5, 8].map((rank, i) => <div key={rank} style={{ "--r": (i - 1) * 6 + "deg" }}><Sleeve album={albums.find(a => a.rank === rank)} size={92} /></div>)}
          </div>
          <h3>Your shelf is empty</h3>
          <p>Log the records you own to see what your collection is worth. Start with your favourites from the Hundred, or add anything manually.</p>
          <div className="mc-empty-cta">
            <button className="mc-btn solid" onClick={() => setAdd({ intent: "collection" })}><span style={{ fontSize: 14 }}>+</span> Add your first record</button>
            <button className="mc-btn ghost" onClick={() => window.__nav && window.__nav.goList()}>Browse the List 100→1</button>
          </div>
        </div>
      )}
      {wishEmpty && (
        <div className="mc-empty">
          <h3>Nothing on the wishlist yet</h3>
          <p>Queue up the records you're hunting for. We'll track the price and give you a one-tap path to buy on vinyl.</p>
          <div className="mc-empty-cta">
            <button className="mc-btn solid" onClick={() => setAdd({ intent: "wishlist" })}><span style={{ fontSize: 14 }}>+</span> Add to wishlist</button>
          </div>
        </div>
      )}

      {tab === "collection" && !empty && needsPressingCount > 0 && (
        <div className="mc-np-banner">
          <span className="mc-np-banner-dot">?</span>
          <span className="mc-np-banner-text">
            {needsPressingCount === 1
              ? "1 record needs a pressing before it can be valued."
              : `${needsPressingCount} records need a pressing before they can be valued.`}
            {" "}Identify them to add their value to your collection total.
          </span>
          <button className="mc-np-banner-link" onClick={() => window.__nav && window.__nav.goPressingGuide && window.__nav.goPressingGuide()}>
            Identification guide →
          </button>
        </div>
      )}

      {tab === "collection" && !empty && (
        <div className="mc-grid">
          {view.map(r => <RecordCard key={r.id} r={r} onRemove={(id) => store.removeFromCollection(id)} onIdentify={identifyPressing} />)}
        </div>
      )}
      {tab === "wishlist" && !wishEmpty && (
        <div className="mc-grid">
          {wishView.map(r => <WishCard key={r.id} r={r} onMove={moveToCollection} onRemove={(id) => store.removeFromWishlist(id)} />)}
        </div>
      )}

      {/* DASHBOARD */}
      {tab === "collection" && resolved.length > 1 && (
        <div className="mc-dash">
          <div className="mc-dash-head">
            <h3>The numbers</h3>
            <span className="mc-eyebrow">Updated live as you grade &amp; add</span>
          </div>
          <div className="mc-dash-grid">
            <div className="mc-panel">
              <div className="mc-panel-k">Collection value · last 12 months</div>
              <TrendChart history={window.BBR_VALUE_HISTORY} current={total} />
            </div>
            <div className="mc-panel">
              <div className="mc-panel-k">Where the value sits</div>
              <Bars rows={byGenre} accentIndex={0} />
              <div style={{ height: 22 }} />
              <div className="mc-panel-k">By decade</div>
              <Bars rows={byDecade} accentIndex={-1} />
            </div>
          </div>
          <div className="mc-tiles">
            <div className="mc-tile">
              <div className="cv"><Sleeve album={mostValuable.album} size={64} /></div>
              <div>
                <div className="mc-tile-k">Crown jewel</div>
                <div className="mc-tile-t">{mostValuable.album.title}</div>
                <div className="mc-tile-s">{mostValuable.condition} · {gbp(mostValuable.value)} · {mostValuable.pressing.label} {mostValuable.pressing.year}</div>
              </div>
            </div>
            <div className="mc-tile">
              <div className="cv"><Sleeve album={latest.album} size={64} /></div>
              <div>
                <div className="mc-tile-k">Latest addition</div>
                <div className="mc-tile-t">{latest.album.title}</div>
                <div className="mc-tile-s">Added {latest.dateAdded} · {gbp(latest.value)}</div>
              </div>
            </div>
          </div>
          <p className="add-pr-attr" style={{ marginTop: 18, justifyContent: "flex-start" }}>
            <svg width="11" height="11" 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>
            Valuations are median sale prices via Discogs for each specific pressing &amp; grade.
          </p>
        </div>
      )}

      {add && (
        <AddRecordFlow
          albums={albums}
          intent={add.intent}
          preset={add.preset}
          onAdd={handleAdd}
          onClose={() => setAdd(null)}
        />
      )}
    </div>
  );
}

// ===========================================================
//  MARKETING SURFACE (logged out)
//  A dynamic pitch for the CURRENT My Collection feature set: a
//  live-feeling collector terminal in the hero, then an animated
//  showcase of what a signed-in shelf gets — Collection DNA, value
//  tracked over time, snap-to-add, one-tap import, Your Next Record,
//  and Grail Watch. Visuals are decorative (aria-hidden); the copy
//  carries the message for screen readers.
// ===========================================================

// A compact sparkline for a light feature card (distinct from the dark
// hero HeroSpark). Wipes in via CSS once its card reveals.
function MktSpark() {
  const pts = [20, 24, 22, 30, 28, 38, 44, 42, 54, 60, 72, 86];
  const W = 200, H = 46, max = 96;
  const x = (i) => (i / (pts.length - 1)) * W, y = (v) => H - (v / max) * H;
  const line = pts.map((v, i) => (i ? "L" : "M") + x(i).toFixed(1) + "," + y(v).toFixed(1)).join(" ");
  const lx = x(pts.length - 1), ly = y(pts[pts.length - 1]);
  return (
    <div className="mkt-sc-spark" aria-hidden="true">
      <svg viewBox={"0 0 " + W + " " + H} preserveAspectRatio="none">
        <defs><linearGradient id="mktSpFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.26" />
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0" />
        </linearGradient></defs>
        <path d={line + " L" + W + "," + H + " L0," + H + " Z"} fill="url(#mktSpFill)" />
        <path className="mkt-sc-spark-line" d={line} fill="none" stroke="var(--accent)" strokeWidth="2.2" strokeLinejoin="round" strokeLinecap="round" vectorEffect="non-scaling-stroke" />
      </svg>
      <span className="mkt-sc-spark-dot" style={{ left: (lx / W * 100) + "%", top: (ly / H * 100) + "%" }} />
    </div>
  );
}

// Mini Collection-DNA fingerprint (genre bars) for the showcase card.
function MktDnaViz() {
  const rows = [["Rock", 88], ["Soul / R&B", 60], ["Jazz", 40], ["Hip-Hop", 26]];
  return (
    <div className="mkt-sc-dna" aria-hidden="true">
      {rows.map((r, i) => (
        <div className="mkt-sc-dna-row" key={r[0]}>
          <span className="mkt-sc-dna-l">{r[0]}</span>
          <span className="mkt-sc-dna-t"><i style={{ "--w": r[1] + "%", transitionDelay: (0.15 + i * 0.09) + "s" }} /></span>
        </div>
      ))}
    </div>
  );
}

function MarketingSurface({ albums, onSignup, onLogin, onGate }) {
  // ?invite=1 only tailors the landing copy (a friend sent the link); it
  // carries no identity, and the page works the same as a cold visit without it.
  const invited = (() => {
    try { return new URLSearchParams(location.search).get("invite") === "1"; }
    catch (e) { return false; }
  })();
  const Ticker = window.HdTicker;

  // Scroll-reveal for the showcase: each card fades + rises as it enters view,
  // adopting the same .in vocabulary the hero dash uses. Falls back to visible.
  const rootRef = useMcRef(null);
  useMcEffect(() => {
    const root = rootRef.current;
    if (!root) return;
    const els = Array.prototype.slice.call(root.querySelectorAll(".mkt-reveal"));
    if (!els.length) return;
    if (!("IntersectionObserver" in window)) { els.forEach((e) => e.classList.add("in")); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((en) => { if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); } });
    }, { rootMargin: "0px 0px -10% 0px", threshold: 0.12 });
    els.forEach((e) => io.observe(e));
    return () => io.disconnect();
  }, []);

  // The six headline features of the current My Collection, each with a small
  // live-feeling visual. `kind` selects the illustration rendered on the card.
  const FEATURES = [
    { kind: "dna", tag: "Collection DNA", h: "Read your taste back to you", p: "We fingerprint your shelf — era, genres, span — and write it up in a paragraph. It shifts as your collection grows." },
    { kind: "value", tag: "Live valuations", h: "Watch every record move", p: "Each pressing is priced at its median market sale, graded for condition, and charted week to week — not a frozen Discogs number." },
    { kind: "snap", tag: "Snap to add", h: "Point your camera at a sleeve", p: "Photograph a cover and we identify the record, match the pressing, and drop it into your shelf. Or import your whole Discogs export in one tap." },
    { kind: "next", tag: "Your Next Record", h: "Know what to hunt next", p: "A pick chosen from the gaps in your collection and the canon you're missing — with the price to watch before you buy." },
    { kind: "grail", tag: "Grail Watch", h: "Catch the rare ones", p: "The most valuable records on your shelf, flagged and tracked, so you know which sleeves are quietly climbing." },
    { kind: "board", tag: "Leaderboards", h: "See where you rank", p: "Go public and your collection climbs the collector leaderboard — by value, by canon owned, by the rarest find." },
  ];

  function FeatureViz({ kind }) {
    if (kind === "dna") return <MktDnaViz />;
    if (kind === "value") return (
      <div className="mkt-sc-value">
        <div className="mkt-sc-value-top"><span className="mkt-sc-value-v">£1,240</span><span className="mkt-sc-value-d">&#9650; 12%</span></div>
        <MktSpark />
      </div>
    );
    if (kind === "snap") return (
      <div className="mkt-sc-scan" aria-hidden="true">
        <img src="/covers/nevermind.jpg" alt="" loading="lazy" draggable="false" />
        <span className="mkt-sc-scan-line" />
        <b className="c tl" /><b className="c tr" /><b className="c bl" /><b className="c br" />
      </div>
    );
    if (kind === "next") return (
      <div className="mkt-sc-next" aria-hidden="true">
        <div className="mkt-sc-next-cv"><img src="/covers/blue.jpg" alt="" loading="lazy" draggable="false" /><span className="mkt-sc-shine" /></div>
        <div className="mkt-sc-next-meta"><span className="mkt-sc-next-k">Suggested for you</span><span className="mkt-sc-next-t">Blue</span><span className="mkt-sc-next-add">+ add to wishlist</span></div>
      </div>
    );
    if (kind === "grail") return (
      <div className="mkt-sc-grail" aria-hidden="true">
        <div className="mkt-sc-grail-cv"><img src="/covers/purple-rain.jpg" alt="" loading="lazy" draggable="false" /><span className="mkt-sc-grail-badge">&#9733;</span></div>
        <div className="mkt-sc-grail-meta"><span className="mkt-sc-next-k">Grail on your shelf</span><span className="mkt-sc-grail-v">£420 <i>&#9650; £38</i></span></div>
      </div>
    );
    // board
    return (
      <div className="mkt-sc-board" aria-hidden="true">
        {[["1", "Marcus R.", "£4,120"], ["2", "Dev P.", "£3,880"], ["4", "You", "£1,240", true]].map((r) => (
          <div className={"mkt-sc-board-row" + (r[3] ? " you" : "")} key={r[0]}>
            <span className="mkt-sc-board-rk">{r[0]}</span><span className="mkt-sc-board-nm">{r[1]}</span><span className="mkt-sc-board-v">{r[2]}</span>
          </div>
        ))}
      </div>
    );
  }

  return (
    <div className="mkt" ref={rootRef}>
      {Ticker ? <Ticker /> : null}
      {window.GuestShelf ? <GuestShelf albums={albums} onSignup={onSignup} /> : null}
      <div className="mkt-hero">
        <div>
          <div className="mkt-eyebrow">My Collection · free for readers</div>
          <h1 className="mkt-h1">{invited
            ? <React.Fragment>Your records deserve a <em>proper home</em>.</React.Fragment>
            : <React.Fragment>Give your records a <em>pulse</em>.</React.Fragment>}</h1>
          <p className="mkt-lede">{invited
            ? "A friend thinks you're ready. Track your vinyl, watch its value move, read your Collection DNA, and see where you rank. Sign in to start."
            : "Value your whole shelf, watch it move week to week, read your Collection DNA, and see where you sit against the hundred greatest records ever pressed."}</p>
          <div className="mkt-cta">
            <button className="mc-btn solid" onClick={onSignup}>Create a free account</button>
            <button className="mc-btn ghost" onClick={onLogin}>Sign in</button>
          </div>
          <div className="mkt-fine">No cost · takes a minute · import from Discogs in one tap</div>
        </div>
        <div className="mkt-preview">
          <div className="mkt-dash in" aria-hidden="true">
            <div className="hd-dash-top">
              <div>
                <div className="hd-dash-val"><span className="cur">£</span>{window.HdCountUp ? <HdCountUp value={1240} /> : "1,240"}</div>
                <div className="hd-dash-cap">estimated collection value</div>
              </div>
              <span className="hd-dash-delta">&#9650; 12% <small>this week</small></span>
            </div>
            {window.HeroSpark ? <HeroSpark /> : null}
            <div className="hd-dash-mover">
              <img className="hd-dash-mover-cv" src="/covers/dark-side-of-the-moon.jpg" alt="" loading="lazy" draggable="false" />
              <span className="hd-dash-mover-info">
                <span className="hd-dash-mover-k">Biggest mover this week</span>
                <span className="hd-dash-mover-t">The Dark Side of the Moon</span>
              </span>
              <span className="hd-dash-mover-move">&#9650; &pound;9</span>
            </div>
            <div className="hd-dash-prog">
              <div className="hd-dash-prog-row"><span>Canon owned</span><span>23 / 100</span></div>
              <div className="hd-dash-bar"><i style={{ "--w": "23%" }} /></div>
            </div>
            <div className="hd-dash-dna-k">Collection DNA</div>
            <div className="hd-gbars">
              {[["Rock", "88%"], ["Soul / R&B", "60%"], ["Jazz", "40%"], ["Hip-Hop", "28%"]].map((row, i) => (
                <div className="hd-gbar" key={row[0]}>
                  <span className="hd-gbar-l">{row[0]}</span>
                  <span className="hd-gbar-t"><i style={{ "--w": row[1], transitionDelay: (i * 80) + "ms" }} /></span>
                </div>))}
            </div>
            <div className="hd-dash-dna-note">1970s heavy &middot; 57 years deep &middot; 7 genres</div>
            <div className="hd-dash-rank"><span className="hd-pulse" /> Ranked 4th on the leaderboard</div>
          </div>
          <button className="mkt-dash-foot mkt-dash-foot-solo" onClick={onGate}>Sign up to value your own shelf &rarr;</button>
        </div>
      </div>

      {/* Feature showcase: the current My Collection feature set, animated in. */}
      <div className="mkt-feat">
        <div className="mkt-feat-head mkt-reveal">
          <div className="mkt-feat-kick">Everything a tracked shelf gets</div>
          <h2 className="mkt-feat-title">More than a list. A living portrait of your collection.</h2>
        </div>
        <div className="mkt-showcase">
          {FEATURES.map((f, i) => (
            <div className={"mkt-sc-card mkt-reveal is-" + f.kind} key={f.kind} style={{ transitionDelay: (i % 3) * 0.08 + "s" }}>
              <div className="mkt-sc-visual"><FeatureViz kind={f.kind} /></div>
              <div className="mkt-sc-body">
                <div className="mkt-sc-tag">{f.tag}</div>
                <h3 className="mkt-sc-h">{f.h}</h3>
                <p className="mkt-sc-p">{f.p}</p>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* Closing CTA band. */}
      <div className="mkt-band mkt-reveal">
        <h2 className="mkt-band-h">Start your collection in a minute.</h2>
        <p className="mkt-band-p">Free for readers. Add your first record, snap a sleeve, or import from Discogs — and watch it come alive.</p>
        <div className="mkt-cta mkt-band-cta">
          <button className="mc-btn solid" onClick={onSignup}>Create a free account</button>
          <button className="mc-btn ghost" onClick={onLogin}>Sign in</button>
        </div>
      </div>
    </div>
  );
}

// ===========================================================
//  PAGE WRAPPER
// ===========================================================
function MyCollection({ albums, user, dataMode, onAuthOpen, onGate }) {
  if (!user) {
    return <MarketingSurface albums={albums} onSignup={() => onAuthOpen("signup")} onLogin={() => onAuthOpen("login")} onGate={() => onGate("collection")} />;
  }
  // v2: verified collection (collection_items) + public leaderboards.
  return <CollectionAppV2 albums={albums} user={user} onGate={onGate} />;
}

Object.assign(window, { MyCollection });
