// Main app shell

function CollectorStudio({ tweaks, setTweaks, user, onSignOut }) {
  const [isPhone, setIsPhone] = React.useState(() =>
    typeof window !== 'undefined' && window.matchMedia('(max-width: 640px)').matches);
  React.useEffect(() => {
    const mql = window.matchMedia('(max-width: 640px)');
    const handler = (e) => setIsPhone(e.matches);
    mql.addEventListener('change', handler);
    return () => mql.removeEventListener('change', handler);
  }, []);
  const [view, setView] = React.useState('collection'); // collection | set | sets | dashboard
  const [viewingSetId, setViewingSetId] = React.useState(null);
  const [gigMode, setGigMode] = React.useState(false);
  const [gigResolved, setGigResolved] = React.useState(null); // optional override; null = use current set
  const [timelineView, setTimelineView] = React.useState(false);
  const [viewStyle, setViewStyle] = React.useState(tweaks.viewStyle || 'grid');
  const [selected, setSelected] = React.useState(null);
  // Set is an array of track IDs like "r01-2" — persisted to localStorage
  const [set, setSet] = React.useState(() => {
    const saved = localStorage.getItem('cs-set');
    if (saved) { try { return JSON.parse(saved); } catch {} }
    return [];
  });
  // Records are persisted too, so Discogs imports and manual edits survive reloads
  const [records, setRecords] = React.useState(() => {
    const saved = localStorage.getItem('cs-records');
    if (saved) { try { return JSON.parse(saved); } catch {} }
    return [];
  });
  const [search, setSearch] = React.useState('');
  const [genreFilter, setGenreFilter] = React.useState('All');
  const [advFilters, setAdvFilters] = React.useState({
    key: 'All', bpmMin: null, bpmMax: null, yearMin: null, yearMax: null, onlyInSet: false,
  });
  const [crates, setCrates] = React.useState(() => {
    const saved = localStorage.getItem('cs-crates');
    if (saved) { try { return JSON.parse(saved); } catch {} }
    return [];
  });
  const [activeCrateId, setActiveCrateId] = React.useState(null);
  React.useEffect(() => { localStorage.setItem('cs-crates', JSON.stringify(crates)); }, [crates]);

  const [profile, setProfile] = React.useState(() => {
    const saved = localStorage.getItem('cs-profile');
    if (saved) { try { return window.migrateProfile(JSON.parse(saved)); } catch {} }
    const seed = window.migrateProfile(null);
    if (user) {
      seed.id = user.id;
      seed.name = user.user_metadata?.full_name || '';
      const avatar = user.user_metadata?.avatar_url || user.user_metadata?.picture;
      if (avatar) seed.photo = avatar;
    }
    return seed;
  });
  React.useEffect(() => { localStorage.setItem('cs-profile', JSON.stringify(profile)); }, [profile]);

  const [savedSets, setSavedSets] = React.useState(() => {
    const saved = localStorage.getItem('cs-saved-sets');
    if (saved) { try { return JSON.parse(saved); } catch {} }
    return [];
  });
  const [activeSetId, setActiveSetId] = React.useState(null);
  const [currentSetName, setCurrentSetName] = React.useState(() => localStorage.getItem('cs-current-name') || '');
  React.useEffect(() => { localStorage.setItem('cs-saved-sets', JSON.stringify(savedSets)); }, [savedSets]);
  React.useEffect(() => { localStorage.setItem('cs-current-name', currentSetName); }, [currentSetName]);

  // Un-highlight active saved set if the current set has diverged
  React.useEffect(() => {
    if (!activeSetId) return;
    const saved = savedSets.find(s => s.id === activeSetId);
    if (!saved) return;
    const same = saved.trackIds.length === set.length &&
      saved.trackIds.every((t, i) => t === set[i]);
    if (!same) setActiveSetId(null);
  }, [set, activeSetId, savedSets]);

  const saveCurrentSet = (name) => {
    const finalName = (name || currentSetName || '').trim() || `Set ${new Date().toLocaleDateString()}`;
    // If the active set id is still current, update in place instead of duplicating
    if (activeSetId && savedSets.some(s => s.id === activeSetId)) {
      setSavedSets(ss => ss.map(s => s.id === activeSetId
        ? { ...s, name: finalName, trackIds: [...set] } : s));
    } else {
      const id = `s${Date.now()}`;
      setSavedSets(ss => [...ss, { id, name: finalName, trackIds: [...set], createdAt: Date.now() }]);
    }
    // Reset the builder for a fresh new set
    setSet([]);
    setActiveSetId(null);
    setCurrentSetName('');
  };
  const openSavedSet = (id) => {
    setViewingSetId(id);
    setView('sets');
  };
  const loadIntoBuilder = (id) => {
    const saved = savedSets.find(s => s.id === id);
    if (!saved) return;
    if (set.length > 0 && !confirm('Replace current builder set with "' + saved.name + '"?')) return;
    setSet(saved.trackIds);
    setActiveSetId(id);
    setCurrentSetName(saved.name);
    setView('set');
  };
  const deleteSavedSet = (id) => {
    setSavedSets(ss => ss.filter(s => s.id !== id));
    if (activeSetId === id) setActiveSetId(null);
    if (viewingSetId === id) { setViewingSetId(null); setView('collection'); }
  };
  const renameSavedSet = (id, name) => {
    setSavedSets(ss => ss.map(s => s.id === id ? { ...s, name } : s));
    if (activeSetId === id) setCurrentSetName(name);
  };
  const updateSavedSetTracks = (id, trackIds) => {
    setSavedSets(ss => ss.map(s => s.id === id ? { ...s, trackIds } : s));
    if (activeSetId === id) setSet(trackIds);
  };
  const updateSavedSetGigs = (id, gigs) => {
    setSavedSets(ss => ss.map(s => s.id === id ? { ...s, gigs } : s));
  };

  const newCrate = (name, firstRecordId) => {
    const id = `c${Date.now()}`;
    setCrates(cs => [...cs, { id, name, recordIds: firstRecordId ? [firstRecordId] : [] }]);
    return id;
  };
  const deleteCrate = (id) => {
    setCrates(cs => cs.filter(c => c.id !== id));
    if (activeCrateId === id) setActiveCrateId(null);
  };
  const addToCrate = (crateId, recordId) => {
    setCrates(cs => cs.map(c => c.id === crateId
      ? { ...c, recordIds: c.recordIds.includes(recordId) ? c.recordIds : [...c.recordIds, recordId] }
      : c));
  };
  const removeFromCrate = (crateId, recordId) => {
    setCrates(cs => cs.map(c => c.id === crateId
      ? { ...c, recordIds: c.recordIds.filter(r => r !== recordId) } : c));
  };
  const [swipeIndex, setSwipeIndex] = React.useState(0);
  const [mobileOpen, setMobileOpen] = React.useState(true);
  const [importOpen, setImportOpen] = React.useState(false);
  const [analyzeOpen, setAnalyzeOpen] = React.useState(false);
  const [formOpen, setFormOpen] = React.useState(false);
  const [editing, setEditing] = React.useState(null); // record being edited or null for new

  const handleDiscogsImport = (imported) => {
    setRecords(cur => {
      const byId = new Map(cur.map(r => [r.id, r]));
      for (const r of imported) byId.set(r.id, { ...byId.get(r.id), ...r });
      return Array.from(byId.values());
    });
  };

  const openNewRecord = () => { setEditing(null); setFormOpen(true); };
  const openEditRecord = (r) => { setEditing(r); setFormOpen(true); };
  const saveRecord = (rec) => {
    setRecords(cur => {
      const i = cur.findIndex(r => r.id === rec.id);
      if (i >= 0) { const next = [...cur]; next[i] = rec; return next; }
      return [rec, ...cur];
    });
    // If we just edited the record currently open in the detail drawer, refresh it
    if (selected && selected.id === rec.id) setSelected(rec);
  };
  const applyAnalysis = (updates) => {
    setRecords(cur => cur.map(r => {
      const ups = updates[r.id];
      if (!ups?.length) return r;
      const tracks = r.tracks.map((t, i) => {
        const u = ups.find(x => x.trackIndex === i);
        if (!u) return t;
        return { ...t, bpm: u.bpm ?? t.bpm, key: u.key ?? t.key };
      });
      const bpms = tracks.map(t => t.bpm).filter(b => b != null);
      const avgBpm = bpms.length ? Math.round(bpms.reduce((a, b) => a + b, 0) / bpms.length) : r.bpm;
      const firstKey = tracks.find(t => t.key)?.key || r.key;
      return { ...r, tracks, bpm: r.bpm ?? avgBpm, key: r.key || firstKey };
    }));
  };

  // Per-track BPM/key refresh. Looks up the single track via the edge function
  // and applies the result through the same pipeline as bulk analysis.
  const refreshTrackBpm = async (recordId, trackIndex) => {
    const rec = records.find(r => r.id === recordId);
    if (!rec) return null;
    const t = rec.tracks[trackIndex];
    if (!t) return null;
    const result = await window.lookupGetSongBpm(rec.artist, t.title || rec.title);
    if (!result || (result.bpm == null && !result.key)) return null;
    applyAnalysis({ [recordId]: [{ trackIndex, bpm: result.bpm, key: result.key }] });
    return result;
  };

  const rateTrack = (recordId, trackIndex, rating) => {
    setRecords(cur => cur.map(r => {
      if (r.id !== recordId) return r;
      const tracks = r.tracks.map((t, i) => i === trackIndex ? { ...t, rating } : t);
      return { ...r, tracks };
    }));
    if (selected && selected.id === recordId) {
      setSelected(s => ({ ...s, tracks: s.tracks.map((t, i) =>
        i === trackIndex ? { ...t, rating } : t) }));
    }
  };

  const deleteRecord = (id) => {
    setRecords(cur => cur.filter(r => r.id !== id));
    setSet(s => s.filter(tid => !tid.startsWith(`${id}-`)));
    if (selected?.id === id) setSelected(null);
  };

  // Refresh a single album from Discogs (authoritative tracklist, cover,
  // label, catalog, RPM, genre). Preserves BPM/key/rating/notes.
  const refreshDiscogsRecord = async (record) => {
    const updated = await window.refreshDiscogsRecord(record);
    setRecords(cur => cur.map(r => r.id === updated.id ? updated : r));
    if (selected?.id === updated.id) setSelected(updated);
    // Invalidate iTunes preview cache for this record so we re-match if
    // the tracklist changed.
    window.iTunesPreview?.clearPreviewCache?.(`${updated.id}-`);
    return updated;
  };

  React.useEffect(() => { setViewStyle(tweaks.viewStyle); }, [tweaks.viewStyle]);
  React.useEffect(() => { localStorage.setItem('cs-set', JSON.stringify(set)); }, [set]);
  React.useEffect(() => {
    localStorage.setItem('cs-records', JSON.stringify(records));
    window.RECORDS = records; // keep global in sync for parseTrackId
  }, [records]);

  // ── Supabase sync ──────────────────────────────────────────────────────
  const [hydrated, setHydrated] = React.useState(false);
  const prevRecordsRef = React.useRef(null);
  const prevSetsRef = React.useRef(null);
  const prevCratesRef = React.useRef(null);

  // Hydrate from Supabase on mount. If cloud is empty but we have local data,
  // migrate local data up so first-time users don't lose what they already built.
  React.useEffect(() => {
    if (!user) return;
    let cancelled = false;
    (async () => {
      const [cloudProfile, cloudRecords, cloudSets, cloudCrates, cloudWorkspace] = await Promise.all([
        window.Sync.fetchProfile(),
        window.Sync.fetchRecords(),
        window.Sync.fetchSavedSets(),
        window.Sync.fetchCrates(),
        window.Sync.fetchWorkspace(),
      ]);
      if (cancelled) return;

      // Merge local + cloud by id. Cloud wins on conflicts; local-only items
      // get uploaded so nothing is lost when a device signs in after another
      // device already populated the cloud.
      const mergeById = (local, cloud) => {
        const m = new Map();
        for (const x of local) m.set(x.id, x);
        for (const x of cloud) m.set(x.id, x); // cloud overwrites
        return Array.from(m.values());
      };
      const localOnly = (local, cloud) => {
        const cloudIds = new Set(cloud.map(x => x.id));
        return local.filter(x => !cloudIds.has(x.id));
      };

      const mergedRecords  = mergeById(records,   cloudRecords);
      const mergedSets     = mergeById(savedSets, cloudSets);
      const mergedCrates   = mergeById(crates,    cloudCrates);

      setProfile(window.migrateProfile(cloudProfile || profile));
      setRecords(mergedRecords);
      setSavedSets(mergedSets);
      setCrates(mergedCrates);
      if (cloudWorkspace) {
        if (Array.isArray(cloudWorkspace.set)) setSet(cloudWorkspace.set);
        if (typeof cloudWorkspace.currentSetName === 'string') setCurrentSetName(cloudWorkspace.currentSetName);
        if (cloudWorkspace.activeSetId !== undefined) setActiveSetId(cloudWorkspace.activeSetId);
      }

      // Push anything that only exists locally so the cloud catches up.
      await Promise.all([
        !cloudProfile && window.Sync.upsertProfile(profile),
        !cloudWorkspace && window.Sync.upsertWorkspace({ set, activeSetId, currentSetName }),
        ...localOnly(records,   cloudRecords).map(r => window.Sync.upsertRecord(r)),
        ...localOnly(savedSets, cloudSets).map(s => window.Sync.upsertSavedSet(s)),
        ...localOnly(crates,    cloudCrates).map(c => window.Sync.upsertCrate(c)),
      ].filter(Boolean));

      // Prime refs so write-through doesn't re-upsert what we just merged.
      prevRecordsRef.current = mergedRecords;
      prevSetsRef.current    = mergedSets;
      prevCratesRef.current  = mergedCrates;
      setHydrated(true);
    })();
    return () => { cancelled = true; };
  }, [user?.id]);

  // Write-through: profile (debounced)
  const upsertProfileDebounced = React.useMemo(
    () => window.debounce(window.Sync.upsertProfile, 600), []);
  React.useEffect(() => {
    if (!hydrated) return;
    upsertProfileDebounced(profile);
  }, [profile, hydrated]);

  // Write-through: records (diff upsert/delete)
  React.useEffect(() => {
    if (!hydrated) return;
    const prev = prevRecordsRef.current || [];
    window.diffArraySync({
      prev, curr: records,
      getId: (r) => r.id,
      onUpsert: (r) => window.Sync.upsertRecord(r),
      onDelete: (id) => window.Sync.deleteRecord(id),
    });
    prevRecordsRef.current = records;
  }, [records, hydrated]);

  // Write-through: saved sets
  React.useEffect(() => {
    if (!hydrated) return;
    const prev = prevSetsRef.current || [];
    window.diffArraySync({
      prev, curr: savedSets,
      getId: (s) => s.id,
      onUpsert: (s) => window.Sync.upsertSavedSet(s),
      onDelete: (id) => window.Sync.deleteSavedSet(id),
    });
    prevSetsRef.current = savedSets;
  }, [savedSets, hydrated]);

  // Write-through: crates
  React.useEffect(() => {
    if (!hydrated) return;
    const prev = prevCratesRef.current || [];
    window.diffArraySync({
      prev, curr: crates,
      getId: (c) => c.id,
      onUpsert: (c) => window.Sync.upsertCrate(c),
      onDelete: (id) => window.Sync.deleteCrate(id),
    });
    prevCratesRef.current = crates;
  }, [crates, hydrated]);

  // Write-through: workspace (current builder + active set + name, debounced)
  const upsertWorkspaceDebounced = React.useMemo(
    () => window.debounce(window.Sync.upsertWorkspace, 600), []);
  React.useEffect(() => {
    if (!hydrated) return;
    upsertWorkspaceDebounced({ set, activeSetId, currentSetName });
  }, [set, activeSetId, currentSetName, hydrated]);

  // Auto-analyze: quietly fill BPM/key for any track that's missing them.
  // Runs in background, throttled, marks tracks as `bpmTried` so we never retry the same miss.
  const autoAnalyzeRunning = React.useRef(false);
  React.useEffect(() => {
    if (!hydrated || autoAnalyzeRunning.current) return;
    const targets = [];
    for (const r of records) {
      for (let i = 0; i < r.tracks.length; i++) {
        const t = r.tracks[i];
        const missing = t.bpm == null || t.key == null || t.key === '';
        if (missing && !t.bpmTried) {
          targets.push({ rid: r.id, idx: i, artist: r.artist, title: t.title || r.title });
        }
      }
    }
    if (targets.length === 0) return;

    autoAnalyzeRunning.current = true;
    let cancelled = false;
    (async () => {
      const apiKey = localStorage.getItem('cs-gsbpm-key') || '92947fe415c8cddf9b400174476de981';
      const results = {};
      for (const t of targets) {
        if (cancelled) break;
        try {
          const r = await window.lookupGetSongBpm(t.artist, t.title, apiKey);
          (results[t.rid] ||= []).push({ trackIndex: t.idx, ...(r || {}) });
        } catch {
          (results[t.rid] ||= []).push({ trackIndex: t.idx });
        }
        await new Promise(res => setTimeout(res, 250)); // throttle
      }
      if (cancelled) return;
      setRecords(cur => cur.map(rec => {
        const ups = results[rec.id];
        if (!ups?.length) return rec;
        const tracks = rec.tracks.map((t, i) => {
          const u = ups.find(x => x.trackIndex === i);
          if (!u) return t;
          return { ...t, bpm: u.bpm ?? t.bpm, key: u.key ?? t.key, bpmTried: true };
        });
        const bpms = tracks.map(t => t.bpm).filter(b => b != null);
        const avgBpm = bpms.length ? Math.round(bpms.reduce((a, b) => a + b, 0) / bpms.length) : rec.bpm;
        const firstKey = tracks.find(t => t.key)?.key || rec.key;
        return { ...rec, tracks, bpm: rec.bpm ?? avgBpm, key: rec.key || firstKey };
      }));
      autoAnalyzeRunning.current = false;
    })();
    return () => { cancelled = true; autoAnalyzeRunning.current = false; };
  }, [records, hydrated]);
  const isTrackInSet = (tid) => set.includes(tid);
  // Record has "some" track in set
  const recordInSet = (rid) => {
    const rec = records.find(r => r.id === rid);
    if (!rec) return false;
    return rec.tracks.some((_, i) => set.includes(`${rid}-${i}`));
  };
  // Toggle single track
  const toggleTrack = (record, trackIdx) => {
    const tid = `${record.id}-${trackIdx}`;
    setSet(s => s.includes(tid) ? s.filter(x => x !== tid) : [...s, tid]);
  };
  // Toggle all tracks of a record (used by grid/list record-level + button, and "add all" in detail)
  const toggleAllTracks = (record) => {
    const allIds = record.tracks.map((_, i) => `${record.id}-${i}`);
    const allIn = allIds.every(tid => set.includes(tid));
    if (allIn) setSet(s => s.filter(x => !allIds.includes(x)));
    else setSet(s => [...s, ...allIds.filter(tid => !s.includes(tid))]);
  };
  // Swipe right from set builder = open record (so user picks tracks)
  const openRecord = (record) => setSelected(record);
  const removeFromSet = (tid) => setSet(s => s.filter(x => x !== tid));
  const reorder = (from, to) => setSet(s => {
    const next = [...s]; const [m] = next.splice(from, 1); next.splice(to, 0, m); return next;
  });

  const filtered = applyFilters(records, {
    search, genre: genreFilter,
    key: advFilters.key, bpmMin: advFilters.bpmMin, bpmMax: advFilters.bpmMax,
    yearMin: advFilters.yearMin, yearMax: advFilters.yearMax,
    onlyInSet: advFilters.onlyInSet, set,
  });
  const availableGenres = ['All', ...new Set(records.map(r => r.genre).filter(Boolean))].sort();
  const [sortBy, setSortBy] = React.useState(() => localStorage.getItem('cs-sort') || 'recent');
  React.useEffect(() => { localStorage.setItem('cs-sort', sortBy); }, [sortBy]);
  const sortedFiltered = sortRecords(filtered, sortBy);

  if (isPhone) {
    return (
      <div className={`app ${tweaks.theme}`} style={{
        height: '100dvh', width: '100vw', overflow: 'hidden',
        background: 'var(--bg)', color: 'var(--fg)',
      }}>
        <MobileApp records={records} set={set} crates={crates} savedSets={savedSets}
          currentSetName={currentSetName} setCurrentSetName={setCurrentSetName}
          onSaveSet={saveCurrentSet}
          onToggleTrack={toggleTrack}
          onRemoveFromSet={removeFromSet}
          onClearSet={() => setSet([])}
          onLoadSavedSet={(id) => { const s = savedSets.find(x => x.id === id); if (s) { setSet(s.trackIds); setActiveSetId(id); setCurrentSetName(s.name); } }}
          profile={profile} setProfile={setProfile}
          user={user} onSignOut={onSignOut}
          darkMode={tweaks.theme === 'dark'} accent={ACCENTS[tweaks.accent] || tweaks.accent} />
      </div>
    );
  }

  return (
    <div className={`app ${tweaks.theme}`} style={{
      display: 'grid',
      gridTemplateColumns: mobileOpen ? '220px 1fr 420px' : '220px 1fr',
      height: '100vh', overflow: 'hidden',
      background: 'var(--bg)', color: 'var(--fg)',
      transition: 'grid-template-columns 0.3s cubic-bezier(0.2, 0, 0.2, 1)',
    }}>
      {/* Sidebar */}
      <Sidebar view={view} setView={setView} set={set} records={records}
        mobileOpen={mobileOpen} setMobileOpen={setMobileOpen}
        onOpenImport={() => setImportOpen(true)}
        onAddRecord={openNewRecord}
        onAnalyze={() => setAnalyzeOpen(true)}
        crates={crates} activeCrateId={activeCrateId} setActiveCrateId={setActiveCrateId}
        onNewCrate={newCrate} onDeleteCrate={deleteCrate}
        savedSets={savedSets} activeSetId={activeSetId} viewingSetId={viewingSetId}
        onSaveSet={saveCurrentSet} onOpenSet={openSavedSet} onDeleteSet={deleteSavedSet}
        profile={profile} user={user} onSignOut={onSignOut} />

      {/* Main */}
      <div style={{ position: 'relative', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
        <TopBar view={view} setView={setView}
          search={search} setSearch={setSearch}
          viewStyle={viewStyle} setViewStyle={setViewStyle}
          genreFilter={genreFilter} setGenreFilter={setGenreFilter}
          availableGenres={availableGenres}
          advFilters={advFilters} setAdvFilters={setAdvFilters}
          records={records}
          count={filtered.length} total={records.length}
          sortBy={sortBy} setSortBy={setSortBy}
          activeCrateName={view === 'crates' && activeCrateId
            ? crates.find(c => c.id === activeCrateId)?.name : null}
          density={tweaks.density} setDensity={d => setTweaks({ ...tweaks, density: d })} />

        <div style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 80px' }}>
          {view === 'collection' && (
            records.length === 0 ? (
              <EmptyCollection onAddRecord={openNewRecord}
                onOpenImport={() => setImportOpen(true)} />
            ) : (
              <>
                {viewStyle === 'grid' && <CollectionGrid records={sortedFiltered} onSelect={setSelected}
                  onAddToSet={toggleAllTracks} inSet={recordInSet} density={tweaks.density}
                  showOverlays={tweaks.showOverlays} />}
                {viewStyle === 'list' && <CollectionList records={sortedFiltered} onSelect={setSelected}
                  onAddToSet={toggleAllTracks} inSet={recordInSet} density={tweaks.density}
                  showOverlays={tweaks.showOverlays} />}
                {viewStyle === 'stack' && <CollectionStack records={sortedFiltered} onSelect={setSelected}
                  onAddToSet={toggleAllTracks} inSet={recordInSet} density={tweaks.density}
                  showOverlays={tweaks.showOverlays} />}
              </>
            )
          )}
          {view === 'set' && (
            <SetBuilder set={set} records={records}
              onRemove={removeFromSet} onReorder={reorder}
              onClear={() => setSet([])}
              onSwipe={openRecord}
              onAddTrack={toggleTrack}
              onSaveSet={saveCurrentSet}
              setName={currentSetName} onSetNameChange={setCurrentSetName}
              activeSetName={savedSets.find(s => s.id === activeSetId)?.name}
              onLaunchGig={() => setGigMode(true)}
              timelineView={timelineView} onToggleTimeline={() => setTimelineView(v => !v)}
              swipeIndex={swipeIndex} setSwipeIndex={setSwipeIndex} />
          )}
          {view === 'dashboard' && (
            <Dashboard records={records} set={set} />
          )}
          {view === 'profile' && (
            <ProfilePage profile={profile} setProfile={setProfile}
              records={records} savedSets={savedSets}
              user={user} onSignOut={onSignOut}
              onRetryBpmAnalysis={() => {
                // Clear the bpmTried flag so the background auto-analyzer
                // will re-run through every track missing BPM or key.
                setRecords(cur => cur.map(r => ({
                  ...r,
                  tracks: r.tracks.map(t => {
                    const missing = t.bpm == null || !t.key;
                    return missing ? { ...t, bpmTried: false } : t;
                  }),
                })));
                autoAnalyzeRunning.current = false;
              }} />
          )}
          {view === 'crates' && (
            <CratesPage crates={crates} records={records}
              activeCrateId={activeCrateId} setActiveCrateId={setActiveCrateId}
              onSelect={setSelected}
              onDeleteCrate={deleteCrate}
              onRemoveFromCrate={removeFromCrate}
              onNewCrate={newCrate}
              onAddToSet={toggleAllTracks} inSet={recordInSet}
              density={tweaks.density} showOverlays={tweaks.showOverlays}
              sortBy={sortBy}
              search={search}
              viewStyle={viewStyle}
              advFilters={advFilters} set={set}
              onBrowseCollection={() => setView('collection')} />
          )}
          {view === 'sets' && (
            <SavedSetPage savedSet={savedSets.find(s => s.id === viewingSetId)}
              records={records}
              onRename={renameSavedSet}
              onUpdateTracks={updateSavedSetTracks}
              onUpdateGigs={updateSavedSetGigs}
              onDelete={deleteSavedSet}
              onLoadToBuilder={loadIntoBuilder}
              onLaunchGig={(resolved) => { setGigResolved(resolved); setGigMode(true); }} />
          )}
        </div>

        {selected && (
          <RecordDetail record={selected} onClose={() => setSelected(null)}
            onAddTrack={toggleTrack} isTrackInSet={isTrackInSet}
            onAddAllTracks={toggleAllTracks} allRecords={records}
            onEdit={() => openEditRecord(selected)}
            crates={crates} onAddToCrate={addToCrate} onRemoveFromCrate={removeFromCrate}
            onNewCrate={newCrate}
            onRateTrack={rateTrack}
            onRefreshTrackBpm={refreshTrackBpm}
            onRefreshDiscogs={refreshDiscogsRecord} />
        )}
      </div>

      {/* Mobile companion */}
      {mobileOpen && (
        <div style={{
          background: 'var(--sidebar-bg)', borderLeft: '1px solid var(--border)',
          padding: '20px 20px 28px', overflow: 'hidden',
          display: 'flex', flexDirection: 'column', alignItems: 'center',
        }}>
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            width: '100%', marginBottom: 14,
          }}>
            <div style={{
              fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 1.5,
              textTransform: 'uppercase', color: 'var(--dim)',
              display: 'flex', alignItems: 'center', gap: 6,
            }}>
              <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--accent)' }} />
              Gig companion · Live
            </div>
            <button onClick={() => setMobileOpen(false)} style={{
              width: 22, height: 22, borderRadius: 11, border: '1px solid var(--border)',
              background: 'transparent', color: 'var(--dim)', cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,
            }}>{Icon.X}</button>
          </div>
          <IOSDevice width={340} height={720} dark={tweaks.theme === 'dark'}>
            <MobileApp records={records} set={set} crates={crates} savedSets={savedSets}
              currentSetName={currentSetName} setCurrentSetName={setCurrentSetName}
              onSaveSet={saveCurrentSet}
              onToggleTrack={toggleTrack}
              onRemoveFromSet={removeFromSet}
              onClearSet={() => setSet([])}
              onLoadSavedSet={(id) => { const s = savedSets.find(x => x.id === id); if (s) { setSet(s.trackIds); setActiveSetId(id); setCurrentSetName(s.name); } }}
              profile={profile} setProfile={setProfile}
              darkMode={tweaks.theme === 'dark'} accent={ACCENTS[tweaks.accent] || tweaks.accent} />
          </IOSDevice>
        </div>
      )}

      {!mobileOpen && (
        <button onClick={() => setMobileOpen(true)} style={{
          position: 'absolute', right: 20, bottom: 20,
          padding: '10px 16px', borderRadius: 999,
          background: 'var(--accent)', color: 'var(--on-accent)',
          border: 'none', fontSize: 11, fontWeight: 700, letterSpacing: 1,
          textTransform: 'uppercase', fontFamily: 'inherit', cursor: 'pointer',
          display: 'flex', alignItems: 'center', gap: 6, zIndex: 30,
          boxShadow: '0 8px 24px rgba(0,0,0,0.3)',
        }}>{Icon.Mobile} Phone preview</button>
      )}

      <DiscogsImportModal open={importOpen}
        onClose={() => setImportOpen(false)}
        onImport={handleDiscogsImport} />

      <RecordFormModal open={formOpen} initial={editing}
        onClose={() => setFormOpen(false)}
        onSave={saveRecord} onDelete={deleteRecord} />

      <AnalyzeModal open={analyzeOpen} records={records}
        onClose={() => setAnalyzeOpen(false)}
        onApply={applyAnalysis} />

      {gigMode && (() => {
        const resolved = gigResolved || set.map(tid => {
          const p = window.parseTrackId(tid);
          return p ? { tid, ...p } : null;
        }).filter(Boolean);
        return <GigMode resolved={resolved}
          onClose={() => { setGigMode(false); setGigResolved(null); }} />;
      })()}
    </div>
  );
}

