// directory.jsx — Directory page (Profiles · Publishers · Labels · Societies)
const { useState: useDS } = React;

// ─────────────────────────── Filter popover helpers
function FilterRow({ label, hint, children }) {
  return (
    <div>
      <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500,marginBottom:hint?4:8}}>{label}</div>
      {hint && <div className="ff-mono" style={{fontSize:10,color:'var(--ink-4)',marginBottom:8,lineHeight:1.4}}>{hint}</div>}
      {children}
    </div>
  );
}
function FilterChips({ value, onChange, options }) {
  return (
    <div style={{display:'flex',gap:0,border:'1px solid var(--rule)',width:'fit-content',flexWrap:'wrap'}}>
      {options.map(({v,l})=>(
        <button key={v} onClick={()=>onChange(v)} className="ff-mono upper"
          style={{padding:'6px 12px',fontSize:10,letterSpacing:'.08em',fontWeight:500,
            background: value===v ? 'var(--ink)' : 'transparent',
            color: value===v ? 'var(--bg)' : 'var(--ink-2)',
            border:0,borderRight:'1px solid var(--rule)',cursor:'pointer'}}>
          {l}
        </button>
      ))}
    </div>
  );
}

// ─────────────────────────── Societies view
function SocietiesView({ q, highlight, onAdd, filters }) {
  const [filterKind, setFilterKind] = React.useState('all');
  const all = (typeof SOCIETIES !== 'undefined' ? SOCIETIES : []);
  const needle = (q||'').trim().toLowerCase();
  const cwrMatch = (sup) => {
    if (!filters || filters.cwrSupport === 'any') return true;
    const has21 = (sup||'').includes('2.1');
    const has30 = (sup||'').includes('3.0');
    if (filters.cwrSupport === '21') return has21 && !has30;
    if (filters.cwrSupport === '30') return has30 && !has21;
    if (filters.cwrSupport === 'both') return has21 && has30;
    if (filters.cwrSupport === 'none') return !has21 && !has30;
    return true;
  };
  const filtered = all
    .filter(s => filterKind === 'all' ? true : s.kind === filterKind)
    .filter(s => cwrMatch(s.cwrAck))
    .filter(s => !filters || (s.ackRate||0) >= (filters.ackMin||0))
    .filter(s => needle === '' ? true : (s.acronym||'').toLowerCase().includes(needle) || (s.name||'').toLowerCase().includes(needle) || (s.territory||'').toLowerCase().includes(needle));

  const KINDS = [
    {k:'all', l:'All'},
    {k:'PRO', l:'Performing'},
    {k:'MRO', l:'Mechanical'},
    {k:'NRO', l:'Neighboring'},
    {k:'CMO', l:'Collective Mgmt.'},
    {k:'HUB', l:'Hubs / Admins'},
  ];

  const ackTone = (n) => n >= 99 ? '#0f6e3a' : n >= 95 ? '#0a4a8c' : n >= 90 ? '#9b6a18' : '#a04432';

  // Per-society derived metrics (deterministic from acronym + members so they're stable across renders).
  // Works registered: 0.4–1.6x members, scaled down a touch for HUB/NRO. Royalties: $/year, scaled with members + ack quality.
  const hash = (s) => { let h=0; for (let i=0;i<s.length;i++) h=(h*31+s.charCodeAt(i))>>>0; return h; };
  const worksFor = (s) => {
    const base = s.members || 1000;
    const factor = s.kind === 'HUB' ? 0.15 : s.kind === 'NRO' ? 0.6 : 1.1;
    const jitter = 0.6 + ((hash(s.acronym) % 1000) / 1000) * 1.2;  // 0.6–1.8
    return Math.round(base * factor * jitter);
  };
  const royaltiesFor = (s) => {
    const base = (s.members || 1000) * (s.kind === 'NRO' ? 38 : s.kind === 'MRO' ? 14 : s.kind === 'HUB' ? 22 : 28);
    const quality = (s.ackRate || 90) / 100;
    const jitter = 0.7 + ((hash(s.acronym) % 1000) / 1000) * 0.8;  // 0.7–1.5
    const v = Math.round(base * quality * jitter);
    if (v >= 1_000_000_000) return `$${(v/1_000_000_000).toFixed(2)}B`;
    if (v >= 1_000_000)     return `$${(v/1_000_000).toFixed(1)}M`;
    if (v >= 1_000)         return `$${(v/1_000).toFixed(0)}K`;
    return `$${v}`;
  };

  return (
    <div>
      <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',padding:'14px 0 0',lineHeight:1.55,maxWidth:780}}>
        Rights societies that collect and distribute royalties on behalf of members.
        <span style={{display:'inline-flex',gap:14,marginLeft:14,flexWrap:'wrap'}}>
          <span><span style={{display:'inline-block',width:6,height:6,background:'#0a4a8c',marginRight:5,verticalAlign:'middle'}}/>PRO — performing rights for songs</span>
          <span><span style={{display:'inline-block',width:6,height:6,background:'#9b6a18',marginRight:5,verticalAlign:'middle'}}/>MRO — mechanical rights for songs</span>
          <span><span style={{display:'inline-block',width:6,height:6,background:'#0f6e3a',marginRight:5,verticalAlign:'middle'}}/>NRO — neighboring rights for sound recordings</span>
          <span><span style={{display:'inline-block',width:6,height:6,background:'#5a3d6e',marginRight:5,verticalAlign:'middle'}}/>CMO — combined / collective management</span>
          <span><span style={{display:'inline-block',width:6,height:6,background:'#a04432',marginRight:5,verticalAlign:'middle'}}/>HUB — multi-society admin or licensing hub</span>
        </span>
      </div>
      <div style={{display:'flex',alignItems:'center',justifyContent:'flex-start',padding:'14px 0',borderBottom:'1px solid var(--rule)',gap:14,flexWrap:'wrap'}}>
        <div style={{display:'flex',gap:0,alignItems:'center',border:'1px solid var(--rule)'}}>
          {KINDS.map(({k,l})=>(
            <button key={k} onClick={()=>setFilterKind(k)} className="ff-mono upper"
              style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,
                background: filterKind===k ? 'var(--ink)' : 'transparent',
                color: filterKind===k ? 'var(--bg)' : 'var(--ink-2)',
                border:0,borderRight:'1px solid var(--rule)',cursor:'pointer'}}>
              {l}
            </button>
          ))}
        </div>
      </div>

      <div style={{borderLeft:'1px solid var(--rule)',borderTop:'1px solid var(--rule)'}}>
        <div className="ff-mono upper" style={{display:'grid',gridTemplateColumns:'120px 1fr 140px 140px',gap:0,fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',fontWeight:500,borderBottom:'1px solid var(--rule)',background:'var(--bg-2)'}}>
          <div style={{padding:'12px 14px',borderRight:'1px solid var(--rule)'}}>CODE</div>
          <div style={{padding:'12px 14px',borderRight:'1px solid var(--rule)'}}>NAME · TERRITORY</div>
          <div style={{padding:'12px 14px',borderRight:'1px solid var(--rule)',textAlign:'right'}}>WORKS REGISTERED</div>
          <div style={{padding:'12px 14px',borderRight:'1px solid var(--rule)',textAlign:'right'}}>ROYALTIES YTD</div>
        </div>
        {filtered.length === 0 && (
          <div className="ff-mono" style={{padding:'40px 14px',fontSize:12,color:'var(--ink-3)',fontStyle:'italic',borderRight:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)',textAlign:'center'}}>
            No societies match the current filter.
          </div>
        )}
        {filtered.map((s, i) => (
          <button key={s.acronym}
            data-society-row={s.acronym}
            onClick={()=>{ const go = window.__astroGo; if (go) go('society', {acronym: s.acronym}); }}
            style={{display:'grid',gridTemplateColumns:'120px 1fr 140px 140px',gap:0,width:'100%',background: highlight === s.acronym ? 'var(--accent)' : 'transparent',border:0,borderBottom:'1px solid var(--rule)',cursor:'pointer',textAlign:'left',padding:0,fontFamily:'inherit',color:'inherit',transition:'background .4s ease'}}
            onMouseEnter={e=>{ if (highlight !== s.acronym) e.currentTarget.style.background='var(--bg-2)'; }}
            onMouseLeave={e=>{ if (highlight !== s.acronym) e.currentTarget.style.background='transparent'; }}>
            <div style={{padding:'14px',borderRight:'1px solid var(--rule)',display:'flex',flexDirection:'column',alignItems:'flex-start',gap:6,minWidth:0}}>
              <span className="ff-mono" style={{fontSize:13,fontWeight:600,letterSpacing:'.02em',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',maxWidth:'100%'}}>{s.acronym}</span>
              <span className="ff-mono upper" style={{display:'inline-flex',alignItems:'center',gap:5,fontSize:9,letterSpacing:'.1em',padding:'2px 6px',background:'var(--bg-2)',color:'var(--ink-2)',fontWeight:600}}>
                <span style={{display:'inline-block',width:6,height:6,background: s.kind === 'PRO' ? '#0a4a8c' : s.kind === 'MRO' ? '#9b6a18' : s.kind === 'NRO' ? '#0f6e3a' : s.kind === 'HUB' ? '#a04432' : '#5a3d6e'}}/>
                {s.kind}
              </span>
            </div>
            <div style={{padding:'14px',borderRight:'1px solid var(--rule)',minWidth:0}}>
              <div style={{fontSize:13,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{s.name}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{s.country} · {s.territory}</div>
            </div>
            <div className="ff-mono num" style={{padding:'14px',borderRight:'1px solid var(--rule)',textAlign:'right',fontSize:13,color:'var(--ink)'}}>
              {worksFor(s).toLocaleString()}
            </div>
            <div className="ff-mono num" style={{padding:'14px',borderRight:'1px solid var(--rule)',textAlign:'right',fontSize:13,fontWeight:500,color:'var(--ink)'}}>
              {royaltiesFor(s)}
            </div>
          </button>
        ))}
      </div>
    </div>
  );
}

function AddSocietyModal({ onClose, initial }) {
  const isEdit = !!initial;

  // Seed contactEntries from legacy flat email/website/address fields when
  // entering edit mode. The detail page passes these in `initial` from
  // SOCIETY_EMAILS / SOCIETY_URLS so the modal must mirror them visibly.
  const seedContactEntries = React.useMemo(() => {
    if (!initial) return [];
    if (Array.isArray(initial.contactEntries) && initial.contactEntries.length) {
      return initial.contactEntries;
    }
    const list = [];
    if (initial.email)   list.push({ contact_type: 'email',   label_code: 'GENERAL', value: initial.email });
    if (initial.website) list.push({ contact_type: 'website', label_code: 'GENERAL', value: initial.website });
    if (initial.address) list.push({ contact_type: 'address', label_code: 'GENERAL', line1: initial.address, line2:'', city:'', state:'', postal_code:'', country_iso: initial.country || '' });
    return list;
  }, [initial]);

  const [form, setForm] = React.useState({
    acronym:'', name:'', kind:'PRO', cwrVersions:['CWR 2.1'],
    cisac:'', website:'',
    ...(initial || {}),
    contactEntries: seedContactEntries,
  });
  const set = (k,v) => setForm(f => ({...f, [k]:v}));
  const save = () => {
    if (!form.acronym || !form.name) return;
    const msg = isEdit
      ? `${form.acronym} updated`
      : `${form.acronym} added to societies directory · pending CWR pairing`;
    window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg,tone:'ok'}}));
    onClose();
  };
  const renderField = (k, l, ph, w, type, hint) => (
    <label key={k} style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:hint?'flex-start':'center',gap:14}}>
      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500,paddingTop:hint?8:0}}>{l}</span>
      <div style={{display:'flex',flexDirection:'column',gap:4}}>
        <input value={form[k]||''} onChange={e=>set(k, e.target.value)} placeholder={ph} type={type||'text'}
          style={{padding:'8px 10px',fontSize:13,fontFamily:'inherit',border:'1px solid var(--rule)',background:'var(--bg-2)',outline:'none',color:'var(--ink)',width: w || '100%'}}/>
        {hint && <span className="ff-mono" style={{fontSize:9,color:'var(--ink-4)',lineHeight:1.4}}>{hint}</span>}
      </div>
    </label>
  );

  // Country/territory selects backed by ref data when available. Storing the
  // ISO-2 for country and the common_name for territory keeps the existing
  // data shape (legacy free-text values like "FR" / "France" still render).
  const refReady = (typeof useRefReady === 'function') ? useRefReady() : !!(window.REF && window.REF.ready);
  const renderCountry = () => {
    const territories = refReady ? (window.REF.raw.territories || []) : [];
    if (!territories.length) return renderField('country', 'Country', 'ISO 3166 (e.g. FR)', 120);
    const sortFn = window.sortCountriesPinned || ((arr) => arr.slice().sort((a,b) => String(a.common_name||'').localeCompare(String(b.common_name||''))));
    const PIN = window.COUNTRY_PIN || [];
    const opts = sortFn(territories.filter(t => t.iso_alpha_2 && t.territory_type_id === 2));
    const lastPinIdx = opts.reduce((acc, t, i) => (PIN.includes(t.iso_alpha_2) ? i : acc), -1);
    const has = opts.some(o => o.iso_alpha_2 === form.country);
    return (
      <label style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:'center',gap:14}}>
        <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>Country</span>
        <select value={form.country || ''} onChange={e=>{
            set('country', e.target.value);
            // Auto-add the picked country to territories if it's empty.
            if (!(form.territories||[]).length && e.target.value) {
              set('territories', [e.target.value]);
            }
          }}
          style={{padding:'8px 10px',fontSize:13,fontFamily:'inherit',border:'1px solid var(--rule)',background:'var(--bg-2)',outline:'none',color:'var(--ink)',width:240}}>
          <option value="">—</option>
          {!has && form.country && <option value={form.country}>{form.country}</option>}
          {opts.map((t, i) => (
            <React.Fragment key={t.iso_alpha_2}>
              <option value={t.iso_alpha_2}>
                {t.emoji_flag ? `${t.emoji_flag}  ` : ''}{t.iso_alpha_2} · {t.common_name}
              </option>
              {i === lastPinIdx && (
                <option disabled value="__sep__">──────────</option>
              )}
            </React.Fragment>
          ))}
        </select>
      </label>
    );
  };

  // ── Territories multi-select ──
  // Picks any number of ref.territories (countries + dependencies + supra-national).
  // Also surfaces ref.territory_groups (Worldwide, EU, NA, etc.) as one-click presets
  // that expand into the underlying territory_codes (ISO-3166 alpha-2).
  const [terrQuery, setTerrQuery] = React.useState('');
  const [terrOpen, setTerrOpen]   = React.useState(false);
  const renderTerritories = () => {
    const territories = refReady ? (window.REF.raw.territories || []) : [];
    const groups      = refReady ? (window.REF.raw.territory_groups || []) : [];
    if (!territories.length) {
      // Graceful fallback — free text comma-separated until ref data loads.
      return (
        <label style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:'center',gap:14}}>
          <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>Territories</span>
          <input value={(form.territories||[]).join(', ')}
            onChange={e=>set('territories', e.target.value.split(',').map(s=>s.trim()).filter(Boolean))}
            placeholder="e.g. FR, BE, LU"
            style={{padding:'8px 10px',fontSize:13,fontFamily:'inherit',border:'1px solid var(--rule)',background:'var(--bg-2)',outline:'none',color:'var(--ink)'}}/>
        </label>
      );
    }
    const selected = form.territories || [];
    const byIso = new Map(territories.map(t => [t.iso_alpha_2, t]));
    const q = terrQuery.trim().toLowerCase();
    const matches = q
      ? territories
          .filter(t => t.iso_alpha_2 && !selected.includes(t.iso_alpha_2))
          .filter(t =>
            (t.iso_alpha_2 || '').toLowerCase().includes(q) ||
            (t.common_name || '').toLowerCase().includes(q) ||
            (t.iso_alpha_3 || '').toLowerCase().includes(q)
          )
          .slice(0, 12)
      : [];
    const addOne = (iso) => {
      if (!iso) return;
      if (!selected.includes(iso)) set('territories', [...selected, iso]);
      setTerrQuery('');
    };
    const removeOne = (iso) => set('territories', selected.filter(x => x !== iso));
    const addGroup = (g) => {
      const codes = g.territory_codes || [];
      const merged = Array.from(new Set([...selected, ...codes.filter(c => byIso.has(c))]));
      set('territories', merged);
    };

    return (
      <label style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:'flex-start',gap:14,paddingTop:4}}>
        <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500,paddingTop:8}}>Territories</span>
        <div style={{display:'flex',flexDirection:'column',gap:8,minWidth:0}}>
          {/* Selected chips */}
          <div style={{display:'flex',flexWrap:'wrap',gap:4,minHeight:32,padding:'4px 6px',border:'1px solid var(--rule)',background:'var(--bg-2)',alignItems:'center'}}>
            {selected.length === 0 && (
              <span className="ff-mono" style={{fontSize:11,color:'var(--ink-4)',padding:'4px 4px'}}>No territories selected</span>
            )}
            {selected.map(iso => {
              const t = byIso.get(iso);
              return (
                <span key={iso} className="ff-mono" style={{display:'inline-flex',alignItems:'center',gap:4,padding:'3px 4px 3px 7px',background:'var(--ink)',color:'var(--bg)',fontSize:11,letterSpacing:'.02em'}}>
                  {t && t.emoji_flag ? <span>{t.emoji_flag}</span> : null}
                  <span>{iso}</span>
                  {t && t.common_name && t.common_name !== iso && (
                    <span style={{opacity:.7,fontSize:10}}>· {t.common_name}</span>
                  )}
                  <button type="button" onClick={()=>removeOne(iso)} aria-label={`Remove ${iso}`}
                    style={{background:'transparent',border:0,color:'var(--bg)',cursor:'pointer',padding:'0 2px',fontSize:12,lineHeight:1,opacity:.7}}>
                    ✕
                  </button>
                </span>
              );
            })}
            <input value={terrQuery}
              onChange={e=>{setTerrQuery(e.target.value); setTerrOpen(true);}}
              onFocus={()=>setTerrOpen(true)}
              onBlur={()=>setTimeout(()=>setTerrOpen(false), 150)}
              onKeyDown={e=>{
                if (e.key === 'Enter' && matches[0]) { e.preventDefault(); addOne(matches[0].iso_alpha_2); }
                if (e.key === 'Backspace' && !terrQuery && selected.length) {
                  removeOne(selected[selected.length - 1]);
                }
              }}
              placeholder={selected.length ? 'Add more…' : 'Search ISO code or country…'}
              style={{flex:'1 1 120px',minWidth:80,border:0,background:'transparent',outline:'none',padding:'4px 4px',fontSize:12,fontFamily:'inherit'}}/>
          </div>

          {/* Search results popover */}
          {terrOpen && matches.length > 0 && (
            <div style={{position:'relative'}}>
              <div style={{position:'absolute',top:0,left:0,right:0,zIndex:5,background:'var(--bg)',border:'1px solid var(--rule)',maxHeight:260,overflowY:'auto'}}>
                {matches.map(t => (
                  <button key={t.iso_alpha_2} type="button"
                    onMouseDown={e=>{e.preventDefault(); addOne(t.iso_alpha_2);}}
                    className="ff-mono"
                    style={{display:'flex',alignItems:'center',gap:8,width:'100%',padding:'7px 10px',background:'transparent',border:0,borderBottom:'1px solid var(--rule-soft)',cursor:'pointer',fontSize:12,textAlign:'left',color:'var(--ink)'}}
                    onMouseEnter={e=>{e.currentTarget.style.background='var(--bg-2)';}}
                    onMouseLeave={e=>{e.currentTarget.style.background='transparent';}}>
                    {t.emoji_flag && <span>{t.emoji_flag}</span>}
                    <span style={{minWidth:32,color:'var(--ink-3)'}}>{t.iso_alpha_2}</span>
                    <span>{t.common_name}</span>
                  </button>
                ))}
              </div>
            </div>
          )}

          {/* Group presets */}
          {groups.length > 0 && (
            <div style={{display:'flex',flexWrap:'wrap',gap:4,alignItems:'center'}}>
              <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-4)',marginRight:4}}>Add group:</span>
              {groups.filter(g => g.is_active !== false).slice(0, 10).map(g => (
                <button key={g.id || g.group_code || g.group_name} type="button" onClick={()=>addGroup(g)}
                  className="ff-mono"
                  style={{padding:'3px 8px',fontSize:10,letterSpacing:'.04em',border:'1px solid var(--rule)',background:'var(--bg-2)',color:'var(--ink-2)',cursor:'pointer'}}
                  title={`Add ${(g.territory_codes||[]).length} territories`}>
                  + {g.group_name}
                </button>
              ))}
              {selected.length > 0 && (
                <button type="button" onClick={()=>set('territories', [])}
                  className="ff-mono"
                  style={{padding:'3px 8px',fontSize:10,letterSpacing:'.04em',border:'1px solid var(--rule)',background:'transparent',color:'var(--ink-3)',cursor:'pointer',marginLeft:'auto'}}>
                  Clear all
                </button>
              )}
            </div>
          )}
        </div>
      </label>
    );
  };
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.4)',zIndex:1000,display:'flex',alignItems:'center',justifyContent:'center',padding:24}}>
      <div onClick={e=>e.stopPropagation()} style={{background:'var(--bg)',width:'min(720px,100%)',border:'1px solid var(--rule)',padding:0,maxHeight:'90vh',display:'flex',flexDirection:'column',minHeight:0}}>
        <div style={{padding:'18px 22px',borderBottom:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center',flexShrink:0,background:'var(--bg)'}}>
          <div>
            <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em'}}>DIRECTORY · SOCIETY</div>
            <div className="heading-swap ff-display" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.03em',marginTop:4}}>{isEdit ? `Edit ${initial.acronym}` : 'Add a society'}</div>
          </div>
          <button onClick={onClose} className="ff-mono" style={{background:'transparent',border:0,fontSize:18,cursor:'pointer',color:'var(--ink-3)',padding:4}}>✕</button>
        </div>
        <div style={{padding:'22px',display:'grid',gap:14,overflowY:'auto',flex:'1 1 auto',minHeight:0}}>
          <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',lineHeight:1.5}}>
            {isEdit
              ? 'Editing this society updates its CWR routing, contact info, and registration metadata.'
              : 'Adding a society makes it selectable as a target for CWR registrations and royalty matching. CWR file format compatibility determines which works can be sent.'}
          </div>

          {/* IDENTITY */}
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.14em',color:'var(--ink-3)',fontWeight:600,paddingTop:6,borderTop:'1px solid var(--rule-soft)'}}>Identity</div>
          {renderField('acronym', 'Acronym', 'e.g. SACEM', 160)}
          {renderField('name', 'Full name', 'e.g. Société des Auteurs, Compositeurs et Éditeurs de Musique')}
          {renderField('cisac', 'CISAC code', 'e.g. 058', 120, 'text', '3-digit CISAC society code (e.g. 061 ASCAP · 021 BMI · 052 PRS · 058 SACEM)')}

          <label style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:'center',gap:14}}>
            <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>Kind</span>
            <div style={{display:'flex',gap:0,border:'1px solid var(--rule)',width:'fit-content'}}>
              {['PRO','MRO','NRO','CMO','HUB'].map(k=>(
                <button key={k} onClick={()=>set('kind',k)} className="ff-mono upper"
                  style={{padding:'7px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,
                    background: form.kind===k ? 'var(--ink)' : 'transparent',
                    color: form.kind===k ? 'var(--bg)' : 'var(--ink-2)',
                    border:0,borderRight:k!=='HUB'?'1px solid var(--rule)':'0',cursor:'pointer'}}>
                  {k}
                </button>
              ))}
            </div>
          </label>
          <label style={{display:'grid',gridTemplateColumns:'120px 1fr',alignItems:'center',gap:14}}>
            <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>CWR support</span>
            <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
              {['CWR 2.1','CWR 2.2','CWR 3.0','CWR 3.1'].map(v => {
                const on = (form.cwrVersions||[]).includes(v);
                const noneOn = (form.cwrVersions||[]).includes('None');
                return (
                  <button key={v} type="button" className="ff-mono"
                    onClick={()=>{
                      const cur = (form.cwrVersions||[]).filter(x=>x!=='None');
                      const next = cur.includes(v) ? cur.filter(x=>x!==v) : [...cur, v];
                      set('cwrVersions', next.length ? next : ['None']);
                    }}
                    style={{
                      padding:'6px 10px',fontSize:12,
                      border:'1px solid '+(on && !noneOn ? 'var(--ink)' : 'var(--rule)'),
                      background: on && !noneOn ? 'var(--ink)' : 'var(--bg-2)',
                      color: on && !noneOn ? 'var(--bg)' : 'var(--ink-2)',
                      cursor:'pointer',
                    }}>
                    {on && !noneOn ? '\u2713 ' : ''}{v}
                  </button>
                );
              })}
              <button type="button" className="ff-mono"
                onClick={()=>set('cwrVersions', ['None'])}
                style={{
                  padding:'6px 10px',fontSize:12,
                  border:'1px solid '+((form.cwrVersions||[]).includes('None') ? 'var(--ink)' : 'var(--rule)'),
                  background:(form.cwrVersions||[]).includes('None') ? 'var(--ink)' : 'var(--bg-2)',
                  color:(form.cwrVersions||[]).includes('None') ? 'var(--bg)' : 'var(--ink-3)',
                  cursor:'pointer',marginLeft:6,
                }}>
                None — manual only
              </button>
            </div>
          </label>

          {/* CONTACT — structured, schema-aligned (ref.contact_labels + ref.territories) */}
          <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.14em',color:'var(--ink-3)',fontWeight:600,paddingTop:14,borderTop:'1px solid var(--rule-soft)',marginTop:6}}>Contact information</div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-4)',lineHeight:1.5,marginTop:-4}}>
            One row per channel. Add multiple emails or phones with role tags (Royalties, Sync, Legal, etc.) — they'll persist as <span style={{fontFamily:'var(--ff-mono, monospace)'}}>contact_entries</span> rows linked to this society.
          </div>
          {window.ContactInfoSection ? (
            <window.ContactInfoSection
              value={form.contactEntries}
              onChange={list => set('contactEntries', list)}
              defaultExpanded={!isEdit}
            />
          ) : (
            <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>Loading contact form…</div>
          )}
        </div>
        <div style={{padding:'14px 22px',borderTop:'1px solid var(--rule)',display:'flex',justifyContent:'flex-end',gap:8,background:'var(--bg-2)',flexShrink:0}}>
          <button onClick={onClose} className="ff-mono upper" style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'transparent',border:'1px solid var(--rule)',cursor:'pointer',color:'var(--ink-2)'}}>Cancel</button>
          <button onClick={save} className="ff-mono upper" style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'var(--ink)',color:'var(--bg)',border:0,cursor: form.acronym && form.name ? 'pointer' : 'not-allowed', opacity: form.acronym && form.name ? 1 : 0.5}}>{isEdit ? 'Save changes' : 'Add society'}</button>
        </div>
      </div>
    </div>
  );
}

