// screens.jsx — Dashboard, Catalog, Work detail
const { useState: useS1, useEffect: useE1, useMemo: useM1 } = React;

// ───────────────────────────────────────────────────────────── DASHBOARD
function ScreenDashboard({ go, heroVariant = 'editorial', firstName = 'Paul', greetIndex }) {
  return (
    <div>
      {window.OnboardingHero && <window.OnboardingHero go={go} firstName={firstName} />}
      <DashboardHero go={go} variant={heroVariant} firstName={firstName} greetIndex={greetIndex} />

      {/* INBOX STRIP — top-of-page operating-loop preview, links to /inbox */}
      {window.InboxStrip && <window.InboxStrip go={go} />}

      {/* OPERATIONS PULSE — operational surfaces at a glance */}
      {window.OperationsPulse && <window.OperationsPulse go={go} />}

      {/* MAIN GRID — 3 cols (Earnings-by-source · Top earners · Top territories) */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr 1fr', gap: 32, marginTop: 32 }}>
        {/* EARNINGS / ANALYTICS */}
        <div>
          <Section num="03" action={<a className="ff-mono upper" onClick={() => go('analytics')} style={{ fontSize: 10, color: 'var(--ink-2)', cursor: 'pointer' }}>Full report →</a>}>Earnings · last 30 days</Section>
          <div style={{ borderTop: '1px solid var(--rule)', padding: '16px 0 8px' }}>
            <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 18, marginBottom: 14 }}>
              <div>
                <div className="ff-display num" style={{ fontSize: 48, fontWeight: 600, letterSpacing: '-0.04em', lineHeight: 1 }}>$248,302</div>
                <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 6 }}>NET · Q2 RUN-RATE · +8.4% YoY</div>
              </div>
              <div style={{ textAlign: 'right' }}>
                <Spark data={sparkRoyalty} w={160} h={48} stroke="var(--ink)" area fill="var(--ink)" />
                <div className="ff-mono num" style={{ fontSize: 10, color: 'var(--ok)', marginTop: 4 }}>▲ +12.4% MoM</div>
              </div>
            </div>
          </div>
          {/* By source — top 4 */}
          <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', padding: '10px 0 6px', borderTop: '1px solid var(--rule-soft)' }}>BY SOURCE</div>
          {[
          { l: 'Streaming · DSPs', v: '$108,422', p: 44, c: 'var(--ink)' },
          { l: 'Performance · PROs', v: '$62,118', p: 25, c: 'var(--ink-2)' },
          { l: 'Mechanical · MROs', v: '$41,022', p: 17, c: 'var(--ink-3)' },
          { l: 'Sync & Neighboring', v: '$36,740', p: 14, c: 'var(--accent)' }].
          map((s) =>
          <div key={s.l} onClick={() => go('analytics')} style={{ display: 'grid', gridTemplateColumns: '1fr 90px 60px', gap: 12, padding: '10px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center', cursor: 'pointer' }}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>{s.l}</div>
                <div style={{ height: 4, background: 'var(--bg-2)', position: 'relative' }}>
                  <div style={{ position: 'absolute', inset: 0, width: s.p + '%', background: s.c }} />
                </div>
              </div>
              <span className="ff-mono num" style={{ fontSize: 13, fontWeight: 600, textAlign: 'right' }}>{s.v}</span>
              <span className="ff-mono num" style={{ fontSize: 11, color: 'var(--ink-3)', textAlign: 'right' }}>{s.p}%</span>
            </div>
          )}
        </div>

        {/* TOP EARNERS */}
        <div>
          <Section num="04" action={<a onClick={() => go('analytics')} className="ff-mono upper" style={{ fontSize: 10, cursor: 'pointer' }}>All →</a>}>Top earners · Q2</Section>
          <div style={{ borderTop: '1px solid var(--rule)' }}>
            {WORKS.slice(0, 7).map((w, i) => {
              const earn = ['$48,210', '$31,402', '$22,118', '$18,902', '$14,711', '$11,308', '$8,442'][i];
              const pct = [+18, +24, +8, -2, +12, +6, +1][i];
              return (
                <div key={w.id} onClick={() => go('work', w)} style={{ display: 'grid', gridTemplateColumns: '24px 1fr auto', gap: 10, padding: '10px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center', cursor: 'pointer' }}>
                  <span className="ff-display num" style={{ fontSize: 14, color: 'var(--ink-4)', fontWeight: 600 }}>{String(i + 1).padStart(2, '0')}</span>
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{w.title}</div>
                    <div className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2 }}>{window.iswcDisplay ? window.iswcDisplay(w.iswc) : w.iswc}</div>
                  </div>
                  <div style={{ textAlign: 'right' }}>
                    <div className="ff-mono num" style={{ fontSize: 12, fontWeight: 600 }}>{earn}</div>
                    <div className="ff-mono num" style={{ fontSize: 9, color: pct >= 0 ? 'var(--ok)' : 'var(--danger)', marginTop: 2 }}>
                      {pct >= 0 ? '▲' : '▼'} {Math.abs(pct)}%
                    </div>
                  </div>
                </div>);

            })}
          </div>
        </div>

        {/* TERRITORIES */}
        <div>
          <Section num="05" action={<a onClick={() => go('analytics')} className="ff-mono upper" style={{ fontSize: 10, cursor: 'pointer' }}>All →</a>}>Top territories</Section>
          <div style={{ borderTop: '1px solid var(--rule)' }}>
            {[
            { c: 'United States', iso: 'US', v: '$108,422', p: 44 },
            { c: 'United Kingdom', iso: 'GB', v: '$32,118', p: 13 },
            { c: 'Germany', iso: 'DE', v: '$24,002', p: 10 },
            { c: 'France', iso: 'FR', v: '$18,440', p: 7 },
            { c: 'Japan', iso: 'JP', v: '$14,712', p: 6 },
            { c: 'Canada', iso: 'CA', v: '$12,308', p: 5 },
            { c: 'Brazil', iso: 'BR', v: '$9,801', p: 4 },
            { c: 'Rest of World', iso: '··', v: '$28,499', p: 11 }].
            map((t) =>
            <div key={t.iso} style={{ display: 'grid', gridTemplateColumns: '28px 1fr 70px', gap: 8, padding: '10px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center' }}>
                <span className="ff-mono" style={{ fontSize: 11, fontWeight: 600, color: 'var(--ink-3)' }}>{t.iso}</span>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 12, fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t.c}</div>
                  <div style={{ height: 3, background: 'var(--bg-2)', position: 'relative', marginTop: 4 }}>
                    <div style={{ position: 'absolute', inset: 0, width: t.p + '%', background: 'var(--ink)' }} />
                  </div>
                </div>
                <span className="ff-mono num" style={{ fontSize: 11, fontWeight: 600, textAlign: 'right' }}>{t.v}</span>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* BOTTOM ROW — Quick actions as oversized link list */}
      <div style={{ marginTop: 48 }}>
        <Section num="06">Operate</Section>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 0, borderTop: '1px solid var(--rule)' }}>
          {[
          { l: 'Generate CWR', k: '⌘ G', goto: 'cwr', payload: { tab: 'gen' } },
          { l: 'Deliver DDEX', k: '⌘ D', goto: 'cwr' },
          { l: 'New work', k: '⌘ N', event: 'astro-add-song' },
          { l: 'Royalty parse', k: '⌘ R', goto: 'analytics' },
          { l: 'Resolve claim', k: '⌘ Ⓘ', goto: 'claims' },
          { l: 'Bulk import', k: '⌘ ⇧ I', goto: 'catalog' },
          { l: 'Search catalog', k: '⌘ K', goto: 'search' },
          { l: 'Public profile', k: '⌘ P', goto: 'public' }].
          map((a, i, arr) =>
          <div key={a.l} onClick={() => {if (a.event) window.dispatchEvent(new CustomEvent(a.event));else go(a.goto, a.payload || null);}} style={{ padding: '24px 18px', cursor: 'pointer',
            borderRight: (i + 1) % 4 === 0 ? 'none' : '1px solid var(--rule)',
            borderBottom: i < arr.length - 4 ? '1px solid var(--rule)' : 'none',
            transition: 'all .15s' }}
          onMouseEnter={(e) => {e.currentTarget.style.background = 'var(--accent)';e.currentTarget.style.color = 'var(--accent-ink)';}}
          onMouseLeave={(e) => {e.currentTarget.style.background = 'transparent';e.currentTarget.style.color = 'var(--ink)';}}>
              <div className="heading-swap ff-display" style={{ fontSize: 24, fontWeight: 600, letterSpacing: '-0.03em' }}>{a.l}</div>
              <div className="ff-mono" style={{ fontSize: 10, opacity: .6, marginTop: 6 }}>{a.k}</div>
            </div>
          )}
        </div>
      </div>
    </div>);

}

// ───────────────────────────────────────────────────────────── CATALOG
function ScreenCatalog({ go, payload }) {
  const [tab, setTab] = useS1(payload?.tab || 'songs');
  // Label filter (e.g. arriving from a Label drawer's "View catalog")
  const [labelFilter, setLabelFilter] = useS1(payload?.label || '');
  const [labelCodeFilter, setLabelCodeFilter] = useS1(payload?.labelCode || '');
  // Publisher filter (from Publisher drawer's "Filter catalog →")
  const [pubFilter, setPubFilter] = useS1(payload?.publisher || '');
  const [pubIpiFilter, setPubIpiFilter] = useS1(payload?.publisherIpi || '');
  React.useEffect(() => {
    // If we navigate back to catalog with a new payload.label, sync.
    if (payload?.label !== undefined) {
      setLabelFilter(payload.label || '');
      setLabelCodeFilter(payload.labelCode || '');
    }
    if (payload?.publisher !== undefined) {
      setPubFilter(payload.publisher || '');
      setPubIpiFilter(payload.publisherIpi || '');
    }
  }, [payload?.label, payload?.labelCode, payload?.publisher, payload?.publisherIpi]);
  // When arriving with a focus id (e.g. from "Open in catalog"), scroll to it
  // and pulse a highlight ring once mounted.
  React.useEffect(() => {
    if (!payload?.focus) return;
    const id = payload.focus;
    let tries = 0;
    const tick = () => {
      const el = document.querySelector(`[data-rec-row="${id}"], [data-work-row="${id}"]`);
      if (el) {
        el.scrollIntoView({ behavior: 'smooth', block: 'center' });
        el.setAttribute('data-focus-pulse', 'on');
        setTimeout(() => el.removeAttribute('data-focus-pulse'), 2400);
        return;
      }
      if (tries++ < 20) setTimeout(tick, 80);
    };
    setTimeout(tick, 120);
  }, [payload?.focus]);
  const [view, setView] = useS1((payload?.tab || 'songs') === 'recordings' ? 'table' : 'grid');
  // Track which tabs the user has manually changed view for, so we don't override their choice
  const viewTouchedRef = React.useRef(new Set());
  const setViewManual = (v) => { viewTouchedRef.current.add(tab); setView(v); };
  // When tab changes, reset view to that tab's default unless the user already touched it
  React.useEffect(() => {
    if (viewTouchedRef.current.has(tab)) return;
    setView(tab === 'recordings' ? 'table' : 'grid');
  }, [tab]);
  const [sort, setSort] = useS1('edited');
  const [q, setQ] = useS1('');
  const [filterOpen, setFilterOpen] = useS1(false);
  const [bulkOpen, setBulkOpen] = useS1(false);
  // Bulk multi-select action menu (separate from the auto-match modal above)
  const [bulkAction, setBulkAction] = useS1(null); // { action, ids }
  const bulkScope = tab === 'recordings' ? 'cat-recordings' : tab === 'songs' ? 'cat-songs' : null;
  const [filters, setFilters] = useS1({
    status: [], // ['Registered','Pending','Conflict','Unmatched']
    societies: [], // ['ASCAP','BMI','SESAC','PRS','GEMA','SACEM','SOCAN']
    yearMin: 2015,
    yearMax: 2025,
    sharesComplete: 'any', // any | complete | incomplete
    hasRecordings: 'any' // any | yes | no
  });
  const activeCount =
  filters.status.length +
  filters.societies.length + (
  filters.yearMin !== 2015 || filters.yearMax !== 2025 ? 1 : 0) + (
  filters.sharesComplete !== 'any' ? 1 : 0) + (
  filters.hasRecordings !== 'any' ? 1 : 0);
  const resetFilters = () => setFilters({ status: [], societies: [], yearMin: 2015, yearMax: 2025, sharesComplete: 'any', hasRecordings: 'any' });

  // Apply a saved view: hydrates every relevant catalog control at once.
  const applyView = (v) => {
    if (!v) return;
    setTab(v.tab || 'songs');
    setView(v.view || 'grid');
    // mark this tab's view as user-touched so the tab-default effect doesn't override it
    viewTouchedRef.current.add(v.tab || 'songs');
    setSort(v.sort || 'edited');
    setQ(v.q || '');
    setLabelFilter(v.labelFilter || '');
    setLabelCodeFilter('');
    setPubFilter(v.pubFilter || '');
    setPubIpiFilter('');
    setFilters({
      status: (v.filters?.status || []).slice(),
      societies: (v.filters?.societies || []).slice(),
      yearMin: v.filters?.yearMin ?? 2015,
      yearMax: v.filters?.yearMax ?? 2025,
      sharesComplete: v.filters?.sharesComplete ?? 'any',
      hasRecordings: v.filters?.hasRecordings ?? 'any',
    });
  };
  // Snapshot of current state for the views component (so it can detect "modified")
  const viewsState = { tab, view, sort, q, labelFilter, pubFilter, filters };

  return (
    <div>
      {/* Heading — uses canonical <PageHeader> */}
      <PageHeader
        eyebrow={(() => {
          const cs = window.CATALOG_STATS || {};
          const w  = cs.works?.total      ?? (window.WORKS||[]).length;
          const r  = cs.recordings?.total ?? (window.RECORDINGS||[]).length;
          const rg = cs.releases?.total   ?? (window.RELEASE_GROUPS||[]).length;
          const ag = cs.agreements?.total ?? (window.AGREEMENTS||[]).length ?? 218;
          const vd = cs.videos?.total ?? ((window.RS && window.RS.videos) || []).length;
          return ['CATALOG', `${w.toLocaleString()}\u00a0WORKS`, `${r.toLocaleString()}\u00a0RECS`, `${rg.toLocaleString()}\u00a0RELEASES`, `${vd.toLocaleString()}\u00a0VIDEOS`, `${ag.toLocaleString()}\u00a0AGREEMENTS`];
        })()}
        title="catalog"
        highlight="catalog"
        sub="Every work, recording, release, video, and agreement on our catalog — what we represent and what we participate in."
        actions={
          <>
            <Btn variant="secondary" icon={<Ic.Filter />} size="sm" onClick={() => setFilterOpen(true)}>
              Filter{activeCount > 0 && <span className="ff-mono num" style={{ marginLeft: 6, padding: '1px 6px', background: 'var(--ink)', color: 'var(--bg)', fontSize: 10, fontWeight: 600 }}>{activeCount}</span>}
            </Btn>
            <Btn variant="secondary" icon={<Ic.Layers />} size="sm" onClick={() => setBulkOpen(true)}>Bulk match</Btn>
            {tab === 'recordings' ?
            <Btn variant="primary" icon={<Ic.Plus />} size="sm" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-recording', { detail: {} }))}>Add recording</Btn> :
            tab === 'releases' ?
            <Btn variant="primary" icon={<Ic.Plus />} size="sm" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-release'))}>New release</Btn> :
            tab === 'videos' ?
            <Btn variant="primary" icon={<Ic.Plus />} size="sm" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-video'))}>New video</Btn> :
            tab === 'agreements' ?
            <>
              <Btn variant="ghost" icon={<Ic.Layers />} size="sm" onClick={() => window.__astroGo && window.__astroGo('agreement-templates')}>Templates</Btn>
              <Btn variant="primary" icon={<Ic.Plus />} size="sm" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-agreement'))}>New agreement</Btn>
            </> :
            <Btn variant="primary" icon={<Ic.Plus />} size="sm" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-song'))}>New work</Btn>}
          </>
        }>
        {(labelFilter || pubFilter) && (
            <div style={{marginTop:14,display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
              {labelFilter && (<>
                <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.1em'}}>FILTERED BY LABEL ·</span>
                <span style={{display:'inline-flex',alignItems:'center',gap:8,padding:'6px 6px 6px 12px',
                  background:'var(--ink)',color:'var(--bg)',fontSize:12,fontWeight:600}}>
                  {labelCodeFilter && (<span className="ff-mono num" style={{fontSize:10,padding:'2px 6px',background:'rgba(255,255,255,.18)'}}>{labelCodeFilter}</span>)}
                  {labelFilter}
                  <button onClick={()=>{ setLabelFilter(''); setLabelCodeFilter(''); }} aria-label="Clear label filter"
                    title="Clear label filter"
                    style={{background:'transparent',border:0,color:'var(--bg)',fontSize:14,cursor:'pointer',padding:'0 6px',lineHeight:1}}>×</button>
                </span>
              </>)}
              {pubFilter && (<>
                <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.1em'}}>FILTERED BY PUBLISHER ·</span>
                <span style={{display:'inline-flex',alignItems:'center',gap:8,padding:'6px 6px 6px 12px',
                  background:'var(--ink)',color:'var(--bg)',fontSize:12,fontWeight:600}}>
                  {pubIpiFilter && (<span className="ff-mono num" style={{fontSize:10,padding:'2px 6px',background:'rgba(255,255,255,.18)'}}>IPI {pubIpiFilter}</span>)}
                  {pubFilter}
                  <button onClick={()=>{ setPubFilter(''); setPubIpiFilter(''); }} aria-label="Clear publisher filter"
                    title="Clear publisher filter"
                    style={{background:'transparent',border:0,color:'var(--bg)',fontSize:14,cursor:'pointer',padding:'0 6px',lineHeight:1}}>×</button>
                </span>
              </>)}
            </div>
          )}
      </PageHeader>

      {/* Saved views row removed — was hardcoded mock filter combos. */}

      {/* Tabs */}
      <div style={{ borderBottom: '1px solid var(--rule)', display: 'flex', gap: 0, marginBottom: 0 }}>
        {(() => {
          const cs = window.CATALOG_STATS || {};
          return [
            { k: 'songs',      l: 'Works',      n: cs.works?.total      ?? 18422, sub: 'Compositions + their recordings' },
            { k: 'recordings', l: 'Recordings', n: cs.recordings?.total ?? 32104 },
            { k: 'releases',   l: 'Releases',   n: cs.releases?.total   ?? 4811  },
            { k: 'videos',     l: 'Videos',     n: cs.videos?.total     ?? ((window.RS && window.RS.videos) || []).length },
            { k: 'agreements', l: 'Agreements', n: cs.agreements?.total ?? 218   },
          ];
        })().
        map((t) =>
        <button key={t.k} onClick={() => setTab(t.k)} className="ff-mono upper"
        style={{ padding: '14px 18px', fontSize: 11, fontWeight: 600, letterSpacing: '.08em',
          borderBottom: tab === t.k ? '3px solid var(--ink)' : '3px solid transparent',
          marginBottom: -1, color: tab === t.k ? 'var(--ink)' : 'var(--ink-3)',
          display: 'flex', alignItems: 'baseline', gap: 8 }}>
            {t.l}
            <span className="ff-mono num" style={{ fontSize: 10, opacity: .6, fontWeight: 400 }}>{t.n.toLocaleString()}</span>
          </button>
        )}
      </div>

      {/* Toolbar */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '14px 0', borderBottom: '1px solid var(--rule)' }}>
        <div style={{ display: 'flex', gap: 0, alignItems: 'center', flex: 1, maxWidth: 400 }}>
          <Ic.Search width={14} height={14} style={{ color: 'var(--ink-3)' }} />
          <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search title, ISWC, ISRC, writer..."
          style={{ flex: 1, padding: '6px 10px', background: 'transparent', border: 'none', fontSize: 13, outline: 'none', color: 'var(--ink)' }} />
        </div>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
          <span className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)' }}>SORT</span>
          <select value={sort} onChange={(e)=>setSort(e.target.value)} className="ff-mono" style={{ padding: '4px 8px', border: '1px solid var(--rule)', background: 'var(--bg)', color: 'var(--ink)', fontSize: 11, cursor: 'pointer' }}>
            <option value="edited">Last edited</option>
            <option value="title">Title A→Z</option>
            <option value="title-desc">Title Z→A</option>
            <option value="plays">Plays ↓</option>
            <option value="royalties">Royalties ↓</option>
            <option value="created">Date added ↓</option>
            {tab === 'recordings' && <option value="predicted">Predicted score ↓</option>}
          </select>
          <span style={{ width: 1, height: 14, background: 'var(--rule)', margin: '0 6px' }} />
          <button onClick={() => setViewManual('grid')} style={{ padding: 6, border: '1px solid var(--rule)', background: view === 'grid' ? 'var(--ink)' : 'transparent', color: view === 'grid' ? 'var(--bg)' : 'var(--ink)' }}><Ic.Grid width={14} height={14} /></button>
          <button onClick={() => setViewManual('table')} style={{ padding: 6, border: '1px solid var(--rule)', background: view === 'table' ? 'var(--ink)' : 'transparent', color: view === 'table' ? 'var(--bg)' : 'var(--ink)' }}><Ic.Rows width={14} height={14} /></button>
        </div>
      </div>

      {bulkScope && (
        <BulkActionBar
          scope={bulkScope}
          kind={tab === 'recordings' ? 'recordings' : 'songs'}
          onAction={(action, ids) => setBulkAction({ action, ids })}
        />
      )}

      {tab === 'songs' && <SongsView go={go} q={q} filters={filters} sort={sort} view={view} labelFilter={labelFilter} publisherFilter={pubFilter} bulkScope={bulkScope}/>}
      {tab === 'recordings' && <RecordingsView go={go} q={q} filters={filters} sort={sort} view={view} labelFilter={labelFilter} bulkScope={bulkScope}/>}
      {tab === 'releases' && <ReleasesView go={go} labelFilter={labelFilter} />}
      {tab === 'videos' && window.VideosView && <window.VideosView go={go} q={q} view={view} />}
      {tab === 'agreements' && <AgreementsView go={go} />}

      {/* Filter drawer */}
      {filterOpen && <CatalogFilterDrawer filters={filters} setFilters={setFilters} reset={resetFilters} close={() => setFilterOpen(false)} activeCount={activeCount} />}
      {bulkOpen && <BulkMatchModal close={() => setBulkOpen(false)} />}
      {bulkAction && (
        <BulkActionModal
          action={bulkAction.action}
          scope={bulkScope}
          kind={tab === 'recordings' ? 'recordings' : 'songs'}
          ids={bulkAction.ids}
          onClose={() => setBulkAction(null)}
        />
      )}
    </div>);

}

function BulkMatchModal({ close }) {
  const CANDIDATES = [
  { id: 'r1', recording: 'Cranes In The Sky (Demo v3)', isrc: 'USRC1-24-00831', artist: 'Solange', work: 'Cranes In The Sky', iswc: 'T-913.221.084-2', conf: 0.98, reason: 'Exact title · ISRC family match' },
  { id: 'r2', recording: 'Cranes In The Sky (Live at Radio City)', isrc: 'USRC1-24-00832', artist: 'Solange', work: 'Cranes In The Sky', iswc: 'T-913.221.084-2', conf: 0.96, reason: 'Title + writer overlap' },
  { id: 'r3', recording: 'Almeda — Pluralis Edit', isrc: 'USRC1-24-00919', artist: 'Solange', work: 'Almeda', iswc: 'T-913.221.092-7', conf: 0.94, reason: 'Phonetic match · same album' },
  { id: 'r4', recording: 'Way to the Show', isrc: 'USRC1-24-01112', artist: 'Saint JHN', work: 'Way to the Show', iswc: 'T-914.002.118-3', conf: 0.91, reason: 'Title + label feed' },
  { id: 'r5', recording: 'Don\'t Touch My Hair (Acoustic)', isrc: 'USRC1-24-01441', artist: 'Solange', work: 'Don\'t Touch My Hair', iswc: 'T-913.221.077-9', conf: 0.89, reason: 'Stripped suffix match' },
  { id: 'r6', recording: 'Borderline (An Ode to Self)', isrc: 'USRC1-24-01502', artist: 'Solange', work: 'Borderline', iswc: 'T-913.221.066-1', conf: 0.87, reason: 'Subtitle inferred' },
  { id: 'r7', recording: 'F.U.B.U. — Radio Edit', isrc: 'USRC1-24-01587', artist: 'Solange', work: 'F.U.B.U.', iswc: 'T-913.221.058-4', conf: 0.84, reason: 'Punctuation normalized' },
  { id: 'r8', recording: 'Things I Imagined', isrc: 'USRC1-24-01612', artist: 'Solange', work: 'Things I Imagined', iswc: 'T-913.221.071-5', conf: 0.79, reason: 'Writer overlap (1 of 3)' },
  { id: 'r9', recording: 'Mad (Sped Up)', isrc: 'USRC1-24-01744', artist: 'Solange', work: 'Mad', iswc: 'T-913.221.060-6', conf: 0.72, reason: 'Likely derivative — flag for review' },
  { id: 'r10', recording: 'Untitled Outro 04', isrc: 'USRC1-24-02019', artist: 'Pluralis collective', work: '(no candidate)', iswc: '—', conf: 0.34, reason: 'Below threshold · skip recommended' }];

  const [selected, setSelected] = useS1(() => new Set(CANDIDATES.filter((c) => c.conf >= 0.85).map((c) => c.id)));
  const [threshold, setThreshold] = useS1(0.85);
  const [running, setRunning] = useS1(false);
  const [progress, setProgress] = useS1(0);
  const [done, setDone] = useS1(false);

  const toggle = (id) => {const n = new Set(selected);n.has(id) ? n.delete(id) : n.add(id);setSelected(n);};
  const selectAtThreshold = (t) => {setThreshold(t);setSelected(new Set(CANDIDATES.filter((c) => c.conf >= t).map((c) => c.id)));};

  React.useEffect(() => {
    if (!running) return;
    const t = setInterval(() => {
      setProgress((p) => {
        if (p >= 100) {clearInterval(t);setDone(true);setRunning(false);return 100;}
        return p + 4;
      });
    }, 80);
    return () => clearInterval(t);
  }, [running]);

  const apply = () => {
    setRunning(true);
    setProgress(0);
  };
  const finish = () => {
    window.dispatchEvent(new CustomEvent('astro-toast', { detail: { msg: `${selected.size} recordings matched · ${CANDIDATES.length - selected.size} skipped`, tone: 'ok' } }));
    close();
  };

  return (
    <>
      <div onClick={running ? undefined : close} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.42)', zIndex: 80 }} />
      <div role="dialog" aria-modal="true" style={{
        position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 'min(960px,96vw)', maxHeight: '90vh',
        background: 'var(--bg)', border: '1px solid var(--rule)', zIndex: 81, display: 'flex', flexDirection: 'column',
        boxShadow: '0 30px 80px rgba(0,0,0,.34)' }}>
        {/* Header */}
        <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 14 }}>
          <div>
            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 4 }}>BULK MATCH · 142 UNMATCHED RECORDINGS</div>
            <div className="ff-display" style={{ fontSize: 26, fontWeight: 700, letterSpacing: '-0.02em', lineHeight: 1.05 }}>
              {done ? 'Match complete' : running ? 'Matching…' : 'Review proposed matches'}
            </div>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 6 }}>
              {done ? `${selected.size} recordings linked to works · ISRC↔ISWC mappings registered` :
              running ? 'Writing CWR registrations and updating splits…' :
              'Confidence is title + writer + ISRC + label-feed. You confirm; ASTRO writes the link.'}
            </div>
          </div>
          {!running && <button onClick={close} aria-label="Close" style={{ width: 30, height: 30, background: 'transparent', border: '1px solid var(--rule)', cursor: 'pointer', fontSize: 16, lineHeight: 1 }}>×</button>}
        </div>

        {!running && !done &&
        <>
            {/* Threshold control */}
            <div style={{ padding: '14px 24px', borderBottom: '1px solid var(--rule-soft)', background: 'var(--bg-2)', display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
              <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.1em', color: 'var(--ink-3)' }}>AUTO-SELECT AT</div>
              {[0.95, 0.9, 0.85, 0.75, 0.5].map((t) =>
            <button key={t} onClick={() => selectAtThreshold(t)} className="ff-mono num" style={{
              padding: '6px 10px', fontSize: 11, cursor: 'pointer',
              background: threshold === t ? 'var(--ink)' : 'transparent',
              color: threshold === t ? 'var(--bg)' : 'var(--ink-2)',
              border: '1px solid ' + (threshold === t ? 'var(--ink)' : 'var(--rule)') }}>≥ {Math.round(t * 100)}%</button>
            )}
              <span style={{ flex: 1 }} />
              <span className="ff-mono num" style={{ fontSize: 12, fontWeight: 600 }}>{selected.size}/{CANDIDATES.length} selected</span>
            </div>

            {/* Table */}
            <div style={{ flex: 1, overflowY: 'auto' }}>
              <table className="ff-body" style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
                <thead>
                  <tr className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.1em', textAlign: 'left', background: 'var(--bg)' }}>
                    <th style={{ padding: '10px 24px', borderBottom: '1px solid var(--rule)', width: 30 }} />
                    <th style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule)' }}>Recording</th>
                    <th style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule)' }}>→ Work</th>
                    <th style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule)' }}>Reason</th>
                    <th style={{ padding: '10px 24px 10px 8px', borderBottom: '1px solid var(--rule)', textAlign: 'right', width: 80 }}>Conf.</th>
                  </tr>
                </thead>
                <tbody>
                  {CANDIDATES.map((c) => {
                  const on = selected.has(c.id);
                  const lo = c.conf < 0.75;
                  return (
                    <tr key={c.id} onClick={() => toggle(c.id)} style={{ cursor: 'pointer', background: on ? 'var(--bg-2)' : 'transparent' }}>
                        <td style={{ padding: '10px 24px', borderBottom: '1px solid var(--rule-soft)' }}>
                          <input type="checkbox" checked={on} onChange={() => toggle(c.id)} onClick={(e) => e.stopPropagation()} />
                        </td>
                        <td style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule-soft)' }}>
                          <div style={{ fontWeight: 500 }}>{c.recording}</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{c.artist} · {c.isrc}</div>
                        </td>
                        <td style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule-soft)' }}>
                          <div style={{ fontWeight: 500, color: c.work === '(no candidate)' ? 'var(--ink-3)' : 'var(--ink)' }}>{c.work}</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{window.iswcDisplay ? window.iswcDisplay(c.iswc) : c.iswc}</div>
                        </td>
                        <td style={{ padding: '10px 8px', borderBottom: '1px solid var(--rule-soft)', color: 'var(--ink-2)' }}>{c.reason}</td>
                        <td style={{ padding: '10px 24px 10px 8px', borderBottom: '1px solid var(--rule-soft)', textAlign: 'right' }}>
                          <span className="ff-mono num" style={{ fontSize: 12, fontWeight: 600, color: lo ? '#c0392b' : c.conf >= 0.9 ? '#2d6a4f' : 'var(--ink)' }}>
                            {Math.round(c.conf * 100)}%
                          </span>
                        </td>
                      </tr>);

                })}
                </tbody>
              </table>
            </div>
          </>
        }

        {running &&
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '60px 24px', gap: 20 }}>
            <div style={{ width: 'min(560px,90%)' }}>
              <div className="ff-mono num" style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, marginBottom: 6 }}>
                <span>{Math.min(Math.round(progress * selected.size / 100), selected.size)} / {selected.size} matched</span>
                <span>{progress}%</span>
              </div>
              <div style={{ height: 4, background: 'var(--rule-soft)', position: 'relative', overflow: 'hidden' }}>
                <div style={{ position: 'absolute', inset: 0, width: `${progress}%`, background: 'var(--ink)', transition: 'width .08s linear' }} />
              </div>
            </div>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.06em' }}>
              Writing CWR · updating ISRC↔ISWC index · pre-registering with societies
            </div>
          </div>
        }

        {done &&
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '60px 24px', gap: 14, textAlign: 'center' }}>
            <div style={{ width: 64, height: 64, border: '2px solid var(--ink)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 28 }}>✓</div>
            <div className="ff-display" style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em' }}>{selected.size} matches written</div>
            <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)', maxWidth: 420, lineHeight: 1.6 }}>
              {CANDIDATES.length - selected.size} recordings remain unmatched and will return to the queue for the next nightly pass.
            </div>
          </div>
        }

        {/* Footer */}
        <div style={{ padding: '14px 24px', borderTop: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'var(--bg-2)', gap: 10 }}>
          {!running && !done &&
          <>
              <button onClick={close} className="ff-mono upper" style={{ padding: '8px 14px', fontSize: 11, letterSpacing: '.08em', background: 'transparent', color: 'var(--ink-2)', border: '1px solid var(--rule)', cursor: 'pointer' }}>Cancel</button>
              <button onClick={apply} disabled={selected.size === 0} className="ff-mono upper" style={{ padding: '8px 18px', fontSize: 11, letterSpacing: '.08em', background: selected.size === 0 ? 'var(--ink-4)' : 'var(--accent)', color: 'var(--ink)', border: 0, cursor: selected.size === 0 ? 'default' : 'pointer', fontWeight: 600 }}>
                Match {selected.size} recording{selected.size === 1 ? '' : 's'} →
              </button>
            </>
          }
          {running && <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.1em' }}>DO NOT CLOSE</div>}
          {done &&
          <>
              <span />
              <button onClick={finish} className="ff-mono upper" style={{ padding: '8px 18px', fontSize: 11, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer', fontWeight: 600 }}>Done</button>
            </>
          }
        </div>
      </div>
    </>);

}

function CatalogFilterDrawer({ filters, setFilters, reset, close, activeCount }) {
  const STATUSES = ['Registered', 'Pending', 'Conflict', 'Unmatched'];
  // Sourced from the central Societies directory
  const SOCIETIES = (window.SOC_WRITER || ['ASCAP', 'BMI', 'SESAC', 'PRS', 'GEMA', 'SACEM', 'SOCAN', 'APRA', 'JASRAC']);
  const toggle = (key, val) => setFilters({ ...filters, [key]: filters[key].includes(val) ? filters[key].filter((v) => v !== val) : [...filters[key], val] });
  return (
    <>
      <div onClick={close} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,.32)', zIndex: 60 }} />
      <div role="dialog" aria-modal="true" style={{
        position: 'fixed', top: 0, right: 0, bottom: 0, width: 'min(420px,92vw)',
        background: 'var(--bg)', borderLeft: '1px solid var(--rule)', zIndex: 61,
        boxShadow: '-30px 0 80px rgba(0,0,0,.18)', display: 'flex', flexDirection: 'column' }}>
        {/* Header */}
        <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
          <div>
            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 4 }}>FILTERS{activeCount > 0 && ` · ${activeCount} ACTIVE`}</div>
            <div className="ff-display" style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em' }}>Refine catalog</div>
          </div>
          <button onClick={close} aria-label="Close" style={{ width: 30, height: 30, background: 'transparent', border: '1px solid var(--rule)', cursor: 'pointer', fontSize: 16, lineHeight: 1 }}>×</button>
        </div>

        {/* Body */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '8px 22px 22px' }}>
          {/* Status */}
          <div style={{ padding: '18px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 10 }}>REGISTRATION STATUS</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {STATUSES.map((s) => {
                const on = filters.status.includes(s);
                return <button key={s} onClick={() => toggle('status', s)} className="ff-mono upper" style={{
                  padding: '7px 12px', fontSize: 10, letterSpacing: '.06em', cursor: 'pointer',
                  background: on ? 'var(--ink)' : 'transparent', color: on ? 'var(--bg)' : 'var(--ink-2)',
                  border: '1px solid ' + (on ? 'var(--ink)' : 'var(--rule)') }}>{s}</button>;
              })}
            </div>
          </div>

          {/* Societies */}
          <div style={{ padding: '18px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 10 }}>WRITER SOCIETY</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
              {SOCIETIES.map((s) => {
                const on = filters.societies.includes(s);
                return <button key={s} onClick={() => toggle('societies', s)} className="ff-mono upper" style={{
                  padding: '7px 12px', fontSize: 10, letterSpacing: '.06em', cursor: 'pointer',
                  background: on ? 'var(--ink)' : 'transparent', color: on ? 'var(--bg)' : 'var(--ink-2)',
                  border: '1px solid ' + (on ? 'var(--ink)' : 'var(--rule)') }}>{s}</button>;
              })}
            </div>
          </div>

          {/* Year range */}
          <div style={{ padding: '18px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
              <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)' }}>COPYRIGHT YEAR</div>
              <div className="ff-mono num" style={{ fontSize: 11, fontWeight: 600 }}>{filters.yearMin} – {filters.yearMax}</div>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
              <div>
                <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginBottom: 4 }}>FROM</div>
                <input type="number" min={1900} max={filters.yearMax} value={filters.yearMin}
                onChange={(e) => setFilters({ ...filters, yearMin: Math.min(filters.yearMax, +e.target.value || 1900) })}
                className="ff-mono num" style={{ width: '100%', padding: '8px 10px', background: 'var(--bg)', border: '1px solid var(--rule)', fontSize: 13, color: 'var(--ink)' }} />
              </div>
              <div>
                <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginBottom: 4 }}>TO</div>
                <input type="number" min={filters.yearMin} max={2030} value={filters.yearMax}
                onChange={(e) => setFilters({ ...filters, yearMax: Math.max(filters.yearMin, +e.target.value || 2025) })}
                className="ff-mono num" style={{ width: '100%', padding: '8px 10px', background: 'var(--bg)', border: '1px solid var(--rule)', fontSize: 13, color: 'var(--ink)' }} />
              </div>
            </div>
          </div>

          {/* Shares complete */}
          <div style={{ padding: '18px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 10 }}>WRITER SHARES</div>
            <div style={{ display: 'flex', gap: 6 }}>
              {[['any', 'Any'], ['complete', '100% allocated'], ['incomplete', 'Incomplete / over']].map(([k, l]) => {
                const on = filters.sharesComplete === k;
                return <button key={k} onClick={() => setFilters({ ...filters, sharesComplete: k })} className="ff-mono upper" style={{
                  flex: 1, padding: '7px 10px', fontSize: 10, letterSpacing: '.06em', cursor: 'pointer',
                  background: on ? 'var(--ink)' : 'transparent', color: on ? 'var(--bg)' : 'var(--ink-2)',
                  border: '1px solid ' + (on ? 'var(--ink)' : 'var(--rule)') }}>{l}</button>;
              })}
            </div>
          </div>

          {/* Has recordings */}
          <div style={{ padding: '18px 0' }}>
            <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 10 }}>RECORDINGS LINKED</div>
            <div style={{ display: 'flex', gap: 6 }}>
              {[['any', 'Any'], ['yes', 'Has recordings'], ['no', 'Unrecorded only']].map(([k, l]) => {
                const on = filters.hasRecordings === k;
                return <button key={k} onClick={() => setFilters({ ...filters, hasRecordings: k })} className="ff-mono upper" style={{
                  flex: 1, padding: '7px 10px', fontSize: 10, letterSpacing: '.06em', cursor: 'pointer',
                  background: on ? 'var(--ink)' : 'transparent', color: on ? 'var(--bg)' : 'var(--ink-2)',
                  border: '1px solid ' + (on ? 'var(--ink)' : 'var(--rule)') }}>{l}</button>;
              })}
            </div>
          </div>
        </div>

        {/* Footer */}
        <div style={{ padding: '14px 22px', borderTop: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'var(--bg-2)', gap: 10 }}>
          <button onClick={reset} className="ff-mono upper" style={{ padding: '8px 14px', fontSize: 11, letterSpacing: '.08em', background: 'transparent', color: 'var(--ink-2)', border: '1px solid var(--rule)', cursor: 'pointer' }}>
            Reset
          </button>
          <button onClick={close} className="ff-mono upper" style={{ padding: '8px 18px', fontSize: 11, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer', fontWeight: 600 }}>
            Apply{activeCount > 0 && ` · ${activeCount}`}
          </button>
        </div>
      </div>
    </>);

}