function Sidebar({ view, setView, set, records, mobileOpen, setMobileOpen, onOpenImport, onAddRecord, onAnalyze, crates, activeCrateId, setActiveCrateId, onNewCrate, onDeleteCrate, savedSets, activeSetId, viewingSetId, onSaveSet, onOpenSet, onDeleteSet, profile, user, onSignOut }) {
  const stats = {
    total: records.length,
    genres: new Set(records.map(r => r.genre)).size,
    value: records.reduce((s, r) => s + r.value, 0),
  };

  return (
    <aside style={{
      background: 'var(--sidebar-bg)', borderRight: '1px solid var(--border)',
      padding: '20px 16px', display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
    }}>
      {/* Wordmark */}
      <div style={{ marginBottom: 18 }}>
        <div style={{
          fontSize: 22, fontWeight: 800, letterSpacing: -0.8, lineHeight: 0.95,
        }}>
          Kollector<br /><span style={{ color: 'var(--accent)' }}>Studio</span><span style={{ color: 'var(--accent)' }}>.</span>
        </div>
        <div style={{
          fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 1.5,
          textTransform: 'uppercase', color: 'var(--dim)', marginTop: 6,
        }}>For vinyl DJs · v0.4</div>
      </div>

      {/* Profile chip — click to open profile page (includes sign out) */}
      <button onClick={() => setView('profile')} style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 10,
        padding: '8px 10px', borderRadius: 10, marginBottom: 20,
        background: view === 'profile' ? 'var(--hover)' : 'transparent',
        border: '1px solid var(--border)',
        color: 'var(--fg)', fontFamily: 'inherit', cursor: 'pointer', textAlign: 'left',
        minWidth: 0,
      }}>
        <ProfileAvatar profile={profile} size={34} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 12, fontWeight: 700,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {profile.djName || profile.name || 'Set up profile'}
          </div>
          <div style={{
            fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 0.5,
            color: 'var(--dim)',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>{user?.email || 'Edit your profile'}</div>
        </div>
      </button>

      <div style={{
        fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 1.5,
        textTransform: 'uppercase', color: 'var(--dim)', marginBottom: 8, padding: '0 6px',
      }}>Workspace</div>
      <nav style={{ display: 'flex', flexDirection: 'column', gap: 2, marginBottom: 20 }}>
        <NavItem icon={Icon.Dig} label="Collection"
          active={view === 'collection'} onClick={() => setView('collection')}
          badge={records.length} />
        <NavItem icon={Icon.Heart} label="Crates"
          active={view === 'crates'} onClick={() => { setActiveCrateId(null); setView('crates'); }}
          badge={crates.length > 0 ? crates.length : null} />
        <NavItem icon={Icon.Deck} label="Set Builder"
          active={view === 'set'} onClick={() => setView('set')}
          badge={set.length > 0 ? set.length : null} accent={set.length > 0} />
        <NavItem icon={Icon.Grid} label="Dashboard"
          active={view === 'dashboard'} onClick={() => setView('dashboard')} />
      </nav>

      <SavedSetsList savedSets={savedSets} currentSet={set}
        activeSetId={activeSetId} viewingSetId={view === 'sets' ? viewingSetId : null}
        onSave={onSaveSet} onOpen={onOpenSet} onDelete={onDeleteSet} />

      <div style={{ flex: 1 }} />

      {/* Add / Import buttons */}
      <button onClick={onAddRecord} style={{
        marginBottom: 6, padding: '10px 12px',
        background: 'var(--accent)', color: 'var(--on-accent)',
        border: 'none', borderRadius: 8,
        cursor: 'pointer', fontFamily: 'inherit', fontSize: 12, fontWeight: 700,
        display: 'flex', alignItems: 'center', gap: 8, textAlign: 'left',
        letterSpacing: 0.3,
      }}>
        {Icon.Plus}
        <span style={{ flex: 1 }}>Add record</span>
      </button>
      <button onClick={onOpenImport} style={{
        marginBottom: 10, padding: '10px 12px',
        background: 'transparent', color: 'var(--fg)',
        border: '1px dashed var(--border)', borderRadius: 8,
        cursor: 'pointer', fontFamily: 'inherit', fontSize: 12, fontWeight: 600,
        display: 'flex', alignItems: 'center', gap: 8, textAlign: 'left',
      }}
      onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'var(--accent)'; e.currentTarget.style.color = 'var(--accent)'; }}
      onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'var(--border)'; e.currentTarget.style.color = 'var(--fg)'; }}>
        {Icon.Discogs}
        <span style={{ flex: 1 }}>Import from Discogs</span>
      </button>

      {/* Collection stats */}
      <div style={{
        padding: 12, borderRadius: 8, background: 'var(--hover)',
        border: '1px solid var(--border)',
      }}>
        <div style={{
          fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 1.5,
          textTransform: 'uppercase', color: 'var(--dim)', marginBottom: 8,
        }}>Collection</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
          <div>
            <div style={{ fontSize: 20, fontWeight: 700, fontFamily: 'JetBrains Mono, monospace' }}>{stats.total}</div>
            <div style={{ fontSize: 10, color: 'var(--dim)' }}>Records</div>
          </div>
          <div>
            <div style={{ fontSize: 20, fontWeight: 700, fontFamily: 'JetBrains Mono, monospace' }}>{stats.genres}</div>
            <div style={{ fontSize: 10, color: 'var(--dim)' }}>Genres</div>
          </div>
        </div>
      </div>

      {/* API status */}
      <div style={{ marginTop: 10, display: 'flex', gap: 6 }}>
        <ApiStatus label="Discogs" ok />
        <ApiStatus label="Match BPM" ok />
      </div>

      {/* Attribution — AcousticBrainz / GetSongBPM for BPM/key, iTunes for previews */}
      <div style={{
        marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--border)',
        fontFamily: 'JetBrains Mono, monospace', fontSize: 9, letterSpacing: 0.5,
        color: 'var(--dim)', lineHeight: 1.5,
      }}>
        Tempo &amp; key via{' '}
        <a href="https://acousticbrainz.org" target="_blank" rel="noreferrer"
          style={{ color: 'var(--accent)', textDecoration: 'none' }}>AcousticBrainz</a>
        {' '}&amp;{' '}
        <a href="https://getsongbpm.com" target="_blank" rel="noreferrer"
          style={{ color: 'var(--accent)', textDecoration: 'none' }}>GetSongBPM</a>
        {' '}· previews via iTunes
      </div>
    </aside>
  );
}

