// enrichment-policy.jsx — Field-level enrichment policy console
// ─────────────────────────────────────────────────────────────────────
// Sister surface to integrations.jsx. Where integrations.jsx handles
// PLUMBING (auth, traffic, scopes, errors), this screen handles POLICY:
//
//   1. Per-field source PRIORITY  — when MusicBrainz says BPM=128 and
//      Spotify Audio Features says BPM=129, who wins? Editable.
//
//   2. CONFLICT inbox             — current rows where two sources
//      disagree on the same field. Drill, accept, override.
//
//   3. ENRICHMENT MATRIX          — every catalog field × every public
//      source: who provides it, who's authoritative, who's fallback.
//
//   4. RUN HISTORY                — the last enrichment passes the
//      pipeline made, what changed, what conflicts were resolved.
//
//   5. SOURCE PROFILES            — per-source readout: license,
//      refresh policy, gotchas (e.g., AcousticBrainz archived;
//      LRCLIB ToS for sync vs plain).
//
// All ten new public-data sources surface here:
//   MusicBrainz · Discogs · AcousticBrainz · ListenBrainz ·
//   YouTube Data · Google KG · Google Places · music-metadata ·
//   Songlink/Odesli · LRCLIB
//
// Plus the existing transactional sources we already enrich from:
//   Spotify (audio features) · Apple (storefront) · Genius (lyrics)
// ─────────────────────────────────────────────────────────────────────