function WorksTable({ rows, go }) {
  return (
    <div style={{ marginTop: 0 }}>
      <table className="ff-body num" style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
        <thead>
          <tr className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', textAlign: 'left' }}>
            <th style={{ padding: '10px 0', borderBottom: '1px solid var(--rule)', width: 30 }}><input type="checkbox" /></th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)' }}>Title</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)' }}>ISWC</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)' }}>Writers</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)' }}>Shares</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)' }}>Status</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)', textAlign: 'right' }}>Plays</th>
            <th style={{ padding: '10px 12px', borderBottom: '1px solid var(--rule)', textAlign: 'right' }}>Soc.</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((w, i) =>
          <tr key={w.id} onClick={() => go('work', w)} style={{ borderBottom: '1px solid var(--rule-soft)', cursor: 'pointer', transition: 'background .1s' }}
          onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-2)'}
          onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
              <td style={{ padding: '12px 0' }}><input type="checkbox" onClick={(e) => e.stopPropagation()} /></td>
              <td style={{ padding: '12px' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                  <span className="ff-display num" style={{ fontSize: 13, color: 'var(--ink-4)', width: 24 }}>{String(i + 1).padStart(2, '0')}</span>
                  <span style={{ fontWeight: 600, letterSpacing: '-0.01em', fontSize: 14 }}>{w.title}</span>
                </div>
              </td>
              <td style={{ padding: '12px', fontFamily: 'IBM Plex Mono', fontSize: 11, color: 'var(--ink-3)' }}>{window.iswcDisplay ? window.iswcDisplay(w.iswc) : w.iswc}</td>
              <td style={{ padding: '12px', fontSize: 12, color: 'var(--ink-2)' }}>{w.writers.join(' · ')}</td>
              <td style={{ padding: '12px', fontFamily: 'IBM Plex Mono', fontSize: 11 }}>{w.shares}</td>
              <td style={{ padding: '12px' }}>
                <Pill tone={w.status === 'registered' ? 'ok' : w.status === 'conflict' ? 'danger' : 'accent'} dot>{w.status}</Pill>
              </td>
              <td style={{ padding: '12px', textAlign: 'right', fontFamily: 'IBM Plex Mono', fontSize: 12 }}>{(w.plays / 1e6).toFixed(1)}M</td>
              <td style={{ padding: '12px', textAlign: 'right', fontFamily: 'IBM Plex Mono', fontSize: 12 }}>{w.societies}</td>
            </tr>
          )}
        </tbody>
      </table>
    </div>);

}

function WorksGrid({ rows, go }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(280px,1fr))', gap: 0,
      borderLeft: '1px solid var(--rule)', marginTop: 0 }}>
      {rows.map((w, i) =>
      <div key={w.id} onClick={() => go('work', w)} style={{ padding: 18, borderRight: '1px solid var(--rule)',
        borderBottom: '1px solid var(--rule)', cursor: 'pointer', position: 'relative', minHeight: 220,
        display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}
      onMouseEnter={(e) => e.currentTarget.style.background = 'var(--bg-2)'}
      onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}>
          <div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 18 }}>
              <span className="ff-mono num" style={{ fontSize: 10, color: 'var(--ink-4)' }}>№ {String(i + 1).padStart(3, '0')}</span>
              <Pill tone={w.status === 'registered' ? 'ok' : w.status === 'conflict' ? 'danger' : 'accent'} dot>{w.status}</Pill>
            </div>
            <div className="heading-swap ff-display" style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.025em', lineHeight: 1.05, marginBottom: 10 }}>
              {w.title}
            </div>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 4 }}>{window.iswcDisplay ? window.iswcDisplay(w.iswc) : w.iswc}</div>
            <div style={{ fontSize: 12, color: 'var(--ink-2)' }}>{w.writers.join(' · ')}</div>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--rule-soft)' }}>
            <div>
              <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-4)' }}>Plays</div>
              <div className="ff-mono num" style={{ fontSize: 14 }}>{(w.plays / 1e6).toFixed(1)}M</div>
            </div>
            <div>
              <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-4)' }}>Shares</div>
              <div className="ff-mono num" style={{ fontSize: 14 }}>{w.shares}</div>
            </div>
            <div>
              <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-4)' }}>Soc.</div>
              <div className="ff-mono num" style={{ fontSize: 14 }}>{w.societies}</div>
            </div>
          </div>
        </div>
      )}
    </div>);

}