function NavItem({ icon, label, active, onClick, badge, muted, accent }) {
  return (
    <button onClick={onClick} style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 10px', borderRadius: 6, border: 'none',
      background: active ? 'var(--accent)' : 'transparent',
      color: active ? 'var(--on-accent)' : (muted ? 'var(--dim)' : 'var(--fg)'),
      cursor: 'pointer', width: '100%',
      fontFamily: 'inherit', fontSize: 13, fontWeight: 500,
      textAlign: 'left', transition: 'all 0.1s',
    }}
    onMouseEnter={e => { if (!active) e.currentTarget.style.background = 'var(--hover)'; }}
    onMouseLeave={e => { if (!active) e.currentTarget.style.background = 'transparent'; }}>
      {icon}
      <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{label}</span>
      {badge != null && (
        <span style={{
          fontFamily: 'JetBrains Mono, monospace', fontSize: 10, fontWeight: 700,
          padding: '2px 6px', borderRadius: 4,
          background: active ? 'rgba(0,0,0,0.15)' : (accent ? 'var(--accent)' : 'var(--border)'),
          color: active ? 'var(--on-accent)' : (accent ? 'var(--on-accent)' : 'var(--fg)'),
        }}>{badge}</span>
      )}
    </button>
  );
}

function ApiStatus({ label, ok }) {
  return (
    <div style={{
      flex: 1, display: 'flex', alignItems: 'center', gap: 6,
      padding: '6px 8px', borderRadius: 4,
      border: '1px solid var(--border)',
      fontFamily: 'JetBrains Mono, monospace', fontSize: 9,
      letterSpacing: 0.8, textTransform: 'uppercase',
    }}>
      <span style={{
        width: 6, height: 6, borderRadius: '50%',
        background: ok ? 'oklch(0.75 0.18 145)' : 'oklch(0.65 0.2 20)',
        boxShadow: ok ? '0 0 6px oklch(0.75 0.18 145)' : 'none',
      }} />
      {label}
    </div>
  );
}