// Add a profile / publisher / label.
// Mutates the underlying source array (ARTISTS / PUB / LABELS) directly so the new
// record shows up in the directory grid without a global state store. The full
// metadata surface (identity, distribution, NROs, contact, logo) is laid out in
// collapsible sections so users can add as much as they have on hand without
// leaving the modal — required fields are visible by default, optional sections
// open on click.
function AddRecordModal({ kind, onClose, onAdded }) {
  const TITLES = { profile:'Add a profile', publisher:'Add a publisher', label:'Add a label' };
  const HEADERS = { profile:'DIRECTORY · PROFILE', publisher:'DIRECTORY · PUBLISHER', label:'DIRECTORY · LABEL' };
  const HINTS = {
    profile: 'Profiles represent songwriters, artists, and rights-bearing individuals. Identity is required; everything else is optional and can be filled in later from the profile.',
    publisher: 'Add identity now; expand sections below to capture splits, deals, contact, and pairings upfront — or skip and edit from the publisher drawer later.',
    label: 'Add identity now; expand sections below to capture distribution, NROs, contact, and logo upfront — or skip and edit from the label drawer later.',
  };

  // Existing labels — for the parent combobox on label / publisher forms.
  const allLabels = (typeof LABELS !== 'undefined') ? LABELS : [];
  const parentOptions = Array.from(new Set([
    ...allLabels.map(x => x.parent).filter(Boolean),
    ...allLabels.map(x => x.name),
  ])).sort();
  // Existing publishers — for the parent-publisher and administrator comboboxes.
  const allPubs = (typeof PUBLISHERS !== 'undefined') ? PUBLISHERS : ((typeof window !== 'undefined' && window.__PUBLISHERS) || []);
  const pubOptions = Array.from(new Set([
    ...allPubs.map(x => x.name).filter(Boolean),
    ...allPubs.map(x => x.parent).filter(p => p && p !== '—'),
  ])).sort();
  // Administrator option list — same as pubOptions but with "Self" prepended
  // (a publisher administering its own catalog is the most common pattern).
  const adminOptions = ['Self', ...pubOptions];

  // ── ID formatters ────────────────────────────────────────────────────────
  // ISNI: strip non-digits/X, group in 4s, space-separated → "0000 0005 1746 0975"
  const formatISNI = (raw) => {
    const clean = (raw || '').replace(/[^\dX]/gi, '').toUpperCase().slice(0,16);
    return clean.replace(/(.{4})(?=.)/g,'$1 ').trim();
  };
  // DPID: enforce "PA-DPIDA-{10 digits}-{1 char}" structure, hyphen-separated.
  // Accept input as raw chars; auto-prefix and segment.
  const formatDPID = (raw) => {
    if (!raw) return '';
    let s = raw.toUpperCase().replace(/[^A-Z0-9-]/g,'');
    // Strip existing hyphens to canonicalize, then re-insert.
    const naked = s.replace(/-/g,'');
    if (!naked) return '';
    // If user types "PA…", treat as PA-DPIDA-{digits}-{char}.
    // We always produce: PA-DPIDA-<10 digits>-<1 char>
    let body = naked;
    if (body.startsWith('PADPIDA')) body = body.slice(7);
    else if (body.startsWith('PA')) body = body.slice(2);
    // Now `body` should be the digit run (+ optional trailing letter)
    const digits = body.replace(/[^\d]/g,'').slice(0,10);
    const trailing = body.slice(digits.length).replace(/[^A-Z]/g,'').slice(0,1);
    let out = 'PA-DPIDA';
    if (digits.length) out += '-' + digits;
    if (trailing) out += '-' + trailing;
    return out;
  };


  const NRO_CATALOG = [
    {org:'SoundExchange',     territory:'United States',  iso:'US'},
    {org:'PPL',               territory:'United Kingdom', iso:'GB'},
    {org:'GVL',               territory:'Germany',        iso:'DE'},
    {org:'SCPP',              territory:'France',         iso:'FR'},
    {org:'SCPF',              territory:'France',         iso:'FR'},
    {org:'SENA',              territory:'Netherlands',    iso:'NL'},
    {org:'GeidankyoCPRA',     territory:'Japan',          iso:'JP'},
    {org:'ABRAMUS',           territory:'Brazil',         iso:'BR'},
    {org:'Re:Sound',          territory:'Canada',         iso:'CA'},
    {org:'PPCA',              territory:'Australia',      iso:'AU'},
    {org:'AGEDI',             territory:'Spain',          iso:'ES'},
    {org:'SCF',               territory:'Italy',          iso:'IT'},
    {org:'STEF',              territory:'Iceland',        iso:'IS'},
    {org:'GRAMEX',            territory:'Denmark',        iso:'DK'},
    {org:'IFPI',              territory:'International',  iso:'WW'},
  ];
  const TERRITORY_OPTIONS = ['United States','United Kingdom','Germany','France','Netherlands','Japan','Brazil','Canada','Australia','Spain','Italy','Iceland','Denmark','Sweden','Norway','Finland','Belgium','Ireland','Mexico','Argentina','South Korea','International'];

  const initial = kind==='profile'
    ? {
        // identity
        name:'', type:'Person', pro:'', country:'US', ipi:'', isni:'', dpid:'',
        // identity expanded
        realName:'', firstName:'', middleName:'', lastName:'', aliases:[], dob:'', nationality:'',
        // identifiers expanded
        ipiBase:'', musicBrainzId:'', wikidataId:'', spotifyUri:'', appleId:'', discogsId:'',
        // contact (optional)
        email:'', phone:'', address:'',
        // pro membership detail (optional)
        memberSince:'', altPros:'',
        // multi-society affiliations (optional, additive)
        affiliations:[],
        // publisher relationship (optional)
        publisher:'', publisherType:'', publisherStart:'', publisherEnd:'', publisherTerritory:'', publisherScope:'',
        // group-only (optional, only if type=Group)
        members:[], splits:'equal', formed:'', dissolved:'',
        // representation (optional)
        manager:'', managerCo:'', agent:'', agency:'', lawyer:'', lawFirm:'',
        // tax & payee (optional)
        payee:'', taxId:'', taxForm:'', taxFormExpiry:'', withholding:'', treatyCountry:'', paymentMethod:'',
        nrPerformer:false, neighbouringRightsBody:'',
        // operational (optional)
        tags:'', notes:'',
      }
    : kind==='publisher'
    ? {
        // identity
        name:'', pro:'', territory:'US', cae:'', parent:'', isni:'', dpid:'', submitterCode:'',
        // chain (parent publisher + administrator are distinct from corporate parent)
        parentPub:'', administrator:'',
        // contact (optional)
        contactName:'', contactRole:'', email:'', phone:'',
      }
    : {
        // identity
        name:'', code:'', labelCode:'', parent:'', kind:'indie', isni:'', dpid:'',
        // distribution (optional)
        deals:'Distribution · Worldwide', distributor:'', dealStart:'', status:'Active partner',
        // contact (optional)
        contactName:'', contactRole:'', email:'', phone:'', address:'',
        // NROs (optional) — empty list, user adds rows
        nros:[],
        // logo (optional)
        logoDataUrl:'',
      };
  const [form, setForm] = React.useState(initial);
  const set = (k,v) => setForm(f => ({...f, [k]:v}));

  // Section open/closed state. Identity is implicit (always shown).
  const [open, setOpen] = React.useState({});
  const toggle = (k) => setOpen(o => ({...o, [k]: !o[k]}));

  // Resolve `form.parent` against the existing label directory.
  const linkedParent = allLabels.find(x => x.name.toLowerCase() === (form.parent||'').trim().toLowerCase());
  const trimmedParent = (form.parent||'').trim();
  const parentIsNew = trimmedParent && !linkedParent && !parentOptions.some(o => o.toLowerCase() === trimmedParent.toLowerCase());

  // Resolve `form.parentPub` (corporate parent publisher) against existing publishers.
  const trimmedParentPub = (form.parentPub||'').trim();
  const linkedParentPub = trimmedParentPub
    ? allPubs.find(x => x.name.toLowerCase() === trimmedParentPub.toLowerCase())
    : null;
  const parentPubIsNew = trimmedParentPub && !linkedParentPub
    && !pubOptions.some(o => o.toLowerCase() === trimmedParentPub.toLowerCase());

  // Resolve `form.administrator`. "Self" / blank / matching the publisher's own
  // name → self-administered. Otherwise look up an existing publisher record.
  const trimmedAdmin = (form.administrator||'').trim();
  const adminIsSelf = !trimmedAdmin || /^self$/i.test(trimmedAdmin)
    || (trimmedAdmin && form.name && trimmedAdmin.toLowerCase() === form.name.trim().toLowerCase());
  const linkedAdmin = !adminIsSelf
    ? allPubs.find(x => x.name.toLowerCase() === trimmedAdmin.toLowerCase())
    : null;
  const adminIsNew = !adminIsSelf && !linkedAdmin
    && !pubOptions.some(o => o.toLowerCase() === trimmedAdmin.toLowerCase());

  const ready = (() => {
    if (kind==='profile')   return form.name.trim() && form.ipi.trim();
    if (kind==='publisher') return form.name.trim() && form.cae.trim();
    if (kind==='label')     return form.name.trim() && form.code.trim();
    return false;
  })();

  // If the user typed a parent that isn't a known label, create a stub label record
  // for it first so the new record links cleanly. The stub is marked unverified.
  const ensureParentLabel = () => {
    if (kind === 'profile') return null;
    if (!trimmedParent || linkedParent) return linkedParent || null;
    // Don't create a stub for the catch-all "Independent" sentinel.
    if (/^independent$/i.test(trimmedParent)) return null;
    if (typeof LABELS === 'undefined') return null;
    const palette = ['#0f1f4f','#1a1a1a','#5a3d1c','#b04a3a','#a02a4d','#000','#d4a02a','#0f4c2a','#3a4a5a'];
    const stub = {
      id: `lb_${Math.random().toString(36).slice(2,6)}`,
      name: trimmedParent,
      code: trimmedParent.replace(/[^A-Za-z]/g,'').slice(0,3).toUpperCase() || 'NEW',
      parent: 'Independent',
      kind: 'indie',
      releases: 0, artists: 0,
      deals: '—',
      color: palette[Math.floor(Math.random()*palette.length)],
    };
    LABELS.unshift(stub);
    return stub;
  };

  // Create a stub publisher record for a free-typed parent-publisher or admin
  // value so the link resolves immediately. Returns the existing record if the
  // name already matches, null if input is empty / "Self" / sentinel.
  const ensureStubPublisher = (rawName) => {
    const t = (rawName || '').trim();
    if (!t || /^self$/i.test(t)) return null;
    if (typeof PUBLISHERS === 'undefined' && !(window.__PUBLISHERS)) return null;
    const list = (typeof PUBLISHERS !== 'undefined') ? PUBLISHERS : window.__PUBLISHERS;
    const existing = list.find(x => x.name.toLowerCase() === t.toLowerCase());
    if (existing) return existing;
    const stub = {
      id: `pb_${Math.random().toString(36).slice(2,6)}`,
      name: t,
      aliases: '—',
      cae: '',                              // unverified
      pro: '—',
      territory: 'Worldwide',
      works: 0,
      parent: 'Independent',
      since: new Date().toISOString().slice(0,4),
      _stub: true,
    };
    list.unshift(stub);
    return stub;
  };

  // Counts of optional fields the user has actually filled in — for the recap line.
  const optionalFilled = (() => {
    let n = 0;
    if (kind === 'profile') {
      if (form.email.trim()) n++;
      if (form.phone.trim()) n++;
      if (form.memberSince.trim()) n++;
      if (form.altPros.trim()) n++;
    } else if (kind === 'publisher') {
      if (form.contactName.trim()) n++;
      if (form.email.trim()) n++;
      if (form.phone.trim()) n++;
    } else if (kind === 'label') {
      if (form.deals && form.deals !== 'Distribution · Worldwide') n++;
      if (form.distributor.trim()) n++;
      if (form.dealStart.trim()) n++;
      if (form.contactName.trim()) n++;
      if (form.email.trim()) n++;
      if (form.phone.trim()) n++;
      if (form.address.trim()) n++;
      if (form.nros.length > 0) n++;
      if (form.logoDataUrl) n++;
    }
    return n;
  })();

  const save = () => {
    if (!ready) return;
    let id, summary;
    const rand = Math.random().toString(36).slice(2,6);
    if (kind === 'profile') {
      id = `ar_${rand}`;
      const rec = {
        id,
        name:form.name.trim(),
        type: form.type || 'Person',
        pro:form.pro,
        country:form.country.toUpperCase().slice(0,2),
        ipi:form.ipi.trim(),
        legal:false,
        since:new Date().toISOString().slice(0,10),
        isni:form.isni.trim() || null,
        dpid:form.dpid.trim() || null,
        email:form.email.trim() || null,
        phone:form.phone.trim() || null,
        memberSince:form.memberSince.trim() || null,
        altPros:form.altPros.trim() || null,
        realName:form.realName.trim() || null,
      };
      if (typeof ARTISTS !== 'undefined') ARTISTS.unshift(rec);
      // Write expanded record into ARTIST_DETAILS so the public profile picks it up.
      const aliasList = Array.isArray(form.aliases)
        ? form.aliases.filter(a => a && a.value && a.value.trim()).map(a => ({value:a.value.trim(), type:a.type||'artist'}))
        : (form.aliases||'').split(',').map(s=>s.trim()).filter(Boolean).map(v=>({value:v,type:'artist'}));
      const tagList = (form.tags||'').split(',').map(s=>s.trim()).filter(Boolean);
      const detail = {
        realName: form.realName.trim() || null,
        firstName: (form.firstName||'').trim() || null,
        middleName: (form.middleName||'').trim() || null,
        lastName: (form.lastName||'').trim() || null,
        aliases: aliasList,
        pronouns: form.pronouns.trim() || null,
        dob: form.dob.trim() || null,
        formed: form.type === 'Group' ? (form.dob.trim() || null) : null,
        nationality: form.nationality.trim() || null,
        ipiBase: form.ipiBase.trim() || null,
        musicBrainzId: form.musicBrainzId.trim() || null,
        wikidataId: form.wikidataId.trim() || null,
        spotifyUri: form.spotifyUri.trim() || null,
        appleId: form.appleId.trim() || null,
        discogsId: form.discogsId.trim() || null,
        affiliations: (form.affiliations||[]).slice(),
        members: form.type === 'Group' ? (form.members||[]).slice() : [],
        splits: form.type === 'Group' ? form.splits : null,
        dissolved: form.type === 'Group' ? (form.dissolved || null) : null,
        publisher: form.publisher.trim() || null,
        publisherType: form.publisherType || null,
        publisherStart: form.publisherStart.trim() || null,
        publisherEnd: form.publisherEnd.trim() || null,
        publisherTerritory: form.publisherTerritory.trim() || null,
        publisherScope: form.publisherScope.trim() || null,
        manager: form.manager.trim() || null,
        managerCo: form.managerCo.trim() || null,
        agent: form.agent.trim() || null,
        agency: form.agency.trim() || null,
        lawyer: form.lawyer.trim() || null,
        lawFirm: form.lawFirm.trim() || null,
        address: form.address.trim() || null,
        payee: form.payee.trim() || null,
        taxId: form.taxId.trim() || null,
        taxForm: form.taxForm || null,
        taxFormExpiry: form.taxFormExpiry.trim() || null,
        withholding: form.withholding.trim() || null,
        treatyCountry: form.treatyCountry.trim() || null,
        paymentMethod: form.paymentMethod.trim() || null,
        nrPerformer: !!form.nrPerformer,
        neighbouringRightsBody: form.neighbouringRightsBody.trim() || null,
        tags: tagList,
        notes: form.notes.trim() || null,
        createdAt: new Date().toISOString().slice(0,10),
        lastVerifiedAt: null,
        conflictsOpen: 0,
        conflictsResolved: 0,
      };
      window.ARTIST_DETAILS = window.ARTIST_DETAILS || {};
      window.ARTIST_DETAILS[id] = detail;
      const extras = optionalFilled ? ` · +${optionalFilled} optional field${optionalFilled>1?'s':''}` : '';
      summary = `${rec.name} added · IPI ${rec.ipi}${extras} · pending verification`;
    } else if (kind === 'publisher') {
      const parentStub = ensureParentLabel();
      // Resolve parent-publisher and administrator chain. Stubs go in BEFORE
      // we insert the new record so the chain references resolve cleanly.
      const parentPubRec = linkedParentPub
        || (parentPubIsNew ? ensureStubPublisher(trimmedParentPub) : null);
      const adminRec = adminIsSelf
        ? null
        : (linkedAdmin || (adminIsNew ? ensureStubPublisher(trimmedAdmin) : null));
      id = `pb_${rand}`;
      const rec = {
        id,
        name:form.name.trim(), pro:form.pro, territory:form.territory, cae:form.cae.trim(),
        aliases:'—',
        parent: trimmedParent || 'Independent',
        // chain — store both the display name (for the grid/drawer) and a
        // ref id (for click-through navigation when present).
        parentPub:    parentPubRec ? parentPubRec.name : null,
        parentPubId:  parentPubRec ? parentPubRec.id   : null,
        administrator:   adminIsSelf ? 'Self' : (adminRec ? adminRec.name : trimmedAdmin || 'Self'),
        administratorId: adminRec ? adminRec.id : null,
        works:0,
        since:new Date().toISOString().slice(0,10),
        isni:form.isni.trim() || null,
        dpid:form.dpid.trim() || null,
        submitterCode:(form.submitterCode||'').trim().toUpperCase() || null,
        contact: (form.contactName||form.email||form.phone) ? {
          name:form.contactName.trim()||null,
          role:form.contactRole.trim()||null,
          email:form.email.trim()||null,
          phone:form.phone.trim()||null,
        } : null,
      };
      if (typeof PUBLISHERS !== 'undefined') PUBLISHERS.unshift(rec);
      else if (typeof window !== 'undefined' && window.__PUBLISHERS) window.__PUBLISHERS.unshift(rec);
      const linkBit = parentStub ? ` · linked → ${parentStub.code}` : (linkedParent ? ` · linked → ${linkedParent.code}` : '');
      const chainBits = [];
      if (parentPubRec) chainBits.push(`parent pub → ${parentPubRec.name}${parentPubRec._stub ? ' (stub)' : ''}`);
      if (adminRec)     chainBits.push(`admin → ${adminRec.name}${adminRec._stub ? ' (stub)' : ''}`);
      else if (adminIsSelf) chainBits.push('self-admin');
      const chainBit = chainBits.length ? ` · ${chainBits.join(' · ')}` : '';
      const extras = optionalFilled ? ` · +${optionalFilled} optional field${optionalFilled>1?'s':''}` : '';
      summary = `${rec.name} added · IPI ${rec.cae}${linkBit}${chainBit}${extras}`;
    } else if (kind === 'label') {
      const parentStub = ensureParentLabel();
      id = `lb_${rand}`;
      const palette = ['#0f1f4f','#1a1a1a','#5a3d1c','#b04a3a','#a02a4d','#000','#d4a02a','#0f4c2a','#3a4a5a'];
      const rec = {
        id,
        name:form.name.trim(), code:form.code.trim().toUpperCase().slice(0,6),
        parent: trimmedParent || 'Independent', kind:form.kind,
        releases:0, artists:0, deals:form.deals,
        color: form.logoDataUrl ? null : palette[Math.floor(Math.random()*palette.length)],
        isni:form.isni.trim() || null,
        dpid:form.dpid.trim() || null,
        labelCode:form.labelCode.trim() || null,
        logo: form.logoDataUrl || null,
        distributor: form.distributor.trim() || null,
        dealStart: form.dealStart.trim() || null,
        status: form.status,
        contact: (form.contactName||form.email||form.phone||form.address) ? {
          name:form.contactName.trim()||null,
          role:form.contactRole.trim()||null,
          email:form.email.trim()||null,
          phone:form.phone.trim()||null,
          address:form.address.trim()||null,
        } : null,
        nros: form.nros.length ? form.nros.map(n => ({org:n.org, territory:n.territory, iso:n.iso, member:'Member', since:''})) : null,
      };
      if (typeof LABELS !== 'undefined') LABELS.unshift(rec);
      const linkBit = parentStub ? ` · linked → ${parentStub.code}` : (linkedParent ? ` · linked → ${linkedParent.code}` : '');
      const extras = optionalFilled ? ` · +${optionalFilled} optional field${optionalFilled>1?'s':''}` : '';
      summary = `${rec.name} added · ${rec.code}${linkBit}${extras}`;
    }
    window.dispatchEvent(new CustomEvent('astro-toast',{detail:{msg:summary,tone:'ok'}}));
    onAdded && onAdded(id);
    onClose();
  };

  // Logo upload handler (label only)
  const onPickLogo = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    const reader = new FileReader();
    reader.onload = (ev) => set('logoDataUrl', ev.target.result);
    reader.readAsDataURL(f);
  };

  const fld = {padding:'8px 10px',fontSize:13,fontFamily:'inherit',border:'1px solid var(--rule)',background:'var(--bg-2)',outline:'none',color:'var(--ink)',width:'100%'};

  // CountryPicker — narrow ISO-2 select, hydrated from ref.territories.
  // Stores iso_alpha_2 (e.g. "US") to match the existing field shape.
  // Falls back to a plain text input if ref data hasn't loaded yet so
  // the modal still works offline.
  const CountryPicker = ({ value, onChange, width=240 }) => {
    const ready = (typeof useRefReady === 'function') ? useRefReady() : !!(window.REF && window.REF.ready);
    const territories = ready ? (window.REF.raw.territories || []) : [];
    if (!territories.length) {
      return (
        <input value={value || ''} onChange={e=>onChange(e.target.value.toUpperCase().slice(0,2))}
          placeholder="US" style={{...fld,width:80}}/>
      );
    }
    const sortFn = window.sortCountriesPinned || ((arr) => arr.slice().sort((a,b) => String(a.common_name||'').localeCompare(String(b.common_name||''))));
    const PIN = window.COUNTRY_PIN || [];
    const opts = sortFn(territories.filter(t => t.iso_alpha_2 && t.territory_type_id === 2));
    const lastPinIdx = opts.reduce((acc, t, i) => (PIN.includes(t.iso_alpha_2) ? i : acc), -1);
    const has = opts.some(o => o.iso_alpha_2 === value);
    return (
      <select value={value || ''} onChange={e=>onChange(e.target.value)} style={{...fld,width}}>
        <option value="">—</option>
        {!has && value && <option value={value}>{value}</option>}
        {opts.map((t, i) => (
          <React.Fragment key={t.iso_alpha_2}>
            <option value={t.iso_alpha_2}>
              {t.emoji_flag ? `${t.emoji_flag}  ` : ''}{t.iso_alpha_2} · {t.common_name}
            </option>
            {i === lastPinIdx && (
              <option disabled value="__sep__">──────────</option>
            )}
          </React.Fragment>
        ))}
      </select>
    );
  };

  const Row = ({label, children, hint}) => (
    <label style={{display:'grid',gridTemplateColumns:'140px 1fr',alignItems:hint?'flex-start':'center',gap:14}}>
      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500,paddingTop:hint?8:0}}>{label}</span>
      <div style={{display:'flex',flexDirection:'column',gap:4,minWidth:0}}>
        {children}
        {hint && <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',lineHeight:1.4}}>{hint}</span>}
      </div>
    </label>
  );

  // Collapsible section with header showing fill state.
  const Section = ({id, title, sub, badge, children}) => {
    const isOpen = !!open[id];
    return (
      <div style={{border:'1px solid var(--rule)',background:'var(--bg)'}}>
        <button type="button" onClick={()=>toggle(id)}
          style={{width:'100%',display:'flex',alignItems:'center',justifyContent:'space-between',gap:10,
            padding:'11px 14px',background: isOpen ? 'var(--bg-2)' : 'transparent',
            border:0,borderBottom: isOpen ? '1px solid var(--rule)' : '0',
            cursor:'pointer',textAlign:'left',color:'var(--ink)',fontFamily:'inherit'}}>
          <div style={{display:'flex',alignItems:'center',gap:10,minWidth:0}}>
            <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',width:10,display:'inline-block'}}>{isOpen?'−':'+'}</span>
            <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',fontWeight:600,color:'var(--ink)'}}>{title}</span>
            {sub && <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{sub}</span>}
          </div>
          {badge && (
            <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',padding:'2px 6px',border:'1px solid var(--rule)',background:'var(--bg)'}}>{badge}</span>
          )}
        </button>
        {isOpen && <div style={{padding:'14px',display:'grid',gap:12}}>{children}</div>}
      </div>
    );
  };

  // Smart parent combobox: datalist + live status hint.
  const ParentCombo = ({placeholder}) => (
    <Row label="Parent" hint={
      linkedParent
        ? <>↳ Linked to <b style={{color:'var(--ink-2)'}}>{linkedParent.code}</b> · {(linkedParent.releases||0).toLocaleString()} releases · {linkedParent.kind === 'major' ? 'Major-owned' : 'Independent'}</>
        : parentIsNew
          ? <>+ <b style={{color:'var(--ink-2)'}}>{trimmedParent}</b> isn't in the directory — a stub label record will be created and linked. You can flesh it out later from the Labels tab.</>
          : <>Type to search existing labels (Sony Music, Beggars Group, Independent…) or enter a new parent name.</>
    }>
      <input value={form.parent} onChange={e=>set('parent', e.target.value)}
        placeholder={placeholder}
        list="add-modal-parent-options"
        style={fld}/>
      <datalist id="add-modal-parent-options">
        {parentOptions.map(o => <option key={o} value={o}/>)}
      </datalist>
    </Row>
  );

  // Parent-publisher and Administrator both pick from the publisher directory
  // with a real linker UI: typeable input → open dropdown of matching pubs
  // (with their IPI / PRO / works) → click to link. Free-typed names that
  // don't match anything trigger an explicit "+ Add as new publisher" row,
  // which creates a stub on save. Administrator additionally has a sticky
  // "Self" pseudo-option at the top (the most common case).
  const PubLinkCombo = ({label, value, onChange, placeholder, allowSelf, hint}) => {
    const [open, setOpen] = React.useState(false);
    const [highlight, setHighlight] = React.useState(0);
    const wrapRef = React.useRef(null);
    const trimmed = (value || '').trim();
    const ql = trimmed.toLowerCase();

    // Resolve current value against the directory.
    const isSelf = allowSelf && (!trimmed || /^self$/i.test(trimmed));
    const linked = !isSelf && trimmed
      ? allPubs.find(x => x.name.toLowerCase() === ql) : null;
    const isNew  = !isSelf && trimmed && !linked;

    // Filtered list of matching publishers.
    const matches = React.useMemo(() => {
      if (!trimmed) return allPubs.slice(0, 8);
      return allPubs.filter(p => {
        const blob = `${p.name} ${p.aliases||''} ${p.parent||''}`.toLowerCase();
        return blob.includes(ql);
      }).slice(0, 8);
    }, [trimmed, allPubs]);

    // Total options shown in the dropdown (including pseudo-rows). Used for
    // keyboard navigation bounds.
    const showSelfRow  = allowSelf && !trimmed;
    const showAddRow   = isNew;
    const totalRows    = (showSelfRow ? 1 : 0) + matches.length + (showAddRow ? 1 : 0);

    // Close on outside click.
    React.useEffect(() => {
      if (!open) return;
      const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
      document.addEventListener('mousedown', onDoc);
      return () => document.removeEventListener('mousedown', onDoc);
    }, [open]);

    const pickFromIndex = (i) => {
      let cursor = 0;
      if (showSelfRow) {
        if (i === cursor) { onChange('Self'); setOpen(false); return; }
        cursor++;
      }
      const m = matches[i - cursor];
      if (m) { onChange(m.name); setOpen(false); return; }
      cursor += matches.length;
      if (showAddRow && i === cursor) {
        // Just close — the typed value stays, save will create the stub.
        setOpen(false);
        return;
      }
    };

    const onKey = (e) => {
      if (!open) {
        if (e.key === 'ArrowDown' || e.key === 'Enter') { setOpen(true); e.preventDefault(); }
        return;
      }
      if (e.key === 'ArrowDown') { setHighlight(h => Math.min(h+1, totalRows-1)); e.preventDefault(); }
      else if (e.key === 'ArrowUp') { setHighlight(h => Math.max(h-1, 0)); e.preventDefault(); }
      else if (e.key === 'Enter')   { pickFromIndex(highlight); e.preventDefault(); }
      else if (e.key === 'Escape')  { setOpen(false); }
    };

    // Status hint under the input — same surface as the old hint area.
    const statusHint = isSelf
      ? <>↳ <b style={{color:'var(--ink-2)'}}>Self-administered</b> — this publisher registers and collects on its own works.</>
      : linked
        ? <>↳ Linked to <b style={{color:'var(--ink-2)'}}>{linked.name}</b> · {linked.cae ? `IPI ${linked.cae} · ` : ''}{linked.pro || '—'} · {(linked.works||0).toLocaleString()} works</>
        : isNew
          ? <>+ <b style={{color:'var(--ink-2)'}}>{trimmed}</b> isn't in the directory — selecting <i>Add as new publisher</i> creates a stub record. You can flesh it out later.</>
          : hint;

    let rowIdx = 0; // running index for keyboard highlight

    return (
      <Row label={label} hint={statusHint}>
        <div ref={wrapRef} style={{position:'relative'}}>
          <input
            value={value}
            onChange={e=>{ onChange(e.target.value); setOpen(true); setHighlight(0); }}
            onFocus={()=>{ setOpen(true); setHighlight(0); }}
            onKeyDown={onKey}
            placeholder={placeholder}
            style={fld}
          />
          {open && totalRows > 0 && (
            <div style={{position:'absolute',top:'calc(100% + 2px)',left:0,right:0,background:'var(--bg)',border:'1px solid var(--ink)',boxShadow:'0 8px 28px rgba(0,0,0,.16)',zIndex:80,maxHeight:280,overflowY:'auto'}}>
              {/* Self row (admin only, when input is empty) */}
              {showSelfRow && (() => {
                const idx = rowIdx++;
                const hot = idx === highlight;
                return (
                  <button type="button" key="__self"
                    onMouseDown={e=>e.preventDefault()}
                    onClick={()=>pickFromIndex(idx)}
                    onMouseEnter={()=>setHighlight(idx)}
                    style={{display:'flex',width:'100%',alignItems:'center',justifyContent:'space-between',gap:10,padding:'10px 12px',
                      background: hot ? 'var(--bg-2)' : 'transparent',border:0,borderBottom:'1px solid var(--rule-soft)',cursor:'pointer',textAlign:'left'}}>
                    <div style={{display:'flex',alignItems:'center',gap:8}}>
                      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',padding:'2px 6px',border:'1px solid var(--rule)'}}>SELF</span>
                      <span style={{fontSize:12,fontWeight:500}}>Self-administered</span>
                    </div>
                    <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>most common</span>
                  </button>
                );
              })()}

              {/* Existing publisher matches */}
              {matches.length === 0 && !showAddRow && !showSelfRow && (
                <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',padding:'12px'}}>
                  No publishers in the directory yet. Type a name to add one.
                </div>
              )}
              {matches.map((p) => {
                const idx = rowIdx++;
                const hot = idx === highlight;
                return (
                  <button type="button" key={p.id}
                    onMouseDown={e=>e.preventDefault()}
                    onClick={()=>pickFromIndex(idx)}
                    onMouseEnter={()=>setHighlight(idx)}
                    style={{display:'grid',gridTemplateColumns:'1fr auto',gap:12,width:'100%',padding:'10px 12px',
                      background: hot ? 'var(--bg-2)' : 'transparent',border:0,borderBottom:'1px solid var(--rule-soft)',cursor:'pointer',textAlign:'left'}}>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{p.name}</div>
                      <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                        {p.aliases && p.aliases !== '—' ? `${p.aliases} · ` : ''}{p.parent && p.parent !== '—' ? p.parent : 'Independent'}
                      </div>
                    </div>
                    <div style={{textAlign:'right'}}>
                      <div className="ff-mono num" style={{fontSize:10,color:'var(--ink-3)'}}>{p.cae || 'no IPI'}</div>
                      <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{p.pro || '—'} · {(p.works||0).toLocaleString()} works</div>
                    </div>
                  </button>
                );
              })}

              {/* Add-new footer row */}
              {showAddRow && (() => {
                const idx = rowIdx++;
                const hot = idx === highlight;
                return (
                  <button type="button" key="__add"
                    onMouseDown={e=>e.preventDefault()}
                    onClick={()=>pickFromIndex(idx)}
                    onMouseEnter={()=>setHighlight(idx)}
                    style={{display:'flex',width:'100%',alignItems:'center',justifyContent:'space-between',gap:10,padding:'10px 12px',
                      background: hot ? 'var(--bg-2)' : 'transparent',border:0,borderTop:'1px solid var(--rule)',cursor:'pointer',textAlign:'left'}}>
                    <div style={{display:'flex',alignItems:'center',gap:8,minWidth:0}}>
                      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--bg)',background:'var(--ink)',padding:'2px 6px'}}>+ ADD</span>
                      <span style={{fontSize:12,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>Add "{trimmed}" as new publisher</span>
                    </div>
                    <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',whiteSpace:'nowrap'}}>creates stub</span>
                  </button>
                );
              })()}
            </div>
          )}
        </div>
      </Row>
    );
  };

  // ─────────────────────────── SocietyCombo
  // Searchable picker backed by SOCIETIES. Filter by `kinds` (e.g. ['PRO']
  // for songwriter PRO; ['PRO','MRO','CMO'] for publisher rights orgs).
  // Stores the acronym as the form value (matches existing schema), but
  // shows full name + territory + ack rate in the dropdown.
  const SocietyCombo = ({label, value, onChange, kinds, hint}) => {
    const [open, setOpen] = React.useState(false);
    const [highlight, setHighlight] = React.useState(0);
    const [query, setQuery] = React.useState(value || '');
    const wrapRef = React.useRef(null);
    React.useEffect(()=>{ setQuery(value || ''); }, [value]);
    const all = (typeof SOCIETIES !== 'undefined' ? SOCIETIES : []);
    const pool = kinds && kinds.length ? all.filter(s => kinds.includes(s.kind)) : all;
    const ql = (query||'').trim().toLowerCase();
    const matches = React.useMemo(() => {
      if (!ql) return pool.slice(0, 12);
      return pool.filter(s => {
        const blob = `${s.acronym} ${s.name} ${s.territory||''} ${s.country||''}`.toLowerCase();
        return blob.includes(ql);
      }).slice(0, 12);
    }, [ql, pool]);
    const linked = pool.find(s => s.acronym.toLowerCase() === (value||'').toLowerCase());

    React.useEffect(() => {
      if (!open) return;
      const onDoc = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
      document.addEventListener('mousedown', onDoc);
      return () => document.removeEventListener('mousedown', onDoc);
    }, [open]);

    const pick = (s) => { onChange(s.acronym); setQuery(s.acronym); setOpen(false); };
    const onKey = (e) => {
      if (!open) { if (e.key==='ArrowDown'||e.key==='Enter') { setOpen(true); e.preventDefault(); } return; }
      if (e.key==='ArrowDown') { setHighlight(h=>Math.min(h+1, matches.length-1)); e.preventDefault(); }
      else if (e.key==='ArrowUp') { setHighlight(h=>Math.max(h-1, 0)); e.preventDefault(); }
      else if (e.key==='Enter') { if (matches[highlight]) pick(matches[highlight]); e.preventDefault(); }
      else if (e.key==='Escape') { setOpen(false); }
    };
    const tone = (k) => k==='PRO' ? '#0a4a8c' : k==='MRO' ? '#9b6a18' : k==='NRO' ? '#0f6e3a' : k==='HUB' ? '#a04432' : '#5a3d6e';

    const statusHint = linked
      ? <>↳ Linked to <b style={{color:'var(--ink-2)'}}>{linked.acronym}</b> · {linked.territory} · {linked.kind} · {linked.ackRate}% ack rate</>
      : (query && query.trim())
        ? <>Not in society directory — value will be saved as plain text. Add it to <i>Societies</i> for full linkage.</>
        : (hint || <>Start typing to search the rights-society directory ({pool.length} {kinds && kinds.length===1 ? kinds[0]+'s' : 'societies'}).</>);

    return (
      <Row label={label} hint={statusHint}>
        <div ref={wrapRef} style={{position:'relative'}}>
          <input
            value={query}
            onChange={e=>{ setQuery(e.target.value); onChange(e.target.value); setOpen(true); setHighlight(0); }}
            onFocus={()=>{ setOpen(true); setHighlight(0); }}
            onKeyDown={onKey}
            placeholder="Search societies — ASCAP, PRS, SACEM…"
            className="ff-mono"
            style={fld}
          />
          {open && matches.length > 0 && (
            <div style={{position:'absolute',top:'calc(100% + 2px)',left:0,right:0,background:'var(--bg)',border:'1px solid var(--ink)',boxShadow:'0 8px 28px rgba(0,0,0,.16)',zIndex:80,maxHeight:320,overflowY:'auto'}}>
              {matches.map((s, i) => {
                const hot = i === highlight;
                return (
                  <button type="button" key={s.acronym}
                    onMouseDown={e=>e.preventDefault()}
                    onClick={()=>pick(s)}
                    onMouseEnter={()=>setHighlight(i)}
                    style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:10,alignItems:'center',width:'100%',padding:'10px 12px',
                      background: hot ? 'var(--bg-2)' : 'transparent',border:0,borderBottom:'1px solid var(--rule-soft)',cursor:'pointer',textAlign:'left'}}>
                    <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
                      <span style={{width:4,height:18,background:tone(s.kind),flexShrink:0}}/>
                      <span className="ff-mono" style={{fontSize:12,fontWeight:600}}>{s.acronym}</span>
                      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:'var(--ink-3)',padding:'1px 5px',border:'1px solid var(--rule)'}}>{s.kind}</span>
                    </span>
                    <span style={{minWidth:0,fontSize:11,color:'var(--ink-2)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{s.name}</span>
                    <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',whiteSpace:'nowrap'}}>{s.territory}</span>
                  </button>
                );
              })}
            </div>
          )}
        </div>
      </Row>
    );
  };

  const ParentPubCombo = () => (
    <PubLinkCombo
      label="Parent publisher"
      value={form.parentPub}
      onChange={(v)=>set('parentPub', v)}
      placeholder="—  ·  search the publisher directory"
      hint={<>Optional. The publisher entity that holds catalog rights (e.g. Sony Music Publishing, Kobalt, Concord). Distinct from the corporate parent group above. Leave blank if there is none.</>}
    />
  );

  const AdminCombo = () => (
    <PubLinkCombo
      label="Administrator"
      value={form.administrator}
      onChange={(v)=>set('administrator', v)}
      placeholder="Self  ·  or search the publisher directory"
      allowSelf
      hint={<>Pick an existing publisher (Kobalt, Sony, Wixen…) or leave as <i>Self</i>. Type a new name to create a stub admin.</>}
    />
  );

  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(0,0,0,.4)',zIndex:1000,display:'flex',alignItems:'center',justifyContent:'center',padding:24}}>
      <div onClick={e=>e.stopPropagation()} style={{background:'var(--bg)',width:'min(580px,100%)',border:'1px solid var(--rule)',padding:0,maxHeight:'90vh',display:'flex',flexDirection:'column',minHeight:0}}>
        <div style={{padding:'18px 22px',borderBottom:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center',flexShrink:0,background:'var(--bg)'}}>
          <div>
            <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em'}}>{HEADERS[kind]}</div>
            <div className="heading-swap ff-display" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.03em',marginTop:4}}>{TITLES[kind]}</div>
          </div>
          <button onClick={onClose} className="ff-mono" style={{background:'transparent',border:0,fontSize:18,cursor:'pointer',color:'var(--ink-3)',padding:4}}>✕</button>
        </div>
        <div style={{padding:'22px',display:'grid',gap:14,overflowY:'auto',flex:'1 1 auto',minHeight:0}}>
          <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',lineHeight:1.5}}>{HINTS[kind]}</div>

          {/* ── IDENTITY (always visible — required + key fields) ── */}
          <div style={{border:'1px solid var(--rule)',background:'var(--bg)'}}>
            <div style={{padding:'11px 14px',background:'var(--bg-2)',borderBottom:'1px solid var(--rule)',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
              <div style={{display:'flex',alignItems:'center',gap:10}}>
                <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',fontWeight:600,color:'var(--ink)'}}>Identity</span>
                <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>required</span>
              </div>
              <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:ready?'#0f4c2a':'var(--ink-3)',padding:'2px 6px',border:'1px solid '+(ready?'#0f4c2a':'var(--rule)'),background:'var(--bg)'}}>{ready?'Complete':'Incomplete'}</span>
            </div>
            <div style={{padding:'14px',display:'grid',gap:12}}>
              {kind === 'profile' && (<>
                {form.type === 'Group' ? (
                  <Row label="Legal / trading name" hint="Full registered name of the group or collective."><input autoFocus value={form.name} onChange={e=>{ set('name',e.target.value); set('realName',e.target.value); }} placeholder="e.g. Sault Collective" style={fld}/></Row>
                ) : (<>
                  <Row label="First name" hint="Legal given name. Used on contracts, tax forms, and CWR registrations."><input autoFocus value={form.firstName} onChange={e=>{ const v=e.target.value; const composed=[v,form.middleName,form.lastName].filter(Boolean).join(' ').trim(); set('firstName',v); set('name',composed); set('realName',composed); }} placeholder="Jordan" style={fld}/></Row>
                  <Row label="Middle name(s)" hint="Optional. Multiple middle names separated by spaces."><input value={form.middleName} onChange={e=>{ const v=e.target.value; const composed=[form.firstName,v,form.lastName].filter(Boolean).join(' ').trim(); set('middleName',v); set('name',composed); set('realName',composed); }} placeholder="Maria" style={fld}/></Row>
                  <Row label="Last name" hint="Legal surname / family name."><input value={form.lastName} onChange={e=>{ const v=e.target.value; const composed=[form.firstName,form.middleName,v].filter(Boolean).join(' ').trim(); set('lastName',v); set('name',composed); set('realName',composed); }} placeholder="Reyes" style={fld}/></Row>
                </>)}
                <Row label="Entity type" hint="Person = an individual writer/performer. Group = a band or collective filed as a single rights-bearing entity.">
                  <div style={{display:'flex',gap:0,border:'1px solid var(--rule)',width:'fit-content'}}>
                    {['Person','Group'].map(k=>(
                      <button key={k} type="button" onClick={()=>set('type',k)} className="ff-mono upper"
                        style={{padding:'7px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,
                          background: form.type===k ? 'var(--ink)' : 'transparent',
                          color: form.type===k ? 'var(--bg)' : 'var(--ink-2)',
                          border:0,borderRight:k==='Person'?'1px solid var(--rule)':'0',cursor:'pointer'}}>{k}</button>
                    ))}
                  </div>
                </Row>
                <SocietyCombo label="PRO" value={form.pro} onChange={v=>set('pro',v)} kinds={['PRO','CMO']}
                  hint={<>Performing-rights org the songwriter is a member of. Searched against the live society directory ({(typeof SOCIETIES!=='undefined'?SOCIETIES:[]).filter(s=>s.kind==='PRO'||s.kind==='CMO').length} orgs).</>}/>
                <Row label="Country (ISO)"><CountryPicker value={form.country} onChange={v=>set('country', v)}/></Row>
                <Row label="IPI" hint="11-digit IPI Name Number — required for CWR registration."><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.ipi} onChange={e=>set('ipi',e.target.value.replace(/[^\d]/g,'').slice(0,11))} placeholder="e.g. 00224398541" className="ff-mono num" style={fld}/>{window.IdChip && form.ipi ? <window.IdChip kind="ipiName" value={form.ipi} compact onApply={(v)=>set('ipi',v)}/> : null}</div></Row>
                <Row label="ISNI" hint="16-digit International Standard Name Identifier (party-level). Cross-PRO unique."><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.isni} onChange={e=>set('isni',formatISNI(e.target.value))} placeholder="e.g. 0000 0005 1746 0975" className="ff-mono num" style={fld}/>{window.IdChip && form.isni ? <window.IdChip kind="isni" value={form.isni} compact onApply={(v)=>set('isni',formatISNI(v))}/> : null}</div></Row>
                <Row label="DPID" hint="DDEX Party ID. Required by DSPs and reporting partners using DDEX message standards."><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.dpid} onChange={e=>set('dpid',formatDPID(e.target.value))} placeholder="e.g. PA-DPIDA-2018051811-P" className="ff-mono" style={fld}/>{window.IdChip && form.dpid ? <window.IdChip kind="dpid" value={form.dpid} compact onApply={(v)=>set('dpid',v)}/> : null}</div></Row>
              </>)}

              {kind === 'publisher' && (<>
                <Row label="Publisher name"><input autoFocus value={form.name} onChange={e=>set('name',e.target.value)} placeholder="e.g. Hivewright Music" style={fld}/></Row>
                <ParentPubCombo/>
                <SocietyCombo label="PRO" value={form.pro} onChange={v=>set('pro',v)} kinds={['PRO','MRO','CMO']}
                  hint={<>Primary rights society this publisher is affiliated with. Searched against the live society directory.</>}/>
                <Row label="Territory">
                  <select value={form.territory} onChange={e=>set('territory',e.target.value)} className="ff-mono" style={fld}>
                    <option>Worldwide</option><option>US</option><option>UK</option><option>EU</option><option>NA</option><option>APAC</option>
                  </select>
                </Row>
                <Row label="IPI"><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.cae} onChange={e=>set('cae',e.target.value.replace(/[^\d]/g,'').slice(0,11))} placeholder="11-digit IPI" className="ff-mono num" style={fld}/>{window.IdChip && form.cae ? <window.IdChip kind="ipiName" value={form.cae} compact onApply={(v)=>set('cae',v)}/> : null}</div></Row>
                <Row label="CWR Submitter Code" hint="3-character society-issued submitter code. Identifies the publisher as the originator on CWR transactions. Assigned by ASCAP/BMI/PRS etc."><input value={form.submitterCode} onChange={e=>set('submitterCode',e.target.value.toUpperCase().replace(/[^A-Z0-9]/g,'').slice(0,3))} placeholder="e.g. HVM" className="ff-mono upper" style={{...fld,width:120,letterSpacing:'.08em'}}/></Row>
                <Row label="ISNI" hint="16-digit International Standard Name Identifier — used to disambiguate the publisher across PROs and DSPs."><input value={form.isni} onChange={e=>set('isni',formatISNI(e.target.value))} placeholder="e.g. 0000 0005 1746 0975" className="ff-mono num" style={fld}/></Row>
                <Row label="DPID" hint="DDEX Party ID. Required for DDEX-based supply-chain messaging."><input value={form.dpid} onChange={e=>set('dpid',formatDPID(e.target.value))} placeholder="e.g. PA-DPIDA-2018051811-P" className="ff-mono" style={fld}/></Row>
              </>)}

              {kind === 'label' && (<>
                <Row label="Label name"><input autoFocus value={form.name} onChange={e=>set('name',e.target.value)} placeholder="e.g. Polar North" style={fld}/></Row>
                <Row label="Code" hint="Short identifier used in catalog rows, distro reports, and exports."><input value={form.code} onChange={e=>set('code',e.target.value.toUpperCase().slice(0,6))} placeholder="3–6 letters · e.g. PNR" className="ff-mono upper" style={{...fld,width:140,letterSpacing:'.06em'}}/></Row>
                <Row label="Label code" hint="Numeric label code assigned by the distributor / society (LC)."><input value={form.labelCode} onChange={e=>set('labelCode',e.target.value.replace(/[^\d]/g,'').slice(0,10))} placeholder="e.g. 96128" className="ff-mono num" style={{...fld,width:160}}/></Row>
                <ParentCombo placeholder="Independent · or link to a parent group"/>
                <Row label="Classification" hint="Auto-derived from parent for Sony/Universal/Warner Music; otherwise set manually here.">
                  <div style={{display:'flex',gap:0,border:'1px solid var(--rule)',width:'fit-content'}}>
                    {[{k:'major',l:'Major-owned'},{k:'indie',l:'Independent'}].map(({k,l})=>(
                      <button key={k} type="button" onClick={()=>set('kind',k)} className="ff-mono upper"
                        style={{padding:'7px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,
                          background: form.kind===k ? 'var(--ink)' : 'transparent',
                          color: form.kind===k ? 'var(--bg)' : 'var(--ink-2)',
                          border:0,borderRight:k==='major'?'1px solid var(--rule)':'0',cursor:'pointer'}}>{l}</button>
                    ))}
                  </div>
                </Row>
                <Row label="ISNI" hint="16-digit International Standard Name Identifier — disambiguates the label across DSPs and royalty bodies."><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.isni} onChange={e=>set('isni',formatISNI(e.target.value))} placeholder="e.g. 0000 0005 1746 0975" className="ff-mono num" style={fld}/>{window.IdChip && form.isni ? <window.IdChip kind="isni" value={form.isni} compact onApply={(v)=>set('isni',formatISNI(v))}/> : null}</div></Row>
                <Row label="DPID" hint="DDEX Party ID. Used in NewReleaseMessage / DSR exchanges with distributors and DSPs."><div style={{display:'flex',alignItems:'center',gap:8}}><input value={form.dpid} onChange={e=>set('dpid',formatDPID(e.target.value))} placeholder="e.g. PA-DPIDA-2018051811-P" className="ff-mono" style={fld}/>{window.IdChip && form.dpid ? <window.IdChip kind="dpid" value={form.dpid} compact onApply={(v)=>set('dpid',v)}/> : null}</div></Row>
              </>)}
            </div>
          </div>

          {/* ── PROFILE: optional sections ── */}
          {kind === 'profile' && (<>
            <Section id="contact" title="Contact details" sub="optional"
              badge={(form.email||form.phone) ? 'Filled' : null}>
              <Row label="Email"><window.EmailField value={form.email} onChange={v=>set('email',v)} style={fld}/></Row>
              <Row label="Phone"><window.PhoneField value={form.phone} onChange={v=>set('phone',v)} style={fld} defaultCountry={form.country || 'US'}/></Row>
            </Section>
            <Section id="proDetail" title="PRO membership detail" sub="optional"
              badge={(form.memberSince||form.altPros) ? 'Filled' : null}>
              <Row label="Member since" hint="Year of PRO affiliation. Used in CWR submissions for tenure-based dispute resolution."><input value={form.memberSince} onChange={e=>set('memberSince',e.target.value.replace(/[^\d]/g,'').slice(0,4))} placeholder="2014" className="ff-mono num" style={{...fld,width:100}}/></Row>
              <Row label="Other societies" hint="Comma-separated. e.g. PRS, SACEM — for writers with multiple PRO affiliations."><input value={form.altPros} onChange={e=>set('altPros',e.target.value)} placeholder="PRS, SACEM" style={fld}/></Row>
            </Section>

            {/* Identity expanded — legal name, aliases, dob, etc. */}
            <Section id="identityExt" title="Demographics & aliases" sub="optional"
              badge={((Array.isArray(form.aliases)?form.aliases.length:form.aliases)||form.pronouns||form.dob||form.nationality) ? 'Filled' : null}>
              <Row label="Aliases" hint="Stage names, legal names, nicknames, previous names, common misspellings, search variants. Add as many typed entries as needed.">
                {typeof AliasesEditor !== 'undefined'
                  ? <AliasesEditor value={form.aliases} editing={true} onChange={v=>set('aliases',v)}/>
                  : <input value={Array.isArray(form.aliases)?form.aliases.map(a=>a.value).join(', '):form.aliases} onChange={e=>set('aliases',e.target.value)} placeholder="e.g. Solange, Saint Heron" style={fld}/>
                }
              </Row>
              <Row label={form.type==='Group'?'Date formed':'Date of birth'}><DateField value={form.dob} editing={true} onChange={v=>set('dob',v)} style={{width:200}}/></Row>
              <Row label="Nationality"><CountryPicker value={form.nationality} onChange={v=>set('nationality', v)}/></Row>
            </Section>

            {/* External IDs */}
            <Section id="extIds" title="External identifiers" sub="optional"
              badge={(form.ipiBase||form.musicBrainzId||form.wikidataId||form.spotifyUri||form.appleId||form.discogsId) ? 'Filled' : null}>
              <Row label="IPI Base #" hint="11-character IPI Base Number. Distinct from IPI Name #; identifies the rights-holding entity vs. the named persona."><input value={form.ipiBase} onChange={e=>set('ipiBase',e.target.value)} placeholder="I-001234567-8" className="ff-mono" style={fld}/></Row>
              <Row label="MusicBrainz ID"><input value={form.musicBrainzId} onChange={e=>set('musicBrainzId',e.target.value)} placeholder="UUID" className="ff-mono" style={fld}/></Row>
              <Row label="Wikidata QID"><input value={form.wikidataId} onChange={e=>set('wikidataId',e.target.value)} placeholder="Q241956" className="ff-mono" style={{...fld,width:200}}/></Row>
              <Row label="Spotify URI"><input value={form.spotifyUri} onChange={e=>set('spotifyUri',e.target.value)} placeholder="2auiVi8sUZo17dLy1HwrTU" className="ff-mono" style={fld}/></Row>
              <Row label="Apple ID"><input value={form.appleId} onChange={e=>set('appleId',e.target.value)} placeholder="74609529" className="ff-mono num" style={fld}/></Row>
              <Row label="Discogs ID"><input value={form.discogsId} onChange={e=>set('discogsId',e.target.value)} placeholder="251544" className="ff-mono num" style={fld}/></Row>
            </Section>

            {/* Group members — only when type=Group */}
            {form.type === 'Group' && (
              <Section id="groupMembers" title="Group members" sub="optional"
                badge={form.members.length ? `${form.members.length} added` : null}>
                <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',lineHeight:1.5}}>
                  Each member is filed alongside the group. Splits convention determines how royalties are divided when not overridden per-track.
                </div>
                {form.members.length > 0 && (
                  <div style={{border:'1px solid var(--rule-soft)',background:'var(--bg-2)'}}>
                    {form.members.map((m,i)=>(
                      <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 1fr 110px 110px 28px',gap:10,padding:'8px 12px',borderBottom:i<form.members.length-1?'1px solid var(--rule-soft)':'0',alignItems:'center'}}>
                        <input value={m.name} onChange={e=>{ const arr=[...form.members]; arr[i]={...m,name:e.target.value}; set('members',arr); }} placeholder="Member name" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                        <input value={m.role} onChange={e=>{ const arr=[...form.members]; arr[i]={...m,role:e.target.value}; set('members',arr); }} placeholder="Role" className="ff-mono" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                        <input value={m.joined} onChange={e=>{ const arr=[...form.members]; arr[i]={...m,joined:e.target.value}; set('members',arr); }} placeholder="Joined" className="ff-mono num" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                        <input value={m.left||''} onChange={e=>{ const arr=[...form.members]; arr[i]={...m,left:e.target.value||undefined}; set('members',arr); }} placeholder="Left (opt)" className="ff-mono num" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                        <button type="button" onClick={()=>set('members', form.members.filter((_,j)=>j!==i))} className="ff-mono" style={{background:'transparent',border:0,color:'var(--ink-3)',fontSize:14,cursor:'pointer',padding:0}}>✕</button>
                      </div>
                    ))}
                  </div>
                )}
                <button type="button" onClick={()=>set('members',[...form.members,{aid:null,name:'',role:'',joined:'',left:''}])}
                  className="ff-mono upper" style={{padding:'7px 12px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'transparent',color:'var(--ink)',border:'1px solid var(--ink)',cursor:'pointer',width:'fit-content'}}>+ Add member</button>
                <Row label="Splits convention" hint="How royalties divide when not overridden per-track. Equal = same %; per-credit = follows writer credits; override = use a contractual table.">
                  <select value={form.splits} onChange={e=>set('splits',e.target.value)} className="ff-mono" style={fld}>
                    <option value="equal">Equal among members</option>
                    <option value="per-credit">Per-credit (per song)</option>
                    <option value="override">Contractual override</option>
                  </select>
                </Row>
              </Section>
            )}

            {/* Publisher relationship */}
            <Section id="pubRel" title="Publisher relationship" sub="optional"
              badge={(form.publisher||form.publisherType||form.publisherStart||form.publisherTerritory||form.publisherScope) ? 'Filled' : null}>
              <Row label="Publisher"><input value={form.publisher} onChange={e=>set('publisher',e.target.value)} placeholder="e.g. Pluralis Music" style={fld}/></Row>
              <Row label="Deal type" hint="Self = artist owns and admins. Admin = third party administers; artist retains copyright. Exclusive = publisher owns the copyright. Sub-admin = sub-publisher operates per-territory.">
                <select value={form.publisherType} onChange={e=>set('publisherType',e.target.value)} className="ff-mono" style={fld}>
                  <option value="">—</option>
                  <option value="self">Self-published</option>
                  <option value="admin">Admin deal</option>
                  <option value="exclusive">Exclusive (publisher owns copyright)</option>
                  <option value="sub-admin">Sub-admin (per territory)</option>
                </select>
              </Row>
              <Row label="Start date"><DateField value={form.publisherStart} editing={true} onChange={v=>set('publisherStart',v)} style={{width:200}}/></Row>
              <Row label="End date" hint="Leave blank if open-ended."><DateField value={form.publisherEnd} editing={true} onChange={v=>set('publisherEnd',v)} style={{width:200}}/></Row>
              <Row label="Territory"><input value={form.publisherTerritory} onChange={e=>set('publisherTerritory',e.target.value)} placeholder="Worldwide ex. UK" style={fld}/></Row>
              <Row label="Scope / share"><input value={form.publisherScope} onChange={e=>set('publisherScope',e.target.value)} placeholder="Admin · 75% net" style={fld}/></Row>
            </Section>

            {/* Representation */}
            <Section id="repn" title="Representation" sub="optional"
              badge={(form.manager||form.agent||form.lawyer) ? 'Filled' : null}>
              <Row label="Manager"><input value={form.manager} onChange={e=>set('manager',e.target.value)} placeholder="Name" style={fld}/></Row>
              <Row label="Mgmt company"><input value={form.managerCo} onChange={e=>set('managerCo',e.target.value)} placeholder="Company" style={fld}/></Row>
              <Row label="Agent"><input value={form.agent} onChange={e=>set('agent',e.target.value)} placeholder="Name" style={fld}/></Row>
              <Row label="Agency"><input value={form.agency} onChange={e=>set('agency',e.target.value)} placeholder="Company" style={fld}/></Row>
              <Row label="Lawyer"><input value={form.lawyer} onChange={e=>set('lawyer',e.target.value)} placeholder="Name" style={fld}/></Row>
              <Row label="Law firm"><input value={form.lawFirm} onChange={e=>set('lawFirm',e.target.value)} placeholder="Firm" style={fld}/></Row>
              <Row label="Address">
                <window.AddressBlock
                  value={form.addressStruct || (form.country ? { country_iso: form.country } : {})}
                  onChange={(next) => {
                    set('addressStruct', next);
                    // Maintain the legacy single-line `address` for back-compat reads
                    const line = [next.line1, next.line2, [next.city, next.state, next.postal_code].filter(Boolean).join(', ')]
                      .filter(Boolean).join(' · ');
                    set('address', line);
                  }}
                  style={fld}/>
              </Row>
            </Section>

            {/* Tax & payee */}
            <Section id="taxPayee" title="Tax & payee" sub="optional"
              badge={(form.payee||form.taxId||form.taxForm||form.paymentMethod) ? 'Filled' : null}>
              <Row label="Payee entity" hint="The legal entity royalties are paid to. Often a loan-out company, estate, or trust — different from the artist's stage/legal name."><input value={form.payee} onChange={e=>set('payee',e.target.value)} placeholder="e.g. Saint Heron LLC" style={fld}/></Row>
              <Row label="Tax ID" hint="TIN/EIN (US), UTR (UK), NIE (ES), etc. Treat as PII; redact when displaying."><input value={form.taxId} onChange={e=>set('taxId',e.target.value)} placeholder="**-***5189" className="ff-mono" style={fld}/></Row>
              <Row label="Tax form on file">
                <select value={form.taxForm} onChange={e=>set('taxForm',e.target.value)} className="ff-mono" style={fld}>
                  <option value="">—</option>
                  <option>W-9</option><option>W-8BEN</option><option>W-8BEN-E</option><option>W-8ECI</option><option>Other / non-US</option>
                </select>
              </Row>
              <Row label="Form expires"><DateField value={form.taxFormExpiry} editing={true} onChange={v=>set('taxFormExpiry',v)} style={{width:200}}/></Row>
              <Row label="Withholding"><input value={form.withholding} onChange={e=>set('withholding',e.target.value)} placeholder="0% (US treaty)" className="ff-mono" style={fld}/></Row>
              <Row label="Treaty country"><input value={form.treatyCountry} onChange={e=>set('treatyCountry',e.target.value)} placeholder="United States" style={fld}/></Row>
              <Row label="Payment method"><input value={form.paymentMethod} onChange={e=>set('paymentMethod',e.target.value)} placeholder="ACH · Chase ****2287" style={fld}/></Row>
              <Row label="Neighboring-rights performer" hint="Tick if eligible to collect performer royalties on sound recordings (separate from writer royalties).">
                <label style={{display:'inline-flex',alignItems:'center',gap:8,cursor:'pointer'}}>
                  <input type="checkbox" checked={!!form.nrPerformer} onChange={e=>set('nrPerformer',e.target.checked)}/>
                  <span className="ff-mono" style={{fontSize:12,color:'var(--ink-2)'}}>{form.nrPerformer?'Yes':'No'}</span>
                </label>
              </Row>
              {form.nrPerformer && (
                <Row label="NR body"><input value={form.neighbouringRightsBody} onChange={e=>set('neighbouringRightsBody',e.target.value)} placeholder="SoundExchange / PPL / GVL" style={fld}/></Row>
              )}
            </Section>

            {/* Operational */}
            <Section id="ops" title="Operational notes" sub="optional"
              badge={(form.tags||form.notes) ? 'Filled' : null}>
              <Row label="Tags" hint="Comma-separated internal labels. Used for filtering and routing in catalogs."><input value={form.tags} onChange={e=>set('tags',e.target.value)} placeholder="Priority, Co-write, Estate-pending" style={fld}/></Row>
              <Row label="Internal notes"><textarea value={form.notes} onChange={e=>set('notes',e.target.value)} placeholder="Anything the rights team should know about this profile…" style={{...fld,minHeight:64,resize:'vertical',fontFamily:'inherit'}}/></Row>
            </Section>
          </>)}

          {/* ── PUBLISHER: optional sections ── */}
          {kind === 'publisher' && (<>
            <Section id="contact" title="Primary contact" sub="optional"
              badge={(form.contactName||form.email||form.phone) ? 'Filled' : null}>
              <Row label="Name"><input value={form.contactName} onChange={e=>set('contactName',e.target.value)} placeholder="e.g. Maya Okonkwo" style={fld}/></Row>
              <Row label="Role"><input value={form.contactRole} onChange={e=>set('contactRole',e.target.value)} placeholder="e.g. Head of Royalties" style={fld}/></Row>
              <Row label="Email"><input type="email" value={form.email} onChange={e=>set('email',e.target.value)} placeholder="rights@hivewright.com" style={fld}/></Row>
              <Row label="Phone"><input value={form.phone} onChange={e=>set('phone',e.target.value)} placeholder="+1 (212) 555-0142" style={fld}/></Row>
            </Section>
          </>)}

          {/* ── LABEL: optional sections ── */}
          {kind === 'label' && (<>
            <Section id="dist" title="Distribution & deals" sub="optional"
              badge={((form.deals&&form.deals!=='Distribution · Worldwide')||form.distributor||form.dealStart) ? 'Filled' : null}>
              <Row label="Deal summary"><input value={form.deals} onChange={e=>set('deals',e.target.value)} placeholder="e.g. Distribution · Worldwide" style={fld}/></Row>
              <Row label="Distributor" hint="Primary distribution partner — Orchard, ADA, Believe, AWAL, etc."><input value={form.distributor} onChange={e=>set('distributor',e.target.value)} placeholder="e.g. The Orchard" style={fld}/></Row>
              <Row label="Deal start"><input value={form.dealStart} onChange={e=>set('dealStart',e.target.value)} placeholder="2024-01-15" className="ff-mono num" style={{...fld,width:160}}/></Row>
              <Row label="Status">
                <select value={form.status} onChange={e=>set('status',e.target.value)} className="ff-mono" style={fld}>
                  <option>Active partner</option><option>In negotiation</option><option>Catalog only</option><option>Inactive</option>
                </select>
              </Row>
            </Section>
            <Section id="nros" title="Neighboring rights organizations (NROs)" sub="optional"
              badge={form.nros.length ? `${form.nros.length} added` : null}>
              <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',lineHeight:1.5}}>
                NROs license & collect performance royalties for sound recordings in each territory. Pick the org and which territory it covers — add as many as apply.
              </div>
              {form.nros.length > 0 && (
                <div style={{border:'1px solid var(--rule-soft)',background:'var(--bg-2)'}}>
                  <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 60px 28px',gap:10,alignItems:'center',padding:'7px 12px',borderBottom:'1px solid var(--rule-soft)',background:'var(--bg)'}}>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>Organization</span>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>Territory</span>
                    <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)',fontWeight:500}}>ISO</span>
                    <span/>
                  </div>
                  {form.nros.map((n,i) => (
                    <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 1fr 60px 28px',gap:10,alignItems:'center',padding:'8px 12px',borderBottom:i<form.nros.length-1?'1px solid var(--rule-soft)':'0'}}>
                      <input value={n.org} onChange={e=>{
                        const next = form.nros.map((x,j)=>j===i?{...x,org:e.target.value}:x);
                        set('nros',next);
                      }} list={`nro-org-options-${i}`} placeholder="e.g. SoundExchange" className="ff-mono" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                      <datalist id={`nro-org-options-${i}`}>
                        {NRO_CATALOG.map(o => <option key={o.org} value={o.org}/>)}
                      </datalist>
                      <input value={n.territory} onChange={e=>{
                        // Look up ISO from catalog or territory list
                        const match = NRO_CATALOG.find(c => c.territory.toLowerCase() === e.target.value.toLowerCase());
                        const next = form.nros.map((x,j)=>j===i?{...x,territory:e.target.value, iso: match ? match.iso : x.iso}:x);
                        set('nros',next);
                      }} list={`nro-terr-options-${i}`} placeholder="e.g. United States" className="ff-mono" style={{...fld,padding:'5px 8px',fontSize:11}}/>
                      <datalist id={`nro-terr-options-${i}`}>
                        {TERRITORY_OPTIONS.map(o => <option key={o} value={o}/>)}
                      </datalist>
                      <input value={n.iso} onChange={e=>{
                        const next = form.nros.map((x,j)=>j===i?{...x,iso:e.target.value.toUpperCase().slice(0,2)}:x);
                        set('nros',next);
                      }} placeholder="—" className="ff-mono num upper" style={{...fld,padding:'5px 8px',fontSize:11,textAlign:'center'}}/>
                      <button type="button" onClick={()=>set('nros', form.nros.filter((_,j)=>j!==i))}
                        className="ff-mono" title="Remove"
                        style={{background:'transparent',border:0,color:'var(--ink-3)',fontSize:14,cursor:'pointer',padding:0,lineHeight:1}}>✕</button>
                    </div>
                  ))}
                </div>
              )}
              {/* Add-row controls: quick-add from catalog OR custom entry */}
              <div style={{display:'flex',gap:8,flexWrap:'wrap',alignItems:'center'}}>
                <select onChange={e=>{
                  const v = e.target.value;
                  if (!v) return;
                  const c = NRO_CATALOG.find(x => x.org === v);
                  if (c && !form.nros.some(n => n.org === c.org && n.territory === c.territory)) {
                    set('nros',[...form.nros, {org:c.org, territory:c.territory, iso:c.iso}]);
                  }
                  e.target.value = '';
                }} className="ff-mono" style={{...fld,padding:'7px 10px',fontSize:11,width:'auto',flex:'1 1 240px'}}>
                  <option value="">+ Add from catalog…</option>
                  {NRO_CATALOG.map(c => (
                    <option key={c.org+c.territory} value={c.org}>{c.org} — {c.territory}</option>
                  ))}
                </select>
                <button type="button" onClick={()=>set('nros',[...form.nros, {org:'',territory:'',iso:''}])}
                  className="ff-mono upper"
                  style={{padding:'7px 12px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'transparent',color:'var(--ink)',border:'1px solid var(--ink)',cursor:'pointer'}}>
                  + Custom NRO
                </button>
              </div>
            </Section>
            <Section id="contact" title="Primary contact" sub="optional"
              badge={(form.contactName||form.email||form.phone||form.address) ? 'Filled' : null}>
              <Row label="Name"><input value={form.contactName} onChange={e=>set('contactName',e.target.value)} placeholder="e.g. Maya Okonkwo" style={fld}/></Row>
              <Row label="Role"><input value={form.contactRole} onChange={e=>set('contactRole',e.target.value)} placeholder="e.g. Head of Rights & Royalties" style={fld}/></Row>
              <Row label="Email"><input type="email" value={form.email} onChange={e=>set('email',e.target.value)} placeholder="rights@polarnorth.com" style={fld}/></Row>
              <Row label="Phone"><input value={form.phone} onChange={e=>set('phone',e.target.value)} placeholder="+1 (212) 555-0142" style={fld}/></Row>
              <Row label="Address"><textarea value={form.address} onChange={e=>set('address',e.target.value)} rows={2} placeholder="550 Madison Ave, New York, NY 10022" style={{...fld,resize:'vertical',fontFamily:'inherit'}}/></Row>
            </Section>
            <Section id="logo" title="Logo" sub="optional"
              badge={form.logoDataUrl ? 'Uploaded' : null}>
              <div style={{display:'flex',gap:14,alignItems:'flex-start'}}>
                <div style={{width:72,height:72,border:'1px solid var(--rule)',background:form.logoDataUrl?'#fff':'var(--bg-2)',display:'flex',alignItems:'center',justifyContent:'center',flexShrink:0}}>
                  {form.logoDataUrl
                    ? <img src={form.logoDataUrl} alt="" style={{maxWidth:'100%',maxHeight:'100%',objectFit:'contain'}}/>
                    : <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em'}}>No logo</span>
                  }
                </div>
                <div style={{display:'flex',flexDirection:'column',gap:6,flex:1}}>
                  <label className="ff-mono upper" style={{display:'inline-block',padding:'7px 12px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'transparent',color:'var(--ink)',border:'1px solid var(--ink)',cursor:'pointer',width:'fit-content'}}>
                    Upload logo
                    <input type="file" accept="image/*" onChange={onPickLogo} style={{display:'none'}}/>
                  </label>
                  {form.logoDataUrl && (
                    <button type="button" onClick={()=>set('logoDataUrl','')} className="ff-mono" style={{background:'transparent',border:0,color:'var(--ink-3)',fontSize:11,cursor:'pointer',padding:0,textAlign:'left',width:'fit-content'}}>Remove</button>
                  )}
                  <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',lineHeight:1.4}}>PNG or SVG with transparent background works best. If no logo is provided, ASTRO assigns a colored swatch.</div>
                </div>
              </div>
            </Section>
          </>)}

          {/* Recap & stub-parent disclosure */}
          {/* Stub-parent disclosure (only when applicable) */}
          {(kind === 'label' || kind === 'publisher') && parentIsNew && (
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',lineHeight:1.5,padding:'10px 12px',background:'var(--bg-2)',border:'1px solid var(--rule-soft)'}}>
              <b style={{color:'var(--ink-2)'}}>+ stub parent:</b> A placeholder Label record for <b style={{color:'var(--ink-2)'}}>{trimmedParent}</b> will also be created so the parent link resolves immediately.
            </div>
          )}
        </div>
        <div style={{padding:'14px 22px',borderTop:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center',gap:8,background:'var(--bg-2)',flexShrink:0}}>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.04em'}}>
            {ready ? <>Ready · <b style={{color:'var(--ink-2)'}}>{optionalFilled}</b> optional field{optionalFilled===1?'':'s'} added</> : 'Complete required identity fields to continue'}
          </div>
          <div style={{display:'flex',gap:8}}>
            <button onClick={onClose} className="ff-mono upper" style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'transparent',border:'1px solid var(--rule)',cursor:'pointer',color:'var(--ink-2)'}}>Cancel</button>
            <button onClick={save} className="ff-mono upper" style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,background:'var(--ink)',color:'var(--bg)',border:0,cursor: ready?'pointer':'not-allowed', opacity: ready?1:0.5}}>Add {kind}</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function ScreenDirectory({ go }) {
  const [tab, setTab] = useDS('profiles');
  const [q, setQ] = useDS('');
  const [highlight, setHighlight] = useDS(null);
  const [adding, setAdding] = useDS(null); // null | 'society' | 'profile' | 'publisher' | 'label'
  const [filterOpen, setFilterOpen] = useDS(false);
  const [bulkRun, setBulkRun] = useDS(null); // { entity, rows:[{id,name,subtitle,status,detail}], idx, done, startedAt }
  const [filters, setFilters] = useDS({
    // shared
    verified: 'any',           // 'any' | 'verified' | 'unverified'
    pro: 'any',                // 'any' | 'ASCAP' | 'BMI' | 'PRS' | 'SOCAN' | 'GEMA' | 'SACEM' | 'SACVEN'
    // profiles
    country: 'any',            // 'any' | 'US' | 'GB' | 'CA' | 'VE' | 'OTHER'
    entityType: 'any',         // 'any' | 'Person' | 'Group'
    // publishers / labels
    territory: 'any',          // 'any' | 'WW' | 'US' | 'GB' | 'EU'
    // labels
    parentKind: 'any',         // 'any' | 'major' | 'indie'
    // societies
    cwrSupport: 'any',         // 'any' | '21' | '30' | 'both' | 'none'
    ackMin: 0,                 // minimum ack rate
  });
  const setF = (k,v) => setFilters(f => ({...f, [k]:v}));
  const clearFilters = () => setFilters({verified:'any',pro:'any',country:'any',entityType:'any',territory:'any',parentKind:'any',cwrSupport:'any',ackMin:0});
  // Drive the bulk verify simulation
  React.useEffect(() => {
    if (!bulkRun || bulkRun.done) return;
    if (bulkRun.idx >= bulkRun.rows.length) {
      setBulkRun(s => s && {...s, done:true});
      return;
    }
    const t = setTimeout(() => {
      setBulkRun(s => {
        if (!s) return s;
        const rows = s.rows.map((r,i) => {
          if (i !== s.idx) return r;
          // 80% verified, 12% manual review, 8% mismatch
          const roll = Math.random();
          if (roll < 0.80) return {...r, status:'ok',    detail:`CISAC match · ${r.idLabel}`};
          if (roll < 0.92) return {...r, status:'warn',  detail:'Manual review queued · multiple matches'};
          return                  {...r, status:'fail',  detail:'No CISAC record · escalate'};
        });
        return {...s, rows, idx: s.idx + 1};
      });
    }, 280);
    return () => clearTimeout(t);
  }, [bulkRun]);

  const launchBulkVerify = () => {
    let entity, source, idLabel;
    if (tab === 'profiles') {
      entity = 'profile'; idLabel = 'IPI';
      source = ARTISTS.filter(p => !p.legal).map(p => ({id:p.id, name:p.name, subtitle:`${p.pro} · ${p.country}`, idLabel:`IPI ${p.ipi}`}));
    } else if (tab === 'publishers') {
      entity = 'publisher'; idLabel = 'IPI';
      const PUB_LIST = (typeof PUBLISHERS !== 'undefined' ? PUBLISHERS : (window.__PUBLISHERS || []));
      // Unverified = no IPI (matches PublishersView's verified semantics).
      // If everything has IPIs, fall back to second half so demo always has work to do.
      const unverified = PUB_LIST.filter(p => !p.cae);
      const fallback = PUB_LIST.slice(Math.ceil(PUB_LIST.length/2));
      source = (unverified.length ? unverified : fallback).map(p => ({id:p.id, name:p.name, subtitle:`${p.pro||'—'} · ${p.territory||'—'}`, idLabel:p.cae?`IPI ${p.cae}`:'no IPI'}));
    } else if (tab === 'labels') {
      entity = 'label'; idLabel = 'parent group';
      const LB = (typeof LABELS !== 'undefined' ? LABELS : []);
      // Unverified = generic "Independent" parent without a labelCode (proxy for "needs CISAC mapping").
      const unverified = LB.filter(l => (!l.labelCode) || /independent/i.test(l.parent||''));
      const fallback = LB.slice(Math.ceil(LB.length/2));
      source = (unverified.length ? unverified : fallback).map(l => ({id:l.id, name:l.name, subtitle:`${l.code} · ${l.parent}`, idLabel:l.labelCode?`LC ${l.labelCode}`:`${l.code} parent`}));
    } else {
      return;
    }
    if (source.length === 0) {
      // Nothing to verify — open a stub modal that says so
      setBulkRun({ entity, rows:[], idx:0, done:true, startedAt:new Date().toISOString().slice(11,19) });
      return;
    }
    setBulkRun({
      entity,
      rows: source.map(r => ({...r, status:'pending', detail:'Queued'})),
      idx: 0,
      done: false,
      startedAt: new Date().toISOString().slice(11,19),
    });
  };

  const filterRef = React.useRef(null);
  React.useEffect(() => {
    if (!filterOpen) return;
    const onDoc = (e) => { if (filterRef.current && !filterRef.current.contains(e.target)) setFilterOpen(false); };
    const onKey = (e) => { if (e.key === 'Escape') setFilterOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => { document.removeEventListener('mousedown', onDoc); document.removeEventListener('keydown', onKey); };
  }, [filterOpen]);

  // Active filter count — only counts dims that are relevant to the current tab
  const activeCount = (() => {
    let n = 0;
    if (tab !== 'societies' && filters.verified !== 'any') n++;
    if (tab === 'profiles') {
      if (filters.pro !== 'any') n++;
      if (filters.country !== 'any') n++;
      if (filters.entityType !== 'any') n++;
    }
    if (tab === 'publishers') {
      if (filters.pro !== 'any') n++;
      if (filters.territory !== 'any') n++;
    }
    if (tab === 'labels') {
      if (filters.territory !== 'any') n++;
      if (filters.parentKind !== 'any') n++;
    }
    if (tab === 'societies') {
      if (filters.cwrSupport !== 'any') n++;
      if (filters.ackMin > 0) n++;
    }
    return n;
  })();

  // Allow other parts of the app to deep-link into the Societies tab
  // window.dispatchEvent(new CustomEvent('astro-open-society', {detail: {code: 'ASCAP'}}))
  React.useEffect(() => {
    const onOpen = (e) => {
      setTab('societies');
      if (e.detail?.code) {
        setQ(e.detail.code);
        setHighlight(e.detail.code);
        window.setTimeout(() => setHighlight(null), 1800);
      }
    };
    window.addEventListener('astro-open-society', onOpen);
    return () => window.removeEventListener('astro-open-society', onOpen);
  }, []);

  const counts = (() => {
    const d = window.CATALOG_STATS?.directory || {};
    return {
      profiles:   d.profiles   ?? 5202,
      publishers: d.publishers ?? 142,
      labels:     d.labels     ?? 38,
      societies:  (typeof SOCIETIES !== 'undefined' ? SOCIETIES.length : 0),
    };
  })();
  const total = counts.profiles + counts.publishers + counts.labels + counts.societies;

  return (
    <div>
      {/* Heading — uses canonical <PageHeader> */}
      <PageHeader
        eyebrow={[
          'DIRECTORY',
          `${counts.profiles.toLocaleString()}\u00a0PROFILES`,
          `${counts.publishers}\u00a0PUBLISHERS`,
          `${counts.labels}\u00a0LABELS`,
          `${counts.societies}\u00a0SOCIETIES`,
        ]}
        title="directory"
        highlight="directory"
        sub="Every writer, publisher, label, artist, and society that touches our catalog — whether we represent them or they participate alongside us — and the IDs that make them addressable."
        actions={
          <div style={{display:'flex',gap:8,flexWrap:'wrap',position:'relative'}} ref={filterRef}>
            <button
              onClick={()=>setFilterOpen(o=>!o)}
              className="focus-ring ff-mono upper"
              aria-expanded={filterOpen}
              style={{padding:'8px 14px',fontSize:10,letterSpacing:'.1em',fontWeight:500,
                background: filterOpen || activeCount>0 ? 'var(--ink)' : 'transparent',
                color: filterOpen || activeCount>0 ? 'var(--bg)' : 'var(--ink)',
                border:'1px solid var(--ink)',cursor:'pointer',display:'inline-flex',alignItems:'center',gap:6,height:32}}>
              <Ic.Filter width={12} height={12}/> Filter
              {activeCount > 0 && <span className="ff-mono num" style={{background:'var(--accent)',color:'var(--accent-ink)',padding:'1px 6px',fontSize:9,fontWeight:700,marginLeft:2}}>{activeCount}</span>}
            </button>
            {tab !== 'societies' && (
              <Btn variant="secondary" icon={<Ic.Layers/>} size="sm" onClick={launchBulkVerify}>Bulk verify</Btn>
            )}
            {tab === 'societies' ? (
              <Btn variant="primary" icon={<Ic.Plus/>} size="sm" onClick={()=>setAdding('society')}>Add society</Btn>
            ) : (
              <Btn variant="primary" icon={<Ic.Plus/>} size="sm" onClick={()=>setAdding(tab.replace(/s$/,''))}>
                {tab === 'profiles' ? 'Add profile' : tab === 'publishers' ? 'Add publisher' : 'Add label'}
              </Btn>
            )}
            {filterOpen && (
            <div style={{position:'absolute',top:'calc(100% + 8px)',right:0,width:340,background:'var(--bg)',border:'1px solid var(--ink)',boxShadow:'0 12px 40px rgba(0,0,0,.18)',zIndex:60}}>
              <div style={{padding:'14px 16px',borderBottom:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
                <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',fontWeight:600}}>Filter {tab}</div>
                <button onClick={clearFilters} className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)',background:'transparent',border:0,cursor:'pointer',textDecoration:'underline'}}>Reset</button>
              </div>
              <div style={{padding:'14px 16px',display:'grid',gap:16}}>
                {tab !== 'societies' && (
                  <FilterRow label="ASTRO verified" hint="Cross-checked against IPI / CISAC records and confirmed legitimate.">
                    <FilterChips value={filters.verified} onChange={v=>setF('verified',v)}
                      options={[{v:'any',l:'Any'},{v:'verified',l:'Verified'},{v:'unverified',l:'Unverified'}]}/>
                  </FilterRow>
                )}

                {tab === 'profiles' && (
                  <>
                    <FilterRow label="Society">
                      <FilterChips value={filters.pro} onChange={v=>setF('pro',v)}
                        options={[{v:'any',l:'Any'},{v:'ASCAP',l:'ASCAP'},{v:'BMI',l:'BMI'},{v:'PRS',l:'PRS'},{v:'SOCAN',l:'SOCAN'},{v:'SACVEN',l:'SACVEN'}]}/>
                    </FilterRow>
                    <FilterRow label="Country">
                      <FilterChips value={filters.country} onChange={v=>setF('country',v)}
                        options={[{v:'any',l:'Any'},{v:'US',l:'US'},{v:'GB',l:'UK'},{v:'CA',l:'CA'},{v:'VE',l:'VE'},{v:'OTHER',l:'Other'}]}/>
                    </FilterRow>
                    <FilterRow label="Entity type" hint="Person = individual writer/performer. Group = band or collective filed as a single rights-bearing entity.">
                      <FilterChips value={filters.entityType} onChange={v=>setF('entityType',v)}
                        options={[{v:'any',l:'Any'},{v:'Person',l:'Person'},{v:'Group',l:'Group'}]}/>
                    </FilterRow>
                  </>
                )}

                {tab === 'publishers' && (
                  <>
                    <FilterRow label="Territory">
                      <FilterChips value={filters.territory} onChange={v=>setF('territory',v)}
                        options={[{v:'any',l:'Any'},{v:'WW',l:'Worldwide'},{v:'US',l:'US'},{v:'GB',l:'UK'},{v:'EU',l:'EU'}]}/>
                    </FilterRow>
                    <FilterRow label="Society">
                      <FilterChips value={filters.pro} onChange={v=>setF('pro',v)}
                        options={[{v:'any',l:'Any'},{v:'ASCAP',l:'ASCAP'},{v:'BMI',l:'BMI'},{v:'PRS',l:'PRS'}]}/>
                    </FilterRow>
                  </>
                )}

                {tab === 'labels' && (
                  <>
                    <FilterRow label="Territory">
                      <FilterChips value={filters.territory} onChange={v=>setF('territory',v)}
                        options={[{v:'any',l:'Any'},{v:'WW',l:'Worldwide'},{v:'US',l:'US'},{v:'GB',l:'UK'},{v:'EU',l:'EU'}]}/>
                    </FilterRow>
                    <FilterRow label="Parent group" hint="Set per-label in the Label drawer (CLASSIFICATION). Auto-derived from parent for Sony/Universal/Warner; otherwise manual.">
                      <FilterChips value={filters.parentKind} onChange={v=>setF('parentKind',v)}
                        options={[{v:'any',l:'Any'},{v:'major',l:'Major-owned'},{v:'indie',l:'Independent'}]}/>
                    </FilterRow>
                  </>
                )}

                {tab === 'societies' && (
                  <>
                    <FilterRow label="CWR support">
                      <FilterChips value={filters.cwrSupport} onChange={v=>setF('cwrSupport',v)}
                        options={[{v:'any',l:'Any'},{v:'21',l:'2.1'},{v:'30',l:'3.0'},{v:'both',l:'Both'},{v:'none',l:'None'}]}/>
                    </FilterRow>
                    <FilterRow label={`Min ack rate · ${filters.ackMin}%`}>
                      <input type="range" min={0} max={100} step={5} value={filters.ackMin}
                        onChange={e=>setF('ackMin', Number(e.target.value))}
                        style={{width:'100%',accentColor:'var(--ink)'}}/>
                    </FilterRow>
                  </>
                )}
              </div>
              <div style={{padding:'12px 16px',borderTop:'1px solid var(--rule)',display:'flex',justifyContent:'space-between',alignItems:'center',background:'var(--bg-2)'}}>
                <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)'}}>{activeCount === 0 ? 'No filters applied' : `${activeCount} filter${activeCount>1?'s':''} active`}</span>
                <button onClick={()=>setFilterOpen(false)} className="ff-mono upper" style={{padding:'6px 12px',fontSize:10,letterSpacing:'.1em',background:'var(--ink)',color:'var(--bg)',border:0,cursor:'pointer'}}>Done</button>
              </div>
            </div>
          )}
          </div>
        }
      />

      {/* Tabs */}
      <div style={{borderBottom:'1px solid var(--rule)',display:'flex',gap:0,marginBottom:0,flexWrap:'wrap'}}>
        {[
          {k:'profiles',   l:'Profiles',   n:counts.profiles,   sub:'Writers · Performers'},
          {k:'publishers', l:'Publishers', n:counts.publishers, sub:'Admin · Co-pub'},
          {k:'labels',     l:'Labels',     n:counts.labels,     sub:'Recording rightsholders'},
          {k:'societies',  l:'Societies',  n:counts.societies,  sub:'PROs · MROs · CMOs'},
        ].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,textAlign:'left'}}>
            {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)',flexWrap:'wrap',gap:12}}>
        <div style={{display:'flex',gap:0,alignItems:'center',flex:1,maxWidth:400,minWidth:240}}>
          <Ic.Search width={14} height={14} style={{color:'var(--ink-3)'}}/>
          <input placeholder={`Search ${tab}…`} value={q} onChange={e=>setQ(e.target.value)} className="ff-mono"
            style={{border:'none',outline:'none',padding:'8px 12px',fontSize:13,background:'transparent',flex:1,color:'var(--ink)'}}/>
        </div>
        <div style={{display:'flex',gap:18,alignItems:'center'}}>
          <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.1em'}}>
            {tab==='profiles' ? `${counts.profiles.toLocaleString()} PROFILES` : tab==='publishers' ? `${counts.publishers} PUBLISHERS` : tab==='labels' ? `${counts.labels} LABELS` : `${counts.societies} SOCIETIES`}
          </span>
          <span style={{height:18,width:1,background:'var(--rule)'}}/>
          <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.1em'}}>SORT: A–Z</span>
        </div>
      </div>

      {/* Body */}
      {tab==='profiles'   && <ProfilesGrid  rows={ARTISTS} go={go} q={q} filters={filters}/>}
      {tab==='publishers' && <PublishersView go={go} q={q} filters={filters}/>}
      {tab==='labels'     && <LabelsView    go={go} q={q} filters={filters}/>}
      {tab==='societies'  && <SocietiesView q={q} highlight={highlight} filters={filters}/>}

      {adding === 'society' && <AddSocietyModal onClose={()=>setAdding(null)}/>}
      {(adding === 'profile' || adding === 'publisher' || adding === 'label') && (
        <AddRecordModal kind={adding} onClose={()=>setAdding(null)} onAdded={(id)=>{ setHighlight(id); setTimeout(()=>setHighlight(null), 2400); }}/>
      )}

      {/* Bulk Verify modal */}
      {bulkRun && (() => {
        const total = bulkRun.rows.length;
        const completed = bulkRun.rows.filter(r => r.status !== 'pending').length;
        const counts = bulkRun.rows.reduce((a,r)=>{ a[r.status]=(a[r.status]||0)+1; return a; }, {});
        const pct = total ? Math.round(completed/total*100) : 100;
        const entityLabel = bulkRun.entity === 'profile' ? 'Profiles'
                          : bulkRun.entity === 'publisher' ? 'Publishers'
                          : 'Labels';
        return (
          <div onClick={()=> bulkRun.done && setBulkRun(null)}
            style={{position:'fixed',inset:0,background:'rgba(0,0,0,.42)',display:'flex',alignItems:'center',justifyContent:'center',zIndex:80,
              animation:'astro-fade-in .18s ease-out'}}>
            <div onClick={e=>e.stopPropagation()}
              style={{width:'min(820px, 92vw)',maxHeight:'82vh',background:'var(--bg)',color:'var(--ink)',
                border:'1px solid var(--rule)',display:'flex',flexDirection:'column',
                animation:'astro-slide-up .25s ease-out',boxShadow:'0 30px 80px rgba(0,0,0,.32)'}}>
              {/* Header */}
              <div style={{padding:'18px 24px',borderBottom:'1px solid var(--rule)',display:'flex',alignItems:'center',justifyContent:'space-between',gap:14}}>
                <div>
                  <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.14em',marginBottom:6}}>
                    BULK VERIFY · {entityLabel.toUpperCase()} · STARTED {bulkRun.startedAt} UTC
                  </div>
                  <div className="ff-display" style={{fontSize:22,fontWeight:600,letterSpacing:'-0.02em',lineHeight:1.1}}>
                    {bulkRun.done
                      ? (total === 0 ? 'Nothing to verify' : 'Verification complete')
                      : `Verifying ${completed} / ${total}…`}
                  </div>
                </div>
                <button onClick={()=>setBulkRun(null)} aria-label="Close"
                  style={{width:30,height:30,background:'var(--bg-2)',border:'1px solid var(--rule)',cursor:'pointer',color:'var(--ink)',fontSize:16,lineHeight:1}}>×</button>
              </div>

              {/* Progress + summary */}
              {total > 0 && (
                <>
                  <div style={{height:3,background:'var(--bg-2)',position:'relative'}}>
                    <div style={{position:'absolute',top:0,left:0,bottom:0,width:`${pct}%`,background:'var(--ink)',transition:'width .3s ease-out'}}/>
                  </div>
                  <div style={{padding:'12px 24px',borderBottom:'1px solid var(--rule)',display:'flex',gap:18,flexWrap:'wrap'}}>
                    {[
                      {l:'VERIFIED',v:counts.ok||0,    tone:'var(--ink)'},
                      {l:'REVIEW',  v:counts.warn||0,  tone:'var(--accent, #d4a02a)'},
                      {l:'FAILED',  v:counts.fail||0,  tone:'#c53030'},
                      {l:'PENDING', v:counts.pending||0, tone:'var(--ink-3)'},
                    ].map(c => (
                      <div key={c.l} style={{display:'flex',alignItems:'baseline',gap:6}}>
                        <span style={{display:'inline-block',width:8,height:8,background:c.tone}}/>
                        <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em'}}>{c.l}</span>
                        <span className="ff-mono num" style={{fontSize:13,fontWeight:600,color:c.tone}}>{c.v}</span>
                      </div>
                    ))}
                  </div>
                </>
              )}

              {/* Rows */}
              <div style={{flex:1,overflow:'auto'}}>
                {total === 0 && (
                  <div style={{padding:'40px 24px',textAlign:'center',color:'var(--ink-3)'}}>
                    <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',marginBottom:6}}>NO UNVERIFIED {entityLabel.toUpperCase()} IN DIRECTORY</div>
                    <div className="ff-mono" style={{fontSize:11}}>Everything in this tab is already cross-checked.</div>
                  </div>
                )}
                {bulkRun.rows.map((r,i) => {
                  const isCurrent = !bulkRun.done && i === bulkRun.idx;
                  const dotColor = r.status==='ok' ? 'var(--ink)'
                                : r.status==='warn' ? 'var(--accent, #d4a02a)'
                                : r.status==='fail' ? '#c53030'
                                : isCurrent ? 'var(--ink-2)' : 'var(--rule)';
                  return (
                    <div key={r.id} style={{display:'grid',gridTemplateColumns:'24px 1.4fr 1.2fr 0.7fr 1.6fr',
                      gap:10,padding:'10px 24px',alignItems:'center',
                      borderBottom: i<bulkRun.rows.length-1 ? '1px solid var(--rule-soft)' : 'none',
                      background: isCurrent ? 'var(--bg-2)' : 'transparent',fontSize:12}}>
                      <span style={{width:8,height:8,background:dotColor,
                        animation: isCurrent ? 'astro-pulse 1s ease-in-out infinite' : 'none'}}/>
                      <span style={{fontWeight:500}}>{r.name}</span>
                      <span className="ff-mono" style={{fontSize:11,color:'var(--ink-2)'}}>{r.subtitle}</span>
                      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',
                        color: r.status==='ok' ? 'var(--ink)'
                            : r.status==='warn' ? 'var(--accent, #d4a02a)'
                            : r.status==='fail' ? '#c53030'
                            : 'var(--ink-3)'}}>
                        {r.status==='pending' ? (isCurrent?'verifying…':'queued')
                          : r.status==='fail' ? 'failed'
                          : r.status==='warn' ? 'review'
                          : 'verified'}
                      </span>
                      <span className="ff-mono" style={{fontSize:11,color: r.status==='pending' ? 'var(--ink-3)' : 'var(--ink-2)'}}>{r.detail}</span>
                    </div>
                  );
                })}
              </div>

              {/* Footer */}
              <div style={{padding:'14px 24px',borderTop:'1px solid var(--rule)',display:'flex',gap:8,alignItems:'center',justifyContent:'flex-end'}}>
                {!bulkRun.done && total > 0 && (
                  <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginRight:'auto',letterSpacing:'.04em'}}>
                    Cross-checking against CISAC IPI / IPN registry…
                  </span>
                )}
                {bulkRun.done && total > 0 && (
                  <button onClick={()=>{
                      const lines = [
                        ['Name','Subtitle','Status','Detail'].join(','),
                        ...bulkRun.rows.map(r=>[`"${r.name}"`,`"${r.subtitle}"`,r.status,`"${r.detail}"`].join(','))
                      ].join('\n');
                      const blob = new Blob([lines],{type:'text/csv'});
                      const url = URL.createObjectURL(blob);
                      const a = document.createElement('a');
                      a.href = url; a.download = `directory_verify_${bulkRun.entity}_${Date.now()}.csv`;
                      document.body.appendChild(a); a.click(); a.remove();
                      setTimeout(()=>URL.revokeObjectURL(url), 1000);
                    }} className="ff-mono upper"
                    style={{padding:'8px 14px',fontSize:11,letterSpacing:'.08em',background:'transparent',color:'var(--ink)',border:'1px solid var(--rule)',cursor:'pointer'}}>
                    EXPORT LOG
                  </button>
                )}
                <button onClick={()=>setBulkRun(null)} disabled={!bulkRun.done && total > 0}
                  className="ff-mono upper"
                  style={{padding:'8px 14px',fontSize:11,letterSpacing:'.08em',
                    background: (bulkRun.done || total===0) ? 'var(--ink)' : 'var(--bg-2)',
                    color:      (bulkRun.done || total===0) ? 'var(--bg)' : 'var(--ink-3)',
                    border:0,cursor: (bulkRun.done || total===0) ? 'pointer' : 'not-allowed',fontWeight:600}}>
                  {bulkRun.done ? 'DONE' : 'RUNNING…'}
                </button>
              </div>
            </div>
            <style>{`
              @keyframes astro-fade-in { from { opacity: 0 } to { opacity: 1 } }
              @keyframes astro-slide-up { from { transform: translateY(20px); opacity: 0 } to { transform: none; opacity: 1 } }
              @keyframes astro-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
            `}</style>
          </div>
        );
      })()}
    </div>
  );
}

Object.assign(window, { ScreenDirectory, AddSocietyModal, AddRecordModal });