function ProfilesGrid({ rows, go, q='', filters }) {
  const f = filters || {};
  const ql = (q||'').trim().toLowerCase();
  const filtered = rows.filter(p => {
    if (ql) {
      const blob = `${p.name} ${p.realName||''} ${p.ipi||''} ${p.pro||''} ${p.country||''}`.toLowerCase();
      if (!blob.includes(ql)) return false;
    }
    if (f.verified === 'verified'   && !p.legal) return false;
    if (f.verified === 'unverified' && p.legal) return false;
    if (f.pro && f.pro !== 'any' && p.pro !== f.pro) return false;
    if (f.country && f.country !== 'any') {
      if (f.country === 'OTHER') {
        if (['US','GB','CA','VE'].includes(p.country)) return false;
      } else if (p.country !== f.country) return false;
    }
    if (f.entityType && f.entityType !== 'any') {
      // Default to 'Person' when the record predates the type field.
      const t = p.type || 'Person';
      if (t !== f.entityType) return false;
    }
    return true;
  });
  if (filtered.length === 0) {
    return (
      <div style={{padding:'48px 24px',borderLeft:'1px solid var(--rule)',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',textAlign:'center',color:'var(--ink-3)'}}>
        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em'}}>NO PROFILES MATCH THE CURRENT FILTERS</div>
      </div>
    );
  }
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(220px,1fr))', gap: 0, borderLeft: '1px solid var(--rule)' }}>
      {filtered.map((p, i) =>
      <div key={p.id} style={{ borderRight: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', padding: 0, cursor: 'pointer' }}
      onClick={() => go('public', p)}>
          {/* Tile artwork — abstract block */}
          <div style={{ aspectRatio: '1', background: p.color, position: 'relative', overflow: 'hidden', borderBottom: '1px solid var(--rule)' }}>
            <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <span className="ff-display" style={{ fontSize: 80, fontWeight: 800, color: 'rgba(0,0,0,.18)', letterSpacing: '-0.06em' }}>
                {p.name.split(' ').map((n) => n[0]).slice(0, 2).join('')}
              </span>
            </div>
            <div style={{ position: 'absolute', top: 8, left: 10, color: 'rgba(0,0,0,.6)' }}>
              <span className="ff-mono num" style={{ fontSize: 9, fontWeight: 600 }}>№ {String(i + 1).padStart(3, '0')}</span>
            </div>
            <div style={{ position: 'absolute', bottom: 8, right: 10, color: 'rgba(0,0,0,.6)' }}>
              <Ic.Disc width={16} height={16} />
            </div>
          </div>
          <div style={{ padding: '12px 14px' }}>
            <div className="heading-swap ff-display" style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.02em', lineHeight: 1.1 }}>{window.PartyIndicator && <window.PartyIndicator party={p}/>}{p.name}</div>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 4, display: 'flex', justifyContent: 'space-between' }}>
              <span><SocietyLink code={p.pro}/> · {p.country}</span>
              <span className="num">{p.roster} works</span>
            </div>
          </div>
        </div>
      )}
    </div>);

}

// ───────────────────────────────────────────────────────────── HERO PLAYER
function makeDemoToneWav(seed = 0, durationSec = 30) {
  // Synthesize a simple, distinct demo tone as a WAV blob.
  // Different seeds → different fundamental frequency + envelope → so each work sounds different.
  const sampleRate = 22050;
  const numSamples = Math.floor(sampleRate * durationSec);
  const fundamental = 110 + Math.abs(seed) % 7 * 22; // 110-242Hz range
  const data = new Float32Array(numSamples);
  for (let i = 0; i < numSamples; i++) {
    const t = i / sampleRate;
    // Three harmonics + slow LFO for movement, fade in/out envelope
    const env = Math.min(1, t / 0.4) * Math.min(1, (durationSec - t) / 0.4);
    const lfo = 0.5 + 0.5 * Math.sin(2 * Math.PI * 0.25 * t);
    const sample = (
    0.45 * Math.sin(2 * Math.PI * fundamental * t) +
    0.20 * Math.sin(2 * Math.PI * fundamental * 2 * t) * lfo +
    0.10 * Math.sin(2 * Math.PI * fundamental * 3 * t) * (1 - lfo)) *
    env * 0.4;
    data[i] = sample;
  }
  // Write WAV
  const buf = new ArrayBuffer(44 + numSamples * 2);
  const view = new DataView(buf);
  const writeStr = (off, s) => {for (let i = 0; i < s.length; i++) view.setUint8(off + i, s.charCodeAt(i));};
  writeStr(0, 'RIFF');
  view.setUint32(4, 36 + numSamples * 2, true);
  writeStr(8, 'WAVE');writeStr(12, 'fmt ');
  view.setUint32(16, 16, true);view.setUint16(20, 1, true);view.setUint16(22, 1, true);
  view.setUint32(24, sampleRate, true);view.setUint32(28, sampleRate * 2, true);
  view.setUint16(32, 2, true);view.setUint16(34, 16, true);
  writeStr(36, 'data');view.setUint32(40, numSamples * 2, true);
  let off = 44;
  for (let i = 0; i < numSamples; i++) {
    const s = Math.max(-1, Math.min(1, data[i]));
    view.setInt16(off, s < 0 ? s * 0x8000 : s * 0x7fff, true);
    off += 2;
  }
  return new Blob([buf], { type: 'audio/wav' });
}

const _DEMO_AUDIO_CACHE = {};
function getDemoAudioUrl(workId) {
  if (_DEMO_AUDIO_CACHE[workId]) return _DEMO_AUDIO_CACHE[workId];
  const seed = (workId || 'x').split('').reduce((s, c) => s + c.charCodeAt(0), 0);
  const blob = makeDemoToneWav(seed, 30);
  const url = URL.createObjectURL(blob);
  _DEMO_AUDIO_CACHE[workId] = url;
  return url;
}

function WorkHeroPlayer({ w, commit }) {
  const audioRef = React.useRef(null);
  const fileInputRef = React.useRef(null);
  const rafRef = React.useRef(0);
  const [playing, setPlaying] = React.useState(false);
  const [time, setTime] = React.useState(0);
  const [duration, setDuration] = React.useState(0);
  const [src, setSrc] = React.useState(null); // {url, label, kind: 'demo'|'upload'|'recording'|'memo', filename?}
  const [menuOpen, setMenuOpen] = React.useState(false);
  const [menuView, setMenuView] = React.useState('main'); // 'main' | 'recordings'
  const [recState, setRecState] = React.useState({ active: false, elapsed: 0, error: null }); // mic capture
  const recRef = React.useRef(null); // {recorder, stream, chunks, startedAt, intervalId}

  const recCount = typeof recordingsForWork === 'function' ? recordingsForWork(w.id).length : 0;

  // Initialize source: prefer uploaded for this work (in localStorage filename map), else demo synthesized.
  React.useEffect(() => {
    setPlaying(false);
    setTime(0);
    // Check stored upload metadata (URL itself isn't persisted across reloads since it's a blob)
    const storedMeta = (() => {try {return JSON.parse(localStorage.getItem('astro:audio:' + w.id) || 'null');} catch {return null;}})();
    if (storedMeta && storedMeta.kind === 'recording') {
      setSrc({ url: getDemoAudioUrl(w.id), label: storedMeta.label || 'Reference recording', kind: 'recording' });
    } else {
      // Default to a synthesized preview. Don't pretend it's a specific linked recording —
      // the user explicitly picks one via SOURCE → "Choose from recordings".
      setSrc({ url: getDemoAudioUrl(w.id), label: 'Demo preview (synthesized)', kind: 'demo' });
    }
  }, [w.id, recCount]);

  // Audio element event listeners
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    const onDur = () => setDuration(a.duration || 0);
    const onEnd = () => {setPlaying(false);setTime(0);};
    const onPlay = () => setPlaying(true);
    const onPause = () => setPlaying(false);
    a.addEventListener('loadedmetadata', onDur);
    a.addEventListener('ended', onEnd);
    a.addEventListener('play', onPlay);
    a.addEventListener('pause', onPause);
    return () => {
      a.removeEventListener('loadedmetadata', onDur);
      a.removeEventListener('ended', onEnd);
      a.removeEventListener('play', onPlay);
      a.removeEventListener('pause', onPause);
    };
  }, [src]);

  // rAF-driven time update — much smoother than relying on `timeupdate` (which fires every ~250ms).
  React.useEffect(() => {
    if (!playing) {
      cancelAnimationFrame(rafRef.current);
      return;
    }
    const tick = () => {
      const a = audioRef.current;
      if (a) setTime(a.currentTime);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [playing]);

  const togglePlay = () => {
    const a = audioRef.current;
    if (!a) return;
    if (a.paused) a.play().catch(() => {});else
    a.pause();
  };

  const onSeek = (e) => {
    const a = audioRef.current;
    if (!a || !duration) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const pct = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    a.currentTime = pct * duration;
    setTime(a.currentTime);
  };

  const onUpload = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    const url = URL.createObjectURL(file);
    setSrc({ url, label: file.name, kind: 'upload', filename: file.name });
    setPlaying(false);
    setTime(0);
    try {localStorage.setItem('astro:audio:' + w.id, JSON.stringify({ kind: 'upload', label: file.name }));} catch {}
    if (commit) commit(`Uploaded audio "${file.name}"`, (d) => d);
    setMenuOpen(false);
  };

  const triggerUpload = () => fileInputRef.current && fileInputRef.current.click();

  const pickRecording = (rec) => {
    // Use the per-recording demo audio (different seed → different tone) as a stand-in for the master.
    const recSeed = (rec.id || '').split('').reduce((s, c) => s + c.charCodeAt(0), 0) || w.id.charCodeAt(2) * 7;
    if (!_DEMO_AUDIO_CACHE['rec:' + rec.id]) {
      const blob = makeDemoToneWav(recSeed, 30);
      _DEMO_AUDIO_CACHE['rec:' + rec.id] = URL.createObjectURL(blob);
    }
    setSrc({
      url: _DEMO_AUDIO_CACHE['rec:' + rec.id],
      label: `${rec.title} — ${rec.artist || rec.album || ''}`.trim().replace(/—\s*$/, ''),
      kind: 'recording',
      recordingId: rec.id
    });
    setPlaying(false);
    setTime(0);
    try {localStorage.setItem('astro:audio:' + w.id, JSON.stringify({ kind: 'recording', label: rec.title, recordingId: rec.id }));} catch {}
    if (commit) commit(`Audio source → ${rec.title}`, (d) => d);
    setMenuOpen(false);
    setMenuView('main');
  };

  // Mic capture (MediaRecorder)
  const startMicRecording = async () => {
    try {
      if (!navigator.mediaDevices || !window.MediaRecorder) {
        setRecState({ active: false, elapsed: 0, error: 'MediaRecorder not supported in this browser.' });
        return;
      }
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const chunks = [];
      // Pick a supported mime type
      const mimeCandidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/ogg;codecs=opus', 'audio/mp4'];
      const mimeType = mimeCandidates.find((t) => window.MediaRecorder.isTypeSupported && MediaRecorder.isTypeSupported(t)) || '';
      const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
      recorder.ondataavailable = (e) => {if (e.data && e.data.size > 0) chunks.push(e.data);};
      recorder.onstop = () => {
        const blob = new Blob(chunks, { type: recorder.mimeType || 'audio/webm' });
        const url = URL.createObjectURL(blob);
        const stamp = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
        const label = `Voice memo · ${stamp}`;
        setSrc({ url, label, kind: 'memo' });
        setPlaying(false);
        setTime(0);
        try {localStorage.setItem('astro:audio:' + w.id, JSON.stringify({ kind: 'memo', label }));} catch {}
        if (commit) commit(`Recorded voice memo`, (d) => d);
        // Cleanup stream tracks
        if (recRef.current && recRef.current.stream) recRef.current.stream.getTracks().forEach((t) => t.stop());
        if (recRef.current && recRef.current.intervalId) clearInterval(recRef.current.intervalId);
        recRef.current = null;
      };
      const startedAt = Date.now();
      const intervalId = setInterval(() => {
        setRecState((s) => s.active ? { ...s, elapsed: (Date.now() - startedAt) / 1000 } : s);
      }, 100);
      recRef.current = { recorder, stream, chunks, startedAt, intervalId };
      recorder.start();
      setRecState({ active: true, elapsed: 0, error: null });
      setMenuOpen(false);
      setMenuView('main');
    } catch (err) {
      setRecState({ active: false, elapsed: 0, error: err.name === 'NotAllowedError' ? 'Microphone permission denied.' : err.message || 'Could not access microphone.' });
    }
  };

  const stopMicRecording = () => {
    const r = recRef.current;
    if (r && r.recorder && r.recorder.state !== 'inactive') {
      r.recorder.stop();
    }
    setRecState({ active: false, elapsed: 0, error: null });
  };

  const cancelMicRecording = () => {
    const r = recRef.current;
    if (r) {
      try {if (r.recorder && r.recorder.state !== 'inactive') r.recorder.stop();} catch {}
      if (r.stream) r.stream.getTracks().forEach((t) => t.stop());
      if (r.intervalId) clearInterval(r.intervalId);
      // override onstop side-effect for cancel: clear chunks first
      r.chunks.length = 0;
      recRef.current = null;
    }
    setRecState({ active: false, elapsed: 0, error: null });
  };

  // Cleanup on unmount
  React.useEffect(() => () => {
    if (recRef.current) {
      if (recRef.current.stream) recRef.current.stream.getTracks().forEach((t) => t.stop());
      if (recRef.current.intervalId) clearInterval(recRef.current.intervalId);
    }
  }, []);

  const revertToDemo = () => {
    setSrc({ url: getDemoAudioUrl(w.id), label: 'Demo preview (synthesized)', kind: 'demo' });
    setPlaying(false);
    setTime(0);
    try {localStorage.removeItem('astro:audio:' + w.id);} catch {}
    setMenuOpen(false);
  };

  const fmt = (s) => {
    if (!isFinite(s) || s < 0) s = 0;
    return `${Math.floor(s / 60)}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
  };

  const totalDur = duration || w.duration || 0;
  const progress = totalDur > 0 ? time / totalDur : 0;
  const peaks = useAudioPeaks(src ? src.url : null);

  return (
    <div style={{ borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', padding: '22px 0 18px', background: 'var(--bg-2)', marginBottom: 0, position: 'relative' }}>
      <audio ref={audioRef} src={src ? src.url : null} preload="metadata" />
      <input ref={fileInputRef} type="file" accept="audio/*" style={{ display: 'none' }} onChange={onUpload} />

      <div style={{ display: 'flex', alignItems: 'center', gap: 18, padding: '0 24px' }}>
        <button onClick={togglePlay}
        style={{ width: 46, height: 46, background: 'var(--ink)', color: 'var(--bg)', border: 'none', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', flex: '0 0 46px' }}
        title={playing ? 'Pause' : 'Play'}>
          {playing ?
          <svg width="18" height="18" viewBox="0 0 18 18" fill="currentColor"><rect x="3" y="2" width="4" height="14" /><rect x="11" y="2" width="4" height="14" /></svg> :

          <Ic.Play width={20} height={20} />
          }
        </button>

        {/* Waveform with click-to-seek + live progress overlay */}
        <div onClick={onSeek} style={{ flex: '1 1 0', minWidth: 0, position: 'relative', cursor: 'pointer', height: 70, overflow: 'hidden' }}>
          <Waveform w={1100} h={70} bars={180} seed={w.id.charCodeAt(2) * 7} peaks={peaks} progress={progress} color="var(--ink-2)" responsive />
          {/* Progress vertical line — no transition, drawn fresh every frame for smooth tracking */}
          <div style={{ position: 'absolute', top: 0, bottom: 0, left: `${progress * 100}%`, width: 1.5, background: 'var(--accent, #c0392b)', pointerEvents: 'none' }} />
        </div>

        <div style={{ textAlign: 'right', minWidth: 110, flex: '0 0 auto' }}>
          <div className="ff-mono num" style={{ fontSize: 14 }}>{fmt(time)} / {fmt(totalDur)}</div>
          <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2, letterSpacing: '.08em' }}>
            {src ? src.kind === 'demo' ? 'DEMO PREVIEW' : src.kind === 'upload' ? 'UPLOADED' : src.kind === 'memo' ? 'VOICE MEMO' : src.kind === 'recording' ? 'LINKED RECORDING' : 'PREVIEW' : '—'}
          </div>
        </div>

        {/* Source menu */}
        <div style={{ position: 'relative', flex: '0 0 auto' }}>
          <button onClick={() => setMenuOpen((o) => !o)} className="ff-mono upper"
          style={{ fontSize: 10, letterSpacing: '.08em', padding: '8px 12px', background: 'var(--bg)', border: '1px solid var(--ink)', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6, color: 'var(--ink)' }}
          title="Audio source">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M2 4 L6 8 L10 4" /></svg>
            SOURCE
          </button>
          {menuOpen &&
          <>
              <div onClick={() => {setMenuOpen(false);setMenuView('main');}} style={{ position: 'fixed', inset: 0, zIndex: 80 }} />
              <div style={{ position: 'absolute', top: 'calc(100% + 4px)', right: 0, minWidth: 320, maxWidth: 380, background: 'var(--bg)', border: '1px solid var(--ink)', boxShadow: '4px 4px 0 rgba(0,0,0,.08)', zIndex: 81 }}>
                {menuView === 'main' &&
              <>
                    <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', padding: '10px 14px 6px', borderBottom: '1px solid var(--rule)' }}>AUDIO SOURCE</div>
                    <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--rule-soft)', background: 'var(--bg-2)' }}>
                      <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 2 }}>{src ? src.label : '—'}</div>
                      <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
                        {src && src.kind === 'demo' && 'Synthesized preview · click upload to attach a real reference'}
                        {src && src.kind === 'upload' && `User upload · ${src.filename}`}
                        {src && src.kind === 'recording' && 'Linked recording from this work'}
                        {src && src.kind === 'memo' && 'Voice memo captured from microphone'}
                      </div>
                    </div>
                    <button onClick={triggerUpload} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', width: '100%', background: 'transparent', border: 'none', borderBottom: '1px solid var(--rule-soft)', cursor: 'pointer', textAlign: 'left', fontSize: 12 }}>
                      <span style={{ width: 24, height: 24, border: '1px solid var(--ink)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>↑</span>
                      <div>
                        <div style={{ fontWeight: 600 }}>Upload audio file</div>
                        <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>WAV, MP3, AIFF, FLAC, M4A</div>
                      </div>
                    </button>
                    {recCount > 0 &&
                <button onClick={() => setMenuView('recordings')} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', width: '100%', background: 'transparent', border: 'none', borderBottom: '1px solid var(--rule-soft)', cursor: 'pointer', textAlign: 'left', fontSize: 12 }}>
                        <span style={{ width: 24, height: 24, border: '1px solid var(--ink)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}><Ic.Disc width={12} height={12} /></span>
                        <div style={{ flex: 1 }}>
                          <div style={{ fontWeight: 600 }}>Choose from recordings ({recCount})</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>Pick a linked master to preview</div>
                        </div>
                        <Ic.Right width={12} height={12} style={{ color: 'var(--ink-3)' }} />
                      </button>
                }
                    <button onClick={startMicRecording} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', width: '100%', background: 'transparent', border: 'none', borderBottom: '1px solid var(--rule-soft)', cursor: 'pointer', textAlign: 'left', fontSize: 12 }}>
                      <span style={{ width: 24, height: 24, border: '1px solid var(--ink)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>♪</span>
                      <div>
                        <div style={{ fontWeight: 600 }}>Record from mic</div>
                        <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>Capture a quick voice memo</div>
                      </div>
                    </button>
                    {src && src.kind !== 'demo' &&
                <button onClick={revertToDemo} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', width: '100%', background: 'transparent', border: 'none', cursor: 'pointer', textAlign: 'left', fontSize: 12, color: 'var(--ink-3)' }}>
                        <span style={{ width: 24, height: 24, display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>×</span>
                        <div>
                          <div>Revert to preview</div>
                        </div>
                      </button>
                }
                  </>
              }
                {menuView === 'recordings' &&
              <>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px 6px', borderBottom: '1px solid var(--rule)' }}>
                      <button onClick={() => setMenuView('main')} className="ff-mono upper"
                  style={{ fontSize: 9, letterSpacing: '.1em', padding: '2px 6px', background: 'transparent', border: '1px solid var(--rule)', cursor: 'pointer', color: 'var(--ink-3)' }}>← BACK</button>
                      <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)' }}>LINKED RECORDINGS · {recCount}</div>
                    </div>
                    <div style={{ maxHeight: 340, overflowY: 'auto' }}>
                      {(typeof recordingsForWork === 'function' ? recordingsForWork(w.id) : []).map((rec) => {
                    const active = src && src.kind === 'recording' && src.recordingId === rec.id;
                    return (
                      <button key={rec.id} onClick={() => pickRecording(rec)}
                      style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 14px', width: '100%',
                        background: active ? 'var(--bg-2)' : 'transparent',
                        border: 'none', borderBottom: '1px solid var(--rule-soft)', cursor: 'pointer', textAlign: 'left', fontSize: 12 }}>
                            <div style={{ width: 32, height: 32, background: rec.art || '#ddd', position: 'relative', flex: '0 0 32px' }}>
                              <Ic.Disc width={14} height={14} style={{ position: 'absolute', top: 9, left: 9, color: 'rgba(0,0,0,.55)' }} />
                            </div>
                            <div style={{ flex: 1, minWidth: 0 }}>
                              <div style={{ fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', display: 'flex', alignItems: 'center', gap: 6 }}>
                                {active && <span style={{ width: 6, height: 6, background: 'var(--accent)', borderRadius: '50%', flex: '0 0 6px' }} />}
                                {rec.title}
                              </div>
                              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                                {rec.artist ? rec.artist + ' · ' : ''}{rec.album || ''}{rec.year ? ` · ${rec.year}` : ''}
                              </div>
                            </div>
                            <div className="ff-mono num" style={{ fontSize: 10, color: 'var(--ink-3)', flex: '0 0 auto' }}>
                              {rec.duration ? `${Math.floor(rec.duration / 60)}:${String(rec.duration % 60).padStart(2, '0')}` : '—'}
                            </div>
                          </button>);

                  })}
                    </div>
                  </>
              }
              </div>
            </>
          }
        </div>
      </div>

      {/* Recording bar */}
      {recState.active &&
      <div style={{ padding: '10px 24px 0', display: 'flex', alignItems: 'center', gap: 14, marginTop: 8 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '8px 14px', background: '#c0392b', color: '#fff', fontWeight: 600, fontSize: 12 }}>
            <span style={{ width: 10, height: 10, background: '#fff', borderRadius: '50%', animation: 'astro-rec-blink 1s infinite' }} />
            REC
          </div>
          <div className="ff-mono num" style={{ fontSize: 18, fontWeight: 600, color: '#c0392b' }}>
            {Math.floor(recState.elapsed / 60)}:{String(Math.floor(recState.elapsed % 60)).padStart(2, '0')}
            <span style={{ fontSize: 11, color: 'var(--ink-3)', marginLeft: 6 }}>.{String(Math.floor(recState.elapsed % 1 * 10))}</span>
          </div>
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', flex: 1 }}>Capturing from microphone…</div>
          <button onClick={stopMicRecording} className="ff-mono upper"
        style={{ fontSize: 10, letterSpacing: '.08em', padding: '8px 14px', background: 'var(--ink)', color: 'var(--bg)', border: '1px solid var(--ink)', cursor: 'pointer' }}>
            ■ STOP & SAVE
          </button>
          <button onClick={cancelMicRecording} className="ff-mono upper"
        style={{ fontSize: 10, letterSpacing: '.08em', padding: '8px 12px', background: 'transparent', color: 'var(--ink)', border: '1px solid var(--ink)', cursor: 'pointer' }}>
            CANCEL
          </button>
          <style>{`@keyframes astro-rec-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0.2; } }`}</style>
        </div>
      }
      {recState.error &&
      <div style={{ padding: '10px 24px 0' }}>
          <div className="ff-mono" style={{ fontSize: 11, color: '#c0392b', padding: '8px 12px', border: '1px solid #c0392b', background: '#f6e0dc', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span>⚠ {recState.error}</span>
            <button onClick={() => setRecState({ active: false, elapsed: 0, error: null })} style={{ background: 'transparent', border: 0, cursor: 'pointer', color: '#c0392b', fontSize: 14, padding: 0, marginLeft: 8 }}>×</button>
          </div>
        </div>
      }

      {/* Audio source line */}
      <div style={{ padding: '10px 24px 0', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
        <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
          <span className="upper" style={{ letterSpacing: '.08em', marginRight: 8 }}>NOW PLAYING:</span>
          <span style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{src ? src.label : '—'}</span>
        </div>
        <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
          {src && src.kind === 'demo' && <span style={{ color: '#b78113' }}>⓪ No real audio attached — preview only</span>}
          {src && src.kind === 'upload' && <span style={{ color: '#5b8a3a' }}>● File loaded locally · not yet synced to server</span>}
          {src && src.kind === 'recording' && <span style={{ color: 'var(--ink-2)' }}>● From linked recording</span>}
          {src && src.kind === 'memo' && <span style={{ color: '#c0392b' }}>● Voice memo · captured locally</span>}
        </div>
      </div>
    </div>);

}

// ───────────────────────────────────────────────────────────── WORK DETAIL
function ScreenWork({ go, work }) {
  const w = work || WORKS[0];
  const [tab, setTab] = useS1('overview');
  const [editing, setEditing] = useS1(false);
  const [historyOpen, setHistoryOpen] = useS1(false);
  const [cwrModalOpen, setCwrModalOpen] = useS1(false);
  const [ddexModalOpen, setDdexModalOpen] = useS1(false);
  const [publicModalOpen, setPublicModalOpen] = useS1(false);

  // Initial editable model — derived from the static work row
  const sharesStr = (w && typeof w.shares === 'string') ? w.shares : '';
  const initialSplits = sharesStr.includes('/')
    ? sharesStr.split('/').map(n => Number(n) || 0)
    : [100];
  const initialModel = useM1(() => ({
    title: w.title,
    iswc: w.iswc,
    catalog: w.catalog,
    copyright: w.copyright,
    status: w.status,
    pro: w.pro,
    writers: w.writers.map((wr, i) => {
      const sh = initialSplits[i] != null ? initialSplits[i] : Math.round(100 / w.writers.length);
      // First writer is self-published (writer keeps publisher share); others are signed
      // to the work's catalog admin.
      const isSelf = i === 0;
      return {
        name: wr,
        share: sh,
        ipi: String(883112 + i * 23),
        pro: w.pro,
        role: i === 0 ? 'Composer/Author' : 'Composer',
        splitShares: false,
        perfShare: sh,
        mechShare: sh,
        syncShare: sh,
        territories: ['World'],
        workForHire: false,
        reversionary: false,
        firstRecRefusal: false,
        pubs: [{
          name: isSelf ? '' : w.catalog || 'Pluralis Music',
          pro: w.pro,
          ipi: isSelf ? '' : String(551020 + i * 47),
          selfPub: isSelf,
          unknown: false,
          controlled: true,
          role: 'Original Publisher',
          share: sh,
          territories: ['World'],
          admins: isSelf ? [] : [{
            role: 'Sub-pub',
            name: 'Kobalt Music Services',
            pro: 'PRS',
            ipi: '00214477331',
            controlled: true,
            pubRole: 'Original Publisher',
            share: Math.round(sh * 0.5),
            territories: ['UK / Ireland']
          }]
        }]
      };
    }),
    societyIds: [
    { society: 'BMI', workId: '22-441-883' },
    { society: 'ASCAP', workId: '884220551' },
    { society: 'MusicBrainz', workId: 'e8a9f2-c1b3-4e88-a7d2-991c' }],

    altTitles: [
    { title: w.title.toUpperCase(), type: 'Original Title', language: 'English' }],

    musicalKey: ['C minor', 'A minor', 'F# minor', 'D major', 'G major', 'E minor', 'B♭ major'][w.id.charCodeAt(2) % 7],
    tempo: 60 + w.id.charCodeAt(2) * 7 % 80,
    language: 'English',
    category: 'Original',
    instrumental: false,
    lyricsLanguage: 'English',
    lyrics: w.title.toUpperCase().includes('INSTRUMENTAL') ? '' :
    `[Verse 1]
Walking through the city lights tonight
Counting all the reasons we'll be alright
Every street a story we can write
Every shadow holding on to light

[Chorus]
And the world keeps turning slowly
Holding everything we've known
We're just trying to find our way home
Find our way home

[Verse 2]
Memory is a river running deep
All the promises we couldn't keep
Folding into hours that we sleep
Quiet seasons, quiet things to keep

[Chorus]
And the world keeps turning slowly
Holding everything we've known
We're just trying to find our way home
Find our way home

[Bridge]
If the morning ever finds us
Standing in the same old place
We will know it by the kindness
Living in each other's face

[Outro]
We're just trying to find our way home
We're just trying to find our way home`,
    lyricsUpdated: '2026-03-14T11:42:00Z',
    rights: [
    {
      id: 'reg-1',
      type: 'PA',
      certNumber: `PA 2-${402 + w.id.charCodeAt(2) % 9 * 100}-${311 + w.id.charCodeAt(2) % 17 * 7}`,
      registrationDate: '2024-08-19',
      effectiveDate: '2024-08-12',
      jurisdiction: 'United States · USCO',
      claimType: 'Original work',
      workForHire: false,
      published: true,
      firstPubDate: '2024-09-04',
      firstPubNation: 'United States',
      depositCopies: 2,
      depositFormat: 'Lead sheet · digital · WAV demo',
      certHolder: 'Pluralis Music LLC'
    }],

    syncPolicy: 'Pre-cleared with publisher — agency-approved',
    syncWithholding: [],
    territorialNotes: 'Worldwide control via Pluralis Music — sub-publishing in JP/KR via Plural Tokyo K.K.',
    reversionaryClause: '35-year termination per 17 U.S.C. § 203 — eligible 2059-08-19',
    astroId: `${w.id.toUpperCase()}_AST_2026_4392`
  }), [w.id]);

  const { data: m, setData, commit, history, cursor, undo, redo, revertTo } = useEditHistory(initialModel);
  const { pulseId, trigger: pulse } = useSavedPulse();

  // ⌘Z / ⌘⇧Z
  useE1(() => {
    if (!editing) return;
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'z') {
        e.preventDefault();
        if (e.shiftKey) redo();else undo();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [editing, undo, redo]);

  const editProps = (id) => ({
    'data-edit-pulse': pulseId === id ? '1' : undefined,
    style: pulseId === id ? { transition: 'background .9s', background: 'oklch(0.92 0.06 80 / 0.5)' } : undefined
  });

  // Splits drag — intermediate setData without history, final snapshot on mouse-up
  const handleDragSplits = (next, isFinal) => {
    if (isFinal) {
      // commit current data as a single history entry
      commit('Adjusted writer splits', (d) => d);
      return;
    }
    setData((d) => ({ ...d, writers: next }));
  };

  return (
    <div>
      {/* Title block */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 360px', gap: 48, alignItems: 'end', marginBottom: 24 }}>
        <div>
          <button onClick={() => go('catalog')} className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 14, display: 'inline-flex', alignItems: 'center', gap: 6 }}>
            <Ic.Left width={12} height={12} /> Catalog / Works
          </button>
          <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8 }}>WORK · {window.iswcDisplay ? window.iswcDisplay(m.iswc) : m.iswc}</div>

          {editing ?
          <InlineText
            as="h1"
            value={m.title}
            onCommit={(v) => {commit(`Title → "${v}"`, (d) => ({ ...d, title: v }));pulse('title');}}
            className="heading-swap ff-display"
            style={{ fontSize: 'clamp(56px,8vw,128px)', fontWeight: 700, letterSpacing: '-0.05em', lineHeight: 0.88, margin: 0, display: 'block' }}
            placeholder="Untitled work" /> :


          <h1 className="heading-swap ff-display" style={{ fontSize: 'clamp(56px,8vw,128px)', fontWeight: 700, letterSpacing: '-0.05em', lineHeight: 0.88, margin: 0 }}>
              {m.title}
            </h1>
          }

          {/* Alternate titles — sit just under the main title */}
          {((m.altTitles || []).length > 0 || editing) &&
          <div style={{ marginTop: 18, paddingTop: 14, borderTop: '1px solid var(--rule-soft)' }}>
              <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 8 }}>ALSO KNOWN AS</div>
              {(m.altTitles || []).length === 0 && editing &&
            <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)', fontStyle: 'italic', marginBottom: 8 }}>No alternate titles</div>
            }
              <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                {(m.altTitles || []).map((a, i) =>
              <div key={i} style={{ display: 'flex', alignItems: 'baseline', gap: 12, flexWrap: 'wrap' }}>
                    {editing ?
                <InlineText value={a.title}
                onCommit={(v) => commit(`Alt title → "${v}"`, (d) => ({ ...d, altTitles: d.altTitles.map((x, j) => j === i ? { ...x, title: v } : x) }))}
                style={{ fontSize: 18, fontWeight: 500, fontFamily: 'inherit', display: 'inline-block', letterSpacing: '-0.01em' }}
                placeholder="Alternate title" /> :

                <span className="ff-display" style={{ fontSize: 18, fontWeight: 500, letterSpacing: '-0.01em' }}>{a.title}</span>
                }
                    <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)' }}>
                      {editing ?
                  <InlineSelect value={a.type}
                  options={['Alternative Title', 'First Line of Text', 'Formal Title', 'Incorrect Title', 'Original Title', 'Original Title Translated', 'Part Title', 'Restricted Title']}
                  onCommit={(v) => commit(`Alt title type → ${v}`, (d) => ({ ...d, altTitles: d.altTitles.map((x, j) => j === i ? { ...x, type: v } : x) }))}
                  style={{ fontSize: 9, letterSpacing: '.1em' }} /> :
                  a.type}
                      <span style={{ margin: '0 6px', color: 'var(--ink-4)' }}>·</span>
                      {editing ?
                  <InlineSelect value={a.language}
                  options={refLanguageOptions()}
                  onCommit={(v) => commit(`Alt title language → ${v}`, (d) => ({ ...d, altTitles: d.altTitles.map((x, j) => j === i ? { ...x, language: v } : x) }))}
                  style={{ fontSize: 9, letterSpacing: '.1em' }} /> :
                  a.language}
                    </span>
                    {editing &&
                <button onClick={() => commit(`Removed alt title "${a.title}"`, (d) => ({ ...d, altTitles: d.altTitles.filter((_, j) => j !== i) }))}
                style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 2, marginLeft: 'auto' }}
                title="Remove">
                        <Ic.X width={12} height={12} />
                      </button>
                }
                  </div>
              )}
              </div>
              {editing &&
            <button onClick={() => commit('Added alt title', (d) => ({ ...d, altTitles: [...(d.altTitles || []), { title: '', type: 'Alternative Title', language: 'English' }] }))}
            style={{ display: 'flex', gap: 6, alignItems: 'center', padding: '6px 10px', marginTop: 10,
              border: '1px solid var(--rule-soft)', background: 'var(--edit-fill)', borderRadius: 3, cursor: 'pointer', fontSize: 11, color: 'var(--ink-2)' }}>
                  <Ic.Plus width={10} height={10} /> Add alt title
                </button>
            }
            </div>
          }

          {/* Composition meta strip — key, tempo, language */}
          <div className="ff-mono" style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', gap: '4px 22px', marginTop: 18, fontSize: 12, color: 'var(--ink-2)' }}>
            <span>
              <span className="upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginRight: 8 }}>KEY</span>
              {editing ?
              <InlineSelect value={m.musicalKey || '— Unspecified —'}
              options={['— Unspecified —', 'C major', 'D♭ major', 'D major', 'E♭ major', 'E major', 'F major', 'F# major', 'G major', 'A♭ major', 'A major', 'B♭ major', 'B major', 'C minor', 'C# minor', 'D minor', 'E♭ minor', 'E minor', 'F minor', 'F# minor', 'G minor', 'G# minor', 'A minor', 'B♭ minor', 'B minor']}
              onCommit={(v) => {const val = v.startsWith('—') ? '' : v;commit(`Key → ${val || 'unspecified'}`, (d) => ({ ...d, musicalKey: val }));pulse('key');}}
              style={{ fontSize: 12 }} /> :
              m.musicalKey || <span style={{ color: 'var(--ink-3)', fontStyle: 'italic' }}>unspecified</span>}
            </span>
            <span>
              <span className="upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginRight: 8 }}>TEMPO</span>
              {editing ?
              <InlineText value={String(m.tempo || '')}
              onCommit={(v) => {const t = +v.replace(/\D/g, '').slice(0, 3) || 0;commit(`Tempo → ${t} BPM`, (d) => ({ ...d, tempo: t }));pulse('tempo');}}
              style={{ fontSize: 12, fontFamily: 'inherit', display: 'inline-block', maxWidth: 50 }}
              placeholder="—" /> :
              <span className="num">{m.tempo || '—'}</span>}
              <span style={{ color: 'var(--ink-3)', marginLeft: 5 }}>BPM
                {m.tempo > 0 && <span style={{ marginLeft: 6, color: 'var(--ink-3)' }}>· {+m.tempo < 70 ? 'Slow' : +m.tempo < 100 ? 'Moderate' : +m.tempo < 130 ? 'Up-tempo' : 'Fast'}</span>}
              </span>
            </span>
            <span>
              <span className="upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginRight: 8 }}>METADATA LANGUAGE</span>
              {editing ?
              <InlineSelect value={m.language}
              options={refLanguageOptions()}
              onCommit={(v) => {commit(`Language → ${v}`, (d) => ({ ...d, language: v }));pulse('lang');}}
              style={{ fontSize: 12 }} /> :
              m.language}
            </span>
            <span>
              <span className="upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginRight: 8 }}>LYRICS</span>
              {m.instrumental ?
              <span style={{ color: 'var(--ink-3)', fontStyle: 'italic' }}>instrumental</span> :
              editing ?
              <InlineSelect value={m.lyricsLanguage}
              options={refLanguageOptions()}
              onCommit={(v) => {commit(`Lyrics language → ${v}`, (d) => ({ ...d, lyricsLanguage: v }));pulse('llang');}}
              style={{ fontSize: 12 }} /> :
              m.lyricsLanguage}
            </span>
          </div>

          <div style={{ display: 'flex', gap: 10, marginTop: 18, flexWrap: 'wrap', alignItems: 'center' }}>
            {editing ?
            <InlineSelect
              value={m.status}
              options={['registered', 'pending', 'conflict']}
              onCommit={(v) => {commit(`Status → ${v}`, (d) => ({ ...d, status: v }));pulse('status');}}
              style={{ fontSize: 10, letterSpacing: '.08em', textTransform: 'uppercase', padding: '4px 10px' }} /> :


            <Pill tone={m.status === 'registered' ? 'ok' : m.status === 'conflict' ? 'danger' : 'accent'} dot>{m.status}</Pill>
            }

            <Pill>{w.duration ? `${Math.floor(w.duration / 60)}:${String(w.duration % 60).padStart(2, '0')}` : '—'}</Pill>
            <Pill>{w.societies} societies</Pill>
            <Pill>{(w.plays / 1e6).toFixed(1)}M plays</Pill>

            {editing ?
            <InlineSelect
              value={m.catalog}
              options={['PluralPub', 'SK Catalog', 'Saint Music', 'Perpetual Novice', 'TM Works', 'KAY Publishing', 'Forever Living', 'HN Pub', 'Madlib Inv. Music', 'Funke Music', 'YAE Cat', 'Arca Cat', 'L\u2019R Cat', 'Perpetual Pub']}
              onCommit={(v) => {commit(`Catalog → ${v}`, (d) => ({ ...d, catalog: v }));pulse('catalog');}} /> :


            <Pill tone="soft">{m.catalog}</Pill>
            }

            {editing ?
            <InlineText
              as="span"
              value={m.copyright}
              onCommit={(v) => {commit(`Copyright → "${v}"`, (d) => ({ ...d, copyright: v }));pulse('copyright');}}
              className="ff-mono"
              style={{ fontSize: 11, color: 'var(--ink-2)', padding: '4px 10px', display: 'inline-block' }}
              placeholder="© year, owner" /> :


            <Pill tone="soft">{m.copyright}</Pill>
            }
          </div>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10, alignItems: 'flex-end' }}>
          <Btn variant="accent" icon={<Ic.Send />} onClick={() => setCwrModalOpen(true)}>Generate CWR</Btn>
          <button onClick={() => go('cwr', { tab:'gen', workIds:[m.id] })} className="ff-mono upper" style={{ background:'transparent', border:'none', color:'var(--ink-3)', fontSize:9, letterSpacing:'.12em', cursor:'pointer', padding:0, marginTop:-4 }}>Open in Generator →</button>
          <Btn variant="secondary" icon={<Ic.File />} onClick={() => setDdexModalOpen(true)}>Export DDEX</Btn>
          {editing ?
          <>
              <Btn variant="ghost" onClick={() => setHistoryOpen(true)}>
                {history.length} change{history.length === 1 ? '' : 's'} · history
              </Btn>
              <Btn variant="primary" icon={<Ic.Check />} onClick={() => setEditing(false)}>Done editing</Btn>
            </> :

          <Btn variant="ghost" icon={<Ic.Edit />} onClick={() => setEditing(true)}>Edit Work</Btn>
          }
          <Btn variant="ghost" icon={<Ic.Settings />} onClick={() => window.dispatchEvent(new CustomEvent('astro-add-song', { detail: { workId: w.id } }))}>Open full editor</Btn>
          <Btn variant="ghost" icon={<Ic.Ext />} onClick={() => setPublicModalOpen(true)}>Public page</Btn>
          <window.DetailDelete kind="work" record={m} title={m.title} subtitle={m.iswc || m.id} onDeleted={() => go && go('catalog')} />
        </div>
      </div>

      {/* Edit-mode floating pill (bottom-right). Quiet, out of layout. */}
      {editing &&
      <div style={{
        position:'fixed',bottom:24,right:24,zIndex:60,
        display:'flex',alignItems:'center',gap:0,
        background:'var(--ink)',color:'var(--bg)',
        boxShadow:'0 6px 24px rgba(0,0,0,.18)',
        borderRadius:999,padding:'4px 4px 4px 14px'}}>
          <span style={{width:6,height:6,background:'oklch(0.78 0.16 140)',borderRadius:'50%',display:'inline-block',marginRight:8,boxShadow:'0 0 0 3px oklch(0.78 0.16 140 / .25)'}}/>
          <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',fontWeight:600,marginRight:14}}>EDITING · AUTO-SAVE</span>
          <button onClick={undo} disabled={cursor <= 0} title="Undo (⌘Z)"
            style={{width:28,height:28,display:'inline-flex',alignItems:'center',justifyContent:'center',
              background:'transparent',border:0,color:cursor<=0?'oklch(1 0 0 / .35)':'var(--bg)',
              cursor:cursor<=0?'default':'pointer',fontSize:14,borderRadius:999}}>↶</button>
          <button onClick={redo} disabled={cursor >= history.length} title="Redo (⌘⇧Z)"
            style={{width:28,height:28,display:'inline-flex',alignItems:'center',justifyContent:'center',
              background:'transparent',border:0,color:cursor>=history.length?'oklch(1 0 0 / .35)':'var(--bg)',
              cursor:cursor>=history.length?'default':'pointer',fontSize:14,borderRadius:999}}>↷</button>
          <button onClick={() => setHistoryOpen(true)} title="Edit history"
            className="ff-mono" style={{fontSize:10,padding:'6px 10px',background:'oklch(1 0 0 / .12)',border:0,color:'var(--bg)',cursor:'pointer',borderRadius:999,marginLeft:4,fontWeight:600,letterSpacing:'.04em'}}>
            {history.length} change{history.length===1?'':'s'}
          </button>
          <button onClick={() => setEditing(false)} title="Done editing"
            className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'6px 12px',background:'var(--bg)',border:0,color:'var(--ink)',cursor:'pointer',borderRadius:999,marginLeft:6,fontWeight:600}}>
            DONE
          </button>
        </div>
      }

      {/* Audio lives on Recordings — not Works (a Work is the abstract composition).
          The "Recordings" tab below lists every master cut of this work, each with its own player. */}

      {/* Tabs */}
      <div style={{ borderBottom: '1px solid var(--rule)', display: 'flex', gap: 0, marginBottom: 24, marginTop: 0 }}>
        {['overview', 'splits', 'splitsheet', 'lyrics', 'recordings', 'rights', 'registrations', 'royalties', 'history'].map((t) =>
        <button key={t} onClick={() => setTab(t)} className="ff-mono upper"
        style={{ padding: '14px 18px', fontSize: 11, fontWeight: 600, letterSpacing: '.08em',
          borderBottom: tab === t ? '3px solid var(--ink)' : '3px solid transparent',
          marginBottom: -1, color: tab === t ? 'var(--ink)' : 'var(--ink-3)' }}>{t}</button>
        )}
      </div>

      {tab === 'overview' &&
      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 32 }}>
          <div>
            <Section num="01">Shares</Section>
            <div>
              {editing && m.writers.length > 1 &&
            <DragSplits writers={m.writers} onCommit={handleDragSplits} />
            }
              {m.writers.map((wr, i) =>
            <React.Fragment key={i}>
                <div style={{ padding: '14px 0', borderBottom: (wr.pubs || []).length > 0 ? 'none' : '1px solid var(--rule-soft)', display: 'grid', gridTemplateColumns: editing ? '1fr 200px 80px 24px' : '1fr 200px 60px', gap: 14, alignItems: 'center' }}>
                  <div>
                    {editing && window.WriterTypeahead ?
                  <window.WriterTypeahead
                    value={wr.name}
                    aid={wr._id}
                    onSelect={(patch) => {
                      commit(`Writer ${i + 1} → "${patch.name}"`, (d) => ({ ...d, writers: d.writers.map((x, j) => j === i ? { ...x, ...patch } : x) }));
                      pulse(`w-${i}`);
                    }} /> :


                  <div style={{ fontSize: 15, fontWeight: 600 }}>{wr.name}</div>
                  }
                    <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{(wr.role || 'writer').toLowerCase()} · {wr.pro} · IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(wr.ipi) : wr.ipi)}</div>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <div style={{ flex: 1, height: 8, background: 'var(--bg-2)', position: 'relative' }}>
                      <div style={{ position: 'absolute', inset: 0, width: `${wr.share}%`, background: 'var(--ink)' }} />
                    </div>
                  </div>
                  {editing ?
                <input
                  type="number" min={0} max={100}
                  value={wr.share}
                  onChange={(e) => {
                    const v = Math.max(0, Math.min(100, +e.target.value || 0));
                    setData((d) => ({ ...d, writers: d.writers.map((x, j) => j === i ? { ...x, share: v } : x) }));
                  }}
                  onBlur={() => {commit(`Adjusted ${wr.name || `writer ${i + 1}`}'s share`, (d) => d);pulse(`w-${i}-share`);}}
                  className="ff-mono num"
                  style={{ width: 70, padding: '6px 8px', fontSize: 14, textAlign: 'right', border: 'none', borderRadius: 3, background: 'var(--edit-fill)' }} /> :


                <span className="ff-mono num" style={{ fontSize: 14, textAlign: 'right' }}>{wr.share}%</span>
                }
                  {editing &&
                <button onClick={() => {
                  if (m.writers.length <= 1) return;
                  commit(`Removed ${wr.name || `writer ${i + 1}`}`, (d) => ({ ...d, writers: d.writers.filter((_, j) => j !== i) }));
                }}
                title={m.writers.length <= 1 ? 'At least one writer required' : 'Remove'}
                style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: m.writers.length <= 1 ? 'default' : 'pointer', padding: 4, opacity: m.writers.length <= 1 ? 0.3 : 1 }}>
                      <Ic.X width={14} height={14} />
                    </button>
                }
                </div>
                {/* Publisher / sub-pub chain (read-only here — full edit on Splits tab) */}
                {(wr.pubs || []).length > 0 &&
              <div style={{ paddingBottom: 14, borderBottom: '1px solid var(--rule-soft)', marginLeft: 14 }}>
                    {(wr.pubs || []).map((p, pi) =>
                <div key={pi} style={{ position: 'relative', paddingLeft: 14, marginTop: 4 }}>
                        <span style={{ position: 'absolute', left: 0, top: 0, bottom: '50%', width: 1, background: 'var(--rule)' }} />
                        <span style={{ position: 'absolute', left: 0, top: '50%', width: 10, height: 1, background: 'var(--rule)' }} />
                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 200px 60px', gap: 14, alignItems: 'center', padding: '4px 0' }}>
                          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
                            {p.unknown ?
                      <span className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.08em', color: 'var(--ink-3)' }}>PUBLISHER UNKNOWN</span> :
                      p.selfPub ?
                      <>
                                <span className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.08em', padding: '1px 5px', background: 'var(--ink)', color: 'var(--bg)', fontWeight: 600 }}>SELF-PUB</span>
                                <span className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-2)' }}>writer retains pub share</span>
                              </> :

                      <>
                                <span style={{ fontSize: 13, fontWeight: 500 }}>{p.name}</span>
                                <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>publisher · <SocietyLink code={p.pro} style={{fontSize:10}}/> · IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(p.ipi) : p.ipi) || '—'}</span>
                              </>
                      }
                          </div>
                          <div />
                          <span className="ff-mono num" style={{ fontSize: 13, textAlign: 'right', color: 'var(--ink-2)' }}>{p.share}%</span>
                        </div>
                        {(p.admins || []).map((a, ai) =>
                  <div key={ai} style={{ position: 'relative', paddingLeft: 14, marginLeft: 14 }}>
                            <span style={{ position: 'absolute', left: 0, top: 0, bottom: '50%', width: 1, background: 'var(--rule)' }} />
                            <span style={{ position: 'absolute', left: 0, top: '50%', width: 10, height: 1, background: 'var(--rule)' }} />
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 200px 60px', gap: 14, alignItems: 'center', padding: '3px 0' }}>
                              <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
                                <span style={{ fontSize: 12, fontWeight: 500, color: 'var(--ink-2)' }}>{a.name}</span>
                                <span className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)' }}>{(a.role || 'sub-pub').toLowerCase()} · <SocietyLink code={a.pro} style={{fontSize:9}}/> · {(a.territories || []).join(', ') || '—'}</span>
                              </div>
                              <div />
                              <span className="ff-mono num" style={{ fontSize: 12, textAlign: 'right', color: 'var(--ink-3)' }}>{a.share}%</span>
                            </div>
                          </div>
                  )}
                      </div>
                )}
                  </div>
              }
                </React.Fragment>
            )}
              {editing &&
            <button
              onClick={() => commit('Added writer', (d) => ({ ...d, writers: [...d.writers, { name: '', _id: null, share: 0, ipi: '', pro: d.pro }] }))}
              style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 10, padding: '8px 12px',
                border: '1px solid var(--rule-soft)', background: 'var(--edit-fill)', borderRadius: 3, cursor: 'pointer' }}>
                  <Ic.Plus width={11} height={11} style={{ color: 'var(--ink-3)' }} />
                  <span style={{ fontSize: 12, color: 'var(--ink-2)' }}>Add co-writer</span>
                </button>
            }
              <div style={{ padding: '14px 0', display: 'grid', gridTemplateColumns: editing ? '1fr 200px 80px 24px' : '1fr 200px 60px', gap: 14, alignItems: 'center', borderTop: '2px solid var(--rule)', marginTop: 6 }}>
                <span className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)' }}>TOTAL</span>
                <span />
                {(() => {
                const tot = m.writers.reduce((a, x) => a + (+x.share || 0), 0);
                return (
                  <span className="ff-mono num" style={{ fontSize: 14, textAlign: 'right', fontWeight: 600,
                    color: tot === 100 ? 'var(--ink)' : '#c0392b' }}>
                      {tot}%{tot !== 100 && (tot < 100 ? ` · ${100 - tot}% short` : ` · ${tot - 100}% over`)}
                    </span>);

              })()}
                {editing && <span />}
              </div>
            </div>

            <Section num="02">Identifiers</Section>
            <div className="ff-mono" style={{ fontSize: 12, display: 'grid', gridTemplateColumns: '140px 1fr', rowGap: 10, alignItems: 'center' }}>
              <span style={{ color: 'var(--ink-3)' }} title={(window.REF && window.REF.ready && window.REF.identifierTypeByCode.get('ISWC')) ? window.REF.identifierTypeByCode.get('ISWC').label : 'International Standard Musical Work Code'}>ISWC</span>
              {editing ?
            <InlineText
              value={window.iswcDisplay ? window.iswcDisplay(m.iswc) : m.iswc}
              onCommit={(v) => {
                const canonical = window.iswcNormalize ? window.iswcNormalize(v) : v;
                if (canonical) {
                  commit(`ISWC → ${canonical}`, (d) => ({ ...d, iswc: canonical }));
                  pulse('iswc');
                } else if (v && v.trim() && v.trim() !== (m.iswc || '')) {
                  // Invalid format — surface a toast and revert.
                  window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Invalid ISWC "${v}" — expected T followed by 10 digits`,tone:'warn'}}));
                }
              }}
              style={{ fontSize: 12, fontFamily: 'inherit', display: 'inline-block', maxWidth: 240 }}
              placeholder="T0000000000" /> :

            <span title={m.iswc /* canonical: T-NNN.NNN.NNN-C */}>{window.iswcDisplay ? window.iswcDisplay(m.iswc) : m.iswc}{window.IdChip && m.iswc ? <span style={{marginLeft:8,verticalAlign:'middle'}}><window.IdChip kind="iswc" value={m.iswc} compact onApply={(fix)=>commit(`ISWC fix → ${fix}`,(d)=>({...d,iswc:fix}))}/></span> : null}</span>}

              <span style={{ color: 'var(--ink-3)' }}>ASTRO ID</span>
              <span>{m.astroId}</span>

              {m.societyIds.map((row, i) =>
            <React.Fragment key={i}>
                  {editing ?
              <InlineSelect
                value={row.society}
                options={[...(window.SOC_CWR || []), 'MusicBrainz', 'Other']}
                onCommit={(v) => commit(`Society label → ${v}`, (d) => ({ ...d, societyIds: d.societyIds.map((x, j) => j === i ? { ...x, society: v } : x) }))}
                style={{ fontSize: 11, maxWidth: 130 }} /> :


              <span style={{ color: 'var(--ink-3)' }}>
                      {row.society === 'BMI' ? 'BMI Work#' :
                row.society === 'ASCAP' ? 'ASCAP TWID' :
                row.society === 'MusicBrainz' ? 'MusicBrainz' :
                row.society}
                    </span>
              }
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    {editing ?
                <InlineText
                  value={row.workId}
                  onCommit={(v) => {commit(`${row.society} ID → ${v}`, (d) => ({ ...d, societyIds: d.societyIds.map((x, j) => j === i ? { ...x, workId: v } : x) }));pulse(`sid-${i}`);}}
                  style={{ fontSize: 12, fontFamily: 'inherit', display: 'inline-block', flex: 1 }}
                  placeholder="—" /> :

                <span>{row.workId}</span>}
                    {editing &&
                <button onClick={() => commit(`Removed ${row.society} ID`, (d) => ({ ...d, societyIds: d.societyIds.filter((_, j) => j !== i) }))}
                style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 2 }}>
                        <Ic.X width={12} height={12} />
                      </button>
                }
                  </div>
                </React.Fragment>
            )}
              {editing &&
            <>
                  <span />
                  <button
                onClick={() => commit('Added society ID', (d) => ({ ...d, societyIds: [...d.societyIds, { society: 'ASCAP', workId: '' }] }))}
                style={{ justifySelf: 'start', display: 'flex', gap: 6, alignItems: 'center', padding: '6px 10px', border: '1px solid var(--rule-soft)', background: 'var(--edit-fill)', borderRadius: 3, cursor: 'pointer', fontSize: 11, color: 'var(--ink-2)' }}>
                    <Ic.Plus width={10} height={10} /> Add society ID
                  </button>
                </>
            }
            </div>

            <Section num="03">Recent activity</Section>
            <button onClick={()=>setTab('history')}
              style={{ display:'block', width:'100%', textAlign:'left', padding:0, background:'transparent', border:0, cursor:'pointer', color:'inherit' }}>
              <div style={{ display:'flex', justifyContent:'flex-end', alignItems:'baseline', padding:'4px 0 8px' }}>
                <span className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', color:'var(--ink-2)', display:'inline-flex', alignItems:'center', gap:4 }}>
                  Open history <Ic.Right width={11} height={11}/>
                </span>
              </div>
              <div>
                {history.length > 0 &&
              history.slice().reverse().slice(0, 3).map((h, i) =>
              <div key={`h-${i}`} style={{ padding: '10px 0', borderBottom: '1px solid var(--rule-soft)', fontSize: 13 }}>
                      <span className="ff-mono num" style={{ fontSize: 10, color: 'var(--ink-4)', marginRight: 10 }}>{relTime(h.ts)}</span>
                      <span className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>you</span> {h.label}
                    </div>
              )
              }
                {RECENT.slice(0, history.length > 0 ? 3 : 4).map((r, i) =>
              <div key={i} style={{ padding: '10px 0', borderBottom: '1px solid var(--rule-soft)', fontSize: 13 }}>
                    <span className="ff-mono num" style={{ fontSize: 10, color: 'var(--ink-4)', marginRight: 10 }}>{r.time}</span>
                    <span className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>{r.who}</span> {r.what}{' '}
                    <b>{r.target}</b>
                  </div>
              )}
              </div>
            </button>
          </div>

          <div>
            <Section num="04">Society registrations</Section>
            <button onClick={()=>setTab('registrations')}
              style={{ display:'block', width:'100%', textAlign:'left', padding:0, background:'transparent', border:0, cursor:'pointer', color:'inherit' }}>
              <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', padding:'4px 0 8px' }}>
                <span className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', color:'var(--ink-3)' }}>{SOCIETIES.length} societies tracked</span>
                <span className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', color:'var(--ink-2)', display:'inline-flex', alignItems:'center', gap:4 }}>
                  Open registrations <Ic.Right width={11} height={11}/>
                </span>
              </div>
              <div>
                {SOCIETIES.slice(0, 6).map((s) =>
              <div key={s.acronym} style={{ display: 'grid', gridTemplateColumns: '60px 1fr 80px', gap: 10, padding: '12px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center' }}>
                    <span className="ff-mono" style={{ fontSize: 12, fontWeight: 600 }}>{s.acronym}</span>
                    <span className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>{s.territory} · sent {s.lastSent}</span>
                    <Pill tone={s.ackRate > 98 ? 'ok' : s.ackRate > 95 ? 'soft' : 'danger'} dot>{s.ackRate > 98 ? 'ack' : 'pending'}</Pill>
                  </div>
              )}
              </div>
            </button>

            <Section num="05">Earnings · 12mo</Section>
            <button onClick={()=>go('analytics')}
              style={{ padding: '14px 0', display:'block', width:'100%', textAlign:'left', background:'transparent', border:0, borderBottom:'1px solid transparent', cursor:'pointer', color:'inherit' }}
              onMouseEnter={e=>e.currentTarget.style.borderBottomColor='var(--ink)'}
              onMouseLeave={e=>e.currentTarget.style.borderBottomColor='transparent'}>
              <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline' }}>
                <div className="ff-display num" style={{ fontSize: 48, fontWeight: 600, letterSpacing: '-0.04em', lineHeight: 1 }}>$48,210</div>
                <span className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', color:'var(--ink-3)', display:'inline-flex', alignItems:'center', gap:4 }}>
                  Open analytics <Ic.Right width={11} height={11}/>
                </span>
              </div>
              <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 4 }}>+12.4% YoY</div>
              <div style={{ marginTop: 14 }}>
                <Spark data={sparkRoyalty} w={320} h={80} stroke="var(--ink)" area fill="var(--ink-2)" strokeWidth={1.5} />
              </div>
            </button>
          </div>
        </div>
      }

      {tab === 'recordings' && <WorkRecordingsTab w={w} go={go} />}
      {tab === 'splitsheet' && window.SplitSheetWorkshop && <window.SplitSheetWorkshop work={{ ...w, ...m }} embedded /> }
      {tab === 'splits' && <WorkSplitsTab m={m} setData={setData} commit={commit} editing={editing} pulse={pulse} pulseId={pulseId} />}
      {tab === 'lyrics' && <WorkLyricsTab m={m} commit={commit} editing={editing} />}
      {tab === 'rights' && <WorkRightsTab m={m} commit={commit} editing={editing} />}
      {tab === 'registrations' && <WorkRegistrationsTab m={m} commit={commit} editing={editing} go={go} />}
      {tab === 'history' && <WorkHistoryTab m={m} history={history} cursor={cursor} onRevert={revertTo} go={go} />}
      {tab === 'royalties' && <WorkRoyaltiesTab m={m} go={go} />}

      {tab !== 'overview' && tab !== 'recordings' && tab !== 'splits' && tab !== 'splitsheet' && tab !== 'lyrics' && tab !== 'rights' && tab !== 'registrations' && tab !== 'history' && tab !== 'royalties' &&
      <div style={{ padding: '80px 0', textAlign: 'center', color: 'var(--ink-3)' }}>
          <div className="heading-swap ff-display" style={{ fontSize: 36, marginBottom: 12, fontWeight: 600 }}>{tab}</div>
          <p>Detailed {tab} pane.</p>
        </div>
      }

      <HistoryDrawer
        open={historyOpen}
        onClose={() => setHistoryOpen(false)}
        history={history}
        cursor={cursor}
        onUndo={undo}
        onRedo={redo}
        onRevert={revertTo} />

      {/* Export modals */}
      {cwrModalOpen && <CwrGenerationModal m={m} onClose={()=>setCwrModalOpen(false)} commit={commit}/>}
      {ddexModalOpen && <DdexExportModal m={m} onClose={()=>setDdexModalOpen(false)} commit={commit}/>}
      {publicModalOpen && <PublicPageModal m={m} onClose={()=>setPublicModalOpen(false)}/>}

    </div>);

}

function WorkRecordingsTab({ w, go }) {
  const [sub, setSub] = React.useState('recordings');
  const recs = typeof recordingsForWork === 'function' ? recordingsForWork(w.id) : [];
  const sampledBy = typeof recordingsThatSampleOrInterpolate === 'function' ? recordingsThatSampleOrInterpolate(w.id) : [];

  // Inbound samples — works/recordings THIS work borrows from. Pulled from the recordings'
  // alsoWorks/derivesFrom edges where the source IS NOT w.id.
  const samplesUsed = React.useMemo(() => {
    const out = [];
    const seen = new Set();
    recs.forEach((rec) => {
      (rec.alsoWorks || []).filter((aw) => aw.workId !== w.id).forEach((aw) => {
        const key = rec.id + '|' + aw.workId;
        if (seen.has(key)) return;
        seen.add(key);
        const sourceWork = typeof WORK_BY_ID !== 'undefined' ? WORK_BY_ID[aw.workId] : null;
        out.push({
          inRecording: rec,
          sourceWork,
          sourceWorkId: aw.workId,
          share: aw.share,
          clearance: aw.clearance,
          kind: 'sample'
        });
      });
      (rec.derivesFrom || []).forEach((d) => {
        const key = rec.id + '|d|' + (d.recId || '');
        if (seen.has(key)) return;
        seen.add(key);
        out.push({
          inRecording: rec,
          sourceRecording: typeof REC_BY_ID !== 'undefined' && d.recId ? REC_BY_ID[d.recId] : null,
          sourceWork: d.workId && typeof WORK_BY_ID !== 'undefined' ? WORK_BY_ID[d.workId] : null,
          kind: d.kind || 'sample',
          clearance: d.clearance || 'cleared',
          share: d.share
        });
      });
    });
    return out;
  }, [recs, w.id]);

  return (
    <div>
      {/* Sub-tabs */}
      <div style={{ display: 'flex', gap: 0, borderBottom: '1px solid var(--rule)', marginBottom: 20 }}>
        {[
        { k: 'recordings', l: 'Recordings', n: recs.length },
        { k: 'sampled by', l: 'Sampled by', n: sampledBy.length },
        { k: 'samples', l: 'Samples', n: samplesUsed.length }].
        map((t) =>
        <button key={t.k} onClick={() => setSub(t.k)} className="ff-mono upper"
        style={{ padding: '10px 14px', fontSize: 10, letterSpacing: '.1em', fontWeight: 600,
          borderBottom: sub === t.k ? '2px solid var(--ink)' : '2px solid transparent',
          marginBottom: -1, color: sub === t.k ? 'var(--ink)' : 'var(--ink-3)',
          background: 'transparent', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
            {t.l}
            <span className="num" style={{
            padding: '1px 6px', fontSize: 9,
            background: sub === t.k ? 'var(--ink)' : 'var(--bg-2)',
            color: sub === t.k ? 'var(--bg)' : 'var(--ink-3)'
          }}>{t.n}</span>
          </button>
        )}
      </div>

      {sub === 'recordings' && <_RecordingsList w={w} recs={recs} go={go} />}
      {sub === 'sampled by' && <_SampledByList w={w} recs={sampledBy} go={go} />}
      {sub === 'samples' && <_SamplesUsedList w={w} items={samplesUsed} go={go} />}
    </div>);

}

function _RecordingsList({ w, recs, go }) {
  if (recs.length === 0) {
    return (
      <div style={{ padding: '60px 0', textAlign: 'center', color: 'var(--ink-3)' }}>
        <div className="heading-swap ff-display" style={{ fontSize: 32, marginBottom: 8, fontWeight: 600, color: 'var(--ink-2)' }}>No recordings yet</div>
        <p style={{ maxWidth: 480, margin: '0 auto 18px', fontSize: 14 }}>This composition is registered but has not been commercially released. Pitch it, attach a demo, or link an existing recording.</p>
        <Btn variant="primary" onClick={() => window.dispatchEvent(new CustomEvent('astro-add-recording', { detail: { workId: w.id } }))}>+ Add recording</Btn>
      </div>);

  }
  const groups = typeof groupRecordingsByArtist === 'function' ? groupRecordingsByArtist(recs) : [['', recs]];
  return (
    <div>
      <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 12, letterSpacing: '.1em' }}>
        {recs.length} RECORDING{recs.length > 1 ? 'S' : ''} · {groups.length} ARTIST{groups.length > 1 ? 'S' : ''}
      </div>
      {groups.map(([artist, list]) =>
      <div key={artist} style={{ marginBottom: 24 }}>
          <div style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.01em', padding: '10px 0', borderBottom: '1px solid var(--rule)' }}>{artist}</div>
          {list.map((rec) => {
          const releases = (rec.releaseIds || []).map((id) => REL_BY_ID?.[id]).filter(Boolean);
          const derivBadge = rec.derivesFrom && rec.derivesFrom[0];
          return (
            <div key={rec.id} data-rec-row={rec.id} onClick={() => window.dispatchEvent(new CustomEvent('astro-open-recording', { detail: { id: rec.id } }))}
            style={{ display: 'grid', gridTemplateColumns: '52px 1fr 1fr 90px 90px 14px', gap: 14, padding: '14px 0',
              borderBottom: '1px solid var(--rule-soft)', alignItems: 'center', cursor: 'pointer' }}>
                <div style={{ width: 48, height: 48, background: rec.art, position: 'relative' }}>
                  <Ic.Disc width={20} height={20} style={{ position: 'absolute', top: 14, left: 14, color: 'rgba(0,0,0,.55)' }} />
                </div>
                <div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                    <span style={{ fontSize: 14, fontWeight: 600 }}>{rec.title}</span>
                    {derivBadge && <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '2px 6px', border: '1px solid #7d3da8', color: '#7d3da8', fontWeight: 600 }}>{derivBadge.kind}</span>}
                    {rec.explicit && <span className="ff-mono" style={{ fontSize: 9, padding: '1px 4px', background: 'var(--ink)', color: 'var(--bg)', fontWeight: 600 }}>E</span>}
                  </div>
                  <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>{rec.isrc} · {Math.floor(rec.duration / 60)}:{String(rec.duration % 60).padStart(2, '0')}</div>
                </div>
                <div>
                  <div style={{ fontSize: 13 }}>{rec.album}</div>
                  <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>{rec.label} · {rec.year}</div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <div className="ff-mono num" style={{ fontSize: 14 }}>{rec.plays}M</div>
                  <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2, letterSpacing: '.08em' }}>plays</div>
                </div>
                <div style={{ textAlign: 'right' }}>
                  <div className="ff-mono num" style={{ fontSize: 13 }}>{releases.length}</div>
                  <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2, letterSpacing: '.08em' }}>releases</div>
                </div>
                <Ic.Right width={14} height={14} style={{ color: 'var(--ink-3)' }} />
              </div>);

        })}
        </div>
      )}
    </div>);

}

function _SampledByList({ w, recs, go }) {
  if (recs.length === 0) {
    return (
      <div style={{ padding: '60px 0', textAlign: 'center', color: 'var(--ink-3)' }}>
        <div className="heading-swap ff-display" style={{ fontSize: 32, marginBottom: 8, fontWeight: 600, color: 'var(--ink-2)' }}>Not sampled yet</div>
        <p style={{ maxWidth: 480, margin: '0 auto', fontSize: 14 }}>No third-party recordings currently sample or interpolate this work.</p>
      </div>);

  }
  const totalShare = recs.reduce((s, r) => s + ((r.alsoWorks || []).find((a) => a.workId === w.id)?.share || 0), 0);
  return (
    <div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 0, marginBottom: 24, border: '1px solid var(--rule)' }}>
        {[
        { l: 'RECORDINGS', v: recs.length },
        { l: 'CLEARED', v: recs.filter((r) => (r.alsoWorks || []).find((a) => a.workId === w.id)?.clearance === 'cleared').length },
        { l: 'AVG SHARE', v: `${(totalShare / recs.length).toFixed(0)}%` }].
        map((s, i) =>
        <div key={i} style={{ padding: '18px 20px', borderRight: i < 2 ? '1px solid var(--rule)' : 'none' }}>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.1em' }}>{s.l}</div>
            <div className="ff-display num" style={{ fontSize: 32, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 6 }}>{s.v}</div>
          </div>
        )}
      </div>

      {recs.map((rec) => {
        const aw = (rec.alsoWorks || []).find((a) => a.workId === w.id);
        const cColor = aw.clearance === 'cleared' ? '#5b8a3a' : aw.clearance === 'pending' ? '#b78113' : '#c0392b';
        return (
          <div key={rec.id} onClick={() => window.dispatchEvent(new CustomEvent('astro-open-recording', { detail: { id: rec.id } }))}
          style={{ display: 'grid', gridTemplateColumns: '56px 1fr 130px 100px 110px 14px', gap: 14, padding: '16px 0',
            borderBottom: '1px solid var(--rule-soft)', alignItems: 'center', cursor: 'pointer' }}>
            <div style={{ width: 52, height: 52, background: rec.art, position: 'relative' }}>
              <Ic.Disc width={22} height={22} style={{ position: 'absolute', top: 15, left: 15, color: 'rgba(0,0,0,.55)' }} />
            </div>
            <div>
              <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: '-0.01em' }}>{rec.title}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-2)', marginTop: 3 }}>{rec.artist} · {rec.album}</div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>{rec.isrc} · {rec.label} · {rec.year}</div>
            </div>
            <span className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.1em', color: '#9b6a18', fontWeight: 600 }}>{aw.type}</span>
            <span className="ff-mono num" style={{ fontSize: 18, textAlign: 'right', fontWeight: 600 }}>{aw.share}%</span>
            <span className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.1em', textAlign: 'right', color: cColor, fontWeight: 600,
              border: `1px solid ${cColor}`, padding: '4px 8px', justifySelf: 'end' }}>{aw.clearance}</span>
            <Ic.Right width={14} height={14} style={{ color: 'var(--ink-3)' }} />
          </div>);

      })}
    </div>);

}

// ───────────────────────────────────────────────────────────── SPLITS TAB
// ─── Ref-backed lists. These are getters so they pick up window.REF as soon as
// it loads — the prototype boots before fetch() resolves, so we can't capture
// the list once at module scope. Hardcoded values are the offline fallback.
const _WRITER_ROLES_FALLBACK = ['Composer', 'Composer/Author', 'Author', 'Arranger', 'Adaptor', 'Translator', 'Sub-Arranger', 'Sub-Author', 'Income Participant'];
const _PUB_ROLES_FALLBACK    = ['Original Publisher', 'Substituted Publisher', 'Income Participant', 'Acquirer'];
const _ADMIN_ROLES_FALLBACK  = ['Sub-pub', 'Administrator', 'Co-publisher'];
const _PRO_FALLBACK          = ['ASCAP', 'BMI', 'SESAC', 'GMR', 'PRS', 'GEMA', 'SACEM', 'SOCAN', 'APRA', 'JASRAC'];
const _TERR_FALLBACK         = ['World', 'World ex. US', 'US/Canada', 'UK / Ireland', 'EU', 'Latin America', 'Asia-Pacific', 'Japan', 'Australia', 'Africa'];

// `WRITER_ROLES` / `PUB_ROLES` / etc. are referenced as plain arrays throughout
// the splits UI — so we expose Proxy-ish arrays that re-resolve on every read.
// Cheap because the lists are tiny (~10 items) and caller code spreads them once.
function _refLabels(kind, filterFn, fallback) {
  const REF = window.REF;
  if (!REF || !REF.ready) return fallback;
  const rows = REF.raw[kind];
  if (!Array.isArray(rows) || !rows.length) return fallback;
  const labels = rows.filter(filterFn || (() => true)).map(r => r.label).filter(Boolean);
  return labels.length ? labels : fallback;
}

// Splits-tab role lists. writer_roles in the DB has 9 rows that map 1:1 to the
// prototype's hardcoded list (Composer, Author, Arranger, Adaptor, Translator…).
// publisher_roles has 6 rows; we split them into "publisher" vs "admin" buckets
// using the cwr_code convention (E/SE = orig/substituted publisher → PUB_ROLES;
// AM/SA/AQ = administrator/sub-pub/acquirer → ADMIN_ROLES).
Object.defineProperty(window, 'WRITER_ROLES', { configurable: true,
  get: () => _refLabels('writer_roles', null, _WRITER_ROLES_FALLBACK) });
// publisher_roles codes (verified against db/ref/all.json):
//   E  = Original Publisher          → PUB
//   ES = Substituted Publisher       → PUB
//   AQ = Acquirer                    → PUB
//   PA = Income Participant          → PUB
//   SE = Sub-Publisher               → ADMIN
//   AM = Administrator               → ADMIN
Object.defineProperty(window, 'PUB_ROLES', { configurable: true,
  get: () => _refLabels('publisher_roles',
    r => /^(E|ES|AQ|PA)$/i.test(r.code),
    _PUB_ROLES_FALLBACK) });
Object.defineProperty(window, 'ADMIN_ROLES', { configurable: true,
  get: () => _refLabels('publisher_roles',
    r => /^(SE|AM)$/i.test(r.code),
    _ADMIN_ROLES_FALLBACK) });

// Local refs (renamed to avoid shadowing window.WRITER_ROLES/PUB_ROLES/ADMIN_ROLES,
// which causes "configurable attribute" errors on Babel re-eval). The window-level
// getters above are still the public API; these are local-scope wrappers.
const _LWRITER_ROLES = new Proxy({}, { get: (_, k) => window.WRITER_ROLES[k] });
const _LPUB_ROLES    = new Proxy({}, { get: (_, k) => window.PUB_ROLES[k] });
const _LADMIN_ROLES  = new Proxy({}, { get: (_, k) => window.ADMIN_ROLES[k] });

// PROS — society acronyms filtered to PRO/MIX/HUB types (writer-side perf rights).
// REF.societies has 407 rows; the most active ~30 (ASCAP, BMI, …) are the practical
// dropdown. Sort by membership count would be ideal; for now just alphabetical.
const PROS_LIST = new Proxy({}, {
  get(_, k) {
    const list = (window.SOC_WRITER && window.SOC_WRITER.length)
      ? window.SOC_WRITER
      : _PRO_FALLBACK;
    const arr = [...list, '—'];
    return arr[k];
  },
  has(_, k) { return k in [...(_PRO_FALLBACK), '—']; },
  ownKeys() { const a = [...(window.SOC_WRITER || _PRO_FALLBACK), '—']; return Object.keys(a); },
  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }; },
});

// Territory presets (W/Wx-US/etc.) live in ref.territory_groups. 12 rows there,
// but the prototype only uses ~10 broad presets — fall back if missing.
const TERRITORY_OPTS = new Proxy({}, {
  get(_, k) {
    const REF = window.REF;
    const groups = REF && REF.ready && REF.raw.territory_groups;
    if (Array.isArray(groups) && groups.length) {
      const arr = groups.map(g => g.group_name).filter(Boolean);
      return arr.length ? arr[k] : _TERR_FALLBACK[k];
    }
    return _TERR_FALLBACK[k];
  },
  ownKeys() {
    const REF = window.REF;
    const groups = REF && REF.ready && REF.raw.territory_groups;
    const arr = (Array.isArray(groups) && groups.length)
      ? groups.map(g => g.group_name).filter(Boolean)
      : _TERR_FALLBACK;
    return Object.keys(arr);
  },
  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }; },
});

function SplitsBar({ label, p, m, s, share, splitShares }) {
  // 3-stripe bar showing perf/mech/sync as percentage of overall share.
  // When splitShares is off, it's a single bar.
  if (!splitShares) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <div style={{ flex: 1, height: 6, background: 'var(--bg-2)', position: 'relative' }}>
          <div style={{ position: 'absolute', inset: 0, width: `${share}%`, background: 'var(--ink)' }} />
        </div>
        <span className="ff-mono num" style={{ fontSize: 12, minWidth: 46, textAlign: 'right', fontWeight: 600 }}>{share}%</span>
      </div>);

  }
  return (
    <div>
      {[['perf', p, 'Performing'], ['mech', m, 'Mechanical'], ['sync', s, 'Sync']].map(([k, v, name]) =>
      <div key={k} style={{ display: 'grid', gridTemplateColumns: '78px 1fr 50px', gap: 8, alignItems: 'center', marginBottom: 4 }}>
          <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)' }}>{name}</span>
          <div style={{ height: 5, background: 'var(--bg-2)', position: 'relative' }}>
            <div style={{ position: 'absolute', inset: 0, width: `${v}%`, background: k === 'perf' ? 'var(--ink)' : k === 'mech' ? 'var(--ink-2)' : 'oklch(0.55 0.13 250)' }} />
          </div>
          <span className="ff-mono num" style={{ fontSize: 11, textAlign: 'right', fontWeight: 600 }}>{v}%</span>
        </div>
      )}
    </div>);

}

function CbInline({ checked, onChange, children, editing }) {
  if (!editing) {
    return checked ?
    <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '3px 7px', border: '1px solid var(--ink)', color: 'var(--ink)', fontWeight: 600 }}>{children}</span> :
    null;
  }
  return (
    <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: checked ? 'var(--ink)' : 'var(--ink-3)', cursor: 'pointer', userSelect: 'none', padding: '3px 7px', border: 'none', borderRadius: 3, background: checked ? 'var(--bg-2)' : 'var(--edit-fill)' }}>
      <input type="checkbox" checked={!!checked} onChange={(e) => onChange(e.target.checked)} style={{ margin: 0 }} />
      <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em' }}>{children}</span>
    </label>);

}

function TerritoryEditor({ value, onChange, editing }) {
  const list = value || [];
  if (!editing) {
    return (
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
        {list.length === 0 && <span className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>—</span>}
        {list.map((t) =>
        <span key={t} className="ff-mono" style={{ fontSize: 10, padding: '3px 8px', background: 'var(--bg-2)', border: '1px solid var(--rule)' }}>{t}</span>
        )}
      </div>);

  }
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center' }}>
      {list.map((t) =>
      <span key={t} className="ff-mono" style={{ fontSize: 10, padding: '3px 4px 3px 8px', background: 'var(--bg-2)', border: '1px solid var(--rule)', display: 'inline-flex', alignItems: 'center', gap: 4 }}>
          {t}
          <button onClick={() => onChange(list.filter((x) => x !== t))} style={{ background: 'transparent', border: 0, cursor: 'pointer', color: 'var(--ink-3)', padding: '0 2px', fontSize: 12, lineHeight: 1 }}>×</button>
        </span>
      )}
      <select value="" onChange={(e) => {if (e.target.value && !list.includes(e.target.value)) onChange([...list, e.target.value]);}}
      className="ff-mono" style={{ fontSize: 10, padding: '4px 6px', background: 'var(--edit-fill)', border: 'none', borderRadius: 3 }}>
        <option value="">+ add territory</option>
        {TERRITORY_OPTS.filter((o) => !list.includes(o)).map((o) => <option key={o}>{o}</option>)}
      </select>
    </div>);

}

function AdminRow({ a, ai, onPatch, onRemove, editing, parentShare }) {
  return (
    <div style={{ padding: '10px 12px', background: 'var(--bg)', border: '1px solid var(--rule-soft)', marginTop: 6, marginLeft: 18, position: 'relative' }}>
      <div style={{ position: 'absolute', left: -9, top: 18, width: 9, height: 1, background: 'var(--rule)' }} />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 110px 110px 70px 24px', gap: 10, alignItems: 'center' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 2 }}>SUB-PUBLISHER / ADMIN {ai + 1}</div>
          {editing ?
          <InlineText value={a.name} onCommit={(v) => onPatch({ name: v })} style={{ fontSize: 13, fontWeight: 500, display: 'inline-block' }} placeholder="Admin name" /> :

          <div style={{ fontSize: 13, fontWeight: 500 }}>{a.name || <em style={{ color: 'var(--ink-3)' }}>(unnamed)</em>}</div>
          }
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>
            {editing ?
            <InlineSelect value={a.role} options={_LADMIN_ROLES} onCommit={(v) => onPatch({ role: v })} style={{ fontSize: 10 }} /> :
            a.role}
            {' · '}<SocietyLink code={a.pro}/> · IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(a.ipi) : a.ipi) || '—'}
          </div>
        </div>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 2 }}>TERRITORY</div>
          <TerritoryEditor value={a.territories} onChange={(t) => onPatch({ territories: t })} editing={editing} />
        </div>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 2 }}>SHARE</div>
          {editing ?
          <input type="number" min={0} max={parentShare} value={a.share}
          onChange={(e) => onPatch({ share: Math.max(0, Math.min(parentShare, +e.target.value || 0)) })}
          className="ff-mono num" style={{ width: '100%', padding: '4px 6px', fontSize: 12, textAlign: 'right', border: 'none', borderRadius: 3, background: 'var(--edit-fill)' }} /> :

          <span className="ff-mono num" style={{ fontSize: 13, fontWeight: 600 }}>{a.share}%</span>
          }
        </div>
        {editing
          ? <CbInline checked={a.controlled} onChange={(v) => onPatch({ controlled: v })} editing={editing}>CTRL</CbInline>
          : (window.ControlledLabel && <window.ControlledLabel share={a} compact={true}/>)}
        {editing &&
        <button onClick={onRemove} style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 4 }}>
            <Ic.X width={12} height={12} />
          </button>
        }
      </div>
    </div>);

}

function PubRow({ p, pi, onPatch, onRemove, onAddAdmin, onPatchAdmin, onRemoveAdmin, editing, writerShare }) {
  const isOpaque = p.unknown || p.selfPub;
  const adminUsed = (p.admins || []).reduce((s, a) => s + (+a.share || 0), 0);
  const adminOver = adminUsed > p.share;
  return (
    <div style={{ padding: '14px 16px', background: 'var(--bg)', border: '1px solid var(--rule)', marginTop: 8, marginLeft: 18, position: 'relative' }}>
      <div style={{ position: 'absolute', left: -10, top: 24, width: 10, height: 1, background: 'var(--rule)' }} />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 130px 130px 90px 28px', gap: 12, alignItems: 'start' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 3 }}>PUBLISHER {pi + 1}</div>
          {p.unknown ?
          <span className="ff-mono upper" style={{ fontSize: 11, letterSpacing: '.08em', color: 'var(--ink-3)' }}>PUBLISHER UNKNOWN</span> :
          p.selfPub ?
          <div>
              <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '2px 6px', background: 'var(--ink)', color: 'var(--bg)', fontWeight: 600 }}>SELF-PUBLISHED</span>
              <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-2)', marginTop: 6 }}>Writer retains publisher share</div>
            </div> :

          editing ?
          <InlineText value={p.name} onCommit={(v) => onPatch({ name: v })} style={{ fontSize: 14, fontWeight: 600, display: 'inline-block' }} placeholder="Publisher name" /> :

          <div style={{ fontSize: 14, fontWeight: 600 }}>{window.PartyIndicator && <window.PartyIndicator party={p}/>}{p.name}</div>

          }
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>
            {editing ?
            <InlineSelect value={p.role} options={_LPUB_ROLES} onCommit={(v) => onPatch({ role: v })} style={{ fontSize: 10 }} /> :
            p.role}
            {!isOpaque && <> · <SocietyLink code={p.pro}/> · IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(p.ipi) : p.ipi) || '—'}</>}
          </div>
        </div>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 3 }}>TERRITORY</div>
          {!isOpaque ?
          <TerritoryEditor value={p.territories} onChange={(t) => onPatch({ territories: t })} editing={editing} /> :
          <span className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)' }}>—</span>}
        </div>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 3 }}>SHARE</div>
          {editing ?
          <input type="number" min={0} max={writerShare} value={p.share}
          onChange={(e) => onPatch({ share: Math.max(0, Math.min(writerShare, +e.target.value || 0)) })}
          className="ff-mono num" style={{ width: '100%', padding: '5px 8px', fontSize: 13, textAlign: 'right', border: 'none', borderRadius: 3, background: 'var(--edit-fill)' }} /> :

          <span className="ff-mono num" style={{ fontSize: 14, fontWeight: 600 }}>{p.share}%</span>
          }
        </div>
        {editing
          ? <CbInline checked={p.controlled} onChange={(v) => onPatch({ controlled: v })} editing={editing}>CTRL</CbInline>
          : (window.ControlledLabel && <window.ControlledLabel share={p} compact={true}/>)}
        {editing &&
        <button onClick={onRemove} title="Remove publisher" style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 4 }}>
            <Ic.X width={14} height={14} />
          </button>
        }
      </div>

      {/* Admins */}
      {(p.admins || []).length > 0 &&
      <div style={{ marginTop: 10 }}>
          <div className="ff-mono upper" style={{ fontSize: 8, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 2, marginLeft: 18 }}>SUB-PUBLISHERS · {adminUsed}% of {p.share}%{adminOver && <span style={{ color: '#c0392b' }}> · over by {adminUsed - p.share}%</span>}</div>
          {(p.admins || []).map((a, ai) =>
        <AdminRow key={ai} a={a} ai={ai} editing={editing} parentShare={p.share}
        onPatch={(patch) => onPatchAdmin(ai, patch)}
        onRemove={() => onRemoveAdmin(ai)} />
        )}
        </div>
      }
      {editing && !isOpaque &&
      <button onClick={onAddAdmin} className="ff-mono upper"
      style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 10, marginLeft: 18, padding: '5px 10px', fontSize: 9, letterSpacing: '.08em',
        background: 'var(--edit-fill)', color: 'var(--ink-3)', border: '1px solid var(--rule-soft)', borderRadius: 3, cursor: 'pointer' }}>
          <Ic.Plus width={10} height={10} /> Add sub-publisher
        </button>
      }
    </div>);

}

function _SamplesUsedList({ w, items, go }) {
  if (items.length === 0) {
    return (
      <div style={{ padding: '60px 0', textAlign: 'center', color: 'var(--ink-3)' }}>
        <div className="heading-swap ff-display" style={{ fontSize: 32, marginBottom: 8, fontWeight: 600, color: 'var(--ink-2)' }}>No samples used</div>
        <p style={{ maxWidth: 480, margin: '0 auto', fontSize: 14 }}>This work does not sample, interpolate, or derive material from another composition.</p>
      </div>);

  }
  const cleared = items.filter((i) => i.clearance === 'cleared').length;
  const pending = items.filter((i) => i.clearance === 'pending').length;
  const blocked = items.filter((i) => i.clearance && i.clearance !== 'cleared' && i.clearance !== 'pending').length;
  const totalShareOut = items.reduce((s, i) => s + (+i.share || 0), 0);
  return (
    <div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 0, marginBottom: 24, border: '1px solid var(--rule)' }}>
        {[
        { l: 'SOURCES', v: items.length },
        { l: 'CLEARED', v: cleared },
        { l: 'PENDING', v: pending, c: pending > 0 ? '#b78113' : null },
        { l: 'SHARE OUT', v: `${totalShareOut.toFixed(0)}%` }].
        map((s, i) =>
        <div key={i} style={{ padding: '18px 20px', borderRight: i < 3 ? '1px solid var(--rule)' : 'none' }}>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.1em' }}>{s.l}</div>
            <div className="ff-display num" style={{ fontSize: 32, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 6, color: s.c || 'inherit' }}>{s.v}</div>
          </div>
        )}
      </div>

      <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', marginBottom: 8, letterSpacing: '.1em', padding: '0 0 8px' }}>SOURCE WORKS / RECORDINGS</div>

      {items.map((it, i) => {
        const cColor = it.clearance === 'cleared' ? '#5b8a3a' : it.clearance === 'pending' ? '#b78113' : '#c0392b';
        const cBg = it.clearance === 'cleared' ? '#eef3e6' : it.clearance === 'pending' ? '#faf0d8' : '#f6e0dc';
        const sw = it.sourceWork;
        const sr = it.sourceRecording;
        const handleClick = () => {
          if (sw) window.dispatchEvent(new CustomEvent('astro-open-work', { detail: { id: sw.id } }));else
          if (sr) window.dispatchEvent(new CustomEvent('astro-open-recording', { detail: { id: sr.id } }));
        };
        return (
          <div key={i} onClick={handleClick}
          style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 110px 100px 90px 14px', gap: 14, padding: '16px 0',
            borderBottom: '1px solid var(--rule-soft)', alignItems: 'center', cursor: sw || sr ? 'pointer' : 'default' }}>
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                <span style={{ fontSize: 14, fontWeight: 600 }}>{sw ? sw.title : sr ? sr.title : '(unknown source)'}</span>
                <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '2px 6px', border: '1px solid #7d3da8', color: '#7d3da8', fontWeight: 600 }}>{it.kind}</span>
              </div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3 }}>
                {sw ? sw.iswc || 'no ISWC' : sr ? sr.isrc : '—'}
                {sw && sw.writers && sw.writers[0] && <span> · {sw.writers.map((x) => x.name).slice(0, 2).join(', ')}{sw.writers.length > 2 ? ` +${sw.writers.length - 2}` : ''}</span>}
              </div>
            </div>
            <div>
              <div style={{ fontSize: 13, color: 'var(--ink-2)' }}>used in</div>
              <div className="ff-mono" style={{ fontSize: 11, marginTop: 3 }}>{it.inRecording.title}</div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>{it.inRecording.album} · {it.inRecording.year}</div>
            </div>
            <div>
              <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '3px 8px', background: cBg, color: cColor, fontWeight: 600 }}>
                {it.clearance || 'unknown'}
              </span>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div className="ff-mono num" style={{ fontSize: 14 }}>{it.share != null ? `${it.share}%` : '—'}</div>
              <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', marginTop: 2, letterSpacing: '.08em' }}>share out</div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <button className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '4px 8px', border: '1px solid var(--rule)', background: 'transparent', cursor: 'pointer' }}
              onClick={(e) => {e.stopPropagation();}}>License</button>
            </div>
            <Ic.Right width={14} height={14} style={{ color: 'var(--ink-3)' }} />
          </div>);

      })}

      <div style={{ marginTop: 18, padding: '14px 16px', background: 'var(--bg-2)', border: '1px solid var(--rule-soft)', borderRadius: 3, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Add a sample source</div>
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>Link an existing work or upload a new clearance contract.</div>
        </div>
        <Btn variant="secondary" onClick={() => window.toast && window.toast('Add a sample / interpolation source — link an existing work or upload a contract', 'soft')}>+ Add source</Btn>
      </div>
    </div>);

}

function SplitsGraph({ writers, writerTotal }) {
  // Color palette — distinct hues for each writer, with publisher slices a darker shade.
  const HUES = [
  { writer: '#1f3d5c', pub: '#4a6f93', kept: '#a9c2d8' },
  { writer: '#7d3da8', pub: '#a36cc8', kept: '#d8c0e6' },
  { writer: '#b8541b', pub: '#d4824a', kept: '#ecc3a3' },
  { writer: '#3a6b3e', pub: '#6b9b6f', kept: '#bcd6bf' },
  { writer: '#8a2c4f', pub: '#b35878', kept: '#dfb1c2' },
  { writer: '#0f5e6e', pub: '#3f8d9d', kept: '#a8cad2' }];


  // Build segments. Each writer becomes a colored cluster of (publisher segments... | kept segment).
  const segments = [];
  writers.forEach((w, wi) => {
    const hue = HUES[wi % HUES.length];
    const pubs = w.pubs || [];
    const pubsUsed = pubs.reduce((s, p) => s + (+p.share || 0), 0);
    const kept = Math.max(0, (+w.share || 0) - pubsUsed);
    pubs.forEach((p, pi) => {
      if ((+p.share || 0) <= 0) return;
      segments.push({
        share: +p.share,
        color: hue.pub,
        label: `${p.name || 'Unnamed publisher'} (${w.name})`,
        sub: `${p.share}% via ${p.role || 'pub'}`,
        writerIdx: wi, kind: 'pub'
      });
    });
    if (kept > 0) {
      segments.push({
        share: kept,
        color: hue.kept,
        label: `${w.name} — kept`,
        sub: `${kept}% writer share`,
        writerIdx: wi, kind: 'kept'
      });
    }
    if (pubs.length === 0 && kept === 0 && (+w.share || 0) > 0) {
      // edge case: writer has share but no pubs and kept is 0 (shouldn't happen, but guard)
      segments.push({
        share: +w.share,
        color: hue.writer,
        label: w.name,
        sub: `${w.share}%`,
        writerIdx: wi, kind: 'writer'
      });
    }
  });

  const denom = Math.max(100, writerTotal); // never compress under 100; show overflow if any
  const [hover, setHover] = React.useState(null);

  return (
    <div style={{ marginBottom: 24, border: '1px solid var(--rule)', background: 'var(--bg)', padding: '18px 20px' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 14 }}>
        <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)' }}>OWNERSHIP DISTRIBUTION</div>
        <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
          {hover ? <span><span style={{ fontWeight: 600, color: 'var(--ink)' }}>{hover.label}</span> · {hover.sub}</span> : <span>Hover any segment for detail</span>}
        </div>
      </div>

      {/* Stacked bar */}
      <div style={{ display: 'flex', height: 36, width: '100%', border: '1px solid var(--rule)', background: 'var(--bg-2)', position: 'relative', overflow: 'hidden' }}>
        {segments.map((s, i) =>
        <div key={i}
        onMouseEnter={() => setHover(s)} onMouseLeave={() => setHover(null)}
        style={{
          width: `${s.share / denom * 100}%`,
          background: s.color,
          borderRight: i < segments.length - 1 ? '1px solid rgba(255,255,255,.35)' : 'none',
          position: 'relative', cursor: 'default',
          transition: 'filter 120ms',
          filter: hover && hover !== s ? 'opacity(.55)' : 'none'
        }}
        title={`${s.label} · ${s.sub}`}>
            {s.share >= 8 &&
          <div className="ff-mono num" style={{
            position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: '#fff', fontSize: 11, fontWeight: 600, textShadow: '0 1px 2px rgba(0,0,0,.3)', pointerEvents: 'none'
          }}>
                {s.share}%
              </div>
          }
          </div>
        )}
        {writerTotal < 100 &&
        <div style={{
          width: `${(100 - writerTotal) / denom * 100}%`,
          background: 'repeating-linear-gradient(45deg,#f6e0dc,#f6e0dc 6px,#fff 6px,#fff 12px)',
          borderLeft: '1px solid #c0392b'
        }} title={`${100 - writerTotal}% unallocated`} />
        }
      </div>

      {/* Writer-level secondary bar (just shows writer split, no pubs) */}
      <div style={{ display: 'flex', height: 6, width: '100%', marginTop: 6, background: 'var(--bg-2)' }}>
        {writers.map((w, wi) => {
          const hue = HUES[wi % HUES.length];
          return (
            <div key={wi} style={{
              width: `${(+w.share || 0) / denom * 100}%`,
              background: hue.writer,
              borderRight: wi < writers.length - 1 ? '1px solid #fff' : 'none'
            }} title={`${w.name} · ${w.share}%`} />);

        })}
      </div>

      {/* Legend */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px 22px', marginTop: 14 }}>
        {writers.map((w, wi) => {
          const hue = HUES[wi % HUES.length];
          const pubsUsed = (w.pubs || []).reduce((s, p) => s + (+p.share || 0), 0);
          const kept = Math.max(0, (+w.share || 0) - pubsUsed);
          return (
            <div key={wi} style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 12 }}>
              <div style={{ width: 10, height: 10, background: hue.writer, flex: '0 0 10px' }} />
              <div>
                <div style={{ fontWeight: 600 }}>{w.name} <span className="ff-mono num" style={{ color: 'var(--ink-3)', fontWeight: 400, marginLeft: 4 }}>{w.share}%</span></div>
                <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>
                  {pubsUsed > 0 && <span><span style={{ display: 'inline-block', width: 8, height: 8, background: hue.pub, marginRight: 4, verticalAlign: 'middle' }} />{pubsUsed}% pub</span>}
                  {pubsUsed > 0 && kept > 0 && <span style={{ margin: '0 6px' }}>·</span>}
                  {kept > 0 && <span><span style={{ display: 'inline-block', width: 8, height: 8, background: hue.kept, marginRight: 4, verticalAlign: 'middle' }} />{kept}% kept</span>}
                </div>
              </div>
            </div>);

        })}
        {writerTotal < 100 &&
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 12 }}>
            <div style={{ width: 10, height: 10, background: 'repeating-linear-gradient(45deg,#f6e0dc,#f6e0dc 3px,#fff 3px,#fff 6px)', border: '1px solid #c0392b', flex: '0 0 10px' }} />
            <div>
              <div style={{ fontWeight: 600, color: '#c0392b' }}>Unallocated <span className="ff-mono num" style={{ fontWeight: 400, marginLeft: 4 }}>{100 - writerTotal}%</span></div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>shares do not sum to 100%</div>
            </div>
          </div>
        }
      </div>
    </div>);

}

function WorkSplitsTab({ m, setData, commit, editing, pulse, pulseId }) {
  const writers = m.writers || [];
  const writerTotal = writers.reduce((s, w) => s + (+w.share || 0), 0);

  // Aggregate publishers across all writers
  const allPubs = writers.flatMap((w) => (w.pubs || []).map((p) => ({ ...p, _ofWriter: w.name, _writerShare: w.share })));
  const pubsAggregateShare = allPubs.reduce((s, p) => s + (+p.share || 0), 0);
  // Controlled aggregate — DERIVED from publisher.indicator per spec 4.1.
  // Counts publisher share that flows through ★/◆ publishers, expressed as
  // a % of the song (out of 100% writer share).
  const ctrlAgg = (() => {
    if (!window.getShareControlled) return null;
    let ctrl = 0;
    for (const p of allPubs) {
      if (window.getShareControlled(p).controlled) ctrl += (+p.share || 0);
    }
    const total = writerTotal || 100;
    return {
      controlledPct: Math.round((ctrl / total) * 100),
      nonControlledPct: Math.round(((total - ctrl) / total) * 100),
    };
  })();
  const writerKeptShare = writers.reduce((s, w) => {
    const used = (w.pubs || []).reduce((a, p) => a + (+p.share || 0), 0);
    return s + Math.max(0, (+w.share || 0) - used);
  }, 0);

  // helpers — all flow through commit() so changes hit history
  const setWriterField = (i, patch, label) => commit(label, (d) => ({ ...d, writers: d.writers.map((w, j) => j === i ? { ...w, ...patch } : w) }));
  const setWriterLive = (i, patch) => setData((d) => ({ ...d, writers: d.writers.map((w, j) => j === i ? { ...w, ...patch } : w) }));
  const setPubField = (wi, pi, patch, label) => commit(label, (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : { ...w, pubs: w.pubs.map((p, k) => k === pi ? { ...p, ...patch } : p) }) }));
  const setPubLive = (wi, pi, patch) => setData((d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : { ...w, pubs: w.pubs.map((p, k) => k === pi ? { ...p, ...patch } : p) }) }));
  const addPub = (wi) => commit('Added publisher', (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : {
      ...w, pubs: [...(w.pubs || []), {
        name: '', pro: w.pro, ipi: '', selfPub: false, unknown: false, controlled: true,
        role: 'Original Publisher', share: Math.max(0, w.share - (w.pubs || []).reduce((s, p) => s + (+p.share || 0), 0)),
        territories: ['World'], admins: []
      }]
    }) }));
  const removePub = (wi, pi, name) => commit(`Removed publisher ${name || ''}`.trim(), (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : { ...w, pubs: w.pubs.filter((_, k) => k !== pi) }) }));
  const setAdminField = (wi, pi, ai, patch, label) => commit(label, (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : {
      ...w, pubs: w.pubs.map((p, k) => k !== pi ? p : { ...p, admins: p.admins.map((a, m) => m === ai ? { ...a, ...patch } : a) })
    }) }));
  const addAdmin = (wi, pi) => commit('Added sub-publisher', (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : {
      ...w, pubs: w.pubs.map((p, k) => k !== pi ? p : { ...p, admins: [...(p.admins || []), {
          role: 'Sub-pub', name: '', pro: 'PRS', ipi: '', controlled: true, pubRole: 'Original Publisher',
          share: Math.max(0, p.share - (p.admins || []).reduce((s, a) => s + (+a.share || 0), 0)),
          territories: ['UK / Ireland']
        }] })
    }) }));
  const removeAdmin = (wi, pi, ai) => commit('Removed sub-publisher', (d) => ({ ...d, writers: d.writers.map((w, j) => j !== wi ? w : {
      ...w, pubs: w.pubs.map((p, k) => k !== pi ? p : { ...p, admins: p.admins.filter((_, m) => m !== ai) })
    }) }));

  return (
    <div>
      {/* Stats strip */}
      {(() => {
        const cells = [
          { l: 'WRITERS', v: writers.length },
          { l: 'WRITER SHARE', v: `${writerTotal}%`, bad: writerTotal !== 100 },
          { l: 'TO PUBLISHERS', v: `${pubsAggregateShare}%` },
          { l: 'KEPT BY WRITERS', v: `${writerKeptShare}%` },
          { l: 'PUBLISHERS', v: allPubs.length },
        ];
        if (ctrlAgg) {
          cells.push({
            l: 'CONTROLLED',
            v: `${ctrlAgg.controlledPct}%`,
            sub: `${ctrlAgg.nonControlledPct}% non-ctrl`,
            tip: 'Derived from publisher indicators (★ Owned / ◆ Administered). Non-controlled shares need their own publisher to register.',
          });
        }
        return (
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cells.length},1fr)`, gap: 0, marginBottom: 24, border: '1px solid var(--rule)' }}>
            {cells.map((s, i) =>
              <div key={i} title={s.tip} style={{ padding: '16px 18px', borderRight: i < cells.length - 1 ? '1px solid var(--rule)' : 'none' }}>
                <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.1em' }}>{s.l}</div>
                <div className="ff-display num" style={{ fontSize: 30, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 4,
                  color: s.bad ? '#c0392b' : 'var(--ink)' }}>{s.v}</div>
                {s.sub && <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{s.sub}</div>}
              </div>
            )}
          </div>
        );
      })()}

      {/* Visual share graph */}
      <SplitsGraph writers={writers} writerTotal={writerTotal} />

      {/* Per-writer cards */}
      {writers.map((wr, wi) => {
        const pubsUsed = (wr.pubs || []).reduce((s, p) => s + (+p.share || 0), 0);
        const writerKept = Math.max(0, (+wr.share || 0) - pubsUsed);
        const pubsOver = pubsUsed > (+wr.share || 0);
        return (
          <div key={wi} style={{ marginBottom: 24, border: '1px solid var(--rule)', background: 'var(--bg)' }}>
            {/* Writer header */}
            <div style={{ padding: '18px 20px', background: 'var(--bg-2)', borderBottom: '1px solid var(--rule)' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px 90px 28px', gap: 16, alignItems: 'start' }}>
                <div>
                  <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>
                    WRITER {wi + 1} OF {writers.length}
                  </div>
                  {editing && window.WriterTypeahead ?
                  <window.WriterTypeahead value={wr.name} aid={wr._id} onSelect={(patch) => setWriterField(wi, patch, `Writer ${wi + 1} → "${patch.name}"`)} /> :

                  <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.01em' }}>{wr.name}</div>
                  }
                  <div className="ff-mono" style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 6, display: 'flex', gap: 14, flexWrap: 'wrap' }}>
                    <span>
                      {editing ?
                      <InlineSelect value={wr.role || 'Composer'} options={_LWRITER_ROLES} onCommit={(v) => setWriterField(wi, { role: v }, `Role → ${v}`)} style={{ fontSize: 11 }} /> :
                      wr.role || 'Composer'}
                    </span>
                    <span>
                      {editing ?
                      <InlineSelect value={wr.pro || '—'} options={PROS_LIST} onCommit={(v) => setWriterField(wi, { pro: v }, `PRO → ${v}`)} style={{ fontSize: 11 }} /> :
                      wr.pro}
                    </span>
                    <span>IPI {(window.ipiDisplayCompact ? window.ipiDisplayCompact(wr.ipi) : wr.ipi)}</span>
                  </div>
                </div>

                <div>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
                    <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)' }}>OWNERSHIP SHARE</span>
                    {editing &&
                    <button onClick={() => setWriterField(wi, { splitShares: !wr.splitShares,
                      ...(wr.splitShares ? {} : { perfShare: wr.share, mechShare: wr.share, syncShare: wr.share })
                    }, wr.splitShares ? 'Joined splits' : 'Separated P/M/S splits')}
                    className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', padding: '2px 6px',
                      background: 'transparent', border: '1px solid var(--rule)', color: 'var(--ink-2)', cursor: 'pointer' }}>
                        {wr.splitShares ? 'merge' : 'split P/M/S'}
                      </button>
                    }
                  </div>
                  <SplitsBar p={wr.perfShare || wr.share} m={wr.mechShare || wr.share} s={wr.syncShare || wr.share} share={wr.share} splitShares={!!wr.splitShares} />
                </div>

                <div style={{ textAlign: 'right' }}>
                  <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>SHARE</div>
                  {editing ?
                  <input type="number" min={0} max={100} value={wr.share}
                  onChange={(e) => {const v = Math.max(0, Math.min(100, +e.target.value || 0));setWriterLive(wi, { share: v, ...(wr.splitShares ? {} : { perfShare: v, mechShare: v, syncShare: v }) });}}
                  onBlur={() => commit(`Adjusted ${wr.name || `writer ${wi + 1}`}'s share`, (d) => d)}
                  className="ff-mono num" style={{ width: '100%', padding: '6px 8px', fontSize: 18, textAlign: 'right', fontWeight: 600, border: 'none', borderRadius: 3, background: 'var(--edit-fill)' }} /> :

                  <span className="ff-mono num" style={{ fontSize: 24, fontWeight: 600 }}>{wr.share}%</span>
                  }
                </div>

                {editing && writers.length > 1 &&
                <button onClick={() => commit(`Removed ${wr.name || `writer ${wi + 1}`}`, (d) => ({ ...d, writers: d.writers.filter((_, j) => j !== wi) }))}
                title="Remove writer" style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 4 }}>
                    <Ic.X width={16} height={16} />
                  </button>
                }
              </div>

              {/* Territory + flags row */}
              <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 16, marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--rule-soft)', alignItems: 'center' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                  <span className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)' }}>TERRITORY</span>
                  <TerritoryEditor value={wr.territories} onChange={(t) => setWriterField(wi, { territories: t }, 'Territory updated')} editing={editing} />
                </div>
                <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                  <CbInline checked={wr.workForHire} onChange={(v) => setWriterField(wi, { workForHire: v }, `Work-for-hire → ${v ? 'on' : 'off'}`)} editing={editing}>WORK FOR HIRE</CbInline>
                  <CbInline checked={wr.reversionary} onChange={(v) => setWriterField(wi, { reversionary: v }, `Reversionary → ${v ? 'on' : 'off'}`)} editing={editing}>REVERSIONARY</CbInline>
                  <CbInline checked={wr.firstRecRefusal} onChange={(v) => setWriterField(wi, { firstRecRefusal: v }, `First-rec refusal → ${v ? 'on' : 'off'}`)} editing={editing}>FIRST REC REFUSAL</CbInline>
                </div>
              </div>
            </div>

            {/* Publisher allocation summary */}
            <div style={{ padding: '14px 20px 4px' }}>
              <div className="ff-mono" style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
                <span className="upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', whiteSpace: 'nowrap' }}>
                  OF {(wr.name || 'WRITER').toUpperCase()}'S {wr.share}%
                </span>
                <div style={{ flex: 1, height: 6, background: 'var(--bg-2)', border: '1px solid var(--rule)', display: 'flex', overflow: 'hidden' }}>
                  <div style={{ width: `${wr.share > 0 ? Math.min(100, pubsUsed / wr.share * 100) : 0}%`, background: pubsOver ? '#c0392b' : 'var(--accent)' }} />
                </div>
                <span style={{ fontSize: 11, color: pubsOver ? '#c0392b' : 'var(--ink-2)', whiteSpace: 'nowrap' }}>
                  {pubsOver ? `publishers claim ${pubsUsed}% — over by ${pubsUsed - wr.share}%` :
                  writerKept > 0 ? `${pubsUsed}% to publishers · ${writerKept}% kept by writer` :
                  `${wr.share}% fully assigned to publishers`}
                </span>
              </div>
            </div>

            {/* Publishers */}
            <div style={{ padding: '4px 20px 20px' }}>
              {(wr.pubs || []).map((p, pi) =>
              <PubRow key={pi} p={p} pi={pi} editing={editing} writerShare={wr.share}
              onPatch={(patch) => {
                // share field uses live setter + commit on blur via input — for everything else, commit immediately
                if ('share' in patch) setPubLive(wi, pi, patch);else
                setPubField(wi, pi, patch, `Publisher updated`);
              }}
              onRemove={() => removePub(wi, pi, p.name)}
              onAddAdmin={() => addAdmin(wi, pi)}
              onPatchAdmin={(ai, patch) => setAdminField(wi, pi, ai, patch, 'Sub-publisher updated')}
              onRemoveAdmin={(ai) => removeAdmin(wi, pi, ai)} />
              )}
              {editing &&
              <button onClick={() => addPub(wi)} className="ff-mono upper"
              style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 8, marginLeft: 18, padding: '6px 12px', fontSize: 10, letterSpacing: '.08em',
                background: 'var(--edit-fill)', color: 'var(--ink-2)', border: '1px solid var(--rule-soft)', borderRadius: 3, cursor: 'pointer' }}>
                  <Ic.Plus width={11} height={11} /> Add publisher
                </button>
              }
            </div>
          </div>);

      })}

      {editing &&
      <button onClick={() => commit('Added writer', (d) => ({ ...d, writers: [...d.writers, {
          name: '', _id: null, share: 0, ipi: '', pro: d.pro, role: 'Composer/Author',
          splitShares: false, perfShare: 0, mechShare: 0, syncShare: 0,
          territories: ['World'], workForHire: false, reversionary: false, firstRecRefusal: false, pubs: []
        }] }))}
      style={{ display: 'flex', gap: 8, alignItems: 'center', padding: '10px 14px',
        border: '1px solid var(--rule-soft)', background: 'var(--edit-fill)', borderRadius: 3, cursor: 'pointer' }}>
          <Ic.Plus width={12} height={12} style={{ color: 'var(--ink-3)' }} />
          <span style={{ fontSize: 13, color: 'var(--ink-2)' }}>Add co-writer</span>
        </button>
      }
    </div>);

}