// ─── Sort helper — shared by collection + crates ──────────────────────
function sortRecords(records, sortBy) {
  if (!sortBy || sortBy === 'recent') return records;
  const clone = [...records];
  const cmpStr = (a, b) => (a || '').localeCompare(b || '', undefined, { sensitivity: 'base' });
  const avgBpm = (r) => {
    const bs = (r.tracks || []).map(t => t.bpm).filter(b => b != null);
    return bs.length ? bs.reduce((s, x) => s + x, 0) / bs.length : (r.bpm ?? 0);
  };
  const avgRating = (r) => {
    const rs = (r.tracks || []).map(t => t.rating || 0);
    return rs.length ? rs.reduce((s, x) => s + x, 0) / rs.length : 0;
  };
  switch (sortBy) {
    case 'title':  clone.sort((a, b) => cmpStr(a.title, b.title)); break;
    case 'artist': clone.sort((a, b) => cmpStr(a.artist, b.artist) || cmpStr(a.title, b.title)); break;
    case 'album':  clone.sort((a, b) => cmpStr(a.title, b.title)); break; // record title == album
    case 'bpm':    clone.sort((a, b) => avgBpm(a) - avgBpm(b)); break;
    case 'rating': clone.sort((a, b) => avgRating(b) - avgRating(a)); break;
    case 'year':   clone.sort((a, b) => (a.year || 0) - (b.year || 0)); break;
    default: break;
  }
  return clone;
}
Object.assign(window, { sortRecords });