(function () {
  'use strict';
  if (typeof window === 'undefined' || !window.React) return;
  const { useState, useMemo } = React;

  // ─── SOURCES — every public/reference source we enrich from ──────
  // Ordered as default priority across most fields. Per-field overrides
  // live in the matrix below.
  const SOURCES = [
    { id:'musicbrainz',    name:'MusicBrainz',     color:'#BA478F', license:'CC0', tier:'authoritative',
      gotchas:'1 req/sec hard cap. Writes require Verified Editor flag.' },
    { id:'discogs',        name:'Discogs',         color:'#000000', license:'CC-BY-NC',  tier:'authoritative',
      gotchas:'NC license — internal use only, no redistribution. Vinyl/CD focus.' },
    { id:'acousticbrainz', name:'AcousticBrainz',  color:'#5C7AEA', license:'CC0', tier:'archived',
      gotchas:'Project shut down 2022. Read-only static dump (~30M tracks).' },
    { id:'listenbrainz',   name:'ListenBrainz',    color:'#EB743B', license:'CC0', tier:'community',
      gotchas:'Listen counts are community-submitted — sample skews tech/EU.' },
    { id:'youtube-data',   name:'YouTube Data',    color:'#FF0000', license:'YT API ToS', tier:'platform',
      gotchas:'10k units/day quota. Captions field gated by partner program.' },
    { id:'google-kg',      name:'Google KG',       color:'#4285F4', license:'CC-BY',     tier:'authoritative',
      gotchas:'Sparse outside English Wikipedia. Schema.org type coverage uneven.' },
    { id:'google-places',  name:'Google Places',   color:'#34A853', license:'GMaps ToS', tier:'platform',
      gotchas:'Cannot store place_id > 30 days unless attaching to user-visible map.' },
    { id:'music-metadata', name:'music-metadata',  color:'#3D7A35', license:'MIT (lib)', tier:'ingest',
      gotchas:'In-process tag reader. Fidelity depends on what the file shipped with.' },
    { id:'songlink',       name:'Songlink',        color:'#FE6E58', license:'API ToS',   tier:'authoritative',
      gotchas:'Cross-DSP equivalence — best for resolving Spotify ID ↔ Apple ID.' },
    { id:'lrclib',         name:'LRCLIB',          color:'#1F1F1F', license:'CC0',       tier:'community',
      gotchas:'Synced LRC quality varies. ISRC + duration match recommended.' },
    // existing transactional sources we also pull enrichment from
    { id:'spotify-feat',   name:'Spotify Features',color:'#1DB954', license:'Spotify ToS', tier:'platform',
      gotchas:'Audio Features (key, BPM, energy) — single point estimate, no confidence.' },
    { id:'apple-meta',     name:'Apple Music',     color:'#FA243C', license:'Apple ToS', tier:'platform',
      gotchas:'Storefront-bound metadata — same track may differ across regions.' },
    { id:'genius',         name:'Genius',          color:'#FFFF64', license:'Genius API ToS', tier:'community',
      gotchas:'Lyrics + writer credits. Commercial use requires partner agreement.' },
  ];
  const SRC_BY = Object.fromEntries(SOURCES.map(s => [s.id, s]));

  // ─── FIELDS — every catalog field that any source can enrich ─────
  // Each field declares its providers and the default priority.
  const FIELDS = [
    { id:'isrc',         label:'ISRC',                 entity:'recording', group:'Identifiers',
      providers:['musicbrainz','discogs','music-metadata'], primary:'musicbrainz' },
    { id:'iswc',         label:'ISWC',                 entity:'work',      group:'Identifiers',
      providers:['musicbrainz','genius'], primary:'musicbrainz' },
    { id:'mbid',         label:'MBID',                 entity:'all',       group:'Identifiers',
      providers:['musicbrainz'], primary:'musicbrainz' },
    { id:'upc',          label:'UPC / Barcode',        entity:'release',   group:'Identifiers',
      providers:['musicbrainz','discogs','music-metadata'], primary:'musicbrainz' },
    { id:'wikidata',     label:'Wikidata QID',         entity:'party',     group:'Identifiers',
      providers:['google-kg','musicbrainz'], primary:'musicbrainz' },

    { id:'title',        label:'Title (canonical)',    entity:'recording', group:'Names',
      providers:['musicbrainz','spotify-feat','apple-meta','music-metadata','discogs'], primary:'musicbrainz' },
    { id:'artistName',   label:'Artist credit',        entity:'recording', group:'Names',
      providers:['musicbrainz','spotify-feat','apple-meta','discogs'], primary:'musicbrainz' },
    { id:'releaseDate',  label:'Release date',         entity:'release',   group:'Names',
      providers:['musicbrainz','discogs','spotify-feat','apple-meta'], primary:'musicbrainz' },
    { id:'duration',     label:'Duration (ms)',        entity:'recording', group:'Names',
      providers:['music-metadata','musicbrainz','spotify-feat','apple-meta'], primary:'music-metadata' },

    { id:'bpm',          label:'BPM',                  entity:'recording', group:'Audio features',
      providers:['acousticbrainz','spotify-feat'], primary:'acousticbrainz' },
    { id:'key',          label:'Key',                  entity:'recording', group:'Audio features',
      providers:['acousticbrainz','spotify-feat'], primary:'acousticbrainz' },
    { id:'mode',         label:'Mode (maj/min)',       entity:'recording', group:'Audio features',
      providers:['acousticbrainz','spotify-feat'], primary:'acousticbrainz' },
    { id:'timeSig',      label:'Time signature',       entity:'recording', group:'Audio features',
      providers:['acousticbrainz','spotify-feat'], primary:'spotify-feat' },
    { id:'energy',       label:'Energy',               entity:'recording', group:'Audio features',
      providers:['spotify-feat','acousticbrainz'], primary:'spotify-feat' },
    { id:'danceability', label:'Danceability',         entity:'recording', group:'Audio features',
      providers:['spotify-feat','acousticbrainz'], primary:'spotify-feat' },
    { id:'valence',      label:'Valence',              entity:'recording', group:'Audio features',
      providers:['spotify-feat','acousticbrainz'], primary:'spotify-feat' },
    { id:'mood',         label:'Mood tags',            entity:'recording', group:'Audio features',
      providers:['acousticbrainz','listenbrainz'], primary:'acousticbrainz' },

    { id:'lyricsPlain',  label:'Lyrics (plain)',       entity:'work',      group:'Lyrics',
      providers:['genius','lrclib','youtube-data'], primary:'genius' },
    { id:'lyricsSynced', label:'Lyrics (synced LRC)',  entity:'recording', group:'Lyrics',
      providers:['lrclib','youtube-data'], primary:'lrclib' },
    { id:'language',     label:'Lyric language',       entity:'work',      group:'Lyrics',
      providers:['lrclib','genius','musicbrainz'], primary:'musicbrainz' },

    { id:'platformLinks',label:'Cross-platform links', entity:'recording', group:'Links',
      providers:['songlink','musicbrainz'], primary:'songlink' },
    { id:'spotifyId',    label:'Spotify track ID',     entity:'recording', group:'Links',
      providers:['songlink','spotify-feat','musicbrainz'], primary:'songlink' },
    { id:'appleId',      label:'Apple track ID',       entity:'recording', group:'Links',
      providers:['songlink','apple-meta','musicbrainz'], primary:'songlink' },
    { id:'tidalId',      label:'Tidal track ID',       entity:'recording', group:'Links',
      providers:['songlink','musicbrainz'], primary:'songlink' },
    { id:'youtubeId',    label:'YouTube video ID',     entity:'recording', group:'Links',
      providers:['youtube-data','songlink','musicbrainz'], primary:'youtube-data' },

    { id:'genres',       label:'Genre tags',           entity:'recording', group:'Classification',
      providers:['discogs','musicbrainz','spotify-feat','apple-meta','listenbrainz'], primary:'discogs' },
    { id:'styles',       label:'Style tags (granular)',entity:'recording', group:'Classification',
      providers:['discogs','musicbrainz','listenbrainz'], primary:'discogs' },
    { id:'topics',       label:'Topic tags',           entity:'recording', group:'Classification',
      providers:['youtube-data','listenbrainz'], primary:'youtube-data' },

    { id:'sessionVenue', label:'Session venue',        entity:'recording', group:'Production',
      providers:['musicbrainz','google-places'], primary:'musicbrainz' },
    { id:'studioPlaceId',label:'Studio place_id',      entity:'recording', group:'Production',
      providers:['google-places'], primary:'google-places' },

    { id:'similarRec',   label:'Similar recordings',   entity:'recording', group:'Discovery',
      providers:['listenbrainz','musicbrainz'], primary:'listenbrainz' },
    { id:'similarArtist',label:'Similar artists',      entity:'party',     group:'Discovery',
      providers:['listenbrainz','musicbrainz'], primary:'listenbrainz' },

    { id:'pressing',     label:'Physical pressing',    entity:'release',   group:'Physical',
      providers:['discogs','musicbrainz'], primary:'discogs' },
    { id:'matrixRunout', label:'Matrix / runout',      entity:'release',   group:'Physical',
      providers:['discogs'], primary:'discogs' },
    { id:'marketPrice',  label:'Marketplace price',    entity:'release',   group:'Physical',
      providers:['discogs'], primary:'discogs' },

    { id:'partyDescription', label:'Artist bio / description', entity:'party', group:'Party metadata',
      providers:['google-kg','musicbrainz'], primary:'google-kg' },
    { id:'partyImage',   label:'Artist image (canonical)', entity:'party', group:'Party metadata',
      providers:['google-kg','spotify-feat','apple-meta'], primary:'spotify-feat' },
  ];

  // Group fields for the matrix view
  const FIELD_GROUPS = (() => {
    const g = {};
    FIELDS.forEach(f => { (g[f.group] ||= []).push(f); });
    return g;
  })();

  // ─── CONFLICTS — synthesized examples of source disagreements ────
  const CONFLICTS = [
    { id:'cf1', field:'bpm', recording:'r_06', recordingLabel:'10% — KAYTRANADA · Kali Uchis',
      values:[
        { src:'acousticbrainz', value:108, confidence:0.92, note:'algorithmic · Essentia 2.1' },
        { src:'spotify-feat',   value:107, confidence:0.84, note:'Spotify Audio Features v1' },
      ], status:'open', detected:'2h ago' },
    { id:'cf2', field:'key', recording:'r_03', recordingLabel:'Bunny Is A Rider · Caroline Polachek',
      values:[
        { src:'acousticbrainz', value:'F#', confidence:0.88, note:'tonal hpcp' },
        { src:'spotify-feat',   value:'Gb', confidence:0.71, note:'enharmonic — same pitch' },
      ], status:'auto-resolved', detected:'1d ago', resolution:'Equivalent (enharmonic) · canonical = F#' },
    { id:'cf3', field:'genres', recording:'r_05', recordingLabel:'Far In · Helado Negro',
      values:[
        { src:'discogs',        value:'Indie Pop · Electronic', note:'Rough Trade release page' },
        { src:'musicbrainz',    value:'electronic · indietronica · ambient pop', note:'community-tagged' },
        { src:'apple-meta',     value:'Alternative', note:'storefront US' },
      ], status:'open', detected:'4h ago' },
    { id:'cf4', field:'releaseDate', recording:'r_04', recordingLabel:'Send Me — Tirzah',
      values:[
        { src:'musicbrainz',    value:'2018-08-10', note:'Release Group · first official' },
        { src:'discogs',        value:'2018-08-08', note:'UK promo pressing' },
        { src:'spotify-feat',   value:'2018-08-10', note:'streaming release date' },
      ], status:'open', detected:'14h ago' },
    { id:'cf5', field:'lyricsPlain', recording:'r_07', recordingLabel:'Raingurl · Yaeji',
      values:[
        { src:'genius',        value:'(version A · 412 lines)', note:'verified Korean+English' },
        { src:'lrclib',        value:'(version B · 388 lines)', note:'community-contributed' },
      ], status:'open', detected:'8h ago' },
    { id:'cf6', field:'duration', recording:'r_08', recordingLabel:'Wildfires · Sault',
      values:[
        { src:'music-metadata', value:'4:38.412', note:'WAV master · accurate' },
        { src:'musicbrainz',    value:'4:38',     note:'rounded · streaming dur.' },
        { src:'spotify-feat',   value:'4:38',     note:'rounded · ms 278413' },
      ], status:'auto-resolved', detected:'2d ago', resolution:'music-metadata wins · master accuracy' },
    { id:'cf7', field:'similarRec', recording:'r_02', recordingLabel:'Birds · Floating Points',
      values:[
        { src:'listenbrainz',  value:'12 candidates', note:'collaborative-filter · 3.2k listeners' },
        { src:'musicbrainz',   value:'4 candidates',  note:'editorial relations' },
      ], status:'open', detected:'6h ago' },
    { id:'cf8', field:'wikidata', recording:'r_01', recordingLabel:'Solange (artist)',
      values:[
        { src:'google-kg',     value:'Q4779', note:'KG canonical' },
        { src:'musicbrainz',   value:'Q4779', note:'wikidata relation' },
      ], status:'agreed', detected:'30m ago', resolution:'Both sources agree · auto-applied' },
  ];

  // ─── RUN HISTORY ─────────────────────────────────────────────────
  const RUNS = [
    { id:'r-2026-04-26-23', ts:'2026-04-26 23:14 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:412, conflicts:8, autoResolved:5, durationMs:184221, status:'ok' },
    { id:'r-2026-04-26-17', ts:'2026-04-26 17:00 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:188, conflicts:3, autoResolved:2, durationMs:142811, status:'ok' },
    { id:'r-2026-04-26-12', ts:'2026-04-26 12:08 UTC', kind:'on-import',     src:'music-metadata',
      scanned:42,   enriched:42,  conflicts:0, autoResolved:0, durationMs:412,    status:'ok' },
    { id:'r-2026-04-26-11', ts:'2026-04-26 11:00 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:62,  conflicts:1, autoResolved:1, durationMs:128432, status:'ok' },
    { id:'r-2026-04-26-05', ts:'2026-04-26 05:00 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:241, conflicts:6, autoResolved:4, durationMs:172218, status:'ok' },
    { id:'r-2026-04-25-23', ts:'2026-04-25 23:00 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:84,  conflicts:0, autoResolved:0, durationMs:118022, status:'ok' },
    { id:'r-2026-04-25-17', ts:'2026-04-25 17:00 UTC', kind:'scheduled · 6h', src:'all',
      scanned:18420, enriched:188, conflicts:4, autoResolved:3, durationMs:144028, status:'ok' },
    { id:'r-2026-04-25-12', ts:'2026-04-25 12:00 UTC', kind:'manual · backfill', src:'lrclib',
      scanned:842,  enriched:188, conflicts:2, autoResolved:0, durationMs:31884,  status:'partial', note:'18 ISRC lookups failed (no match)' },
  ];

  // ─── helpers ─────────────────────────────────────────────────────
  function Mono({ children, size=10, color='var(--ink-3)', upper=true, style={} }) {
    return <span className="ff-mono" style={{ fontSize:size, color, letterSpacing:'.12em',
      textTransform: upper ? 'uppercase' : 'none', ...style }}>{children}</span>;
  }
  function Swatch({ id, size=10 }) {
    const s = SRC_BY[id]; if (!s) return null;
    return <span style={{display:'inline-block',width:size,height:size,background:s.color,
      boxShadow:'inset 0 0 0 1px rgba(0,0,0,.08)',flexShrink:0}}/>;
  }
  function SrcChip({ id, primary }) {
    const s = SRC_BY[id]; if (!s) return null;
    return (
      <span style={{display:'inline-flex',alignItems:'center',gap:5,padding:'3px 7px',
        border: '1px solid '+ (primary ? 'var(--ink)' : 'var(--rule)'),
        background: primary ? 'var(--ink)' : 'transparent',
        color: primary ? 'var(--bg)' : 'var(--ink-2)',
        fontSize:10, lineHeight:1}}>
        <Swatch id={id} size={8}/>
        <span style={{fontWeight: primary ? 600 : 400}}>{s.name}</span>
      </span>
    );
  }
  function fmtMs(ms){
    if (ms < 1000) return ms+'ms';
    if (ms < 60_000) return (ms/1000).toFixed(1)+'s';
    return Math.floor(ms/60_000)+'m '+Math.floor((ms%60_000)/1000)+'s';
  }

  // ═════════════════════════════════════════════════════════════════
  // 01 OVERVIEW
  // ═════════════════════════════════════════════════════════════════
  function EPOverviewTab({ priorities, setTab }) {
    const stats = useMemo(() => {
      const fieldsCovered = FIELDS.length;
      const totalAssignments = FIELDS.reduce((s,f) => s + f.providers.length, 0);
      const conflictsOpen = CONFLICTS.filter(c => c.status === 'open').length;
      const conflictsAuto = CONFLICTS.filter(c => c.status === 'auto-resolved').length;
      const lastRun = RUNS[0];
      return { fieldsCovered, totalAssignments, conflictsOpen, conflictsAuto, lastRun };
    }, []);

    return (
      <div>
        {/* KPI strip */}
        <div style={{display:'grid',gridTemplateColumns:'repeat(5, 1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
          {[
            { l:'ENRICHMENT SOURCES', v:SOURCES.length, sub:`${SOURCES.filter(s=>s.tier==='authoritative').length} authoritative · ${SOURCES.filter(s=>s.tier==='community').length} community` },
            { l:'FIELDS GOVERNED',    v:stats.fieldsCovered, sub:`across ${Object.keys(FIELD_GROUPS).length} groups · ${stats.totalAssignments} source assignments` },
            { l:'CONFLICTS OPEN',     v:stats.conflictsOpen, sub:`${stats.conflictsAuto} auto-resolved last 24h`, danger: stats.conflictsOpen > 4 },
            { l:'LAST RUN',           v:stats.lastRun.ts.split(' ')[1], sub:`${stats.lastRun.enriched.toLocaleString()} fields enriched · ${fmtMs(stats.lastRun.durationMs)}` },
            { l:'ROW-LEVEL COVERAGE', v:'94.2%', sub:'works + recordings with ≥1 enriched field' },
          ].map((k, i) => (
            <div key={k.l} style={{padding:'18px 22px', borderRight: i<4?'1px solid var(--rule)':0}}>
              <Mono size={9}>{k.l}</Mono>
              <div className="ff-mono num" style={{fontSize:26,fontWeight:600,marginTop:6, color:k.danger?'var(--danger)':'var(--ink)'}}>{k.v}</div>
              {k.sub && <div style={{fontSize:11,color:'var(--ink-3)',marginTop:6,lineHeight:1.4}}>{k.sub}</div>}
            </div>
          ))}
        </div>

        {/* 2-col body: source ladder + recent conflicts */}
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:30,marginTop:30}}>
          {/* Source ladder */}
          <div>
            <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:12}}>
              <Mono size={10}>SOURCE LADDER · TIER × FIELD COUNT</Mono>
              <button onClick={()=>setTab('matrix')} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)',background:'transparent',border:0,cursor:'pointer',padding:0}}>
                OPEN MATRIX →
              </button>
            </div>
            <div style={{border:'1px solid var(--rule)'}}>
              {SOURCES.map((s, i) => {
                const fieldsServed = FIELDS.filter(f => f.providers.includes(s.id));
                const primaryFor   = FIELDS.filter(f => f.primary === s.id);
                const tierLabel = { authoritative:'AUTH', archived:'ARCH', community:'COMM', platform:'PLAT', ingest:'LOCAL' }[s.tier] || s.tier.toUpperCase();
                return (
                  <div key={s.id} style={{display:'grid',gridTemplateColumns:'auto 1fr 60px 80px 70px',gap:14,padding:'12px 14px',
                    alignItems:'center',borderBottom:i<SOURCES.length-1?'1px solid var(--rule-soft, var(--bg-2))':0}}>
                    <Swatch id={s.id} size={10}/>
                    <div>
                      <div style={{fontSize:12,fontWeight:600}}>{s.name}</div>
                      <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',marginTop:2,letterSpacing:'.04em'}}>{s.license}</div>
                    </div>
                    <Mono size={8} style={{textAlign:'center'}}>{tierLabel}</Mono>
                    <div className="ff-mono num" style={{fontSize:11,textAlign:'right'}}>
                      <div>{primaryFor.length}</div>
                      <Mono size={8} style={{display:'block',color:'var(--ink-4)'}}>PRIMARY</Mono>
                    </div>
                    <div className="ff-mono num" style={{fontSize:11,textAlign:'right',color:'var(--ink-3)'}}>
                      <div>{fieldsServed.length}</div>
                      <Mono size={8} style={{display:'block',color:'var(--ink-4)'}}>TOTAL</Mono>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>

          {/* Recent conflicts */}
          <div>
            <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:12}}>
              <Mono size={10}>RECENT CONFLICTS</Mono>
              <button onClick={()=>setTab('conflicts')} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)',background:'transparent',border:0,cursor:'pointer',padding:0}}>
                OPEN INBOX →
              </button>
            </div>
            <div style={{border:'1px solid var(--rule)'}}>
              {CONFLICTS.slice(0, 6).map((c, i) => {
                const f = FIELDS.find(x => x.id === c.field);
                const tone = c.status === 'open' ? 'var(--warn, #d4881f)'
                  : c.status === 'auto-resolved' ? 'var(--ink-3)'
                  : 'var(--ok)';
                return (
                  <div key={c.id} style={{display:'grid',gridTemplateColumns:'1fr auto',gap:12,padding:'12px 14px',
                    borderBottom:i<5?'1px solid var(--rule-soft, var(--bg-2))':0,alignItems:'flex-start'}}>
                    <div>
                      <div style={{display:'flex',alignItems:'baseline',gap:8,marginBottom:4}}>
                        <span style={{fontSize:12,fontWeight:600}}>{f?.label || c.field}</span>
                        <Mono size={9}>{c.values.length} sources</Mono>
                      </div>
                      <div style={{fontSize:11,color:'var(--ink-3)'}}>{c.recordingLabel}</div>
                      <div style={{display:'flex',gap:6,marginTop:6,flexWrap:'wrap'}}>
                        {c.values.map(v => <Swatch key={v.src} id={v.src} size={8}/>)}
                      </div>
                    </div>
                    <div style={{textAlign:'right'}}>
                      <Mono size={9} style={{color: tone}}>{c.status.replace('-',' ')}</Mono>
                      <div style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>{c.detected}</div>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        </div>

        {/* Latest run summary */}
        <div style={{marginTop:30}}>
          <Mono size={10} style={{display:'block',marginBottom:12}}>LATEST RUN · {stats.lastRun.ts}</Mono>
          <div style={{display:'grid',gridTemplateColumns:'repeat(5, 1fr)',border:'1px solid var(--rule)'}}>
            {[
              ['Scanned',     stats.lastRun.scanned.toLocaleString()],
              ['Enriched',    stats.lastRun.enriched.toLocaleString()],
              ['Conflicts',   stats.lastRun.conflicts],
              ['Auto-resolved', stats.lastRun.autoResolved],
              ['Duration',    fmtMs(stats.lastRun.durationMs)],
            ].map(([l, v], i) => (
              <div key={l} style={{padding:'14px 18px',borderRight:i<4?'1px solid var(--rule)':0}}>
                <Mono size={9}>{l.toUpperCase()}</Mono>
                <div className="ff-mono num" style={{fontSize:18,marginTop:4,fontWeight:500}}>{v}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // 02 PRIORITY EDITOR — drag-reorder source priority per field group
  // ═════════════════════════════════════════════════════════════════
  function EPPriorityTab({ priorities, setPriorities }) {
    const [filter, setFilter] = useState('all');
    const groups = filter === 'all' ? Object.entries(FIELD_GROUPS) : [[filter, FIELD_GROUPS[filter] || []]];

    const move = (fieldId, srcId, dir) => {
      setPriorities(prev => {
        const cur = prev[fieldId] || (FIELDS.find(f=>f.id===fieldId)?.providers || []).slice();
        const idx = cur.indexOf(srcId);
        if (idx < 0) return prev;
        const next = cur.slice();
        const swap = idx + dir;
        if (swap < 0 || swap >= next.length) return prev;
        [next[idx], next[swap]] = [next[swap], next[idx]];
        return { ...prev, [fieldId]: next };
      });
    };

    return (
      <div>
        {/* Filter strip */}
        <div style={{display:'flex',gap:6,flexWrap:'wrap',marginBottom:18,paddingBottom:14,borderBottom:'1px solid var(--rule)'}}>
          <button onClick={()=>setFilter('all')} className="ff-mono upper" style={{
            fontSize:10,letterSpacing:'.08em',padding:'6px 12px',
            background: filter==='all' ? 'var(--ink)' : 'transparent',
            color: filter==='all' ? 'var(--bg)' : 'var(--ink-2)',
            border:'1px solid '+(filter==='all'?'var(--ink)':'var(--rule)'),cursor:'pointer'}}>
            ALL <span className="ff-mono num" style={{marginLeft:6,opacity:.7}}>{FIELDS.length}</span>
          </button>
          {Object.keys(FIELD_GROUPS).map(g => (
            <button key={g} onClick={()=>setFilter(g)} className="ff-mono upper" style={{
              fontSize:10,letterSpacing:'.08em',padding:'6px 12px',
              background: filter===g ? 'var(--ink)' : 'transparent',
              color: filter===g ? 'var(--bg)' : 'var(--ink-2)',
              border:'1px solid '+(filter===g?'var(--ink)':'var(--rule)'),cursor:'pointer'}}>
              {g} <span className="ff-mono num" style={{marginLeft:6,opacity:.7}}>{FIELD_GROUPS[g].length}</span>
            </button>
          ))}
        </div>

        <div style={{fontSize:12,color:'var(--ink-2)',maxWidth:680,lineHeight:1.5,marginBottom:24}}>
          For each field, drag sources up to give them precedence when values disagree. The leftmost
          source is <span style={{color:'var(--ink)'}}>primary</span> — its value wins on conflict.
          Falls through to the next source when the primary returns null or fails confidence checks.
        </div>

        {groups.map(([groupName, fields]) => (
          <div key={groupName} style={{marginBottom:32}}>
            <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',
              marginBottom:12,paddingBottom:8,borderBottom:'1px solid var(--rule)'}}>
              {groupName} <span className="ff-mono num" style={{color:'var(--ink-4)',marginLeft:8}}>{fields.length} fields</span>
            </div>
            <div style={{border:'1px solid var(--rule)'}}>
              {fields.map((f, fi) => {
                const order = priorities[f.id] || f.providers;
                return (
                  <div key={f.id} style={{display:'grid',gridTemplateColumns:'220px 1fr',gap:18,padding:'14px 16px',
                    alignItems:'center',borderBottom: fi<fields.length-1?'1px solid var(--rule-soft, var(--bg-2))':0}}>
                    <div>
                      <div style={{fontSize:13,fontWeight:600}}>{f.label}</div>
                      <Mono size={9} style={{display:'block',marginTop:3}}>{f.entity} · {f.id}</Mono>
                    </div>
                    <div style={{display:'flex',gap:6,flexWrap:'wrap',alignItems:'center'}}>
                      {order.map((srcId, i) => (
                        <span key={srcId} style={{display:'inline-flex',alignItems:'center',gap:4}}>
                          <SrcChip id={srcId} primary={i===0}/>
                          <span style={{display:'inline-flex',flexDirection:'column',gap:1}}>
                            <button onClick={()=>move(f.id, srcId, -1)} disabled={i===0}
                              style={{border:'1px solid var(--rule)',background:'var(--paper)',
                                color: i===0?'var(--ink-4)':'var(--ink-2)',
                                width:14,height:11,padding:0,cursor:i===0?'default':'pointer',fontSize:7,lineHeight:1}}>▲</button>
                            <button onClick={()=>move(f.id, srcId, 1)} disabled={i===order.length-1}
                              style={{border:'1px solid var(--rule)',background:'var(--paper)',
                                color: i===order.length-1?'var(--ink-4)':'var(--ink-2)',
                                width:14,height:11,padding:0,cursor:i===order.length-1?'default':'pointer',fontSize:7,lineHeight:1}}>▼</button>
                          </span>
                          {i < order.length - 1 && <span style={{color:'var(--ink-4)',fontSize:10,marginRight:2,marginLeft:2}}>›</span>}
                        </span>
                      ))}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // 03 CONFLICTS — inbox of disagreeing sources per field per row
  // ═════════════════════════════════════════════════════════════════
  function EPConflictsTab() {
    const [tab, setTab] = useState('open');
    const [openId, setOpenId] = useState(null);

    const filtered = CONFLICTS.filter(c => {
      if (tab === 'open') return c.status === 'open';
      if (tab === 'auto') return c.status === 'auto-resolved';
      if (tab === 'agreed') return c.status === 'agreed';
      return true;
    });

    const open = openId ? CONFLICTS.find(c => c.id === openId) : null;

    return (
      <div>
        <div style={{display:'flex',gap:0,marginBottom:18,borderBottom:'1px solid var(--rule)'}}>
          {[['open','Open'],['auto','Auto-resolved'],['agreed','Agreed'],['all','All']].map(([k,l]) => {
            const n = k === 'all' ? CONFLICTS.length : CONFLICTS.filter(c => c.status === (k === 'auto' ? 'auto-resolved' : k)).length;
            return (
              <button key={k} onClick={()=>setTab(k)} className="ff-mono upper" style={{
                padding:'10px 16px',background:'transparent',border:0,
                borderBottom:'2px solid '+(tab===k?'var(--ink)':'transparent'),
                color: tab===k?'var(--ink)':'var(--ink-3)', fontSize:11,letterSpacing:'.08em',
                cursor:'pointer',display:'flex',alignItems:'baseline',gap:6,fontWeight:tab===k?600:400}}>
                {l} <span className="ff-mono num" style={{fontSize:10,opacity:.7}}>{n}</span>
              </button>
            );
          })}
        </div>

        <div style={{display:'grid',gridTemplateColumns: open ? '1fr 420px' : '1fr',gap:24}}>
          <div style={{border:'1px solid var(--rule)'}}>
            {/* header */}
            <div style={{display:'grid',gridTemplateColumns:'1.4fr 1.6fr 1.2fr 80px 80px',gap:14,padding:'10px 14px',
              background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
              {['Field','Recording','Sources','Status','Detected'].map(h => (
                <Mono key={h} size={9}>{h.toUpperCase()}</Mono>
              ))}
            </div>
            {filtered.map((c, i) => {
              const f = FIELDS.find(x => x.id === c.field);
              const tone = c.status === 'open' ? 'var(--warn, #d4881f)'
                : c.status === 'auto-resolved' ? 'var(--ink-3)'
                : 'var(--ok)';
              return (
                <div key={c.id} onClick={()=>setOpenId(c.id)} style={{display:'grid',gridTemplateColumns:'1.4fr 1.6fr 1.2fr 80px 80px',
                  gap:14,padding:'12px 14px',cursor:'pointer',
                  borderBottom: i<filtered.length-1?'1px solid var(--rule-soft, var(--bg-2))':0,
                  background: openId===c.id ? 'var(--bg-2)' : 'transparent',alignItems:'center'}}>
                  <div>
                    <div style={{fontSize:13,fontWeight:600}}>{f?.label || c.field}</div>
                    <Mono size={9} style={{display:'block',marginTop:2}}>{f?.group || '—'}</Mono>
                  </div>
                  <div>
                    <div style={{fontSize:12}}>{c.recordingLabel.split(' · ')[0]}</div>
                    <div style={{fontSize:10,color:'var(--ink-3)',marginTop:2}}>{c.recordingLabel.split(' · ')[1] || ''}</div>
                  </div>
                  <div style={{display:'flex',gap:4,flexWrap:'wrap'}}>
                    {c.values.map(v => <SrcChip key={v.src} id={v.src} primary={false}/>)}
                  </div>
                  <Mono size={9} style={{color: tone}}>{c.status.replace('-',' ')}</Mono>
                  <Mono size={9} style={{color:'var(--ink-3)'}}>{c.detected}</Mono>
                </div>
              );
            })}
            {filtered.length === 0 && (
              <div style={{padding:30,textAlign:'center',color:'var(--ink-3)',fontSize:12}}>
                No conflicts in this view.
              </div>
            )}
          </div>

          {/* Drawer */}
          {open && (() => {
            const f = FIELDS.find(x => x.id === open.field);
            return (
              <div style={{border:'1px solid var(--ink)',padding:18}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:14}}>
                  <Mono size={10}>CONFLICT · {open.id}</Mono>
                  <button onClick={()=>setOpenId(null)} style={{background:'transparent',border:0,fontSize:18,cursor:'pointer',color:'var(--ink-3)',padding:0}}>×</button>
                </div>
                <div style={{fontSize:18,fontWeight:600,marginBottom:4}}>{f?.label || open.field}</div>
                <div style={{fontSize:12,color:'var(--ink-2)',marginBottom:14}}>{open.recordingLabel}</div>

                <Mono size={9} style={{display:'block',marginBottom:8}}>VALUES REPORTED</Mono>
                <div style={{border:'1px solid var(--rule)',marginBottom:18}}>
                  {open.values.map((v, i) => {
                    const isPrimary = (f?.primary === v.src);
                    return (
                      <div key={v.src} style={{padding:'10px 12px',
                        borderBottom: i<open.values.length-1?'1px solid var(--rule-soft, var(--bg-2))':0,
                        background: isPrimary ? 'var(--bg-2)' : 'transparent'}}>
                        <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:4}}>
                          <SrcChip id={v.src} primary={isPrimary}/>
                          {v.confidence != null && <Mono size={9}>conf {(v.confidence*100).toFixed(0)}%</Mono>}
                        </div>
                        <div className="ff-mono" style={{fontSize:13,fontWeight:600,marginTop:4}}>{String(v.value)}</div>
                        <div style={{fontSize:11,color:'var(--ink-3)',marginTop:4,lineHeight:1.4}}>{v.note}</div>
                      </div>
                    );
                  })}
                </div>

                {open.resolution && (
                  <>
                    <Mono size={9} style={{display:'block',marginBottom:6}}>RESOLUTION</Mono>
                    <div style={{padding:'10px 12px',background:'var(--bg-2)',marginBottom:18,fontSize:12}}>
                      {open.resolution}
                    </div>
                  </>
                )}

                {open.status === 'open' && (
                  <div style={{display:'flex',flexDirection:'column',gap:6}}>
                    <Mono size={9} style={{display:'block',marginBottom:4}}>RESOLVE</Mono>
                    {open.values.map(v => (
                      <button key={v.src} className="ff-mono upper" style={{
                        padding:'10px 12px',fontSize:10,letterSpacing:'.08em',textAlign:'left',
                        background:'transparent',border:'1px solid var(--rule)',cursor:'pointer',
                        display:'flex',alignItems:'center',gap:8}}>
                        <Swatch id={v.src} size={8}/>
                        <span>ACCEPT {SRC_BY[v.src]?.name.toUpperCase()}</span>
                        <span className="ff-mono" style={{marginLeft:'auto',color:'var(--ink-3)',textTransform:'none',letterSpacing:0,fontSize:11}}>{String(v.value)}</span>
                      </button>
                    ))}
                    <button className="ff-mono upper" style={{
                      padding:'10px 12px',fontSize:10,letterSpacing:'.08em',marginTop:6,
                      background:'var(--ink)',color:'var(--bg)',border:0,cursor:'pointer'}}>
                      OVERRIDE WITH MANUAL VALUE →
                    </button>
                  </div>
                )}
              </div>
            );
          })()}
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // 04 MATRIX — fields × sources, who provides what
  // ═════════════════════════════════════════════════════════════════
  function EPMatrixTab() {
    return (
      <div>
        <div style={{fontSize:12,color:'var(--ink-2)',maxWidth:680,lineHeight:1.5,marginBottom:18}}>
          Every catalog field × every enrichment source. <span style={{color:'var(--ink)'}}>Solid</span>{' '}
          = primary source; <span style={{color:'var(--ink-3)'}}>outline</span> = fallback. Click any field
          to edit its priority order.
        </div>

        <div style={{overflowX:'auto',border:'1px solid var(--rule)'}}>
          <table style={{borderCollapse:'collapse',fontSize:11,minWidth:'100%'}}>
            <thead>
              <tr style={{background:'var(--bg-2)'}}>
                <th style={{padding:'10px 14px',textAlign:'left',position:'sticky',left:0,
                  background:'var(--bg-2)',zIndex:1,borderRight:'1px solid var(--rule)',
                  borderBottom:'1px solid var(--rule)',minWidth:200}}>
                  <Mono size={9}>FIELD</Mono>
                </th>
                {SOURCES.map(s => (
                  <th key={s.id} style={{padding:'10px 6px',borderBottom:'1px solid var(--rule)',minWidth:60}}>
                    <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:4}}>
                      <Swatch id={s.id} size={9}/>
                      <span className="ff-mono" style={{fontSize:9,color:'var(--ink-2)',
                        writingMode:'vertical-rl',transform:'rotate(180deg)',whiteSpace:'nowrap'}}>{s.name}</span>
                    </div>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {Object.entries(FIELD_GROUPS).map(([gname, gfields]) => (
                <React.Fragment key={gname}>
                  <tr>
                    <td colSpan={SOURCES.length + 1} style={{padding:'10px 14px',background:'var(--bg-2)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
                      <Mono size={10} style={{color:'var(--ink-2)'}}>{gname.toUpperCase()}</Mono>
                    </td>
                  </tr>
                  {gfields.map((f, i) => (
                    <tr key={f.id} style={{borderBottom:i<gfields.length-1?'1px solid var(--rule-soft, var(--bg-2))':'none'}}>
                      <td style={{padding:'9px 14px',position:'sticky',left:0,background:'var(--paper)',
                        borderRight:'1px solid var(--rule)',fontSize:12}}>
                        <div style={{fontWeight:500}}>{f.label}</div>
                        <Mono size={8} style={{display:'block',marginTop:2}}>{f.entity}</Mono>
                      </td>
                      {SOURCES.map(s => {
                        const provides = f.providers.includes(s.id);
                        const isPrimary = f.primary === s.id;
                        return (
                          <td key={s.id} style={{padding:'8px 6px',textAlign:'center'}}>
                            {isPrimary ? (
                              <span style={{display:'inline-block',width:10,height:10,background:s.color,
                                boxShadow:'inset 0 0 0 1px rgba(0,0,0,.15)'}}/>
                            ) : provides ? (
                              <span style={{display:'inline-block',width:10,height:10,
                                background:'transparent',border:'1px solid '+s.color}}/>
                            ) : (
                              <span style={{color:'var(--ink-4)',fontSize:10}}>·</span>
                            )}
                          </td>
                        );
                      })}
                    </tr>
                  ))}
                </React.Fragment>
              ))}
            </tbody>
          </table>
        </div>

        <div style={{marginTop:14,display:'flex',gap:18,fontSize:11,color:'var(--ink-2)',alignItems:'center'}}>
          <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
            <span style={{display:'inline-block',width:10,height:10,background:'var(--ink)'}}/>
            Primary
          </span>
          <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
            <span style={{display:'inline-block',width:10,height:10,background:'transparent',border:'1px solid var(--ink)'}}/>
            Fallback
          </span>
          <span style={{color:'var(--ink-3)'}}>·</span>
          <span className="ff-mono num">{FIELDS.length}</span> <span>fields</span>
          <span className="ff-mono num" style={{marginLeft:6}}>{SOURCES.length}</span> <span>sources</span>
          <span className="ff-mono num" style={{marginLeft:6}}>{FIELDS.reduce((s,f)=>s+f.providers.length,0)}</span> <span>assignments</span>
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // 05 RUN HISTORY — log of enrichment passes
  // ═════════════════════════════════════════════════════════════════
  function EPRunsTab() {
    return (
      <div>
        <div style={{fontSize:12,color:'var(--ink-2)',maxWidth:680,lineHeight:1.5,marginBottom:18}}>
          Each scheduled or manual enrichment pass: how many rows scanned, fields enriched,
          conflicts produced, and how long it took. The pipeline runs every 6 hours by default,
          plus on-import for the music-metadata library.
        </div>
        <div style={{border:'1px solid var(--rule)'}}>
          <div style={{display:'grid',gridTemplateColumns:'180px 130px 110px 90px 90px 100px 90px 1fr',gap:14,padding:'10px 14px',
            background:'var(--bg-2)',borderBottom:'1px solid var(--rule)'}}>
            {['When','Kind','Source','Scanned','Enriched','Conflicts','Duration','Note'].map(h => (
              <Mono key={h} size={9}>{h.toUpperCase()}</Mono>
            ))}
          </div>
          {RUNS.map((r, i) => (
            <div key={r.id} style={{display:'grid',gridTemplateColumns:'180px 130px 110px 90px 90px 100px 90px 1fr',gap:14,padding:'10px 14px',
              alignItems:'center',borderBottom:i<RUNS.length-1?'1px solid var(--rule-soft, var(--bg-2))':0,fontSize:11}}>
              <span className="ff-mono" style={{color:'var(--ink-2)'}}>{r.ts}</span>
              <span className="ff-mono" style={{color:'var(--ink-2)',fontSize:10}}>{r.kind}</span>
              <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
                {r.src !== 'all' && <Swatch id={r.src} size={8}/>}
                <span className="ff-mono" style={{fontSize:10}}>{r.src === 'all' ? 'all sources' : SRC_BY[r.src]?.name}</span>
              </span>
              <span className="ff-mono num" style={{textAlign:'right'}}>{r.scanned.toLocaleString()}</span>
              <span className="ff-mono num" style={{textAlign:'right',color:r.enriched>0?'var(--ok)':'var(--ink-3)'}}>{r.enriched.toLocaleString()}</span>
              <span className="ff-mono num" style={{textAlign:'right'}}>
                {r.conflicts > 0 ? <span style={{color:'var(--warn, #d4881f)'}}>{r.conflicts}</span> : <span style={{color:'var(--ink-3)'}}>0</span>}
                {r.autoResolved > 0 && <span style={{color:'var(--ink-3)',marginLeft:4,fontSize:9}}>({r.autoResolved} auto)</span>}
              </span>
              <span className="ff-mono" style={{color:'var(--ink-2)'}}>{fmtMs(r.durationMs)}</span>
              <span style={{fontSize:11,color: r.status==='partial'?'var(--warn, #d4881f)':'var(--ink-3)'}}>{r.note || '—'}</span>
            </div>
          ))}
        </div>
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // 06 SOURCE PROFILES — per-source dossier
  // ═════════════════════════════════════════════════════════════════
  function EPSourcesTab({ go }) {
    return (
      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(360px, 1fr))',gap:14}}>
        {SOURCES.map(s => {
          const fieldsServed = FIELDS.filter(f => f.providers.includes(s.id));
          const primaryFor   = FIELDS.filter(f => f.primary === s.id);
          const tierLabel = { authoritative:'AUTHORITATIVE', archived:'ARCHIVED · READ-ONLY', community:'COMMUNITY', platform:'PLATFORM ToS', ingest:'IN-PROCESS' }[s.tier] || s.tier;
          const tierColor = s.tier === 'archived' ? 'var(--warn, #d4881f)' : 'var(--ink-3)';
          return (
            <div key={s.id} style={{border:'1px solid var(--rule)',padding:16,background:'var(--paper)',display:'flex',flexDirection:'column'}}>
              <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:10}}>
                <span style={{display:'inline-block',width:24,height:24,background:s.color,boxShadow:'inset 0 0 0 1px rgba(0,0,0,.1)',flexShrink:0}}/>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:14,fontWeight:600}}>{s.name}</div>
                  <Mono size={9} style={{display:'block',color: tierColor}}>{tierLabel}</Mono>
                </div>
              </div>

              <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,border:'1px solid var(--rule)',marginBottom:12}}>
                <div style={{padding:'10px 12px',borderRight:'1px solid var(--rule)'}}>
                  <Mono size={8}>PRIMARY FOR</Mono>
                  <div className="ff-mono num" style={{fontSize:18,fontWeight:600,marginTop:2}}>{primaryFor.length}</div>
                </div>
                <div style={{padding:'10px 12px'}}>
                  <Mono size={8}>FALLBACK FOR</Mono>
                  <div className="ff-mono num" style={{fontSize:18,fontWeight:600,marginTop:2,color:'var(--ink-3)'}}>{fieldsServed.length - primaryFor.length}</div>
                </div>
              </div>

              <Mono size={8} style={{display:'block',marginBottom:6}}>LICENSE</Mono>
              <div style={{fontSize:11,color:'var(--ink-2)',marginBottom:10}} className="ff-mono">{s.license}</div>

              <Mono size={8} style={{display:'block',marginBottom:6}}>OPERATOR NOTE</Mono>
              <div style={{fontSize:11,color:'var(--ink-2)',lineHeight:1.5,marginBottom:14,flex:1}}>{s.gotchas}</div>

              <Mono size={8} style={{display:'block',marginBottom:6}}>FIELDS SERVED</Mono>
              <div style={{display:'flex',flexWrap:'wrap',gap:4,marginBottom:14,maxHeight:80,overflowY:'auto'}}>
                {fieldsServed.slice(0, 16).map(f => (
                  <span key={f.id} className="ff-mono" style={{fontSize:10,padding:'2px 6px',
                    background: f.primary===s.id ? 'var(--ink)' : 'var(--bg-2)',
                    color: f.primary===s.id ? 'var(--bg)' : 'var(--ink-2)',
                    border:'1px solid '+(f.primary===s.id?'var(--ink)':'var(--rule)')}}>
                    {f.label}
                  </span>
                ))}
                {fieldsServed.length > 16 && (
                  <span className="ff-mono" style={{fontSize:10,padding:'2px 6px',color:'var(--ink-3)'}}>+{fieldsServed.length - 16} more</span>
                )}
              </div>

              <button onClick={()=>go && go('integrations', { connector: s.id })} className="ff-mono upper" style={{
                fontSize:10,letterSpacing:'.08em',padding:'8px 12px',background:'transparent',
                border:'1px solid var(--rule)',color:'var(--ink-2)',cursor:'pointer'}}>
                OPEN IN INTEGRATIONS →
              </button>
            </div>
          );
        })}
      </div>
    );
  }

  // ═════════════════════════════════════════════════════════════════
  // MAIN
  // ═════════════════════════════════════════════════════════════════
  function ScreenEnrichmentPolicy({ go, payload }) {
    const PageHeader = window.PageHeader;
    const [tab, setTab] = useState(payload?.tab || 'overview');
    // priorities is an override map { fieldId -> [srcId, ...] }; defaults from FIELDS
    const [priorities, setPriorities] = useState({});

    const TABS = [
      { k:'overview',  l:'Overview' },
      { k:'priority',  l:'Priority editor', n: FIELDS.length },
      { k:'conflicts', l:'Conflicts',       n: CONFLICTS.filter(c=>c.status==='open').length, warn: true },
      { k:'matrix',    l:'Matrix' },
      { k:'runs',      l:'Run history',     n: RUNS.length },
      { k:'sources',   l:'Sources',         n: SOURCES.length },
    ];

    const conflictsOpen = CONFLICTS.filter(c=>c.status==='open').length;

    return (
      <div>
        {PageHeader && (
          <PageHeader
            eyebrow={['SETTINGS', 'ENRICHMENT POLICY', `${SOURCES.length} SOURCES · ${FIELDS.length} FIELDS`]}
            title="Enrichment policy"
            highlight="Enrichment policy"
            sub="Plumbing for public-data sources lives in Integrations. This is where you decide which source wins for each catalog field — when MusicBrainz says BPM=128 and Spotify says 129, who's right? Edit per-field priority, work the conflict inbox, and audit every enrichment pass."
            actions={
              <div style={{display:'flex',gap:8,alignItems:'center'}}>
                <span style={{display:'inline-flex',alignItems:'center',gap:6,fontSize:11,color: conflictsOpen ? 'var(--warn, #d4881f)' : 'var(--ink-2)'}}>
                  <span style={{display:'inline-block',width:8,height:8,borderRadius:4,background: conflictsOpen ? 'var(--warn, #d4881f)' : 'var(--ok)'}}/>
                  {conflictsOpen} open conflict{conflictsOpen===1?'':'s'}
                </span>
                <button onClick={() => go && go('integrations')} className="ff-mono upper" style={{
                  padding:'8px 14px',fontSize:10,letterSpacing:'.1em',
                  background:'transparent',border:'1px solid var(--rule)',color:'var(--ink-2)',cursor:'pointer'}}>
                  ← INTEGRATIONS
                </button>
              </div>
            }
          />
        )}

        {/* Tab strip */}
        <div style={{borderBottom:'1px solid var(--rule)',display:'flex',gap:0}}>
          {TABS.map(t => (
            <button key={t.k} onClick={()=>setTab(t.k)} style={{
              padding:'14px 22px',background:'transparent',border:0,
              borderBottom:'2px solid '+(tab===t.k?'var(--ink)':'transparent'),
              cursor:'pointer',color:tab===t.k?'var(--ink)':'var(--ink-3)',
              fontSize:13,fontWeight:tab===t.k?600:400,
              display:'flex',alignItems:'baseline',gap:8}}>
              {t.l}
              {t.n != null && (
                <span className="ff-mono num" style={{fontSize:10, color: t.warn && t.n > 0 ? 'var(--warn, #d4881f)' : 'var(--ink-3)'}}>{t.n}</span>
              )}
            </button>
          ))}
        </div>

        <div style={{paddingTop:20}}>
          {tab === 'overview'  && <EPOverviewTab priorities={priorities} setTab={setTab}/>}
          {tab === 'priority'  && <EPPriorityTab priorities={priorities} setPriorities={setPriorities}/>}
          {tab === 'conflicts' && <EPConflictsTab/>}
          {tab === 'matrix'    && <EPMatrixTab/>}
          {tab === 'runs'      && <EPRunsTab/>}
          {tab === 'sources'   && <EPSourcesTab go={go}/>}
        </div>
      </div>
    );
  }

  Object.assign(window, { ScreenEnrichmentPolicy });
  console.log('[enrichment-policy] loaded');
})();