// ───────────────────────────────────────────────────────────── LYRICS TAB
function WorkLyricsTab({ m, commit, editing }) {
  const lines = (m.lyrics || '').split('\n');
  const lineCount = lines.filter((l) => l.trim() && !l.trim().startsWith('[')).length;
  const charCount = (m.lyrics || '').length;
  const sectionCount = lines.filter((l) => /^\s*\[/.test(l)).length;
  const updated = m.lyricsUpdated ? new Date(m.lyricsUpdated).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }) : '—';

  if (m.instrumental) {
    return (
      <div style={{ padding: '80px 20px', textAlign: 'center', color: 'var(--ink-3)', border: '1px solid var(--rule-soft)', background: 'var(--bg-2)' }}>
        <div className="heading-swap ff-display" style={{ fontSize: 36, marginBottom: 8, fontWeight: 600, color: 'var(--ink-2)' }}>Instrumental work</div>
        <p style={{ maxWidth: 480, margin: '0 auto', fontSize: 14 }}>This work has no lyrics. Toggle the <b>instrumental</b> flag off to add a lyric sheet.</p>
      </div>);

  }

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: 32 }}>
      <div>
        {/* Toolbar */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '0 0 14px', marginBottom: 14, borderBottom: '1px solid var(--rule)' }}>
          <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)' }}>
            LYRIC SHEET · {m.lyricsLanguage || 'English'}
          </div>
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-4)' }}>
            Last updated <span className="num">{updated}</span>
          </div>
        </div>

        {/* Lyrics body */}
        {editing ?
        <textarea
          value={m.lyrics || ''}
          onChange={(e) => commit('Lyrics edited', (d) => ({ ...d, lyrics: e.target.value, lyricsUpdated: new Date().toISOString() }))}
          spellCheck={false}
          style={{
            width: '100%', minHeight: 520, padding: '18px 20px',
            background: 'var(--bg)', border: '1px solid var(--rule)',
            fontFamily: 'ui-monospace, Menlo, monospace', fontSize: 14, lineHeight: 1.7,
            color: 'var(--ink)', resize: 'vertical', outline: 'none'
          }}
          placeholder="[Verse 1]&#10;Type your lyrics here…" /> :


        (m.lyrics || '').trim() === '' ?
        <div style={{ padding: '80px 20px', textAlign: 'center', color: 'var(--ink-3)', border: '1px dashed var(--rule)' }}>
              <div className="ff-display" style={{ fontSize: 24, marginBottom: 6, fontWeight: 600, color: 'var(--ink-2)' }}>No lyrics on file</div>
              <p style={{ fontSize: 13 }}>Click <b>Edit Work</b> in the toolbar to add lyrics.</p>
            </div> :

        <div style={{ maxWidth: 680 }}>
              {lines.map((line, i) => {
            const isSection = /^\s*\[.+\]\s*$/.test(line);
            const isBlank = line.trim() === '';
            if (isSection) {
              return (
                <div key={i} className="ff-mono upper" style={{
                  fontSize: 10, letterSpacing: '.18em', color: 'var(--ink-3)',
                  padding: i === 0 ? '0 0 12px' : '24px 0 12px',
                  borderTop: i === 0 ? 'none' : '1px solid var(--rule-soft)',
                  marginTop: i === 0 ? 0 : 4
                }}>
                      {line.trim().replace(/^\[|\]$/g, '')}
                    </div>);

            }
            if (isBlank) return <div key={i} style={{ height: 8 }} />;
            return (
              <div key={i} className="ff-display" style={{
                fontSize: 18, lineHeight: 1.55, fontWeight: 400, letterSpacing: '-0.005em',
                padding: '2px 0', color: 'var(--ink)'
              }}>{line}</div>);

          })}
            </div>

        }
      </div>

      {/* Side rail */}
      <div>
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 12, paddingBottom: 10, borderBottom: '1px solid var(--rule)' }}>
          DETAILS
        </div>
        <div className="ff-mono" style={{ fontSize: 12, display: 'grid', gridTemplateColumns: '1fr 1fr', rowGap: 14, marginBottom: 24 }}>
          <div>
            <div style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', marginBottom: 3 }}>LANGUAGE</div>
            {editing ?
            <InlineSelect value={m.lyricsLanguage || 'English'}
            options={refLanguageOptions()}
            onCommit={(v) => commit(`Lyrics language → ${v}`, (d) => ({ ...d, lyricsLanguage: v }))}
            style={{ fontSize: 12 }} /> :
            <div style={{ fontSize: 13, color: 'var(--ink)' }}>{m.lyricsLanguage || 'English'}</div>}
          </div>
          <div>
            <div style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', marginBottom: 3 }}>SECTIONS</div>
            <div className="num" style={{ fontSize: 13, color: 'var(--ink)' }}>{sectionCount || '—'}</div>
          </div>
          <div>
            <div style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', marginBottom: 3 }}>LINES</div>
            <div className="num" style={{ fontSize: 13, color: 'var(--ink)' }}>{lineCount || '—'}</div>
          </div>
          <div>
            <div style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', marginBottom: 3 }}>CHARACTERS</div>
            <div className="num" style={{ fontSize: 13, color: 'var(--ink)' }}>{charCount.toLocaleString()}</div>
          </div>
        </div>

        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 12, paddingBottom: 10, borderBottom: '1px solid var(--rule)' }}>
          USAGE NOTES
        </div>
        <div className="ff-mono" style={{ fontSize: 11, lineHeight: 1.6, color: 'var(--ink-2)', marginBottom: 18 }}>
          Lyrics are reproduced under the underlying composition copyright. Sync requests using full or substantial lyric content require explicit clearance — coordinate with Pluralis Music admin desk.
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <button onClick={() => {
            const txt = `${m.title}\n${'='.repeat(m.title.length)}\n\n${m.lyrics || '(no lyrics on file)'}\n\n— ${(m.writers||[]).map(w=>w.name).join(', ')}\nISWC ${m.iswc||''}`;
            const blob = new Blob([txt], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url; a.download = `${m.title.replace(/[^A-Za-z0-9]+/g,'_')}_lyrics.txt`; a.click();
            setTimeout(()=>URL.revokeObjectURL(url), 2000);
          }} className="ff-mono upper" style={{ padding: '10px 14px', fontSize: 10, letterSpacing: '.08em', background: 'var(--bg)', border: '1px solid var(--rule)', cursor: 'pointer', textAlign: 'left' }}>
            Export as .txt
          </button>
          <button onClick={() => {
            const w = window.open('', '_blank');
            if (!w) return;
            const safeLyrics = (m.lyrics || '(no lyrics on file)').replace(/&/g,'&amp;').replace(/</g,'&lt;');
            w.document.write(`<!doctype html><html><head><title>${m.title} — lyric sheet</title><style>
              @page { size: letter; margin: 0.75in; }
              body { font-family: Georgia, serif; font-size: 12pt; line-height: 1.6; color: #111; }
              h1 { font-size: 28pt; font-weight: 600; letter-spacing: -0.02em; margin: 0 0 4pt; }
              .meta { font-family: ui-monospace, monospace; font-size: 9pt; color: #666; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 24pt; }
              .lyrics { white-space: pre-wrap; font-size: 13pt; }
              .footer { margin-top: 36pt; padding-top: 12pt; border-top: 1px solid #ccc; font-family: ui-monospace, monospace; font-size: 8pt; color: #888; }
            </style></head><body>
              <h1>${m.title}</h1>
              <div class="meta">${(m.writers||[]).map(wr=>wr.name).join(' · ')} ${m.iswc?'· ISWC '+m.iswc:''}</div>
              <div class="lyrics">${safeLyrics}</div>
              <div class="footer">© ${m.copyright||'—'} · Lyric sheet generated by ASTRO</div>
              <script>setTimeout(()=>window.print(), 300);</script>
            </body></html>`);
            w.document.close();
          }} className="ff-mono upper" style={{ padding: '10px 14px', fontSize: 10, letterSpacing: '.08em', background: 'var(--bg)', border: '1px solid var(--rule)', cursor: 'pointer', textAlign: 'left' }}>
            Print lyric sheet
          </button>
        </div>
      </div>
    </div>);

}