// Small dropdown control
function SortDropdown({ sortBy, setSortBy }) {
  const [open, setOpen] = React.useState(false);
  const options = [
    { id: 'recent', label: 'Recently added' },
    { id: 'title',  label: 'Title (A–Z)' },
    { id: 'artist', label: 'Artist (A–Z)' },
    { id: 'album',  label: 'Album (A–Z)' },
    { id: 'bpm',    label: 'BPM (low → high)' },
    { id: 'rating', label: 'Rating (high → low)' },
    { id: 'year',   label: 'Year (old → new)' },
  ];
  const current = options.find(o => o.id === sortBy) || options[0];
  return (
    <div style={{ position: 'relative' }}>
      <button onClick={() => setOpen(o => !o)} style={{
        display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px',
        borderRadius: 6, background: 'var(--hover)', border: '1px solid var(--border)',
        color: 'var(--fg)', fontFamily: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
      }}>
        <span style={{ fontFamily: 'JetBrains Mono, monospace', fontSize: 10,
          letterSpacing: 1, color: 'var(--dim)', textTransform: 'uppercase' }}>Sort</span>
        <span>{current.label}</span>
        <span style={{ color: 'var(--dim)', fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{
            position: 'fixed', inset: 0, zIndex: 10,
          }} />
          <div style={{
            position: 'absolute', top: '100%', right: 0, marginTop: 4,
            background: 'var(--panel)', border: '1px solid var(--border)', borderRadius: 8,
            zIndex: 11, minWidth: 200, overflow: 'hidden',
            boxShadow: '0 12px 32px rgba(0,0,0,0.3)',
          }}>
            {options.map(o => (
              <button key={o.id} onClick={() => { setSortBy(o.id); setOpen(false); }} style={{
                width: '100%', padding: '9px 12px', textAlign: 'left',
                background: sortBy === o.id ? 'var(--hover)' : 'transparent',
                border: 'none', color: 'var(--fg)', fontFamily: 'inherit',
                fontSize: 12, fontWeight: sortBy === o.id ? 700 : 500, cursor: 'pointer',
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <span style={{ width: 6, height: 6, borderRadius: 3,
                  background: sortBy === o.id ? 'var(--accent)' : 'transparent',
                  border: sortBy === o.id ? 'none' : '1px solid var(--border)' }} />
                {o.label}
              </button>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function TopBar({ view, search, setSearch, viewStyle, setViewStyle, genreFilter, setGenreFilter, availableGenres, advFilters, setAdvFilters, records, count, total, sortBy, setSortBy, activeCrateName, density, setDensity }) {
  const showToolbar = view === 'collection' || (view === 'crates' && activeCrateName);
  return (
    <div style={{
      padding: '20px 32px 14px', borderBottom: '1px solid var(--border)',
      display: 'flex', flexDirection: 'column', gap: 14,
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 20 }}>
        <div>
          <div style={{
            fontFamily: 'JetBrains Mono, monospace', fontSize: 10, letterSpacing: 1.5,
            textTransform: 'uppercase', color: 'var(--dim)', marginBottom: 4,
          }}>{
            view === 'collection' ? 'Your shelves' :
            view === 'crates' ? 'Curated groups' :
            view === 'set' ? 'Build a set' :
            view === 'sets' ? 'Saved sets' :
            view === 'dashboard' ? 'Collection health' : ''
          }</div>
          <h1 style={{
            margin: 0, fontSize: 44, fontWeight: 800, letterSpacing: -1.6, lineHeight: 1,
          }}>
            {view === 'collection' && <>Collection<span style={{ color: 'var(--accent)' }}>.</span></>}
            {view === 'crates' && (activeCrateName
              ? <>{activeCrateName}<span style={{ color: 'var(--accent)' }}>.</span></>
              : <>Crates<span style={{ color: 'var(--accent)' }}>.</span></>)}
            {view === 'set' && <>Set builder<span style={{ color: 'var(--accent)' }}>.</span></>}
            {view === 'sets' && <>Your sets<span style={{ color: 'var(--accent)' }}>.</span></>}
            {view === 'dashboard' && <>Dashboard<span style={{ color: 'var(--accent)' }}>.</span></>}
          </h1>
        </div>
        <div style={{
          fontFamily: 'JetBrains Mono, monospace', fontSize: 11, color: 'var(--dim)',
          letterSpacing: 0.5, textTransform: 'uppercase', textAlign: 'right',
        }}>
          {view === 'collection' && <>Showing <span style={{ color: 'var(--fg)', fontWeight: 700 }}>{count}</span> / {total}</>}
          {view === 'crates' && <>Organize records into named groups</>}
          {view === 'set' && <>Swipe right to add · swipe left to skip</>}
          {view === 'sets' && <>Pick a set from the sidebar</>}
          {view === 'dashboard' && <>Everything at a glance</>}
        </div>
      </div>

      {showToolbar && (
        <>
          <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
            {/* Search */}
            <div style={{
              flex: 1, display: 'flex', alignItems: 'center', gap: 8,
              padding: '8px 12px', borderRadius: 6,
              background: 'var(--hover)', border: '1px solid var(--border)',
            }}>
              <span style={{ color: 'var(--dim)' }}>{Icon.Search}</span>
              <input value={search} onChange={e => setSearch(e.target.value)}
                placeholder="Search title, artist, label…"
                style={{
                  flex: 1, background: 'transparent', border: 'none', outline: 'none',
                  color: 'var(--fg)', fontSize: 13, fontFamily: 'inherit',
                }} />
            </div>

            {/* Advanced filter popover */}
            <FilterPopover filters={advFilters} setFilters={setAdvFilters} records={records} />

            {/* Sort */}
            <SortDropdown sortBy={sortBy} setSortBy={setSortBy} />

            {/* View style */}
            <div style={{ display: 'flex', gap: 4, border: '1px solid var(--border)', borderRadius: 6, padding: 3 }}>
              {[
                { id: 'grid', icon: Icon.Grid },
                { id: 'list', icon: Icon.List },
                { id: 'stack', icon: Icon.Stack },
              ].map(v => (
                <button key={v.id} onClick={() => setViewStyle(v.id)} style={{
                  width: 28, height: 24, border: 'none', borderRadius: 4,
                  background: viewStyle === v.id ? 'var(--accent)' : 'transparent',
                  color: viewStyle === v.id ? 'var(--on-accent)' : 'var(--fg)',
                  cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}>{v.icon}</button>
              ))}
            </div>
          </div>

          {/* Genre filter chips — dedicated row for breathing room */}
          <div style={{ display: 'flex', gap: 6, overflowX: 'auto', paddingBottom: 2 }}>
            {availableGenres.map(g => (
              <Tag key={g} onClick={() => setGenreFilter(g)} active={genreFilter === g} size="sm">{g}</Tag>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

function EmptyCollection({ onAddRecord, onOpenImport }) {
  return (
    <div style={{
      maxWidth: 520, margin: '60px auto 0', padding: 36, textAlign: 'center',
      borderRadius: 16, border: '1px dashed var(--border)',
    }}>
      <div style={{
        width: 64, height: 64, borderRadius: 32, margin: '0 auto 18px',
        background: 'var(--accent)', color: 'var(--on-accent)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
          <circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>
        </svg>
      </div>
      <h2 style={{ margin: '0 0 6px', fontSize: 24, fontWeight: 700, letterSpacing: -0.4 }}>
        Your shelves are empty
      </h2>
      <p style={{ margin: '0 0 22px', color: 'var(--dim)', fontSize: 13, lineHeight: 1.5 }}>
        Add your first record manually, or pull your collection straight from Discogs.
      </p>
      <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>
        <button onClick={onAddRecord} style={{
          padding: '10px 18px', borderRadius: 8, border: 'none',
          background: 'var(--accent)', color: 'var(--on-accent)',
          fontSize: 12, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase',
          fontFamily: 'inherit', cursor: 'pointer',
        }}>Add your first record</button>
        <button onClick={onOpenImport} style={{
          padding: '10px 18px', borderRadius: 8,
          background: 'transparent', color: 'var(--fg)',
          border: '1px solid var(--border)',
          fontSize: 12, fontWeight: 700, letterSpacing: 0.5, textTransform: 'uppercase',
          fontFamily: 'inherit', cursor: 'pointer',
        }}>Import from Discogs</button>
      </div>
    </div>
  );
}

Object.assign(window, { CollectorStudio });
