// My Collection, add-record flow.
// Stepped modal: (1) search the Hundred or add manually, (2) pick the exact
// pressing from Discogs-shaped data, (3) grade the condition (Goldmine).
// Used for adding to collection, adding to wishlist, and "move to collection".
const { useState: useAddState, useMemo: useAddMemo, useEffect: useAddEffect } = React;

function pressingsForAlbum(album) {
  if (!album) return [];
  if (album.slug && window.BBR_PRESSINGS[album.slug]) return window.BBR_PRESSINGS[album.slug];
  return window.BBR_GENERIC_PRESSINGS(album);
}

function fmtGBP(n) { return "£" + Math.round(n).toLocaleString("en-GB"); }

// Normalise a catalogue/matrix string for loose comparison.
function norm(s) { return (s || "").toString().toLowerCase().replace(/[^a-z0-9]/g, ""); }

// A sentinel pressing for "I don't know yet". Carries zero value so it never
// contributes to the collection total, and a flag the collection view reads to
// prompt the user to come back and identify it.
const UNKNOWN_PRESSING = {
  id: "unknown",
  unknown: true,
  label: "Pressing not identified",
  country: "—",
  year: "—",
  catno: "—",
  format: ["LP"],
  notes: "",
  baseNM: 0,
  lastSold: "—",
  have: 0,
  want: 0,
};

function PressingValue({ baseNM, intent, pending }) {
  // wishlist shows current median (NM); collection shows the NM reference.
  // A picker candidate arrives unpriced (baseNM null) and is priced on tap —
  // "£NaN" is what this rendered for that case before the P4 picker existed.
  return (
    <div className="add-pr-val">
      <div className="num">{typeof baseNM === "number" ? fmtGBP(baseNM) : (pending ? "…" : "—")}</div>
      <div className="lbl">{intent === "wishlist" ? "Median" : "NM median"}</div>
    </div>
  );
}