// ───────────────────────────────────────────────────────────── RIGHTS TAB
function WorkRightsTab({ m, commit, editing }) {
  const regs = m.rights || [];
  // REF-driven option lists for copyright registrations.
  const REF = window.REF;
  const _refReady = REF && REF.ready;
  const typeCodes = (_refReady && Array.isArray(REF.copyrightTypes) && REF.copyrightTypes.length)
    ? REF.copyrightTypes.map(r => r.code).filter(Boolean)
    : ['PA', 'SR', 'VA', 'TX', 'SE'];
  const jurisdictionLabels = (_refReady && Array.isArray(REF.copyrightJurisdictions) && REF.copyrightJurisdictions.length)
    ? REF.copyrightJurisdictions.map(j => j.label).filter(Boolean)
    : ['United States (eCO)', 'Canada', 'United Kingdom', 'European Union (EUIPO)', 'Australia', 'Japan'];
  const defaultJurisdictionLabel = jurisdictionLabels[0] || 'United States (eCO)';

  const setReg = (idx, patch, label) => commit(label, (d) => ({
    ...d, rights: (d.rights || []).map((r, i) => i === idx ? { ...r, ...patch } : r)
  }));
  const addReg = () => commit('Added registration', (d) => ({
    ...d, rights: [...(d.rights || []), {
      id: 'reg-' + Date.now(), type: 'PA', certNumber: '', registrationDate: '',
      effectiveDate: '', jurisdiction: defaultJurisdictionLabel, claimType: 'Original work',
      workForHire: false, published: false, firstPubDate: '', firstPubNation: '',
      depositCopies: 1, depositFormat: '', certHolder: 'Pluralis Music LLC'
    }]
  }));
  const removeReg = (idx) => commit('Removed registration', (d) => ({
    ...d, rights: (d.rights || []).filter((_, i) => i !== idx)
  }));

  return (
    <div>
      {/* Header strip */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '0 0 14px', marginBottom: 18, borderBottom: '1px solid var(--rule)' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>
            COPYRIGHT REGISTRATIONS
          </div>
          <div className="ff-display" style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.01em' }}>
            {regs.length} active {regs.length === 1 ? 'registration' : 'registrations'}
          </div>
        </div>
        {editing &&
        <button onClick={addReg}
        style={{ padding: '8px 14px', fontSize: 11, background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 6 }}>
            <Ic.Plus width={12} height={12} /> Add registration
          </button>
        }
      </div>

      {/* Registration cards */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 18, marginBottom: 32 }}>
        {regs.length === 0 &&
        <div style={{ padding: '60px 20px', textAlign: 'center', color: 'var(--ink-3)', border: '1px dashed var(--rule)' }}>
            <div className="ff-display" style={{ fontSize: 22, marginBottom: 6, fontWeight: 600, color: 'var(--ink-2)' }}>No registrations on file</div>
            <p style={{ fontSize: 13 }}>{editing ? 'Use "Add registration" above.' : 'Switch to edit mode to add a USCO or jurisdictional copyright registration.'}</p>
          </div>
        }
        {regs.map((r, i) =>
        <div key={r.id} style={{ border: '1px solid var(--rule)', background: 'var(--bg)' }}>
            {/* Card header */}
            <div style={{ padding: '14px 20px', borderBottom: '1px solid var(--rule-soft)', display: 'flex', alignItems: 'center', gap: 14, background: 'var(--bg-2)' }}>
              <div className="ff-mono upper" style={{
              padding: '4px 10px', fontSize: 10, letterSpacing: '.1em',
              background: r.type === 'PA' ? 'var(--ink)' : 'var(--ink-2)',
              color: 'var(--bg)', fontWeight: 600
            }}>
                {editing ?
              <InlineSelect value={r.type} options={typeCodes}
              onCommit={(v) => setReg(i, { type: v }, `Registration type → ${v}`)}
              style={{ fontSize: 10, letterSpacing: '.1em', color: 'var(--bg)' }} /> :
              r.type}
              </div>
              <div style={{ flex: 1 }}>
                <div className="ff-mono num" style={{ fontSize: 14, fontWeight: 600, letterSpacing: '-0.01em' }}>
                  {editing ?
                <InlineText value={r.certNumber} onCommit={(v) => setReg(i, { certNumber: v }, `Cert # → ${v}`)}
                placeholder="PA 0-000-000" style={{ fontSize: 14, fontWeight: 600, fontFamily: 'inherit' }} /> :
                r.certNumber || <em style={{ color: 'var(--ink-3)', fontStyle: 'italic' }}>(unfiled)</em>}
                </div>
                <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2, letterSpacing: '.04em' }}>
                  {editing ?
                <InlineSelect value={r.jurisdiction} options={jurisdictionLabels}
                onCommit={(v) => setReg(i, { jurisdiction: v }, `Jurisdiction → ${v}`)}
                style={{ fontSize: 10, fontFamily: 'inherit' }} /> :
                r.jurisdiction}
                </div>
              </div>
              <Pill tone={r.certNumber ? 'ok' : 'soft'} dot>{r.certNumber ? 'registered' : 'pending'}</Pill>
              {editing &&
            <button onClick={() => removeReg(i)}
            style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', padding: 6 }}
            title="Remove registration">
                  <Ic.X width={14} height={14} />
                </button>
            }
            </div>

            {/* Card body — grid of fields */}
            <div className="ff-mono" style={{ padding: '18px 20px', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', rowGap: 18, columnGap: 24, fontSize: 12 }}>
              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>REGISTRATION DATE</div>
                {editing ?
              <DateField value={r.registrationDate} editing={true} onChange={(v) => setReg(i, { registrationDate: v }, `Reg date → ${v}`)}
              size="sm" style={{ display:'inline-block' }} /> :
              <span className="num">{formatDate(r.registrationDate) || '—'}</span>}
              </div>
              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>EFFECTIVE DATE</div>
                {editing ?
              <DateField value={r.effectiveDate} editing={true} onChange={(v) => setReg(i, { effectiveDate: v }, `Effective → ${v}`)}
              size="sm" style={{ display:'inline-block' }} /> :
              <span className="num">{formatDate(r.effectiveDate) || '—'}</span>}
              </div>
              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>CLAIM TYPE</div>
                {editing ?
              <InlineSelect value={r.claimType}
              options={['Original work', 'Compilation', 'Derivative work', 'Translation', 'Arrangement']}
              onCommit={(v) => setReg(i, { claimType: v }, `Claim → ${v}`)}
              style={{ fontSize: 12 }} /> :
              r.claimType}
              </div>

              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>CERTIFICATE HOLDER</div>
                {editing ?
              <InlineText value={r.certHolder} onCommit={(v) => setReg(i, { certHolder: v }, `Holder → ${v}`)}
              style={{ fontSize: 13, fontFamily: 'inherit' }} /> :
              r.certHolder}
              </div>
              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>WORK MADE FOR HIRE</div>
                {editing ?
              <InlineSelect value={r.workForHire ? 'Yes' : 'No'} options={['No', 'Yes']}
              onCommit={(v) => setReg(i, { workForHire: v === 'Yes' }, `WFH → ${v}`)}
              style={{ fontSize: 12 }} /> :
              r.workForHire ? 'Yes' : 'No'}
              </div>
              <div>
                <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>PUBLISHED</div>
                {editing ?
              <InlineSelect value={r.published ? 'Yes' : 'No'} options={['No', 'Yes']}
              onCommit={(v) => setReg(i, { published: v === 'Yes' }, `Published → ${v}`)}
              style={{ fontSize: 12 }} /> :
              r.published ? 'Yes' : 'No'}
              </div>

              {r.published &&
            <>
                  <div>
                    <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>FIRST PUBLICATION</div>
                    {editing ?
                <DateField value={r.firstPubDate} editing={true} onChange={(v) => setReg(i, { firstPubDate: v }, `1st pub → ${v}`)}
                size="sm" style={{ display:'inline-block' }} /> :
                <span className="num">{formatDate(r.firstPubDate) || '—'}</span>}
                  </div>
                  <div>
                    <div style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 4 }}>NATION OF 1ST PUB.</div>
                    {editing ?
                <InlineText value={r.firstPubNation} onCommit={(v) => setReg(i, { firstPubNation: v }, `Nation → ${v}`)}
                style={{ fontSize: 13, fontFamily: 'inherit' }} /> :
                r.firstPubNation || '—'}
                  </div>
                  <div />
                </>
            }

              <div style={{ gridColumn: '1 / 4', paddingTop: 14, borderTop: '1px solid var(--rule-soft)', display: 'grid', gridTemplateColumns: '140px 1fr', rowGap: 10, alignItems: 'baseline' }}>
                <span style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)' }}>DEPOSIT COPIES</span>
                <span>
                  {editing ?
                <InlineText value={String(r.depositCopies)}
                onCommit={(v) => setReg(i, { depositCopies: +v.replace(/\D/g, '') || 0 }, `Deposit → ${v}`)}
                style={{ fontSize: 13, fontFamily: 'inherit', display: 'inline-block', maxWidth: 60 }} /> :
                <span className="num">{r.depositCopies}</span>}
                  <span style={{ color: 'var(--ink-3)', marginLeft: 6 }}>
                    on file ·{' '}
                    {editing ?
                  <InlineText value={r.depositFormat} onCommit={(v) => setReg(i, { depositFormat: v }, `Deposit format → ${v}`)}
                  style={{ fontSize: 13, fontFamily: 'inherit' }} /> :
                  r.depositFormat || '—'}
                  </span>
                </span>
              </div>
            </div>
          </div>
        )}
      </div>

      {/* Territorial control + sync policy + reversionary — supplementary */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, marginBottom: 24 }}>
        <div style={{ border: '1px solid var(--rule)', padding: '18px 20px' }}>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--rule-soft)' }}>
            TERRITORIAL CONTROL
          </div>
          {editing ?
          <textarea value={m.territorialNotes || ''}
          onChange={(e) => commit('Territorial notes edited', (d) => ({ ...d, territorialNotes: e.target.value }))}
          style={{ width: '100%', minHeight: 80, padding: '8px 10px', background: 'var(--bg)', border: '1px solid var(--rule)',
            fontFamily: 'inherit', fontSize: 12, lineHeight: 1.5, color: 'var(--ink)', resize: 'vertical' }} /> :

          <div style={{ fontSize: 13, lineHeight: 1.55, color: 'var(--ink-2)' }}>{m.territorialNotes || <em style={{ color: 'var(--ink-3)' }}>—  no notes</em>}</div>
          }
        </div>
        <div style={{ border: '1px solid var(--rule)', padding: '18px 20px' }}>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--rule-soft)' }}>
            REVERSIONARY CLAUSE
          </div>
          {editing ?
          <textarea value={m.reversionaryClause || ''}
          onChange={(e) => commit('Reversionary clause edited', (d) => ({ ...d, reversionaryClause: e.target.value }))}
          style={{ width: '100%', minHeight: 80, padding: '8px 10px', background: 'var(--bg)', border: '1px solid var(--rule)',
            fontFamily: 'inherit', fontSize: 12, lineHeight: 1.5, color: 'var(--ink)', resize: 'vertical' }} /> :

          <div style={{ fontSize: 13, lineHeight: 1.55, color: 'var(--ink-2)' }}>{m.reversionaryClause || <em style={{ color: 'var(--ink-3)' }}>—  none</em>}</div>
          }
        </div>
      </div>

      <div style={{ border: '1px solid var(--rule)', padding: '18px 20px' }}>
        <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)', marginBottom: 10, paddingBottom: 10, borderBottom: '1px solid var(--rule-soft)' }}>
          SYNC POLICY
        </div>
        {editing ?
        <InlineText value={m.syncPolicy || ''}
        onCommit={(v) => commit(`Sync policy → ${v}`, (d) => ({ ...d, syncPolicy: v }))}
        style={{ fontSize: 14, fontFamily: 'inherit' }} /> :

        <div style={{ fontSize: 14, color: 'var(--ink-2)' }}>{m.syncPolicy || <em style={{ color: 'var(--ink-3)' }}>—  no policy on file</em>}</div>
        }
      </div>
    </div>);

}

// ───────────────────────────────────────────────────────────── REGISTRATIONS TAB
// Live from Directory → Societies (kept rich for territory coverage on registration target picks)
const SOC_LIST = (window.SOC_CWR || ['ASCAP','BMI','SESAC','GMR','PRS','SACEM','GEMA','SIAE','JASRAC','SOCAN','APRA','STIM','BUMA','SUISA','SOZA','SACVEN','UCMR','CASH','MCSC']);

function WorkRegistrationsTab({ m, commit, editing, go }) {
  const ids = m.societyIds || [];
  const [retransmitOpen, setRetransmitOpen] = useS1(false);
  const [resendRow, setResendRow] = useS1(null);
  // Synthesize per-society transmission state from work id (deterministic but varied)
  const seed = (m.iswc || m.id || m.title || '').split('').reduce((s,c)=>s+c.charCodeAt(0),0);
  const enrich = (row, i) => {
    const k = (seed + i*7) % 100;
    const status = k < 62 ? 'acknowledged' : k < 80 ? 'submitted' : k < 92 ? 'pending' : 'rejected';
    const cwrVer = k % 3 === 0 ? 'V3.0.0' : 'V2.1';
    const days = (k % 28) + 1;
    const sentDate = `2026-04-${String(28 - (i*3 + (k%5))).padStart(2,'0')}`;
    const ackDate = status==='acknowledged' ? `2026-04-${String(28 - (i*3 + (k%5)) + Math.max(1, k%4)).padStart(2,'0')}` : '—';
    const filename = `CW${sentDate.slice(2,4)}${sentDate.slice(5,7)}${sentDate.slice(8,10)}${row.society}.${cwrVer.replace('V','V').replace('.','').slice(0,3)}`;
    return {...row, status, cwrVer, sentDate, ackDate, filename, days};
  };
  const rows = ids.map(enrich);
  const tone = (s) => s==='acknowledged' ? '#5b8a3a' : s==='submitted' ? 'var(--ink-2)' : s==='pending' ? '#b78113' : '#c0392b';

  const setRow = (i, patch, label) => commit(label, (d) => ({
    ...d, societyIds: (d.societyIds || []).map((r, j) => j === i ? { ...r, ...patch } : r)
  }));
  const addRow = () => commit('Added society registration', (d) => ({
    ...d, societyIds: [...(d.societyIds || []), { society: 'ASCAP', workId: '' }]
  }));
  const removeRow = (i) => commit('Removed society registration', (d) => ({
    ...d, societyIds: (d.societyIds || []).filter((_, j) => j !== i)
  }));

  const counts = rows.reduce((a,r)=>(a[r.status]=(a[r.status]||0)+1,a),{});
  const ackedCount = counts.acknowledged || 0;
  const totalCount = rows.length;

  return (
    <div>
      {/* Header */}
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', padding:'0 0 14px', marginBottom:18, borderBottom:'1px solid var(--rule)' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:4 }}>SOCIETY REGISTRATIONS</div>
          <div className="ff-display" style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em' }}>
            {ackedCount} of {totalCount} acknowledged
          </div>
        </div>
        <div style={{ display:'flex', gap:8 }}>
          {editing &&
            <button onClick={addRow}
              style={{ padding:'8px 14px', fontSize:11, background:'var(--bg)', color:'var(--ink)', border:'1px solid var(--rule)', cursor:'pointer', display:'flex', alignItems:'center', gap:6 }}>
              <Ic.Plus width={12} height={12}/> Add society
            </button>
          }
          <button onClick={()=>setRetransmitOpen(true)}
            style={{ padding:'8px 14px', fontSize:11, background:'var(--ink)', color:'var(--bg)', border:0, cursor:'pointer', display:'flex', alignItems:'center', gap:6 }}>
            <Ic.Send width={12} height={12}/> Retransmit all
          </button>
        </div>
      </div>

      {/* Status tally strip */}
      <div style={{ display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:12, marginBottom:24 }}>
        {[
          {k:'acknowledged', l:'Acknowledged', tone:'#5b8a3a'},
          {k:'submitted',    l:'Submitted',    tone:'var(--ink-2)'},
          {k:'pending',      l:'Pending',      tone:'#b78113'},
          {k:'rejected',     l:'Rejected',     tone:'#c0392b'},
        ].map(s => (
          <div key={s.k} style={{ padding:'12px 14px', border:'1px solid var(--rule)', background:'var(--bg)' }}>
            <div className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.12em', color:'var(--ink-3)' }}>{s.l}</div>
            <div className="ff-display num" style={{ fontSize:28, fontWeight:600, letterSpacing:'-0.02em', color:s.tone, marginTop:2 }}>{counts[s.k] || 0}</div>
          </div>
        ))}
      </div>

      {/* Rows */}
      {rows.length === 0 ? (
        <div style={{ padding:'60px 20px', textAlign:'center', color:'var(--ink-3)', border:'1px dashed var(--rule)' }}>
          <div className="ff-display" style={{ fontSize:22, marginBottom:6, fontWeight:600, color:'var(--ink-2)' }}>No society registrations</div>
          <p style={{ fontSize:13 }}>{editing ? 'Use "Add society" to register this work with a PRO/CMO.' : 'Switch to edit mode to add society registrations, or generate CWR to register with multiple societies at once.'}</p>
        </div>
      ) : (
        <div style={{ border:'1px solid var(--rule)' }}>
          {/* Header row */}
          <div className="ff-mono upper" style={{
            display:'grid', gridTemplateColumns:'90px 1fr 130px 120px 100px 100px 80px',
            gap:14, padding:'10px 14px', fontSize:10, color:'var(--ink-3)',
            background:'var(--bg-2)', borderBottom:'1px solid var(--rule)' }}>
            <span>SOCIETY</span><span>WORK ID / CWR FILE</span><span>SENT</span><span>ACKNOWLEDGED</span><span>STATUS</span><span style={{textAlign:'right'}}>VERSION</span><span/>
          </div>
          {rows.map((r, i) => (
            <div key={i} style={{
              display:'grid', gridTemplateColumns:'90px 1fr 130px 120px 100px 100px 80px',
              gap:14, padding:'14px', alignItems:'center',
              borderBottom: i < rows.length-1 ? '1px solid var(--rule-soft)' : 'none' }}>
              {/* Society */}
              {editing ? (
                <select value={r.society} onChange={e=>setRow(i,{society:e.target.value},`Changed society to ${e.target.value}`)}
                  className="ff-mono" style={{ padding:'6px 8px', background:'var(--edit-fill)', border:'1px solid var(--rule-soft)', fontSize:11, fontWeight:600, color:'var(--ink)' }}>
                  {SOC_LIST.map(s=><option key={s}>{s}</option>)}
                </select>
              ) : (
                <span className="ff-mono" style={{ fontSize:13, fontWeight:600 }}>{r.society}</span>
              )}
              {/* Work ID + filename */}
              <div>
                {editing ? (
                  <input value={r.workId} onChange={e=>setRow(i,{workId:e.target.value}, `Set ${r.society} work ID`)} placeholder="—"
                    className="ff-mono" style={{ padding:'5px 7px', background:'var(--edit-fill)', border:'1px solid var(--rule-soft)', fontSize:12, color:'var(--ink)', width:'100%', boxSizing:'border-box' }}/>
                ) : (
                  <div className="ff-mono" style={{ fontSize:12, color:'var(--ink)' }}>{r.workId || <span style={{color:'var(--ink-3)'}}>— no work ID assigned —</span>}</div>
                )}
                <div className="ff-mono" style={{ fontSize:9, color:'var(--ink-3)', marginTop:3, letterSpacing:'.04em' }}>{r.filename}</div>
              </div>
              {/* Sent */}
              <span className="ff-mono num" style={{ fontSize:11, color:'var(--ink-2)' }}>{r.sentDate}</span>
              {/* Acked */}
              <span className="ff-mono num" style={{ fontSize:11, color: r.status==='acknowledged' ? 'var(--ink-2)' : 'var(--ink-3)' }}>{r.ackDate}</span>
              {/* Status */}
              <span className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', color:tone(r.status), display:'inline-flex', alignItems:'center', gap:5, fontWeight:600 }}>
                <span style={{ width:6, height:6, borderRadius:'50%', background:tone(r.status), display:'inline-block' }}/>
                {r.status}
              </span>
              {/* Version */}
              <span className="ff-mono" style={{ fontSize:10, color:'var(--ink-3)', textAlign:'right' }}>{r.cwrVer}</span>
              {/* Action */}
              <div style={{ display:'flex', gap:4, justifyContent:'flex-end' }}>
                {editing ? (
                  <button onClick={()=>removeRow(i)} aria-label="Remove"
                    style={{ width:24, height:24, padding:0, background:'transparent', border:'1px solid var(--rule-soft)', cursor:'pointer', color:'var(--ink-3)', fontSize:12 }}>×</button>
                ) : (
                  <button onClick={()=>setResendRow(r)} aria-label={`Resend to ${r.society}`}
                    style={{ width:26, height:26, padding:0, background:'transparent', border:'1px solid var(--rule)', cursor:'pointer', color:'var(--ink-2)', display:'inline-flex', alignItems:'center', justifyContent:'center' }}>
                    <Ic.Send width={11} height={11}/>
                  </button>
                )}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Activity feed */}
      <div style={{ marginTop:32 }}>
        <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:10 }}>RECENT TRANSMISSIONS</div>
        <div style={{ borderTop:'1px solid var(--rule-soft)' }}>
          {rows.slice(0, 5).map((r, i) => (
            <div key={i} style={{ display:'grid', gridTemplateColumns:'120px 80px 1fr 100px', gap:12, padding:'10px 0', borderBottom:'1px solid var(--rule-soft)', alignItems:'baseline' }}>
              <span className="ff-mono num" style={{ fontSize:10, color:'var(--ink-3)' }}>{r.sentDate}</span>
              <span className="ff-mono" style={{ fontSize:11, fontWeight:600 }}>{r.society}</span>
              <span style={{ fontSize:12, color:'var(--ink-2)' }}>
                Sent <span className="ff-mono" style={{ color:'var(--ink)' }}>{r.filename}</span>
                {r.status==='acknowledged' && <> · ack’d on <span className="ff-mono">{r.ackDate}</span></>}
                {r.status==='rejected' && <> · <span style={{color:'#c0392b'}}>rejected</span> — invalid PRO affiliation in writer record</>}
                {r.status==='pending' && <> · awaiting ack ({r.days}d)</>}
                {r.status==='submitted' && <> · queued in society inbox</>}
              </span>
              <button onClick={()=>go && go('cwr')}
                className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.06em', padding:'4px 8px', background:'transparent', color:'var(--ink-2)', border:'1px solid var(--rule)', cursor:'pointer', justifySelf:'end' }}>
                View transmission →
              </button>
            </div>
          ))}
        </div>
      </div>

      {/* Help footer */}
      <div style={{ marginTop:28, padding:16, background:'var(--bg-2)', border:'1px solid var(--rule-soft)', display:'flex', gap:14, alignItems:'flex-start' }}>
        <div className="ff-display" style={{ width:28, height:28, border:'1px solid var(--rule)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, background:'var(--bg)', fontSize:14, fontStyle:'italic', fontWeight:600 }}>
          i
        </div>
        <div>
          <div style={{ fontSize:13, fontWeight:600, marginBottom:4 }}>About society registrations</div>
          <div style={{ fontSize:12, color:'var(--ink-2)', lineHeight:1.55 }}>
            Each PRO/CMO assigns its own work ID after a CWR transmission is accepted. The ISWC stays the same across all societies. Use <span className="ff-mono" style={{color:'var(--ink)'}}>Generate CWR</span> on the work header to send registration files; replies appear here as they arrive (typically within 2–14 days depending on society).
          </div>
        </div>
      </div>

      {retransmitOpen && (
        <RetransmitModal
          title={m.title}
          rows={rows}
          onClose={()=>setRetransmitOpen(false)}
          onConfirm={(selected)=>{
            setRetransmitOpen(false);
            window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Queued CWR retransmission for "${m.title}" to ${selected.length} societ${selected.length===1?'y':'ies'}`,tone:'ok'}}));
          }}
        />
      )}
      {resendRow && (
        <RetransmitModal
          title={m.title}
          rows={[resendRow]}
          single
          onClose={()=>setResendRow(null)}
          onConfirm={()=>{
            const soc = resendRow.society;
            setResendRow(null);
            window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Queued CWR resend to ${soc}`,tone:'ok'}}));
          }}
        />
      )}
    </div>);
}