function AddRecordFlow({ albums, intent = "collection", preset = null, onAdd, onClose }) {
  // step: 'search' | 'manual' | 'pressing' | 'condition'
  const needsCondition = intent !== "wishlist";
  const [step, setStep] = useAddState(preset ? "pressing" : "search");
  const [query, setQuery] = useAddState("");
  const [album, setAlbum] = useAddState(preset ? preset.album : null);
  const [pressing, setPressing] = useAddState(preset ? preset.pressing || null : null);
  const [condition, setCondition] = useAddState("NM");
  const [manual, setManual] = useAddState({ title: "", artist: "", year: "", genre: "Rock" });

  // "Every record ever made" search — MusicBrainz-backed, debounced.
  const [catalogResults, setCatalogResults] = useAddState([]);
  const [catalogBusy, setCatalogBusy] = useAddState(false);
  const [catalogNote, setCatalogNote] = useAddState("");
  const [pricing, setPricing] = useAddState(false); // fetching Discogs price on select
  const [catalogPressing, setCatalogPressing] = useAddState(null); // ranked Discogs candidates (array) for a catalog pick — the P4 picker
  const catalogTimer = React.useRef(null);

  // Full-screen sheet on mobile: lock the page behind it. iOS Safari ignores
  // body{overflow:hidden} (the page still pans, dragging the fixed layer so the
  // header — and its × close — scroll out of reach), so pin the body at
  // -scrollY and restore on close. Same pattern as the v2 add/import/detail modals.
  useAddEffect(() => {
    const scrollY = window.scrollY || window.pageYOffset || 0;
    const b = document.body;
    const prev = {
      position: b.style.position, top: b.style.top, left: b.style.left,
      right: b.style.right, width: b.style.width, overflow: b.style.overflow,
    };
    b.style.position = "fixed";
    b.style.top = "-" + scrollY + "px";
    b.style.left = "0"; b.style.right = "0"; b.style.width = "100%"; b.style.overflow = "hidden";
    return () => {
      b.style.position = prev.position; b.style.top = prev.top;
      b.style.left = prev.left; b.style.right = prev.right;
      b.style.width = prev.width; b.style.overflow = prev.overflow;
      window.scrollTo(0, scrollY);
    };
  }, []);

  // Matrix / runout pressing finder
  const [matrixOpen, setMatrixOpen] = useAddState(false);
  const [matrixInput, setMatrixInput] = useAddState("");
  const [matrixBusy, setMatrixBusy] = useAddState(false);
  const [matrixNote, setMatrixNote] = useAddState("");
  const [matrixResults, setMatrixResults] = useAddState([]);

  async function runMatrix() {
    const m = matrixInput.trim();
    if (!m || !album) return;
    setMatrixBusy(true);
    setMatrixNote("");
    setMatrixResults([]);
    try {
      const q = encodeURIComponent(album.artist + " " + album.title);
      const matrix = encodeURIComponent(m);
      const r = await fetch(`/api/identify?q=${q}&matrix=${matrix}`);
      const j = await r.json();
      const cands = j.candidates || [];
      setMatrixResults(cands);
      if (!cands.length) {
        // Nothing matched among this album's pressings. Most likely the runout
        // is from a different record, or it isn't catalogued on Discogs.
        if (j.checked > 0) {
          setMatrixNote(`That runout doesn't match any known pressing of "${album.title}". Double-check you've read it correctly, or that it's from this record and not another in your stack.`);
        } else {
          setMatrixNote(j.note || "No match found. Try checking your transcription, or pick a pressing manually below.");
        }
      } else {
        setMatrixNote(j.note || "");
      }
    } catch (e) {
      setMatrixNote("Couldn't reach the identifier just now. Try again, or pick a pressing manually.");
    } finally {
      setMatrixBusy(false);
    }
  }

  // When the user picks a Discogs candidate, see if it maps to one of our known
  // pressings (by catalogue number or year); if so select that. Otherwise build
  // a pressing object from the Discogs details so the collection shows the right
  // copy. Discogs-sourced pressings carry no internal value estimate.
  function pickDiscogs(c) {
    const known = pressings.find(
      (p) =>
        (c.catno && p.catno && norm(p.catno) === norm(c.catno)) ||
        (c.year && String(p.year) === String(c.year) && p.country && c.country && p.country.toLowerCase().slice(0, 2) === c.country.toLowerCase().slice(0, 2))
    );
    if (known) {
      setPressing({ ...known, fromMatrix: true, discogsId: c.id });
    } else {
      const base = (typeof c.lowestPrice === "number" && c.lowestPrice > 0) ? Math.round(c.lowestPrice) : 0;
      setPressing({
        id: "discogs-" + c.id,
        label: c.label || "Unknown label",
        country: c.country || "—",
        year: c.year || "—",
        catno: c.catno || "—",
        format: c.format ? c.format.split(", ") : ["LP"],
        notes: base > 0 ? "Identified via Discogs. Value from current Discogs market price." : "Identified from matrix / runout via Discogs",
        baseNM: base,
        lastSold: "—",
        have: 0, want: c.numForSale || 0,
        discogsId: c.id,
        fromMatrix: true,
        priceSource: base > 0 ? "discogs" : null,
      });
    }
    setMatrixResults([]);
    setMatrixNote("Pressing selected below. Review and continue.");
  }

  const results = useAddMemo(() => {
    const q = query.trim().toLowerCase();
    const list = albums.slice().sort((a, b) => a.rank - b.rank);
    if (!q) return list.slice(0, 60);
    return list.filter(a => (a.title + " " + a.artist).toLowerCase().includes(q)).slice(0, 60);
  }, [query, albums]);

  // Debounced search across "every record ever made" (MusicBrainz). Runs
  // alongside the curated filter above; results show under a separate heading.
  // We only fire once the user has typed something meaningful, and we wait
  // 350ms after they stop typing so we're not hammering the API per keystroke.
  useAddEffect(() => {
    const q = query.trim();
    if (catalogTimer.current) clearTimeout(catalogTimer.current);
    if (q.length < 3) { setCatalogResults([]); setCatalogBusy(false); setCatalogNote(""); return; }
    setCatalogBusy(true);
    catalogTimer.current = setTimeout(() => {
      fetch("/api/search?q=" + encodeURIComponent(q))
        .then(r => r.json())
        .then(j => {
          // Drop catalog hits that duplicate a curated album already shown.
          const curatedKeys = new Set(albums.map(a => (a.title + "|" + a.artist).toLowerCase()));
          const filtered = (j.results || []).filter(
            x => !curatedKeys.has((x.title + "|" + x.artist).toLowerCase())
          );
          setCatalogResults(filtered);
          setCatalogNote(filtered.length ? "" : (j.note || "no matches"));
          setCatalogBusy(false);
        })
        .catch(() => { setCatalogResults([]); setCatalogBusy(false); setCatalogNote("search unavailable"); });
    }, 350);
    return () => { if (catalogTimer.current) clearTimeout(catalogTimer.current); };
  }, [query, albums]);

  // Selecting a catalog (non-curated) record: fetch its live Discogs price and
  // ranked pressing candidates, and drop into the normal flow.
  async function chooseCatalog(rec) {
    setPricing(true);
    let price = null, cover = rec.coverUrl || null, country = null, label = null, format = null, catno = null, year = rec.year;
    let have = null, want = null, numForSale = null;
    // Hoisted: the candidate list below is built OUTSIDE the try, and a fetch
    // failure must degrade to "no candidates", not a ReferenceError.
    let j = null;
    try {
      const params = "artist=" + encodeURIComponent(rec.artist) +
                     "&title=" + encodeURIComponent(rec.title) +
                     (rec.year ? "&year=" + rec.year : "");
      j = await fetch("/api/discogs-price?" + params).then(r => r.json());
      if (j) {
        if (typeof j.price === "number") price = j.price;
        if (j.cover) cover = j.cover;
        if (j.country) country = j.country;
        if (j.label) label = j.label;
        if (j.format) format = j.format;
        if (j.catno) catno = j.catno;
        if (j.year) year = j.year;
        if (typeof j.have === "number") have = j.have;
        if (typeof j.want === "number") want = j.want;
        if (typeof j.numForSale === "number") numForSale = j.numForSale;
      }
    } catch (e) { /* price stays null — record added unvalued */ }

    const a = {
      title: rec.title, artist: rec.artist, year: year || null,
      genre: "Rock", manual: true, fromCatalog: true, coverUrl: cover,
    };
    // The pressing PICKER (P4). This used to build exactly one synthetic
    // option — our silently-chosen top hit — so "confirming the pressing"
    // meant rubber-stamping a guess. The API now returns the ranked,
    // bootleg-filtered candidates, and the collector holding the sleeve picks
    // the one that matches it (year · country · label · catno). Only the top
    // candidate arrives priced; picking another prices it on tap (one extra
    // call for the candidate actually chosen, not five up front).
    const hasPrice = typeof price === "number" && price > 0;
    const topId = j && j.discogsId ? j.discogsId : null;
    const cands = (j && Array.isArray(j.candidates) ? j.candidates : [])
      .map((c) => (c && c.id === topId && hasPrice)
        ? { id: "discogs-" + c.id, discogsId: c.id, label: label || c.label || "Discogs pressing",
            country: c.country || country || "—", year: c.year || year || null, catno: c.catno || catno || "",
            format: (c.format || format || "LP").split(", "),
            notes: "Matched on Discogs · valued at the current market low", baseNM: price, fromDiscogs: true,
            have, want, numForSale }
        : c && c.id
          ? { id: "discogs-" + c.id, discogsId: c.id, label: c.label || "Discogs pressing",
              country: c.country || "—", year: c.year || null, catno: c.catno || "",
              format: (c.format || "LP").split(", "),
              notes: "Matched on Discogs", baseNM: null, fromDiscogs: true, needsPrice: true }
          : null)
      .filter(Boolean);
    // No candidates (or an API shape from before this shipped): fall back to
    // the single synthetic option, exactly the old behaviour.
    const prs = cands.length ? cands
      : (hasPrice
        ? [{ id: "discogs-" + (topId || rec.mbid || Date.now()), discogsId: topId || null,
             label: label || "Discogs listing", country: country || "—",
             year: year || null, catno: catno || "", format: format ? format.split(", ") : ["LP"],
             notes: "Matched on Discogs · valued at the current market low", baseNM: price, fromDiscogs: true,
             have, want, numForSale }]
        : []); // no price → no synthetic pressing; user picks "not sure" / identifies later

    setAlbum(a);
    setCatalogPressing(prs);
    setPressing(prs.length ? prs[0] : null);  // preselect our best candidate
    setPricing(false);
    setStep("pressing");        // always confirm the pressing in-flow
  }

  const pressings = useAddMemo(() => {
    // A catalog (non-curated) record carries the ranked Discogs candidates.
    if (album && album.manual && catalogPressing) {
      return Array.isArray(catalogPressing) ? catalogPressing : [catalogPressing];
    }
    return pressingsForAlbum(album);
  }, [album, catalogPressing]);

  // Lazy pricing for a picked candidate (P4): only the preselected top hit
  // arrives priced; tapping another fetches ITS pressing-level value — one
  // call for the copy the collector actually owns, not five on spec. The
  // pressing object is patched in place in the candidate list so tapping away
  // and back doesn't refetch, and a failure just leaves it honestly unpriced
  // ("not sure" remains the escape hatch).
  useAddEffect(() => {
    if (!pressing || !pressing.needsPrice || !pressing.discogsId) return;
    let cancelled = false;
    fetch("/api/discogs-price?release_id=" + encodeURIComponent(pressing.discogsId) + "&grade=NM")
      .then(r => r.json())
      .then(j => {
        if (cancelled) return;
        const v = (j && typeof j.est_value === "number") ? j.est_value
          : (j && typeof j.lowest_price === "number") ? j.lowest_price : null;
        const patched = { ...pressing, baseNM: v, needsPrice: false,
          notes: v != null ? "Matched on Discogs · valued at this pressing's guide" : "Matched on Discogs · no market data yet" };
        setCatalogPressing(prev => Array.isArray(prev)
          ? prev.map(p => (p.id === patched.id ? patched : p)) : prev);
        setPressing(patched);
      })
      .catch(() => { /* stays unpriced; the flow still allows the add */ });
    return () => { cancelled = true; };
  }, [pressing && pressing.id]);

  const stepIndex = { search: 0, manual: 0, pressing: 1, condition: 2 }[step];
  const condObj = window.BBR_condition(condition);

  function chooseAlbum(a) { setAlbum(a); setPressing(null); setStep("pressing"); }
  function startManual() { setStep("manual"); }
  function confirmManual() {
    if (!manual.title.trim() || !manual.artist.trim()) return;
    const a = { title: manual.title.trim(), artist: manual.artist.trim(), year: parseInt(manual.year) || new Date().getFullYear(), genre: manual.genre, manual: true };
    setAlbum(a); setPressing(null); setStep("pressing");
  }

  function finish() {
    finishWith(album, pressing);
  }
  function finishWith(alb, pr) {
    if (!alb || !pr) return;
    const isUnknown = !!pr.unknown;
    const base = {
      id: "u" + Date.now(),
      pressing: pr,
      pressingId: pr.id,
      dateAdded: new Date().toISOString().slice(0, 10),
      trend: { pct: 0, dir: "flat", spark: Array(12).fill(isUnknown ? 0 : window.BBR_valueOf(pr.baseNM, needsCondition ? condition : "NM")) },
    };
    if (isUnknown) base.pressingUnknown = true;
    if (alb.slug) base.slug = alb.slug; else base.manual = { title: alb.title, artist: alb.artist, year: alb.year, genre: alb.genre, coverUrl: alb.coverUrl || null };
    if (needsCondition && !isUnknown) base.condition = condition;
    else if (!isUnknown) base.vendor = "Amazon UK";
    onAdd(base);
  }

  // album object usable by <Sleeve>
  const sleeveAlbum = album ? (album.slug ? albums.find(a => a.slug === album.slug) : { ...album, rank: -1 }) : null;

  const title = intent === "wishlist" ? "Add to wishlist" : intent === "move" ? "Move to collection" : "Add a record";

  return (
    <div className="add-backdrop" onClick={onClose}>
      <div className="add-modal" onClick={(e) => e.stopPropagation()}>
        <div className="add-head">
          <div className="add-steps">
            <span className={"add-step " + (stepIndex === 0 ? "active" : "done")}>
              <span className="n">{stepIndex > 0 ? "✓" : "1"}</span> Find
            </span>
            <span className="add-step-sep" />
            <span className={"add-step " + (stepIndex === 1 ? "active" : stepIndex > 1 ? "done" : "")}>
              <span className="n">{stepIndex > 1 ? "✓" : "2"}</span> Pressing
            </span>
            {needsCondition && (<>
              <span className="add-step-sep" />
              <span className={"add-step " + (stepIndex === 2 ? "active" : "")}>
                <span className="n">3</span> Condition
              </span>
            </>)}
          </div>
          <button className="add-x" onClick={onClose}>×</button>
        </div>

        <div className="add-body">
          {/* STEP 1, search */}
          {step === "search" && (
            <>
              <h3 className="add-title">{title}</h3>
              <p className="add-hint">Search the Hundred, or any record ever made.</p>
              <div className="add-search">
                <svg width="16" height="16" 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 autoFocus placeholder="Search by title or artist…" value={query} onChange={(e) => setQuery(e.target.value)} />
              </div>
              <div className="add-results">
                {results.map(a => (
                  <button className="add-res" key={a.slug} onClick={() => chooseAlbum(a)}>
                    <div style={{ width: 46, height: 46 }}><Sleeve album={a} size={46} /></div>
                    <div>
                      <div className="add-res-t">{a.title}</div>
                      <div className="add-res-a">{a.artist} · {a.year}</div>
                    </div>
                    <div className="add-res-rank">№ {String(a.rank).padStart(3, "0")}</div>
                  </button>
                ))}

                {/* Every record ever made — MusicBrainz catalog results */}
                {(catalogBusy || catalogResults.length > 0 || catalogNote) && (
                  <div className="add-cat-head">
                    {results.length > 0 ? "Every record" : "Results"}
                    {catalogBusy && <span className="add-cat-spin"> · searching…</span>}
                  </div>
                )}
                {catalogResults.map(rec => (
                  <button className="add-res add-res-cat" key={rec.id} onClick={() => chooseCatalog(rec)} disabled={pricing}>
                    <div className="add-res-cover" style={{ width: 46, height: 46 }}>
                      {rec.coverUrl
                        ? <img src={rec.coverUrl} alt="" loading="lazy" style={{ width: "100%", height: "100%", objectFit: "cover", display: "block", background: "#eee" }} onError={(e) => { e.target.style.display = "none"; }} />
                        : <div style={{ width: "100%", height: "100%", background: "var(--paper-2, #eee)" }} />}
                    </div>
                    <div>
                      <div className="add-res-t">{rec.title}</div>
                      <div className="add-res-a">{rec.artist}{rec.year ? " · " + rec.year : ""}</div>
                    </div>
                    <div className="add-res-rank add-res-cat-tag">Add</div>
                  </button>
                ))}
                {!catalogBusy && catalogNote && catalogResults.length === 0 && query.trim().length >= 3 && (
                  <div className="add-cat-empty">{catalogNote}</div>
                )}
              </div>
              {pricing && <div className="add-cat-pricing">Fetching current value…</div>}
              <button className="add-manual-link" onClick={startManual}>
                <span style={{ fontSize: 14 }}>+</span> Can't find it? Add a record manually
              </button>
            </>
          )}

          {/* STEP 1b, manual */}
          {step === "manual" && (
            <>
              <h3 className="add-title">Add manually</h3>
              <p className="add-hint">For records that aren't in the Hundred. We'll still pull pressings and prices.</p>
              <div className="add-manual-grid add-mf">
                <div className="full">
                  <label>Title</label>
                  <input value={manual.title} onChange={(e) => setManual({ ...manual, title: e.target.value })} placeholder="Album title" autoFocus />
                </div>
                <div className="full">
                  <label>Artist</label>
                  <input value={manual.artist} onChange={(e) => setManual({ ...manual, artist: e.target.value })} placeholder="Artist name" />
                </div>
                <div>
                  <label>Year</label>
                  <input value={manual.year} onChange={(e) => setManual({ ...manual, year: e.target.value })} placeholder="1975" inputMode="numeric" />
                </div>
                <div>
                  <label>Genre</label>
                  <select value={manual.genre} onChange={(e) => setManual({ ...manual, genre: e.target.value })}>
                    {["Rock","Soul / R&B","Jazz","Hip-Hop","Folk / Country","Punk / Indie","Electronic","Pop","Metal"].map(g => <option key={g}>{g}</option>)}
                  </select>
                </div>
              </div>
            </>
          )}

          {/* STEP 2, pressing picker */}
          {step === "pressing" && album && (
            <>
              <div className="add-sel">
                <div className="cv"><Sleeve album={sleeveAlbum} size={54} /></div>
                <div>
                  <div className="add-sel-t">{album.title}</div>
                  <div className="add-sel-a">{album.artist} · {album.year}</div>
                </div>
                {!preset && <button className="add-sel-change" onClick={() => setStep(album.manual && !album.fromCatalog ? "manual" : "search")}>Change</button>}
              </div>
              <p className="add-pr-attr">
                <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>
                Pressings &amp; prices via Discogs, pick the exact copy you {intent === "wishlist" ? "want" : "own"}.
              </p>

              {/* Confirmation banner when a pressing has been chosen via the matrix finder */}
              {pressing && pressing.fromMatrix && (
                <div className="add-matrix-chosen">
                  <div className="add-matrix-chosen-tick">✓</div>
                  <div className="add-matrix-chosen-body">
                    <div className="add-matrix-chosen-label">Identified: {pressing.label} · {pressing.country} · {pressing.year}</div>
                    <div className="add-matrix-chosen-cat">{pressing.catno}{Array.isArray(pressing.format) ? " · " + pressing.format.join(", ") : ""}</div>
                  </div>
                  <button className="add-matrix-chosen-continue" onClick={() => (needsCondition ? setStep("condition") : finish())}>
                    {needsCondition ? "Continue →" : "Add to wishlist →"}
                  </button>
                </div>
              )}

              {/* Matrix / runout finder — only for records you physically own
                 (collection / move), not wishlist where you're choosing the
                 pressing you want. */}
              {needsCondition && (
              <div className="add-matrix">
                <div className="add-matrix-cta-row">
                  <button className="add-matrix-toggle add-matrix-toggle-prom" onClick={() => setMatrixOpen(o => !o)}>
                    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7"/><path d="M16 16l5 5"/></svg>
                    Identify my exact pressing by matrix / runout
                    <span className="add-matrix-chevron">{matrixOpen ? "▲" : "▼"}</span>
                  </button>
                  <span className="add-matrix-tip" tabIndex="0">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
                    <span className="add-matrix-tip-bubble">
                      The matrix (or runout) is the code etched into the smooth vinyl between the last track and the centre label. It identifies the exact pressing, so we can match your copy precisely. Tilt the record under a light to read it.
                    </span>
                  </span>
                </div>
                {matrixOpen && (
                  <div className="add-matrix-panel">
                    <p className="add-matrix-hint">
                      Read the letters and numbers etched in the run-out groove (between the last track and the label) and type them here. We'll match them against known pressings.
                    </p>
                    <div className="add-matrix-row">
                      <input
                        className="add-matrix-input"
                        placeholder="e.g. STMA 8018 A-1 1U  420"
                        value={matrixInput}
                        onChange={(e) => setMatrixInput(e.target.value)}
                        onKeyDown={(e) => { if (e.key === "Enter") runMatrix(); }}
                      />
                      <button className="add-matrix-go" disabled={matrixBusy || !matrixInput.trim()} onClick={runMatrix}>
                        {matrixBusy ? "Searching…" : "Identify"}
                      </button>
                    </div>
                    {matrixBusy && (
                      <div className="add-matrix-loading">
                        <span className="add-matrix-spinner" aria-hidden="true" />
                        <span className="add-matrix-loading-text">
                          Searching for matching pressings… this can take a few seconds.
                        </span>
                      </div>
                    )}
                    {!matrixBusy && matrixNote && <div className="add-matrix-note">{matrixNote}</div>}
                    {matrixResults.length > 0 && (
                      <div className="add-matrix-results">
                        {matrixResults.map((c) => (
                          <button key={c.id} className="add-matrix-res" onClick={() => pickDiscogs(c)}>
                            {c.thumb ? <img className="add-matrix-thumb" src={c.thumb} alt="" /> : <span className="add-matrix-thumb add-matrix-thumb-blank" />}
                            <span className="add-matrix-res-main">
                              <span className="add-matrix-res-label">{c.label || "Unknown label"} · {c.country || "—"} · {c.year || "—"}</span>
                              <span className="add-matrix-res-cat">{c.catno || "—"}{c.format ? " · " + c.format : ""}</span>
                              {(typeof c.lowestPrice === "number" && c.lowestPrice > 0) && (
                                <span className="add-matrix-res-market">
                                  from £{Math.round(c.lowestPrice).toLocaleString("en-GB")}
                                  {typeof c.numForSale === "number" && c.numForSale > 0 ? ` · ${c.numForSale} for sale` : ""}
                                </span>
                              )}
                            </span>
                            {(c.rank != null || c.score != null) && (
                              <span className="add-matrix-res-score">{Math.round((c.rank || c.score) * 100)}%</span>
                            )}
                          </button>
                        ))}
                      </div>
                    )}
                    <button
                      className="add-pr-guide-link"
                      onClick={() => window.__nav && window.__nav.goPressingGuide && window.__nav.goPressingGuide()}>
                      Where do I find the matrix? Read the guide →
                    </button>
                  </div>
                )}
              </div>
              )}

              <div className="add-pr-list">
                {pressings.map(p => (
                  <button key={p.id} className={"add-pr " + (pressing && pressing.id === p.id ? "sel" : "")} onClick={() => setPressing(p)}>
                    <div className="add-pr-main">
                      <div className="add-pr-label">{p.label} · {p.country} · {p.year}</div>
                      <div className="add-pr-tags">
                        {p.catno && p.catno !== "—" && <span className="add-pr-tag">{p.catno}</span>}
                        {p.format.map((f, i) => <span className="add-pr-tag" key={i}>{f}</span>)}
                      </div>
                      <div className="add-pr-notes">{p.notes}</div>
                      {/* Candidates from the picker don't carry sale history —
                          only render the meta line where the data exists. */}
                      {(p.lastSold != null || p.have != null || p.want != null) && (
                        <div className="add-pr-meta">
                          {p.lastSold != null && <>Last sold {p.lastSold} · </>}
                          {p.have != null && <>{p.have} have · </>}
                          {p.want != null && <>{p.want} want</>}
                        </div>
                      )}
                    </div>
                    <PressingValue baseNM={p.baseNM} intent={intent}
                      pending={!!(p.needsPrice && pressing && pressing.id === p.id)} />
                  </button>
                ))}

                {/* "I don't know" — add now, identify the pressing later */}
                <button
                  className={"add-pr add-pr-unknown " + (pressing && pressing.id === "unknown" ? "sel" : "")}
                  onClick={() => setPressing(UNKNOWN_PRESSING)}>
                  <div className="add-pr-main">
                    <div className="add-pr-label">I'm not sure which pressing</div>
                    <div className="add-pr-notes">
                      Add it now and identify it later. It will sit in your collection unvalued until you choose a pressing.
                    </div>
                  </div>
                  <div className="add-pr-unknown-mark">?</div>
                </button>
              </div>

              <button
                className="add-pr-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 how to tell? Read the pressing identification guide
              </button>
            </>
          )}

          {/* STEP 3, condition */}
          {step === "condition" && pressing && (
            <>
              <h3 className="add-title">Grade your copy</h3>
              <p className="add-hint">Goldmine standard. The value updates to the median for this pressing in that grade.</p>
              <div className="add-cond-list">
                {window.BBR_CONDITIONS.map(c => (
                  <button key={c.code} className={"add-cond " + (condition === c.code ? "sel" : "")} onClick={() => setCondition(c.code)}>
                    <div>
                      <div className="add-cond-code">{c.code}</div>
                    </div>
                    <div>
                      <div className="add-cond-name">{c.name}</div>
                      <div className="add-cond-blurb">{c.blurb}</div>
                    </div>
                    <div className="add-cond-val">{fmtGBP(window.BBR_valueOf(pressing.baseNM, c.code))}</div>
                  </button>
                ))}
              </div>
            </>
          )}
        </div>

        <div className="add-foot">
          {step === "search" && <span />}
          {step === "manual" && <button className="add-back" onClick={() => setStep("search")}>← Back</button>}
          {step === "manual" && <button className="add-next" disabled={!manual.title.trim() || !manual.artist.trim()} onClick={confirmManual}>Continue</button>}

          {step === "pressing" && (
            <>
              {preset ? <span /> : <button className="add-back" onClick={() => setStep(album && album.manual && !album.fromCatalog ? "manual" : "search")}>← Back</button>}
              <button className="add-next" disabled={!pressing} onClick={() => (needsCondition && !(pressing && pressing.unknown)) ? setStep("condition") : finish()}>
                {(pressing && pressing.unknown)
                  ? (intent === "wishlist" ? "Add to wishlist" : "Add to collection")
                  : (needsCondition ? "Continue" : "Add to wishlist")}
              </button>
            </>
          )}

          {step === "condition" && (
            <>
              <button className="add-back" onClick={() => setStep("pressing")}>← Back</button>
              <button className="add-next" onClick={finish}>
                {intent === "move" ? "Move to collection" : "Add to collection"} · {fmtGBP(window.BBR_valueOf(pressing.baseNM, condition))}
              </button>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { AddRecordFlow });