// ───────────────────────────────────────────────────────────── ROYALTIES TAB
function WorkRoyaltiesTab({ m, go }) {
  const [period, setPeriod] = useS1('12mo');
  const [view, setView] = useS1('source'); // source | territory | writer

  const seed = (m.iswc || m.id || m.title || '').split('').reduce((s,c)=>s+c.charCodeAt(0),0);
  const r = (n) => ((Math.sin(seed*0.137 + n) + 1) / 2);

  // Period totals
  const totals = {
    'q':   { l:'This quarter', earned: 12480 + Math.round(r(1)*8000), pending: 3210 + Math.round(r(2)*1500), period:'Apr–Jun 2026' },
    '12mo':{ l:'Last 12 months', earned: 48210 + Math.round(r(3)*15000), pending: 6840 + Math.round(r(4)*2000), period:'May 2025 – Apr 2026' },
    'all': { l:'All time', earned: 184220 + Math.round(r(5)*40000), pending: 11200 + Math.round(r(6)*3000), period:`Since ${(m.dateCreated || m.created || '2023').slice(0,4)}` },
  };
  const T = totals[period];
  const fmt = (n) => '$' + n.toLocaleString();

  // Source breakdown — performance, mechanical, sync, neighboring, other
  const sourceRows = [
    { k:'performance', l:'Performance', detail:'Public performance · radio · venues · DSPs PRO',     pct: 0.41 + r(7)*0.04 },
    { k:'mechanical',  l:'Mechanical',  detail:'Streams · downloads · physical · sync mechanicals', pct: 0.34 + r(8)*0.03 },
    { k:'sync',        l:'Sync',        detail:'Film · TV · advertising · games',                    pct: 0.14 + r(9)*0.05 },
    { k:'neighboring', l:'Neighboring', detail:'Master rights · digital performance',                pct: 0.07 + r(10)*0.02 },
    { k:'print',       l:'Print & other', detail:'Sheet music · lyrics licensing',                   pct: 0.04 + r(11)*0.01 },
  ];
  // Normalize
  const sourceTotal = sourceRows.reduce((a,b)=>a+b.pct,0);
  sourceRows.forEach(s => { s.pct = s.pct/sourceTotal; s.amt = Math.round(T.earned * s.pct); });

  // Territory breakdown
  const territoryRows = [
    { k:'us', l:'United States', flag:'🇺🇸', pct:0.42 + r(12)*0.05 },
    { k:'gb', l:'United Kingdom', flag:'🇬🇧', pct:0.13 + r(13)*0.03 },
    { k:'de', l:'Germany',        flag:'🇩🇪', pct:0.09 + r(14)*0.02 },
    { k:'fr', l:'France',         flag:'🇫🇷', pct:0.07 + r(15)*0.02 },
    { k:'jp', l:'Japan',          flag:'🇯🇵', pct:0.06 + r(16)*0.02 },
    { k:'ca', l:'Canada',         flag:'🇨🇦', pct:0.05 + r(17)*0.01 },
    { k:'au', l:'Australia',      flag:'🇦🇺', pct:0.04 + r(18)*0.01 },
    { k:'ww', l:'Rest of world',  flag:'🌐', pct:0.14 + r(19)*0.02 },
  ];
  const tTotal = territoryRows.reduce((a,b)=>a+b.pct,0);
  territoryRows.forEach(s => { s.pct = s.pct/tTotal; s.amt = Math.round(T.earned * s.pct); });

  // Writer breakdown — derived from m.writers + their share %
  const writers = (m.writers || []).map((w, i) => ({
    name: w.name || `Writer ${i+1}`,
    role: w.role || 'Writer',
    share: +w.share || 0,
    amt: Math.round(T.earned * (+w.share || 0) / 100),
  }));

  // Recent statements
  const statements = [
    { soc:'BMI',   ts:'2026-04-12', amount: Math.round(T.earned * 0.22), period:'Q1 2026', status:'parsed' },
    { soc:'ASCAP', ts:'2026-03-30', amount: Math.round(T.earned * 0.19), period:'Q1 2026', status:'parsed' },
    { soc:'PRS',   ts:'2026-03-18', amount: Math.round(T.earned * 0.11), period:'Q4 2025', status:'parsed' },
    { soc:'GEMA',  ts:'2026-02-22', amount: Math.round(T.earned * 0.08), period:'Q4 2025', status:'parsed' },
    { soc:'SACEM', ts:'2026-02-11', amount: Math.round(T.earned * 0.06), period:'Q4 2025', status:'pending' },
    { soc:'SIAE',  ts:'2026-01-28', amount: Math.round(T.earned * 0.04), period:'Q4 2025', status:'parsed' },
  ];

  // 12mo trend (use sparkRoyalty if available)
  const trend = (typeof sparkRoyalty !== 'undefined' ? sparkRoyalty : Array.from({length:30},(_,i)=>20+i*3+r(i)*10));

  const PALETTE = ['oklch(0.62 0.16 50)','oklch(0.55 0.13 240)','oklch(0.68 0.14 140)','oklch(0.58 0.17 25)','oklch(0.50 0.10 290)','oklch(0.65 0.12 85)','oklch(0.45 0.08 200)','oklch(0.72 0.10 60)'];

  const rowsByView = view === 'source' ? sourceRows : view === 'territory' ? territoryRows : writers;

  return (
    <div>
      {/* Header */}
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', padding:'0 0 14px', marginBottom:18, borderBottom:'1px solid var(--rule)' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:4 }}>ROYALTIES</div>
          <div className="ff-display" style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em' }}>
            Earnings & statements
          </div>
        </div>
        <div style={{ display:'flex', gap:8 }}>
          <button onClick={()=>go && go('analytics')}
            className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', padding:'8px 14px', background:'var(--bg)', color:'var(--ink-2)', border:'1px solid var(--rule)', cursor:'pointer', display:'flex', alignItems:'center', gap:6 }}>
            <Ic.Bar width={12} height={12}/> Open analytics
          </button>
          <button onClick={()=>window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Royalty statement for "${m.title}" exported as CSV`,tone:'ok'}}))}
            className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', padding:'8px 14px', background:'var(--ink)', color:'var(--bg)', border:0, cursor:'pointer', display:'flex', alignItems:'center', gap:6 }}>
            <Ic.File width={12} height={12}/> Export statement
          </button>
        </div>
      </div>

      {/* Period selector */}
      <div style={{ display:'flex', gap:6, marginBottom:24 }}>
        {Object.entries(totals).map(([k, v]) => {
          const active = period === k;
          return (
            <button key={k} onClick={()=>setPeriod(k)}
              className="ff-mono upper" style={{
                fontSize:10, letterSpacing:'.08em', padding:'8px 14px',
                background: active ? 'var(--ink)' : 'var(--bg)',
                color: active ? 'var(--bg)' : 'var(--ink-2)',
                border:'1px solid '+(active ? 'var(--ink)' : 'var(--rule)'),
                cursor:'pointer'
              }}>{v.l}</button>
          );
        })}
      </div>

      {/* Hero KPIs */}
      <div style={{ display:'grid', gridTemplateColumns:'1.4fr 1fr 1fr', gap:0, border:'1px solid var(--rule)', marginBottom:28 }}>
        <div style={{ padding:'22px 24px', borderRight:'1px solid var(--rule)' }}>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:6 }}>EARNED · {T.period}</div>
          <div className="ff-display num" style={{ fontSize:48, fontWeight:600, letterSpacing:'-0.04em', lineHeight:1 }}>{fmt(T.earned)}</div>
          <div className="ff-mono num" style={{ fontSize:11, color:'var(--ok, #5b8a3a)', marginTop:6 }}>▲ +{(8 + r(20)*15).toFixed(1)}% vs prior period</div>
          <div style={{ marginTop:14, marginLeft:-4 }}>
            <Spark data={trend} w={400} h={68} stroke="var(--ink)" area fill="var(--ink-2)" strokeWidth={1.5} responsive />
          </div>
        </div>
        <div style={{ padding:'22px 24px', borderRight:'1px solid var(--rule)' }}>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:6 }}>PENDING</div>
          <div className="ff-display num" style={{ fontSize:36, fontWeight:600, letterSpacing:'-0.03em', lineHeight:1 }}>{fmt(T.pending)}</div>
          <div className="ff-mono" style={{ fontSize:11, color:'var(--ink-3)', marginTop:6 }}>across {Math.round(2 + r(21)*4)} societies</div>
          <div className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.1em', color:'var(--ink-3)', marginTop:18 }}>NEXT PAYOUT</div>
          <div className="ff-mono" style={{ fontSize:13, color:'var(--ink)', marginTop:2 }}>BMI · 2026-05-15</div>
        </div>
        <div style={{ padding:'22px 24px' }}>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:6 }}>STREAMS · 12MO</div>
          <div className="ff-display num" style={{ fontSize:36, fontWeight:600, letterSpacing:'-0.03em', lineHeight:1 }}>{(2.4 + r(22)*1.8).toFixed(1)}M</div>
          <div className="ff-mono" style={{ fontSize:11, color:'var(--ink-3)', marginTop:6 }}>${(T.earned/2400).toFixed(4)} per stream</div>
          <div className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.1em', color:'var(--ink-3)', marginTop:18 }}>TOP DSP</div>
          <div className="ff-mono" style={{ fontSize:13, color:'var(--ink)', marginTop:2 }}>Spotify · 58%</div>
        </div>
      </div>

      {/* Breakdown view toggle */}
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:14 }}>
        <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)' }}>BREAKDOWN</div>
        <div style={{ display:'flex', gap:0, border:'1px solid var(--rule)' }}>
          {[{k:'source',l:'By source'},{k:'territory',l:'By territory'},{k:'writer',l:'By writer'}].map((v, i) => {
            const active = view === v.k;
            return (
              <button key={v.k} onClick={()=>setView(v.k)}
                className="ff-mono upper" style={{
                  fontSize:10, letterSpacing:'.08em', padding:'7px 12px',
                  background: active ? 'var(--ink)' : 'var(--bg)',
                  color: active ? 'var(--bg)' : 'var(--ink-2)',
                  border:0, borderRight: i<2 ? '1px solid var(--rule)' : 0,
                  cursor:'pointer'
                }}>{v.l}</button>
            );
          })}
        </div>
      </div>

      {/* Stacked bar */}
      <div style={{ display:'flex', height:32, border:'1px solid var(--ink)', marginBottom:2, overflow:'hidden' }}>
        {rowsByView.map((row, i) => {
          const pct = view==='writer' ? (row.share/100) : row.pct;
          if (pct < 0.001) return null;
          return (
            <div key={i} title={`${view==='writer'?row.name:row.l}: ${(pct*100).toFixed(1)}%`}
              style={{ width: (pct*100)+'%', background: PALETTE[i%PALETTE.length], borderRight: i < rowsByView.length-1 ? '1px solid var(--bg)' : 0 }}/>
          );
        })}
      </div>

      {/* Breakdown rows */}
      <div style={{ border:'1px solid var(--rule)', borderTop:0, marginBottom:32 }}>
        {rowsByView.length === 0 ? (
          <div style={{ padding:30, textAlign:'center', color:'var(--ink-3)', fontSize:13 }}>No writers on this work yet.</div>
        ) : rowsByView.map((row, i) => {
          const pct = view==='writer' ? (row.share/100) : row.pct;
          const amt = view==='writer' ? row.amt : row.amt;
          const labelL = view==='writer' ? row.name : row.l;
          const labelR = view==='source' ? row.detail : view==='territory' ? row.k.toUpperCase() : row.role;
          return (
            <div key={i} style={{
              display:'grid', gridTemplateColumns:'12px 1.4fr 1fr 80px 110px',
              gap:14, padding:'12px 14px', alignItems:'center',
              borderBottom: i < rowsByView.length-1 ? '1px solid var(--rule-soft)' : 'none',
            }}>
              <span style={{ width:10, height:10, background:PALETTE[i%PALETTE.length], display:'inline-block' }}/>
              <div>
                <div style={{ fontSize:13, fontWeight:600 }}>
                  {view==='territory' && <span style={{marginRight:6}}>{row.flag}</span>}
                  {labelL}
                </div>
                <div className="ff-mono" style={{ fontSize:10, color:'var(--ink-3)', marginTop:2, letterSpacing:'.04em' }}>{labelR}</div>
              </div>
              {/* Inline mini-bar */}
              <div style={{ height:4, background:'var(--rule-soft)', position:'relative' }}>
                <div style={{ position:'absolute', inset:0, width:(pct*100)+'%', background:PALETTE[i%PALETTE.length] }}/>
              </div>
              <span className="ff-mono num" style={{ fontSize:12, color:'var(--ink-2)', textAlign:'right' }}>{(pct*100).toFixed(1)}%</span>
              <span className="ff-mono num" style={{ fontSize:13, fontWeight:600, textAlign:'right' }}>{fmt(amt)}</span>
            </div>
          );
        })}
      </div>

      {/* Recent statements */}
      <div style={{ marginBottom:28 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:14 }}>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)' }}>RECENT STATEMENTS</div>
          <button onClick={()=>go && go('cwr')}
            className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.06em', padding:'4px 8px', background:'transparent', color:'var(--ink-2)', border:'1px solid var(--rule)', cursor:'pointer' }}>
            View all in transmissions →
          </button>
        </div>
        <div style={{ border:'1px solid var(--rule)' }}>
          <div className="ff-mono upper" style={{
            display:'grid', gridTemplateColumns:'90px 1fr 130px 100px 100px',
            gap:14, padding:'10px 14px', fontSize:10, color:'var(--ink-3)',
            background:'var(--bg-2)', borderBottom:'1px solid var(--rule)' }}>
            <span>SOCIETY</span><span>PERIOD</span><span>RECEIVED</span><span>STATUS</span><span style={{textAlign:'right'}}>AMOUNT</span>
          </div>
          {statements.map((s, i) => (
            <div key={i} style={{
              display:'grid', gridTemplateColumns:'90px 1fr 130px 100px 100px',
              gap:14, padding:'12px 14px', alignItems:'center',
              borderBottom: i < statements.length-1 ? '1px solid var(--rule-soft)' : 'none',
            }}>
              <span className="ff-mono" style={{ fontSize:13, fontWeight:600 }}>{s.soc}</span>
              <span style={{ fontSize:13, color:'var(--ink)' }}>{s.period}</span>
              <span className="ff-mono num" style={{ fontSize:11, color:'var(--ink-2)' }}>{s.ts}</span>
              <span className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.1em', fontWeight:600,
                color: s.status==='parsed' ? '#5b8a3a' : '#b78113',
                display:'inline-flex', alignItems:'center', gap:5 }}>
                <span style={{ width:6, height:6, borderRadius:'50%', background: s.status==='parsed' ? '#5b8a3a' : '#b78113', display:'inline-block' }}/>
                {s.status}
              </span>
              <span className="ff-mono num" style={{ fontSize:13, fontWeight:600, textAlign:'right' }}>{fmt(s.amount)}</span>
            </div>
          ))}
        </div>
      </div>

      {/* Help footer */}
      <div style={{ padding:16, background:'var(--bg-2)', border:'1px solid var(--rule-soft)', display:'flex', gap:14, alignItems:'flex-start' }}>
        <div className="ff-display" style={{ width:28, height:28, border:'1px solid var(--rule)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, background:'var(--bg)', fontSize:14, fontStyle:'italic', fontWeight:600 }}>
          i
        </div>
        <div>
          <div style={{ fontSize:13, fontWeight:600, marginBottom:4 }}>About these figures</div>
          <div style={{ fontSize:12, color:'var(--ink-2)', lineHeight:1.55 }}>
            Earnings reflect parsed royalty statements only. Pending amounts are accrued from ingested statements awaiting payout schedules. Writer amounts are calculated from the splits on this work — see the <span className="ff-mono" style={{color:'var(--ink)'}}>Splits</span> tab to adjust.
          </div>
        </div>
      </div>
    </div>);
}

// ───────────────────────────────────────────────────────────── HISTORY TAB
function WorkHistoryTab({ m, history, cursor, onRevert, go }) {
  const [filter, setFilter] = useS1('all');

  // Synthesize lifecycle events from work data so history tab is meaningful
  // even before the user makes any local edits.
  const lifecycle = React.useMemo(() => {
    const seed = (m.iswc || m.id || m.title || '').split('').reduce((s,c)=>s+c.charCodeAt(0),0);
    const ev = [];
    const created = m.dateCreated || m.created || '2023-08-14';
    ev.push({ ts: new Date(created).getTime(), kind:'lifecycle', actor:m.writers?.[0]?.name || 'Original writer', label:'Work created', detail:`Initial registration with ${m.writers?.length || 0} writer${m.writers?.length===1?'':'s'}` });
    ev.push({ ts: new Date(created).getTime() + 86400000*2, kind:'rights', actor:'System', label:'ISWC assigned', detail:m.iswc || 'Pending' });
    if (m.recordings?.length) {
      m.recordings.slice(0, 2).forEach((r, i) => {
        ev.push({ ts: new Date(created).getTime() + 86400000*(45+i*30), kind:'recordings', actor:'A&R', label:`Recording linked: "${r.title || 'Untitled'}"`, detail:`ISRC ${r.isrc || '—'}${r.artist?` · ${r.artist}`:''}` });
      });
    }
    (m.societyIds || []).slice(0, 4).forEach((s, i) => {
      const k = (seed + i*7) % 100;
      const baseDate = new Date(created).getTime() + 86400000*(60+i*22);
      ev.push({ ts: baseDate, kind:'registrations', actor:'CWR engine', label:`CWR sent to ${s.society}`, detail:`Work ID ${s.workId || 'pending'}` });
      if (k < 80) ev.push({ ts: baseDate + 86400000*(2+(k%6)), kind:'registrations', actor:s.society, label:`${s.society} acknowledged registration`, detail:`Work ID confirmed${s.workId?` · ${s.workId}`:''}` });
      else if (k < 92) ev.push({ ts: baseDate + 86400000*5, kind:'registrations', actor:s.society, label:`${s.society} registration pending`, detail:'Awaiting acknowledgment' });
      else ev.push({ ts: baseDate + 86400000*4, kind:'registrations', actor:s.society, label:`${s.society} rejected registration`, detail:'Invalid PRO affiliation in writer record' });
    });
    // Royalty milestones
    [['First royalties received', 0.18], ['Crossed $10K earned', 0.42], ['Crossed $25K earned', 0.71]].forEach(([lbl, frac], i) => {
      ev.push({ ts: new Date(created).getTime() + (Date.now()-new Date(created).getTime())*frac, kind:'royalties', actor:'System', label:lbl, detail:'Earnings milestone' });
    });
    return ev.sort((a,b)=>b.ts-a.ts);
  }, [m]);

  // Map edit-history (local, in-session edits) into the same shape
  const edits = (history || []).map((h, i) => ({
    ts: h.ts, kind:'edit', actor:'You', label:h.label, detail:'In-session edit',
    editIndex: i, isCurrent: i === cursor - 1, isFuture: i >= cursor,
  }));
  const merged = [...edits, ...lifecycle].sort((a,b)=>b.ts-a.ts);
  const filtered = filter === 'all' ? merged : merged.filter(e => e.kind === filter);

  const kindMeta = {
    edit:          { l:'Edits',         tone:'var(--ink)' },
    lifecycle:     { l:'Lifecycle',     tone:'#5b8a3a' },
    rights:        { l:'Rights',        tone:'#7a4ca0' },
    recordings:    { l:'Recordings',    tone:'#3d6da3' },
    registrations: { l:'Registrations', tone:'#b78113' },
    royalties:     { l:'Royalties',     tone:'#5b8a3a' },
  };

  const counts = merged.reduce((a,e)=>(a[e.kind]=(a[e.kind]||0)+1,a),{});
  const fmtDate = (ts) => {
    const d = new Date(ts);
    return d.toISOString().slice(0,10);
  };
  const fmtTime = (ts) => {
    const d = new Date(ts);
    return d.toTimeString().slice(0,5);
  };

  // Group by date for visual rhythm
  const groups = filtered.reduce((acc, e) => {
    const k = fmtDate(e.ts);
    (acc[k] = acc[k] || []).push(e);
    return acc;
  }, {});
  const groupKeys = Object.keys(groups);

  return (
    <div>
      {/* Header */}
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline', padding:'0 0 14px', marginBottom:18, borderBottom:'1px solid var(--rule)' }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)', marginBottom:4 }}>WORK HISTORY</div>
          <div className="ff-display" style={{ fontSize:22, fontWeight:600, letterSpacing:'-0.01em' }}>
            {merged.length} event{merged.length===1?'':'s'} · {edits.length} edit{edits.length===1?'':'s'} this session
          </div>
        </div>
        <button onClick={()=>window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:`Audit log for "${m.title}" exported as CSV`,tone:'ok'}}))}
          className="ff-mono upper" style={{ fontSize:10, letterSpacing:'.08em', padding:'8px 14px', background:'var(--bg)', color:'var(--ink)', border:'1px solid var(--rule)', cursor:'pointer', display:'flex', alignItems:'center', gap:6 }}>
          <Ic.File width={12} height={12}/> Export audit log
        </button>
      </div>

      {/* Filter chips */}
      <div style={{ display:'flex', gap:6, flexWrap:'wrap', marginBottom:20 }}>
        {[{k:'all',l:'All'}, ...Object.keys(kindMeta).map(k=>({k, l:kindMeta[k].l}))].map(f => {
          const active = filter === f.k;
          const n = f.k === 'all' ? merged.length : (counts[f.k] || 0);
          return (
            <button key={f.k} onClick={()=>setFilter(f.k)}
              className="ff-mono upper" style={{
                fontSize:10, letterSpacing:'.08em', padding:'7px 11px',
                background: active ? 'var(--ink)' : 'var(--bg)',
                color: active ? 'var(--bg)' : 'var(--ink-2)',
                border: '1px solid '+(active ? 'var(--ink)' : 'var(--rule)'),
                cursor:'pointer', display:'inline-flex', alignItems:'center', gap:6
              }}>
              {f.l} <span style={{opacity:.6, fontWeight:400}}>{n}</span>
            </button>
          );
        })}
      </div>

      {/* Timeline */}
      {filtered.length === 0 ? (
        <div style={{ padding:'60px 20px', textAlign:'center', color:'var(--ink-3)', border:'1px dashed var(--rule)' }}>
          <div className="ff-display" style={{ fontSize:22, marginBottom:6, fontWeight:600, color:'var(--ink-2)' }}>No events for this filter</div>
          <p style={{ fontSize:13 }}>Try a different filter or make an edit to record a change.</p>
        </div>
      ) : (
        <div style={{ borderTop:'1px solid var(--rule)' }}>
          {groupKeys.map((dateKey, gi) => (
            <div key={dateKey}>
              {/* Date header */}
              <div className="ff-mono upper" style={{
                display:'flex', justifyContent:'space-between', alignItems:'baseline',
                padding:'14px 0 10px', borderBottom:'1px solid var(--rule-soft)',
                fontSize:10, letterSpacing:'.12em', color:'var(--ink-3)' }}>
                <span>{dateKey}</span>
                <span style={{ color:'var(--ink-4)' }}>{groups[dateKey].length} event{groups[dateKey].length===1?'':'s'}</span>
              </div>
              {/* Events */}
              {groups[dateKey].map((e, i) => {
                const meta = kindMeta[e.kind] || kindMeta.edit;
                return (
                  <div key={i} style={{
                    display:'grid', gridTemplateColumns:'70px 14px 1fr auto',
                    gap:14, padding:'14px 0', borderBottom: i < groups[dateKey].length-1 ? '1px solid var(--rule-soft)' : '1px solid var(--rule-soft)',
                    alignItems:'flex-start',
                    opacity: e.isFuture ? 0.42 : 1,
                  }}>
                    {/* Time */}
                    <span className="ff-mono num" style={{ fontSize:11, color:'var(--ink-3)', paddingTop:1 }}>{fmtTime(e.ts)}</span>
                    {/* Bullet */}
                    <span style={{
                      width:8, height:8, borderRadius:'50%',
                      background: e.isCurrent ? 'var(--ink)' : meta.tone,
                      marginTop:6, justifySelf:'center',
                      boxShadow: e.isCurrent ? '0 0 0 3px var(--bg), 0 0 0 4px var(--ink)' : 'none',
                    }}/>
                    {/* Content */}
                    <div>
                      <div style={{ display:'flex', gap:8, alignItems:'baseline', flexWrap:'wrap' }}>
                        <span className="ff-mono upper" style={{
                          fontSize:9, letterSpacing:'.1em', padding:'2px 6px',
                          background:'var(--bg-2)', border:'1px solid var(--rule-soft)', color:meta.tone, fontWeight:600,
                        }}>{meta.l}</span>
                        <span style={{ fontSize:14, fontWeight: e.isCurrent ? 600 : 500, color:'var(--ink)' }}>{e.label}</span>
                        {e.isCurrent && (
                          <span className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.1em', color:'var(--ink-3)' }}>· current</span>
                        )}
                      </div>
                      <div style={{ fontSize:12, color:'var(--ink-2)', marginTop:3 }}>
                        {e.detail} · <span className="ff-mono">by {e.actor}</span>
                      </div>
                    </div>
                    {/* Action */}
                    <div style={{ paddingTop:2 }}>
                      {e.kind === 'edit' && !e.isCurrent && onRevert && (
                        <button onClick={()=>onRevert(e.editIndex)}
                          className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.08em', padding:'5px 9px', background:'transparent', color:'var(--ink-2)', border:'1px solid var(--rule)', cursor:'pointer' }}>
                          {e.isFuture ? 'Redo to here' : 'Revert to here'}
                        </button>
                      )}
                      {e.kind === 'registrations' && go && (
                        <button onClick={()=>{}}
                          className="ff-mono upper" style={{ fontSize:9, letterSpacing:'.08em', padding:'5px 9px', background:'transparent', color:'var(--ink-3)', border:'1px solid var(--rule-soft)', cursor:'pointer' }}>
                          View →
                        </button>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          ))}
        </div>
      )}

      {/* Help footer */}
      <div style={{ marginTop:28, padding:16, background:'var(--bg-2)', border:'1px solid var(--rule-soft)', display:'flex', gap:14, alignItems:'flex-start' }}>
        <div className="ff-display" style={{ width:28, height:28, border:'1px solid var(--rule)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, background:'var(--bg)', fontSize:14, fontStyle:'italic', fontWeight:600 }}>
          i
        </div>
        <div>
          <div style={{ fontSize:13, fontWeight:600, marginBottom:4 }}>About this audit log</div>
          <div style={{ fontSize:12, color:'var(--ink-2)', lineHeight:1.55 }}>
            History records every change to this work — edits made by collaborators, society acknowledgments, recording links, and royalty milestones. In-session edits can be reverted directly. External events (society replies, royalty payments) are immutable. Export the full log as CSV for compliance reviews or splits-dispute resolution.
          </div>
        </div>
      </div>
    </div>);
}

// ───────────────────────────────────────────────────────────── RETRANSMIT MODAL
function RetransmitModal({ title, rows, single, onClose, onConfirm }) {
  const [selected, setSelected] = useS1(rows.map((_,i)=>i));
  const [reason, setReason] = useS1('updated-shares');
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  const toggle = (i) => setSelected(s => s.includes(i) ? s.filter(x=>x!==i) : [...s, i]);
  const allOn = selected.length === rows.length;
  const dateStr = new Date().toISOString().slice(0,10).replace(/-/g,'').slice(2);
  const tone = (s) => s==='acknowledged' ? '#5b8a3a' : s==='submitted' ? 'var(--ink-2)' : s==='pending' ? '#b78113' : '#c0392b';
  const reasons = [
    { k:'updated-shares', l:'Updated splits / shares' },
    { k:'corrected-metadata', l:'Corrected metadata or writer info' },
    { k:'rejection-fix', l:'Resubmitting after rejection' },
    { k:'new-territory', l:'Added territory or sub-publisher' },
    { k:'admin', l:'Administrative reissue' },
  ];
  const chosen = rows.filter((_,i)=>selected.includes(i));
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.45)',zIndex:200,display:'flex',alignItems:'center',justifyContent:'center',padding:24}}>
      <div onClick={(e)=>e.stopPropagation()}
        style={{background:'var(--bg)',width:'min(680px,100%)',maxHeight:'85vh',overflow:'auto',border:'1px solid var(--ink)',boxShadow:'8px 8px 0 rgba(0,0,0,.12)',position:'relative'}}>
        <div style={{position:'sticky',top:0,background:'var(--bg)',borderBottom:'1px solid var(--rule)',padding:'18px 24px',display:'flex',alignItems:'center',justifyContent:'space-between',zIndex:1}}>
          <div>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)'}}>CWR · RETRANSMIT</div>
            <div className="ff-display" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.02em',marginTop:2}}>
              {single ? `Resend to ${rows[0].society}` : 'Retransmit registrations'}
            </div>
            <div style={{fontSize:12,color:'var(--ink-2)',marginTop:4}}>"{title}"</div>
          </div>
          <button onClick={onClose} style={{background:'transparent',border:'1px solid var(--ink)',width:32,height:32,cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center'}}>
            <Ic.X width={14} height={14}/>
          </button>
        </div>

        <div style={{padding:'20px 24px'}}>
          {/* Reason */}
          <div style={{marginBottom:20}}>
            <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:8}}>REASON FOR RETRANSMISSION</div>
            <div style={{display:'flex',flexDirection:'column',gap:6}}>
              {reasons.map(r => (
                <label key={r.k} style={{display:'flex',alignItems:'center',gap:8,padding:'8px 10px',border:'1px solid '+(reason===r.k?'var(--ink)':'var(--rule)'),background:reason===r.k?'var(--bg-2)':'var(--bg)',cursor:'pointer',fontSize:13}}>
                  <input type="radio" name="cwr-reason" checked={reason===r.k} onChange={()=>setReason(r.k)} style={{margin:0}}/>
                  <span>{r.l}</span>
                </label>
              ))}
            </div>
          </div>

          {/* Society selection */}
          {!single && (
            <div style={{marginBottom:20}}>
              <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:8}}>
                <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)'}}>SOCIETIES ({selected.length} of {rows.length})</div>
                <button onClick={()=>setSelected(allOn?[]:rows.map((_,i)=>i))}
                  className="ff-mono upper" style={{fontSize:10,letterSpacing:'.06em',padding:'4px 8px',background:'transparent',border:'1px solid var(--rule)',cursor:'pointer',color:'var(--ink-2)'}}>
                  {allOn ? 'Deselect all' : 'Select all'}
                </button>
              </div>
              <div style={{border:'1px solid var(--rule)',maxHeight:220,overflow:'auto'}}>
                {rows.map((r,i) => (
                  <label key={i} style={{display:'grid',gridTemplateColumns:'24px 80px 1fr 90px',gap:10,alignItems:'center',padding:'10px 12px',borderBottom:i<rows.length-1?'1px solid var(--rule-soft)':'none',cursor:'pointer',fontSize:12}}>
                    <input type="checkbox" checked={selected.includes(i)} onChange={()=>toggle(i)} style={{margin:0}}/>
                    <span className="ff-mono" style={{fontSize:12,fontWeight:600}}>{r.society}</span>
                    <span className="ff-mono" style={{fontSize:11,color:'var(--ink-2)'}}>{r.workId || <span style={{color:'var(--ink-3)'}}>— no work ID —</span>}</span>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:tone(r.status),display:'inline-flex',alignItems:'center',gap:5,fontWeight:600,justifyContent:'flex-end'}}>
                      <span style={{width:5,height:5,borderRadius:'50%',background:tone(r.status),display:'inline-block'}}/>
                      {r.status}
                    </span>
                  </label>
                ))}
              </div>
            </div>
          )}

          {/* Summary */}
          <div style={{padding:'14px 16px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)',marginBottom:8}}>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:8}}>TRANSMISSION PREVIEW</div>
            <div style={{display:'grid',gridTemplateColumns:'auto 1fr',gap:'4px 16px',fontSize:12}}>
              <span style={{color:'var(--ink-3)'}}>Files</span>
              <span className="ff-mono" style={{color:'var(--ink)'}}>{chosen.length} CWR file{chosen.length===1?'':'s'} ({chosen.map(c=>`CW${dateStr}${c.society}`).slice(0,3).join(', ')}{chosen.length>3?`, +${chosen.length-3} more`:''})</span>
              <span style={{color:'var(--ink-3)'}}>Format</span>
              <span className="ff-mono" style={{color:'var(--ink)'}}>CWR V2.1 / V3.0.0 (per-society)</span>
              <span style={{color:'var(--ink-3)'}}>Schedule</span>
              <span className="ff-mono" style={{color:'var(--ink)'}}>Immediately, batched per-society inbox</span>
              <span style={{color:'var(--ink-3)'}}>Acks</span>
              <span style={{color:'var(--ink-2)'}}>Replies will appear in this tab as they arrive (2–14 days typical)</span>
            </div>
          </div>
        </div>

        <div style={{position:'sticky',bottom:0,background:'var(--bg)',borderTop:'1px solid var(--rule)',padding:'14px 24px',display:'flex',gap:8,justifyContent:'flex-end'}}>
          <button onClick={onClose}
            style={{padding:'9px 16px',fontSize:12,background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',cursor:'pointer'}}>
            Cancel
          </button>
          <button onClick={()=>onConfirm(chosen)}
            disabled={chosen.length===0}
            style={{padding:'9px 16px',fontSize:12,background:chosen.length===0?'var(--ink-3)':'var(--ink)',color:'var(--bg)',border:0,cursor:chosen.length===0?'not-allowed':'pointer',display:'flex',alignItems:'center',gap:6}}>
            <Ic.Send width={12} height={12}/> Queue {chosen.length} transmission{chosen.length===1?'':'s'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────── EXPORT MODALS

function ExportModalShell({ title, kind, onClose, children }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.45)',zIndex:200,display:'flex',alignItems:'center',justifyContent:'center',padding:24}}>
      <div onClick={(e)=>e.stopPropagation()}
        style={{background:'var(--bg)',width:'min(720px,100%)',maxHeight:'85vh',overflow:'auto',border:'1px solid var(--ink)',boxShadow:'8px 8px 0 rgba(0,0,0,.12)',position:'relative'}}>
        <div style={{position:'sticky',top:0,background:'var(--bg)',borderBottom:'1px solid var(--rule)',padding:'18px 24px',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
          <div>
            <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)'}}>{kind}</div>
            <div className="ff-display" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.02em',marginTop:2}}>{title}</div>
          </div>
          <button onClick={onClose} style={{background:'transparent',border:'1px solid var(--ink)',width:32,height:32,cursor:'pointer',display:'inline-flex',alignItems:'center',justifyContent:'center'}}>
            <Ic.X width={14} height={14}/>
          </button>
        </div>
        {children}
      </div>
    </div>
  );
}

function CwrGenerationModal({ m, onClose, commit }) {
  const SOC_OPTIONS = (window.SOC_CWR || ['ASCAP','BMI','SESAC','PRS','GEMA','SACEM','SOCAN','APRA','JASRAC','SIAE','SGAE']);
  const [selected, setSelected] = React.useState(() => {
    const fromIds = (m.societyIds||[]).map(s => s.society).filter(s => SOC_OPTIONS.includes(s));
    return new Set(fromIds.length ? fromIds : ['ASCAP']);
  });
  const [version, setVersion] = React.useState('v2.2');
  const [transmissionType, setTransmissionType] = React.useState('NWR');
  const [stage, setStage] = React.useState('config'); // 'config' | 'generating' | 'done'
  const [progress, setProgress] = React.useState(0);

  const toggle = (s) => {
    const next = new Set(selected);
    if (next.has(s)) next.delete(s); else next.add(s);
    setSelected(next);
  };

  const generate = () => {
    if (selected.size === 0) return;
    setStage('generating');
    setProgress(0);
    const total = 100;
    const step = () => {
      setProgress(p => {
        const np = p + 4 + Math.random() * 8;
        if (np >= total) {
          setStage('done');
          if (commit) commit(`Generated CWR for ${[...selected].join(', ')}`, d => d);
          return total;
        }
        setTimeout(step, 80 + Math.random() * 120);
        return np;
      });
    };
    setTimeout(step, 200);
  };

  const fileLines = React.useMemo(() => {
    if (stage !== 'done') return [];
    // Render a tiny CWR-like preview (HDR/GRH/NWR/SPU/SPT/PWR/GRT/TRL skeleton)
    const pad = (s,n,c=' ') => String(s).slice(0,n).padEnd(n,c);
    const num = (v,n) => String(v).padStart(n,'0');
    const txns = [];
    txns.push(`HDR PB${num(123456, 9)}PLURALIS MUSIC PUBLISHING            01.10${new Date().toISOString().slice(0,10).replace(/-/g,'')}`);
    txns.push(`GRH${transmissionType}0000100000` + version.replace('v','').replace('.','').padEnd(3,'0'));
    txns.push(`${transmissionType}0000000100000001${pad((window.iswcDisplay ? window.iswcDisplay(m.iswc) : m.iswc) || '',11)}${pad(m.title.toUpperCase(),60)}EN${pad('',60)}${num(Math.round((m.duration||360)*1000),6)}UNCYORI`);
    (m.writers||[]).forEach((wr,i) => {
      txns.push(`SWR${num(0,8)}${num(i+1,8)}${pad(wr.ipi||'',11)}${pad((wr.name||'').toUpperCase(),45)}${pad(wr.role||'CA',2)}${num(Math.round((wr.share||0)*100),5)}`);
    });
    (m.writers||[]).forEach((wr,i) => {
      (wr.pubs||[]).forEach((pub,pi) => {
        // Spec: only controlled publisher shares are emitted in CWR.
        // Non-controlled shares are registered independently via their own publisher.
        if (window.getShareControlled && !window.getShareControlled(pub).controlled) return;
        txns.push(`SPU${num(0,8)}${num(pi+1,8)}E${pad(pub.ipi||'',11)}${pad((pub.name||'').toUpperCase(),45)}${num(Math.round((pub.share||0)*100),5)}`);
      });
    });
    txns.push(`GRT0000100000${num(txns.length,8)}${num(txns.length+2,8)}`);
    txns.push(`TRL00001${num(txns.length+1,8)}${num(txns.length+2,8)}`);
    return txns;
  }, [stage, m, version, transmissionType]);

  const downloadFile = () => {
    const content = fileLines.join('\n');
    const blob = new Blob([content], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    const date = new Date().toISOString().slice(0,10).replace(/-/g,'');
    a.href = url;
    a.download = `CW${date}_${(m.iswc||'work').replace(/[^A-Za-z0-9]/g,'')}_${[...selected][0]||'PUB'}.V21`;
    a.click();
    setTimeout(()=>URL.revokeObjectURL(url), 2000);
  };

  return (
    <ExportModalShell kind="CWR REGISTRATION" title="Generate CWR file" onClose={onClose}>
      <div style={{padding:24}}>
        {stage === 'config' && (
          <>
            {/* Work summary */}
            <div style={{padding:14,background:'var(--bg-2)',border:'1px solid var(--rule)',marginBottom:24}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>WORK</div>
              <div style={{fontSize:18,fontWeight:600}}>{m.title}</div>
              <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:4,display:'flex',gap:14,flexWrap:'wrap'}}>
                <span>ISWC: {m.iswc||'—'}</span>
                <span>·</span>
                <span>{(m.writers||[]).length} writer{(m.writers||[]).length===1?'':'s'}</span>
                <span>·</span>
                <span>{(m.writers||[]).flatMap(w=>w.pubs||[]).length} publisher entr{(m.writers||[]).flatMap(w=>w.pubs||[]).length===1?'y':'ies'}</span>
                <span>·</span>
                <span>shares: {(m.writers||[]).reduce((s,w)=>s+(+w.share||0),0)}%</span>
                {(() => {
                  if (!window.getShareControlled) return null;
                  const allPubs = (m.writers||[]).flatMap(w=>w.pubs||[]);
                  const ctrl = allPubs.filter(p => window.getShareControlled(p).controlled).length;
                  const nonCtrl = allPubs.length - ctrl;
                  if (!allPubs.length) return null;
                  return (
                    <>
                      <span>·</span>
                      <span title="Only controlled publisher shares (★ Owned / ◆ Administered) are emitted. Non-controlled shares must register via their own publisher.">
                        <strong style={{color:'var(--ink)'}}>{ctrl}</strong> controlled
                        {nonCtrl > 0 && <span style={{color:'#c79538',marginLeft:6}}>· {nonCtrl} excluded (non-ctrl)</span>}
                      </span>
                    </>
                  );
                })()}
              </div>
            </div>

            {/* Non-controlled banner */}
            {(() => {
              if (!window.getShareControlled) return null;
              const allPubs = (m.writers||[]).flatMap(w=>w.pubs||[]);
              const nonCtrl = allPubs.filter(p => !window.getShareControlled(p).controlled);
              if (!nonCtrl.length) return null;
              return (
                <div style={{padding:'10px 14px',background:'rgba(199,149,56,0.08)',border:'1px solid #c79538',marginBottom:24,fontSize:12,color:'var(--ink-2)',display:'flex',gap:10,alignItems:'flex-start'}}>
                  <span style={{color:'#c79538',fontWeight:700,fontSize:13,lineHeight:1.2}}>!</span>
                  <div>
                    <div style={{fontWeight:600,marginBottom:2}}>{nonCtrl.length} non-controlled share{nonCtrl.length===1?'':'s'} excluded</div>
                    <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>
                      {nonCtrl.slice(0,3).map(p=>p.name||'(unnamed)').join(' · ')}{nonCtrl.length>3?` +${nonCtrl.length-3} more`:''}
                      {' '}— these need to register independently through their own publisher.
                    </div>
                  </div>
                </div>
              );
            })()}

            {/* Recipient societies */}
            <div style={{marginBottom:24}}>
              <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:10}}>RECIPIENT SOCIETIES <span style={{color:'var(--ink)',fontWeight:600,marginLeft:6}}>{selected.size} selected</span></div>
              <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:8}}>
                {SOC_OPTIONS.map(s => {
                  const on = selected.has(s);
                  return (
                    <button key={s} onClick={()=>toggle(s)}
                      style={{padding:'10px 12px',border:'1px solid '+(on?'var(--ink)':'var(--rule)'),background:on?'var(--ink)':'var(--bg)',color:on?'var(--bg)':'var(--ink)',cursor:'pointer',fontSize:12,fontWeight:600,textAlign:'left',display:'flex',alignItems:'center',gap:6}}>
                      <span style={{width:10,height:10,border:'1px solid '+(on?'var(--bg)':'var(--ink)'),background:on?'var(--bg)':'transparent',display:'inline-flex',alignItems:'center',justifyContent:'center',fontSize:9,color:on?'var(--ink)':'transparent'}}>✓</span>
                      {s}
                    </button>
                  );
                })}
              </div>
            </div>

            {/* Options */}
            <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:16,marginBottom:24}}>
              <div>
                <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>CWR VERSION</div>
                <div style={{display:'flex',gap:6}}>
                  {['v2.1','v2.2','v3.0'].map(v => (
                    <button key={v} onClick={()=>setVersion(v)}
                      style={{padding:'8px 14px',border:'1px solid '+(version===v?'var(--ink)':'var(--rule)'),background:version===v?'var(--ink)':'var(--bg)',color:version===v?'var(--bg)':'var(--ink)',cursor:'pointer',fontSize:11,fontWeight:600}}>
                      {v}
                    </button>
                  ))}
                </div>
              </div>
              <div>
                <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>TRANSACTION TYPE</div>
                <div style={{display:'flex',gap:6}}>
                  {[{k:'NWR',l:'New work'},{k:'REV',l:'Revision'},{k:'ISW',l:'ISWC notify'}].map(t=>(
                    <button key={t.k} onClick={()=>setTransmissionType(t.k)}
                      style={{padding:'8px 14px',border:'1px solid '+(transmissionType===t.k?'var(--ink)':'var(--rule)'),background:transmissionType===t.k?'var(--ink)':'var(--bg)',color:transmissionType===t.k?'var(--bg)':'var(--ink)',cursor:'pointer',fontSize:11,fontWeight:600}}>
                      {t.k} <span style={{fontWeight:400,opacity:.7,marginLeft:4}}>{t.l}</span>
                    </button>
                  ))}
                </div>
              </div>
            </div>

            {/* Validation checks */}
            <div style={{padding:12,border:'1px solid var(--rule)',marginBottom:24}}>
              <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>PRE-FLIGHT CHECKS</div>
              {[
                { ok: !!m.iswc, label: 'ISWC present' },
                { ok: (m.writers||[]).reduce((s,w)=>s+(+w.share||0),0) === 100, label: 'Writer shares total 100%' },
                { ok: (m.writers||[]).every(w => w.ipi), label: 'All writers have IPI numbers' },
                { ok: (m.writers||[]).every(w => (w.pubs||[]).length > 0), label: 'All writers have at least one publisher' },
                { ok: selected.size > 0, label: 'At least one recipient society' },
              ].map((c,i)=>(
                <div key={i} style={{display:'flex',alignItems:'center',gap:8,padding:'4px 0',fontSize:12}}>
                  <span style={{color:c.ok?'#5b8a3a':'#b78113',fontWeight:600,width:14,display:'inline-flex',justifyContent:'center'}}>{c.ok?'✓':'⚠'}</span>
                  <span style={{color:c.ok?'var(--ink-2)':'var(--ink)'}}>{c.label}</span>
                  {!c.ok && <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:'#b78113',marginLeft:'auto'}}>warning</span>}
                </div>
              ))}
            </div>

            <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
              <Btn variant="ghost" onClick={onClose}>Cancel</Btn>
              <Btn variant="primary" icon={<Ic.Send/>} onClick={generate}>
                Generate CWR ({selected.size})
              </Btn>
            </div>
          </>
        )}

        {stage === 'generating' && (
          <div style={{padding:'40px 0',textAlign:'center'}}>
            <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:14}}>BUILDING CWR FILE…</div>
            <div style={{height:6,background:'var(--bg-2)',border:'1px solid var(--rule)',marginBottom:12,position:'relative',overflow:'hidden'}}>
              <div style={{height:'100%',background:'var(--ink)',width:`${progress}%`,transition:'width 80ms linear'}}/>
            </div>
            <div className="ff-mono num" style={{fontSize:14,fontWeight:600}}>{Math.floor(progress)}%</div>
            <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:8}}>
              {progress < 30 && 'Validating shares…'}
              {progress >= 30 && progress < 60 && 'Encoding writer/publisher records…'}
              {progress >= 60 && progress < 90 && 'Building transmission header…'}
              {progress >= 90 && 'Finalizing trailer record…'}
            </div>
          </div>
        )}

        {stage === 'done' && (
          <>
            <div style={{padding:14,background:'#eef3e6',border:'1px solid #5b8a3a',marginBottom:18,display:'flex',alignItems:'center',gap:12}}>
              <span style={{width:32,height:32,background:'#5b8a3a',color:'#fff',display:'inline-flex',alignItems:'center',justifyContent:'center',fontSize:16,fontWeight:600,flex:'0 0 32px'}}>✓</span>
              <div>
                <div style={{fontWeight:600,fontSize:13}}>CWR file generated</div>
                <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:2}}>{fileLines.length} records · {[...selected].join(', ')}</div>
              </div>
            </div>

            <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>FILE PREVIEW</div>
            <pre style={{padding:12,background:'#0f1115',color:'#e8eaee',fontFamily:'JetBrains Mono, ui-monospace, monospace',fontSize:10.5,lineHeight:1.5,overflow:'auto',maxHeight:200,marginBottom:18,whiteSpace:'pre'}}>
{fileLines.join('\n')}
            </pre>

            <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
              <Btn variant="ghost" onClick={onClose}>Close</Btn>
              <Btn variant="secondary" onClick={()=>setStage('config')}>← Back</Btn>
              <Btn variant="primary" icon={<Ic.Send/>} onClick={downloadFile}>Download .V21 file</Btn>
            </div>
          </>
        )}
      </div>
    </ExportModalShell>
  );
}

function DdexExportModal({ m, onClose, commit }) {
  const [profile, setProfile] = React.useState('ERN-4.3');
  const [done, setDone] = React.useState(false);

  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<NewReleaseMessage xmlns="http://ddex.net/xml/ern/43" MessageSchemaVersionId="ern/43">
  <MessageHeader>
    <MessageThreadId>PLURALIS-${(m.iswc||'').replace(/[^A-Za-z0-9]/g,'')}</MessageThreadId>
    <MessageId>MSG-${Date.now()}</MessageId>
    <MessageSender><PartyId>PA-PLURALIS</PartyId><PartyName><FullName>Pluralis Music</FullName></PartyName></MessageSender>
    <MessageRecipient><PartyId>RECIPIENT-DSP</PartyId></MessageRecipient>
    <MessageCreatedDateTime>${new Date().toISOString()}</MessageCreatedDateTime>
  </MessageHeader>
  <WorkList>
    <MusicalWork ISWC="${m.iswc||''}">
      <ReferenceTitle><TitleText>${m.title}</TitleText></ReferenceTitle>
      <Duration>PT${Math.floor((m.duration||0)/60)}M${(m.duration||0)%60}S</Duration>
${(m.writers||[]).map(wr => `      <Contributor>
        <ContributorPartyReference><PartyId IsDPID="false">${wr.ipi||''}</PartyId><PartyName><FullName>${wr.name}</FullName></PartyName></ContributorPartyReference>
        <Role>${wr.role||'Composer'}</Role>
        <RightShare><Percentage>${wr.share||0}</Percentage></RightShare>
      </Contributor>`).join('\n')}
    </MusicalWork>
  </WorkList>
</NewReleaseMessage>`;

  const downloadXml = () => {
    const blob = new Blob([xml], { type: 'application/xml' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${(m.iswc||'work').replace(/[^A-Za-z0-9]/g,'')}_DDEX_${profile}.xml`;
    a.click();
    setDone(true);
    if (commit) commit(`Exported DDEX ${profile}`, d => d);
    setTimeout(()=>URL.revokeObjectURL(url), 2000);
  };

  return (
    <ExportModalShell kind="DDEX EXPORT" title="Export DDEX message" onClose={onClose}>
      <div style={{padding:24}}>
        <div style={{padding:14,background:'var(--bg-2)',border:'1px solid var(--rule)',marginBottom:20}}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>WORK</div>
          <div style={{fontSize:18,fontWeight:600}}>{m.title}</div>
          <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:4}}>ISWC {window.iswcDisplay ? window.iswcDisplay(m.iswc) : m.iswc||'—'} · {(m.writers||[]).length} contributors</div>
        </div>

        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>DDEX PROFILE</div>
        <div style={{display:'flex',gap:6,marginBottom:20,flexWrap:'wrap'}}>
          {['ERN-4.3','ERN-4.2','MWN-1.0','RDR-N-1.0'].map(p => (
            <button key={p} onClick={()=>setProfile(p)}
              style={{padding:'8px 14px',border:'1px solid '+(profile===p?'var(--ink)':'var(--rule)'),background:profile===p?'var(--ink)':'var(--bg)',color:profile===p?'var(--bg)':'var(--ink)',cursor:'pointer',fontSize:11,fontWeight:600}}>
              {p}
            </button>
          ))}
        </div>

        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>XML PREVIEW</div>
        <pre style={{padding:12,background:'#0f1115',color:'#e8eaee',fontFamily:'JetBrains Mono, ui-monospace, monospace',fontSize:10.5,lineHeight:1.5,overflow:'auto',maxHeight:280,marginBottom:18,whiteSpace:'pre'}}>
{xml}
        </pre>

        {done && (
          <div style={{padding:10,background:'#eef3e6',border:'1px solid #5b8a3a',marginBottom:14,fontSize:12,fontWeight:600,color:'#3a6b3e'}}>
            ✓ XML downloaded.
          </div>
        )}

        <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
          <Btn variant="ghost" onClick={onClose}>Close</Btn>
          <Btn variant="primary" icon={<Ic.File/>} onClick={downloadXml}>Download .xml</Btn>
        </div>
      </div>
    </ExportModalShell>
  );
}

function PublicPageModal({ m, onClose }) {
  const slug = (m.title||'').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'');
  const url = `https://pluralis.music/works/${slug}`;
  const [copied, setCopied] = React.useState(false);
  const copy = () => {
    try { navigator.clipboard.writeText(url); setCopied(true); setTimeout(()=>setCopied(false), 2000); } catch {}
  };
  return (
    <ExportModalShell kind="PUBLIC PAGE" title="Share work publicly" onClose={onClose}>
      <div style={{padding:24}}>
        <div style={{padding:14,background:'var(--bg-2)',border:'1px solid var(--rule)',marginBottom:18}}>
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:4}}>WORK</div>
          <div style={{fontSize:18,fontWeight:600}}>{m.title}</div>
        </div>

        <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:8}}>PUBLIC URL</div>
        <div style={{display:'flex',gap:0,marginBottom:18}}>
          <input readOnly value={url} className="ff-mono"
            style={{flex:1,padding:'10px 12px',border:'1px solid var(--ink)',background:'var(--bg-2)',fontSize:12}}/>
          <button onClick={copy} className="ff-mono upper" style={{padding:'10px 16px',border:'1px solid var(--ink)',borderLeft:'none',background:copied?'#5b8a3a':'var(--ink)',color:'var(--bg)',cursor:'pointer',fontSize:10,letterSpacing:'.08em',fontWeight:600,minWidth:80}}>
            {copied ? '✓ COPIED' : 'COPY'}
          </button>
        </div>

        <div style={{padding:14,border:'1px solid var(--rule)',marginBottom:18}}>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color:'var(--ink-3)',marginBottom:10}}>PUBLIC PAGE INCLUDES</div>
          {[
            'Title, ISWC, alternate titles',
            'Writer credits (names + roles, no IPI numbers)',
            'Linked recordings + streaming services',
            'Lyrics (if marked public)',
          ].map((l,i)=>(
            <div key={i} style={{padding:'4px 0',fontSize:12,display:'flex',alignItems:'center',gap:8}}>
              <span style={{color:'#5b8a3a',width:14,display:'inline-flex',justifyContent:'center'}}>✓</span>
              <span>{l}</span>
            </div>
          ))}
          {[
            'Earnings, splits, identifiers',
            'Society work IDs',
          ].map((l,i)=>(
            <div key={'h'+i} style={{padding:'4px 0',fontSize:12,display:'flex',alignItems:'center',gap:8,color:'var(--ink-3)'}}>
              <span style={{color:'var(--ink-3)',width:14,display:'inline-flex',justifyContent:'center'}}>×</span>
              <span style={{textDecoration:'line-through'}}>{l}</span>
              <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',marginLeft:'auto',color:'var(--ink-3)'}}>private</span>
            </div>
          ))}
        </div>

        <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
          <Btn variant="ghost" onClick={onClose}>Close</Btn>
          <Btn variant="primary" icon={<Ic.Ext/>} onClick={()=>window.open(url,'_blank')}>Open public page</Btn>
        </div>
      </div>
    </ExportModalShell>
  );
}

Object.assign(window, { ScreenDashboard, ScreenCatalog, ScreenWork, makeDemoToneWav, getDemoAudioUrl, _DEMO_AUDIO_CACHE });
