// integrations.jsx — DSP & API connector console
// ─────────────────────────────────────────────────────────────────────
// ASTRO sits at the center of ~9 external API integrations. Today
// they're scattered: Spotify-for-Artists pings live in audience, MLC
// pings live in MLC sync, CWR pings live in cwr-acks, etc. Operators
// who run the org need ONE place where they can:
//   - see every wired integration at a glance
//   - tell at a glance which ones are healthy / degraded / silent
//   - re-auth what's expired
//   - see exactly what data each connector pulls and pushes
//   - drill into the per-connector activity log
//
// This is that screen. It does not replace audience.jsx or mlc-sync —
// those are domain-specific. This is the connection layer beneath them.
//
// Tabs:
//   01 OVERVIEW     — connector grid + sync timeline + traffic chart
//   02 CONNECTORS   — full-detail list with capabilities + actions
//   03 ACTIVITY     — unified API log (last 24h)
//   04 PERMISSIONS  — what each connector can read / write
//
// Schema (mock):
//   connector: id, name, vendor, category, status, lastSync, freq,
//              ipiOrId, scopes[], capabilities[], traffic{in,out},
//              authExpires, errorRate, p95ms, calls24h, dataSurfaces[]

const { useState: useIS, useMemo: useIM, useEffect: useIE } = React;

// ─────────── The 9 wired integrations + a few system-internal ones
const CONNECTORS = [
  // === DSPs ===
  {
    id:'spotify', name:'Spotify', vendor:'Spotify AB', category:'dsp',
    status:'healthy', lastSync:'2m', freq:'Every 5m',
    ident:'rocketscience-music · partner-id 8a4f291e',
    auth:'OAuth · refresh', authExpires:'2026-07-12',
    scopes:['analytics:read','catalog:write','playlist:read','editorial:read'],
    capabilities:[
      { kind:'in',  l:'Streams + saves + skips',          surface:'Audience · Royalties' },
      { kind:'in',  l:'Track audio features (key/BPM/etc)',surface:'Recording detail' },
      { kind:'in',  l:'Editorial & user playlist adds',    surface:'Recording → Platforms' },
      { kind:'out', l:'Canvas video upload',               surface:'Release → Spotify Canvas' },
      { kind:'out', l:'Pre-save campaigns',                surface:'Release → Pre-release' },
    ],
    traffic:{ in: 1842914, out: 4112 }, p95ms: 184, errorRate: 0.4, calls24h: 38240,
    color:'#1DB954',
  },
  {
    id:'apple', name:'Apple Music', vendor:'Apple Inc.', category:'dsp',
    status:'healthy', lastSync:'4m', freq:'Every 15m',
    ident:'rs-music · provider 4023115',
    auth:'JWT · ES256', authExpires:'2026-09-04',
    scopes:['analytics:read','catalog:read','catalog:write','airplay:read'],
    capabilities:[
      { kind:'in',  l:'Plays, Shazams, library adds', surface:'Audience' },
      { kind:'in',  l:'Apple Music for Artists trends', surface:'Audience' },
      { kind:'in',  l:'iTunes Store sales',           surface:'Royalties' },
      { kind:'out', l:'Apple Digital Masters delivery', surface:'Release → Delivery' },
    ],
    traffic:{ in: 622140, out: 142 }, p95ms: 211, errorRate: 0.2, calls24h: 8412,
    color:'#FA243C',
  },
  {
    id:'amazon', name:'Amazon Music', vendor:'Amazon.com Services', category:'dsp',
    status:'degraded', lastSync:'2h', freq:'Every 1h',
    ident:'rsm-publisher · vendor 9F210C',
    auth:'OAuth · client-creds', authExpires:'2026-06-30',
    scopes:['analytics:read','catalog:read'],
    capabilities:[
      { kind:'in', l:'Streams + station inclusions', surface:'Audience' },
      { kind:'in', l:'Alexa voice-request analytics', surface:'Audience' },
    ],
    traffic:{ in: 188422, out: 0 }, p95ms: 642, errorRate: 8.4, calls24h: 1840,
    color:'#FF9900',
    note:'Elevated 5xx rate from amzn endpoint since 14:22 — vendor incident',
  },
  {
    id:'tidal', name:'Tidal', vendor:'Tidal Music AS', category:'dsp',
    status:'healthy', lastSync:'12m', freq:'Every 30m',
    ident:'rs-publisher · partner P-0091',
    auth:'OAuth', authExpires:'2026-08-22',
    scopes:['analytics:read','catalog:read','hifi-master:read'],
    capabilities:[
      { kind:'in', l:'Streams (HiFi + Max tier split)', surface:'Audience · Royalties' },
      { kind:'in', l:'Master quality delivery confirmations', surface:'Recording detail' },
    ],
    traffic:{ in: 41200, out: 8 }, p95ms: 298, errorRate: 0.6, calls24h: 612,
    color:'#000000',
  },
  {
    id:'youtube', name:'YouTube Music', vendor:'Google LLC', category:'dsp',
    status:'healthy', lastSync:'1m', freq:'Every 5m',
    ident:'CMS · UC0918... · publisher 9921',
    auth:'OAuth + Content ID', authExpires:'2026-10-01',
    scopes:['analytics:read','contentid:read','contentid:write','asset:write'],
    capabilities:[
      { kind:'in',  l:'Streams + Music premium splits',     surface:'Audience · Royalties' },
      { kind:'in',  l:'Content ID match reports',           surface:'Recording → Platforms · Issues' },
      { kind:'in',  l:'UGC use-of-music claims',            surface:'Issues' },
      { kind:'out', l:'Asset (sound recording) registration', surface:'Recording → CMS sync' },
      { kind:'out', l:'Composition share registrations',    surface:'Work → CMS sync' },
    ],
    traffic:{ in: 942188, out: 1244 }, p95ms: 401, errorRate: 1.2, calls24h: 18420,
    color:'#FF0000',
  },
  {
    id:'pandora', name:'Pandora', vendor:'Pandora Media', category:'dsp',
    status:'healthy', lastSync:'48m', freq:'Every 1h',
    ident:'AMP · publisher 218',
    auth:'API key', authExpires:null,
    scopes:['analytics:read'],
    capabilities:[
      { kind:'in', l:'Spins + thumbs + station adds', surface:'Audience' },
      { kind:'in', l:'AMP playlist features', surface:'Audience' },
    ],
    traffic:{ in: 28220, out: 0 }, p95ms: 188, errorRate: 0.1, calls24h: 240,
    color:'#005483',
  },
  {
    id:'deezer', name:'Deezer', vendor:'Deezer SA', category:'dsp',
    status:'healthy', lastSync:'24m', freq:'Every 30m',
    ident:'rs-pub · biz-id 442098',
    auth:'OAuth', authExpires:'2026-05-18',
    scopes:['analytics:read'],
    capabilities:[
      { kind:'in', l:'Plays + saves (FR/BR/DE focus)', surface:'Audience' },
    ],
    traffic:{ in: 21044, out: 0 }, p95ms: 244, errorRate: 0.8, calls24h: 188,
    color:'#A238FF',
  },
  {
    id:'soundcloud', name:'SoundCloud', vendor:'SoundCloud Ltd.', category:'dsp',
    status:'healthy', lastSync:'1h', freq:'Every 1h',
    ident:'rs-music · partner 41203',
    auth:'OAuth', authExpires:'2026-11-30',
    scopes:['analytics:read','catalog:read'],
    capabilities:[
      { kind:'in', l:'Plays + reposts + comments', surface:'Audience · Recording detail' },
      { kind:'in', l:'Track ownership claims', surface:'Issues' },
    ],
    traffic:{ in: 14820, out: 0 }, p95ms: 212, errorRate: 1.0, calls24h: 88,
    color:'#FF5500',
  },
  // === Discovery / metadata ===
  {
    id:'genius', name:'Genius', vendor:'Genius Media Group', category:'metadata',
    status:'healthy', lastSync:'6h', freq:'Every 12h',
    ident:'partner ID 0911',
    auth:'OAuth', authExpires:'2026-12-15',
    scopes:['lyrics:read','annotations:read'],
    capabilities:[
      { kind:'in', l:'Lyrics + writer-credit verification', surface:'Work detail' },
      { kind:'in', l:'Annotations + reference catalog', surface:'Work detail' },
    ],
    traffic:{ in: 4220, out: 0 }, p95ms: 320, errorRate: 0.0, calls24h: 18,
    color:'#FFFF64',
  },
  // === Social ===
  {
    id:'meta', name:'Meta (FB · Instagram)', vendor:'Meta Platforms', category:'social',
    status:'reauth', lastSync:'4d', freq:'Every 6h',
    ident:'business 7142098 · catalog 9988',
    auth:'OAuth · long-lived', authExpires:'2026-04-12 (expired)',
    scopes:['music_partner:read','reels:read','rights:write'],
    capabilities:[
      { kind:'in',  l:'Reels music usage + retention', surface:'Audience' },
      { kind:'in',  l:'IG/FB Music library plays', surface:'Audience' },
      { kind:'out', l:'Rights manager · take-downs', surface:'Issues · Take-downs' },
    ],
    traffic:{ in: 0, out: 0 }, p95ms: null, errorRate: null, calls24h: 0,
    color:'#1877F2',
    note:'OAuth refresh token expired 18 days ago. Click Re-authorize.',
  },
  // === Rights orgs ===
  {
    id:'mlc', name:'The MLC', vendor:'Mechanical Licensing Collective', category:'rights',
    status:'healthy', lastSync:'14m', freq:'Every 2h',
    ident:'publisher RSMP · IPI 00731298011',
    auth:'API key + IP allow-list', authExpires:'2026-09-12',
    scopes:['works:read','works:write','distribution:read','claims:write'],
    capabilities:[
      { kind:'in',  l:'Bulk Match results', surface:'MLC sync · Royalties' },
      { kind:'in',  l:'Quarterly distribution feed', surface:'Royalties' },
      { kind:'out', l:'Work registrations',  surface:'MLC sync' },
      { kind:'out', l:'Claims & disputes',   surface:'MLC sync' },
    ],
    traffic:{ in: 14200, out: 612 }, p95ms: 182, errorRate: 0.3, calls24h: 412,
    color:'#0F62FE',
  },
  {
    id:'cwr', name:'CWR · Society Hub', vendor:'CISAC · Multi-society', category:'rights',
    status:'healthy', lastSync:'2h', freq:'On-batch',
    ident:'submitter SO · IPI 00731298011',
    auth:'SFTP · per-society', authExpires:null,
    scopes:['cwr:write','ack:read'],
    capabilities:[
      { kind:'out', l:'CWR 2.1 / 3.0 batch transmission', surface:'CWR · DDEX' },
      { kind:'in',  l:'NWR / REV / EXC acknowledgements', surface:'CWR · DDEX' },
    ],
    traffic:{ in: 882, out: 188 }, p95ms: null, errorRate: 0.2, calls24h: 22,
    color:'#5A4FCF',
  },
  // === Workflow ===
  {
    id:'docuseal', name:'Docuseal', vendor:'Docuseal', category:'workflow',
    status:'healthy', lastSync:'8m', freq:'Webhook',
    ident:'org rsmp · template-set 12',
    auth:'API key', authExpires:'2027-01-31',
    scopes:['envelope:write','envelope:read','webhook:read'],
    capabilities:[
      { kind:'out', l:'Send envelope for signature', surface:'Agreements' },
      { kind:'in',  l:'Status callbacks (sent / signed / declined)', surface:'Agreements' },
    ],
    traffic:{ in: 88, out: 14 }, p95ms: 244, errorRate: 0.0, calls24h: 18,
    color:'#222222',
  },
  // === Charts / market intel ===
  {
    id:'chartmetric', name:'Chartmetric', vendor:'Chartmetric Inc.', category:'metadata',
    status:'healthy', lastSync:'1h', freq:'Every 1h',
    ident:'org-key cm-rs9912',
    auth:'API key', authExpires:'2026-10-08',
    scopes:['charts:read','playlist-tracking:read','social:read'],
    capabilities:[
      { kind:'in', l:'Cross-platform chart positions', surface:'Audience' },
      { kind:'in', l:'Playlist-add tracking',          surface:'Audience' },
      { kind:'in', l:'Social follower velocity',       surface:'Audience' },
    ],
    traffic:{ in: 18420, out: 0 }, p95ms: 411, errorRate: 0.4, calls24h: 240,
    color:'#7B61FF',
  },
  // ════════════════════════════════════════════════════════════════════
  // === ENRICHMENT — public reference + open-data sources ==============
  // Read-only sources that enrich catalog metadata. No commercial
  // contract; rate-limited or community-maintained. Field-level
  // priority is governed by Enrichment policy (separate screen).
  // ════════════════════════════════════════════════════════════════════
  {
    id:'musicbrainz', name:'MusicBrainz', vendor:'MetaBrainz Foundation', category:'enrichment',
    status:'healthy', lastSync:'4h', freq:'Every 6h',
    ident:'app rocketscience-astro/1.0 (rs@rocktscience.com)',
    auth:'User-Agent + OAuth (writes)', authExpires:'2027-02-14',
    scopes:['ws/2:read','isrc:read','iswc:read','release:read','artist:read','recording:read','place:read'],
    capabilities:[
      { kind:'in', l:'MBID resolution (artist/release/recording/work)', surface:'Catalog → MBID column' },
      { kind:'in', l:'ISRC ↔ recording authoritative mapping',          surface:'Recording detail · Issues' },
      { kind:'in', l:'ISWC ↔ work cross-references',                    surface:'Work detail' },
      { kind:'in', l:'Release-group + artist credits',                  surface:'Release detail' },
      { kind:'in', l:'Place codes (studios, venues)',                   surface:'Recording detail · sessions' },
      { kind:'out', l:'ISRC submission (verified labels)',              surface:'Recording detail · MB sync' },
    ],
    traffic:{ in: 18420, out: 24 }, p95ms: 318, errorRate: 0.3, calls24h: 412,
    color:'#BA478F',
    note:'Hard rate limit: 1 req/sec per IP — backoff active',
  },
  {
    id:'discogs', name:'Discogs', vendor:'Discogs Inc.', category:'enrichment',
    status:'healthy', lastSync:'6h', freq:'Every 12h',
    ident:'consumer rs-astro · token v2',
    auth:'OAuth 1.0a + Personal token', authExpires:'2026-12-30',
    scopes:['database:read','marketplace:read','user-collection:read'],
    capabilities:[
      { kind:'in', l:'Master + release catalog (vinyl/CD/cassette)', surface:'Release detail · physical' },
      { kind:'in', l:'Label hierarchy + sublabel relations',         surface:'Imprint hierarchy' },
      { kind:'in', l:'Discogs barcode → release resolution',         surface:'Catalog · UPC lookup' },
      { kind:'in', l:'Marketplace median + low/high price',          surface:'Release · physical valuation' },
      { kind:'in', l:'Format details (matrix/runout/pressing)',      surface:'Release detail · pressings' },
    ],
    traffic:{ in: 4220, out: 0 }, p95ms: 421, errorRate: 0.6, calls24h: 188,
    color:'#000000',
  },
  {
    id:'acousticbrainz', name:'AcousticBrainz', vendor:'MetaBrainz Foundation', category:'enrichment',
    status:'archived', lastSync:'archived', freq:'Read-only archive',
    ident:'static dump 2022.05 · 30M tracks',
    auth:'None (public dump)', authExpires:null,
    scopes:['low-level:read','high-level:read'],
    capabilities:[
      { kind:'in', l:'Pre-computed BPM / key / tonal features',  surface:'Recording detail · features' },
      { kind:'in', l:'High-level mood + danceability classifiers', surface:'Recording → audio intelligence' },
      { kind:'in', l:'Loudness / dynamic-complexity metrics',    surface:'Recording detail · technical' },
    ],
    traffic:{ in: 1284, out: 0 }, p95ms: null, errorRate: 0.0, calls24h: 0,
    color:'#5C7AEA',
    note:'Project shut down 2022 — this is the read-only dump. Live analysis runs on internal Essentia instead.',
  },
  {
    id:'listenbrainz', name:'ListenBrainz', vendor:'MetaBrainz Foundation', category:'enrichment',
    status:'healthy', lastSync:'2h', freq:'Every 4h',
    ident:'submitter rocketscience-astro · token lb-v3',
    auth:'Token', authExpires:'2027-04-02',
    scopes:['listens:read','recommendations:read','stats:read','radio:read'],
    capabilities:[
      { kind:'in', l:'Open listen counts (community-submitted)',        surface:'Audience · open-data overlay' },
      { kind:'in', l:'Similar-recordings / similar-artists graph',      surface:'Recording → similar' },
      { kind:'in', l:'Listening-context tags (yearly stats)',           surface:'Audience · cohorts' },
      { kind:'in', l:'Radio playlist seed → recommended tracklists',    surface:'Pitch · sync · neighbors' },
    ],
    traffic:{ in: 2840, out: 0 }, p95ms: 244, errorRate: 0.4, calls24h: 84,
    color:'#EB743B',
  },
  {
    id:'youtube-data', name:'YouTube Data API v3', vendor:'Google LLC', category:'enrichment',
    status:'healthy', lastSync:'14m', freq:'Every 30m',
    ident:'project astro-yt-data · key AIza••••',
    auth:'API key + OAuth (uploads)', authExpires:'2026-10-01',
    scopes:['youtube.readonly','youtube.upload','captions.read'],
    capabilities:[
      { kind:'in', l:'Channel + video search by ISRC / title',     surface:'Recording → Platforms · YouTube' },
      { kind:'in', l:'Captions / lyrics tracks (community + auto)', surface:'Work detail · lyrics' },
      { kind:'in', l:'Topic-tag classification',                   surface:'Recording detail · genre' },
      { kind:'in', l:'Playlist & topic-channel membership',        surface:'Audience' },
      { kind:'out', l:'Video upload (releases & lyric videos)',    surface:'Release · Promo' },
    ],
    traffic:{ in: 84200, out: 22 }, p95ms: 388, errorRate: 0.7, calls24h: 1840,
    color:'#FF0000',
    note:'Quota: 10k units/day · current burn 7.2k — review uploads if approaching cap',
  },
  {
    id:'google-kg', name:'Google Knowledge Graph', vendor:'Google LLC', category:'enrichment',
    status:'healthy', lastSync:'12h', freq:'On-demand',
    ident:'project astro-kg · key AIza••••',
    auth:'API key', authExpires:'2026-10-01',
    scopes:['kg.search:read'],
    capabilities:[
      { kind:'in', l:'Entity disambiguation (artist / band / venue)', surface:'Catalog · entity resolve' },
      { kind:'in', l:'Wikipedia/Wikidata canonical IDs',              surface:'Party detail · external IDs' },
      { kind:'in', l:'Schema.org type tags (MusicGroup, Person)',     surface:'Party detail · roles' },
      { kind:'in', l:'Entity description + image',                    surface:'Party detail · presentation' },
    ],
    traffic:{ in: 412, out: 0 }, p95ms: 280, errorRate: 0.0, calls24h: 18,
    color:'#4285F4',
  },
  {
    id:'google-places', name:'Google Places', vendor:'Google LLC', category:'enrichment',
    status:'healthy', lastSync:'1d', freq:'On-demand',
    ident:'project astro-places · key AIza••••',
    auth:'API key', authExpires:'2026-10-01',
    scopes:['places.details:read','places.search:read'],
    capabilities:[
      { kind:'in', l:'Studio + venue resolution (place_id)',     surface:'Recording detail · session location' },
      { kind:'in', l:'Tour-routing geocoding (artist itinerary)', surface:'Audience · tour overlay' },
      { kind:'in', l:'Rights-territory inference from venue ISO', surface:'Live royalties · territory match' },
    ],
    traffic:{ in: 88, out: 0 }, p95ms: 240, errorRate: 0.0, calls24h: 4,
    color:'#34A853',
  },
  {
    id:'music-metadata', name:'music-metadata', vendor:'Borewit (npm) · self-hosted', category:'enrichment',
    status:'healthy', lastSync:'live', freq:'On-upload',
    ident:'package music-metadata@10.5.0 · runtime astro-ingest',
    auth:'Library (no auth)', authExpires:null,
    scopes:['file:read'],
    capabilities:[
      { kind:'in', l:'ID3v1 / ID3v2 / APE / Vorbis tag extraction', surface:'Bulk import · ingest pipeline' },
      { kind:'in', l:'MP4 / iTunes / FLAC / OGG tag map',           surface:'Recording detail · technical' },
      { kind:'in', l:'Embedded ISRC / barcode / UPC',               surface:'Catalog · auto-populate' },
      { kind:'in', l:'Audio fingerprint precursor (PCM hash)',      surface:'Audio FP · ingest hook' },
    ],
    traffic:{ in: 41200, out: 0 }, p95ms: 41, errorRate: 0.0, calls24h: 412,
    color:'#3D7A35',
    note:'Runs in-process during file upload; not an external API but counts as an integration source.',
  },
  {
    id:'songlink', name:'Songlink · Odesli', vendor:'Song, Link Inc.', category:'enrichment',
    status:'healthy', lastSync:'1h', freq:'Every 2h',
    ident:'partner rs-astro · key sl-v1',
    auth:'API key', authExpires:'2026-12-31',
    scopes:['links:read'],
    capabilities:[
      { kind:'in', l:'Cross-platform link resolution (one URL → all DSPs)', surface:'Release · smart-link · pre-save' },
      { kind:'in', l:'Equivalence map (Spotify ↔ Apple ↔ Tidal IDs)',       surface:'Recording → Platforms' },
      { kind:'in', l:'Region-availability map per platform',                surface:'Release · territory windows' },
    ],
    traffic:{ in: 1840, out: 0 }, p95ms: 188, errorRate: 0.2, calls24h: 88,
    color:'#FE6E58',
  },
  {
    id:'lrclib', name:'LRCLIB', vendor:'LRCLIB (open lyric DB)', category:'enrichment',
    status:'healthy', lastSync:'30m', freq:'On-lookup',
    ident:'public · no key',
    auth:'None (public)', authExpires:null,
    scopes:['lyrics:read','lrc:read'],
    capabilities:[
      { kind:'in', l:'Plain lyrics (community-contributed)',        surface:'Work detail · lyrics' },
      { kind:'in', l:'Synced (timestamped) LRC for karaoke / sync', surface:'Lyric video · sync · captions' },
      { kind:'in', l:'Track-by-track lookup via ISRC + duration',   surface:'Recording detail · lyrics' },
    ],
    traffic:{ in: 8420, out: 0 }, p95ms: 142, errorRate: 0.4, calls24h: 184,
    color:'#1F1F1F',
  },
];

// ─────────── Activity log (last 24h) — unified across all connectors
function buildActivity() {
  if (window.__INTEGRATIONS_LOG_CACHE) return window.__INTEGRATIONS_LOG_CACHE;
  const out = [];
  const now = Date.now();
  // Synthesize ~80 events deterministically across connectors
  CONNECTORS.forEach((c, ci) => {
    const eventCount = c.status === 'reauth' ? 2
      : Math.max(2, Math.min(14, Math.floor((c.calls24h||10)/600)));
    for (let i = 0; i < eventCount; i++) {
      const seed = (ci * 37 + i * 7);
      const minutesAgo = (i * 41 + seed * 3) % 1440;
      const isErr = c.status === 'degraded' && (i % 3 === 0);
      const isReauth = c.status === 'reauth' && i === 0;
      const kind = isReauth ? 'auth-expired'
        : isErr ? 'error'
        : (i % 5 === 0) ? 'write'
        : 'read';
      const cap = c.capabilities[i % c.capabilities.length];
      out.push({
        ts: new Date(now - minutesAgo * 60000).toISOString(),
        minutesAgo,
        connector: c.id, connectorName: c.name, color: c.color,
        kind, status: isErr ? 502 : isReauth ? 401 : (kind === 'write' ? 201 : 200),
        path: cap ? cap.l : 'sync',
        ms: c.p95ms ? c.p95ms + (seed % 200) - 100 : 200,
        bytesIn: kind === 'read' ? (50000 + (seed*1024)%4000000) : 0,
        bytesOut: kind === 'write' ? (8000 + (seed*322)%200000) : 0,
      });
    }
  });
  out.sort((a, b) => a.minutesAgo - b.minutesAgo);
  window.__INTEGRATIONS_LOG_CACHE = out;
  return out;
}

// ─────────── helpers
const fmtBig = (n) => {
  if (n == null) return '—';
  if (n >= 1e9) return (n/1e9).toFixed(1) + 'B';
  if (n >= 1e6) return (n/1e6).toFixed(1) + 'M';
  if (n >= 1e3) return (n/1e3).toFixed(1) + 'k';
  return n.toLocaleString();
};
const fmtBytes = (n) => {
  if (n == null || n === 0) return '—';
  if (n >= 1e9) return (n/1e9).toFixed(1) + ' GB';
  if (n >= 1e6) return (n/1e6).toFixed(1) + ' MB';
  if (n >= 1e3) return (n/1e3).toFixed(1) + ' KB';
  return n + ' B';
};
const STATUS_TONE = {
  healthy:  { fg:'var(--ok)',     bg:'var(--ok)',     l:'Healthy'      },
  degraded: { fg:'var(--warn, #d4881f)', bg:'var(--warn, #d4881f)', l:'Degraded' },
  reauth:   { fg:'var(--danger)', bg:'var(--danger)', l:'Re-auth'      },
  silent:   { fg:'var(--ink-3)',  bg:'var(--ink-3)',  l:'Silent'       },
  archived: { fg:'var(--ink-3)',  bg:'var(--ink-3)',  l:'Archived'     },
};
const CATEGORY_LABEL = {
  dsp:'DSPs · Streaming',
  metadata:'Metadata · Discovery',
  enrichment:'Enrichment · Public reference',
  social:'Social · UGC',
  rights:'Rights orgs',
  workflow:'Workflow',
};

// ─────────── connector swatch (categorical color block, not a logo)
function ConnSwatch({ color, size=12 }) {
  return <span style={{display:'inline-block',width:size,height:size,background:color,flexShrink:0,
    boxShadow:'inset 0 0 0 1px rgba(0,0,0,.08)'}}/>;
}

// ─────────── KPI cell
function ICKpi({ l, v, sub, danger, accent }) {
  return (
    <div style={{padding:'18px 20px',display:'flex',flexDirection:'column',gap:4,minHeight:96}}>
      <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.12em',color:'var(--ink-3)'}}>{l}</div>
      <div className="ff-mono num" style={{fontSize:28,lineHeight:1,fontWeight:500,
        color: danger ? 'var(--danger)' : (accent ? 'var(--accent-ink)' : 'var(--ink)'),
        background: accent ? 'var(--accent)' : 'transparent',
        padding: accent ? '2px 6px' : 0, alignSelf:'flex-start', marginTop:4,
      }}>{v}</div>
      {sub && <div style={{fontSize:11,color:'var(--ink-3)',marginTop:4,lineHeight:1.4}}>{sub}</div>}
    </div>
  );
}

// ─────────── connector card (overview grid)
function ConnectorCard({ c, onOpen }) {
  const tone = STATUS_TONE[c.status];
  const inflow = c.capabilities.filter(x=>x.kind==='in').length;
  const outflow = c.capabilities.filter(x=>x.kind==='out').length;
  return (
    <button onClick={()=>onOpen(c.id)} style={{
      textAlign:'left', padding:18, border:'1px solid var(--rule)', background:'var(--paper)',
      cursor:'pointer', display:'flex', flexDirection:'column', gap:0, minHeight:170, position:'relative',
    }}>
      {/* status indicator strip */}
      <div style={{position:'absolute',top:0,left:0,right:0,height:2,background:tone.bg}}/>
      {/* head */}
      <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:14}}>
        <ConnSwatch color={c.color} size={16}/>
        <div style={{flex:1,minWidth:0}}>
          <div style={{fontSize:13,fontWeight:600,letterSpacing:'-0.01em'}}>{c.name}</div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:1,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{c.vendor}</div>
        </div>
        <span style={{display:'inline-flex',alignItems:'center',gap:4,fontSize:10,
          color: tone.fg, padding:'2px 6px', border:'1px solid '+tone.fg}}
          className="ff-mono upper">
          <span style={{display:'inline-block',width:5,height:5,borderRadius:3,background:tone.bg}}/>
          {tone.l}
        </span>
      </div>

      {/* metrics row */}
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:0,borderTop:'1px solid var(--rule)',marginTop:'auto'}}>
        <div style={{padding:'10px 0',borderRight:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)'}}>LAST SYNC</div>
          <div className="ff-mono" style={{fontSize:12,marginTop:2}}>{c.lastSync}</div>
        </div>
        <div style={{padding:'10px 10px',borderRight:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)'}}>CALLS · 24H</div>
          <div className="ff-mono num" style={{fontSize:12,marginTop:2}}>{fmtBig(c.calls24h)}</div>
        </div>
        <div style={{padding:'10px 0 10px 10px'}}>
          <div className="ff-mono upper" style={{fontSize:8,letterSpacing:'.1em',color:'var(--ink-3)'}}>P95</div>
          <div className="ff-mono num" style={{fontSize:12,marginTop:2,
            color: c.p95ms == null ? 'var(--ink-3)'
                  : c.p95ms < 300 ? 'var(--ok)'
                  : c.p95ms < 600 ? 'var(--ink)'
                  : 'var(--danger)'}}>
            {c.p95ms == null ? '—' : c.p95ms+'ms'}
          </div>
        </div>
      </div>

      {/* flow arrows */}
      <div style={{display:'flex',gap:14,marginTop:12,fontSize:11,color:'var(--ink-2)'}}>
        <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
          <span className="ff-mono" style={{color:'var(--ok)'}}>↓</span>
          <span className="ff-mono num">{inflow}</span>
          <span style={{color:'var(--ink-3)'}}>read</span>
        </span>
        <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
          <span className="ff-mono" style={{color: outflow ? 'var(--info)' : 'var(--ink-4)'}}>↑</span>
          <span className="ff-mono num">{outflow}</span>
          <span style={{color:'var(--ink-3)'}}>write</span>
        </span>
        <span style={{marginLeft:'auto',color: c.errorRate == null ? 'var(--ink-3)'
                    : c.errorRate < 1 ? 'var(--ok)'
                    : c.errorRate < 5 ? 'var(--warn, #d4881f)'
                    : 'var(--danger)'}}>
          <span className="ff-mono num">{c.errorRate == null ? '—' : c.errorRate.toFixed(1)+'%'}</span>
          <span style={{color:'var(--ink-3)',marginLeft:4}}>err</span>
        </span>
      </div>
    </button>
  );
}

// ─────────── traffic chart (24h hourly)
function TrafficChart() {
  // Synthesize a deterministic 24-bar profile of cumulative calls
  const H = 24;
  const data = useIM(() => {
    const out = [];
    for (let h=0; h<H; h++) {
      const dayCurve = Math.sin((h-7)/24 * Math.PI*2)*0.4 + 0.6; // peaks midday
      let total = 0; const breakdown = {};
      CONNECTORS.forEach(c => {
        const base = (c.calls24h || 0) / H;
        const v = Math.max(0, Math.round(base * (0.5 + dayCurve * 1.2)));
        breakdown[c.id] = v; total += v;
      });
      out.push({ h, total, breakdown });
    }
    return out;
  }, []);
  const max = Math.max(...data.map(d => d.total), 1);
  const W = 760, HH = 110;
  const bw = W / H;
  return (
    <svg width="100%" height={HH+24} viewBox={`0 0 ${W} ${HH+24}`} preserveAspectRatio="none">
      {data.map((d,i) => {
        const h = (d.total/max) * HH;
        let y = HH;
        return (
          <g key={i}>
            {CONNECTORS.map(c => {
              const v = d.breakdown[c.id];
              if (!v) return null;
              const seg = (v/max) * HH;
              y -= seg;
              return <rect key={c.id} x={i*bw + bw*0.1} y={y} width={bw*0.8} height={seg} fill={c.color} opacity={0.92}/>;
            })}
            {(i%4===0) && (
              <text x={i*bw + bw*0.5} y={HH+14} textAnchor="middle" fontSize="9" fill="var(--ink-3)" fontFamily="var(--ff-mono)">
                {String((i+0)).padStart(2,'0')}:00
              </text>
            )}
          </g>
        );
      })}
      <line x1={0} y1={HH} x2={W} y2={HH} stroke="var(--rule)" strokeWidth="1"/>
    </svg>
  );
}

// ─────────── 01 OVERVIEW
function ICOverviewTab({ onOpenConnector, setTab }) {
  const stats = useIM(() => {
    const c = { healthy:0, degraded:0, reauth:0, silent:0 };
    CONNECTORS.forEach(x => c[x.status]++);
    const totalCalls = CONNECTORS.reduce((s,x)=>s+(x.calls24h||0),0);
    const totalIn   = CONNECTORS.reduce((s,x)=>s+(x.traffic.in||0),0);
    const totalOut  = CONNECTORS.reduce((s,x)=>s+(x.traffic.out||0),0);
    return { ...c, totalCalls, totalIn, totalOut };
  }, []);

  const grouped = useIM(() => {
    const g = {};
    CONNECTORS.forEach(c => { (g[c.category] ||= []).push(c); });
    return g;
  }, []);

  return (
    <div>
      {/* KPI strip */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(5, 1fr)',borderTop:'1px solid var(--rule)',borderBottom:'1px solid var(--rule)'}}>
        <ICKpi l="WIRED INTEGRATIONS" v={CONNECTORS.length}
          sub={`${stats.healthy} healthy · ${stats.degraded} degraded · ${stats.reauth} re-auth`}/>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="API CALLS · 24H" v={fmtBig(stats.totalCalls)} sub="across all wired endpoints"/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="DATA IN" v={fmtBig(stats.totalIn)} sub="rows ingested · last 24h"/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="DATA OUT" v={fmtBig(stats.totalOut)} sub="registrations + writes · last 24h"/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="NEED ATTENTION"
            v={stats.degraded + stats.reauth}
            danger={stats.degraded + stats.reauth > 0}
            sub={(stats.reauth > 0 ? `${stats.reauth} expired credential${stats.reauth>1?'s':''}` : 'all credentials valid')
              + (stats.degraded > 0 ? ` · ${stats.degraded} elevated error rate` : '')}/>
        </div>
      </div>

      {/* Traffic chart */}
      <div style={{marginTop:30}}>
        <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:14}}>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)'}}>
            API TRAFFIC · LAST 24H · STACKED BY CONNECTOR
          </div>
          <div className="ff-mono num" style={{fontSize:11,color:'var(--ink-3)'}}>{fmtBig(stats.totalCalls)} calls</div>
        </div>
        <div style={{border:'1px solid var(--rule)',padding:'12px 16px 4px',background:'var(--paper)'}}>
          <TrafficChart/>
        </div>
        <div style={{display:'flex',flexWrap:'wrap',gap:14,marginTop:12,fontSize:10,color:'var(--ink-2)'}}>
          {CONNECTORS.filter(c => c.calls24h > 50).slice(0, 10).map(c => (
            <span key={c.id} style={{display:'inline-flex',alignItems:'center',gap:5}}>
              <ConnSwatch color={c.color} size={9}/> {c.name}
            </span>
          ))}
        </div>
      </div>

      {/* Connector grid grouped by category */}
      {Object.entries(grouped).map(([cat, items]) => (
        <div key={cat} style={{marginTop:32}}>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:14,paddingBottom:8,borderBottom:'1px solid var(--rule)'}}>
            {CATEGORY_LABEL[cat] || cat} <span className="ff-mono num" style={{color:'var(--ink-4)',marginLeft:8}}>{items.length}</span>
          </div>
          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(280px, 1fr))',gap:14}}>
            {items.map(c => <ConnectorCard key={c.id} c={c} onOpen={onOpenConnector}/>)}
          </div>
        </div>
      ))}

      {/* CTA strip */}
      <div style={{marginTop:36,padding:18,background:'var(--bg-2)',border:'1px solid var(--rule)',display:'flex',gap:18,alignItems:'center'}}>
        <div style={{flex:1}}>
          <div style={{fontSize:13,fontWeight:600,marginBottom:4}}>Looking for something else?</div>
          <div style={{fontSize:12,color:'var(--ink-2)',lineHeight:1.5}}>
            ASTRO supports <span className="ff-mono num">{CONNECTORS.length}</span> wired integrations today. Need a new society, a regional DSP, or a custom partner endpoint? File a request — most are 5–10 days end-to-end including onboarding.{' '}
            <a onClick={(e)=>{e.preventDefault(); window.dispatchEvent(new CustomEvent('astro:go',{detail:{name:'enrichment-policy'}}));}}
              href="#" style={{color:'var(--ink)',borderBottom:'1px solid var(--ink-3)'}}>Open Enrichment policy →</a>
          </div>
        </div>
        <button className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'10px 16px',background:'var(--ink)',color:'var(--bg)',border:0,cursor:'pointer',whiteSpace:'nowrap'}}>
          REQUEST NEW INTEGRATION →
        </button>
      </div>
    </div>
  );
}

// ─────────── 02 CONNECTORS — full detail list
function ICConnectorsTab({ openId, setOpenId }) {
  const c = openId ? CONNECTORS.find(x => x.id === openId) : null;
  if (c) return <ICConnectorDetail c={c} onBack={()=>setOpenId(null)}/>;

  return (
    <div style={{paddingTop:8}}>
      <div style={{border:'1px solid var(--rule)'}}>
        {CONNECTORS.map((c, i) => {
          const tone = STATUS_TONE[c.status];
          const inflow = c.capabilities.filter(x=>x.kind==='in').length;
          const outflow = c.capabilities.filter(x=>x.kind==='out').length;
          return (
            <div key={c.id} onClick={()=>setOpenId(c.id)}
              style={{display:'grid',gridTemplateColumns:'auto 1.4fr 1fr 0.8fr 0.7fr 0.7fr 0.7fr auto',gap:14,padding:'14px 18px',
                alignItems:'center',borderBottom: i<CONNECTORS.length-1?'1px solid var(--rule)':'none',cursor:'pointer'}}>
              <ConnSwatch color={c.color} size={14}/>
              <div>
                <div style={{fontSize:13,fontWeight:600}}>{c.name}</div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:1}}>{c.ident}</div>
              </div>
              <div>
                <span style={{display:'inline-flex',alignItems:'center',gap:6,fontSize:11,color:tone.fg,padding:'2px 6px',border:'1px solid '+tone.fg}} className="ff-mono upper">
                  <span style={{display:'inline-block',width:5,height:5,borderRadius:3,background:tone.bg}}/>{tone.l}
                </span>
                {c.note && <div style={{fontSize:10,color:'var(--danger)',marginTop:4}}>{c.note}</div>}
              </div>
              <div className="ff-mono" style={{fontSize:11,color:'var(--ink-2)'}}>
                <div>{c.lastSync}</div>
                <div style={{fontSize:10,color:'var(--ink-3)'}}>{c.freq}</div>
              </div>
              <div className="ff-mono num" style={{fontSize:11,textAlign:'right'}}>
                <div>{fmtBig(c.calls24h)}</div>
                <div style={{fontSize:10,color:'var(--ink-3)'}}>calls/24h</div>
              </div>
              <div className="ff-mono num" style={{fontSize:11,textAlign:'right'}}>
                <div style={{color: c.errorRate==null ? 'var(--ink-3)' : c.errorRate < 1 ? 'var(--ok)' : c.errorRate < 5 ? 'var(--warn, #d4881f)' : 'var(--danger)'}}>
                  {c.errorRate == null ? '—' : c.errorRate.toFixed(1)+'%'}
                </div>
                <div style={{fontSize:10,color:'var(--ink-3)'}}>err</div>
              </div>
              <div style={{fontSize:11,color:'var(--ink-2)'}}>
                <span className="ff-mono num" style={{color:'var(--ok)'}}>↓{inflow}</span>{' '}
                <span className="ff-mono num" style={{color: outflow ? 'var(--info)' : 'var(--ink-4)',marginLeft:6}}>↑{outflow}</span>
              </div>
              <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)'}}>OPEN →</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─────────── connector detail (drill-in)
function ICConnectorDetail({ c, onBack }) {
  const tone = STATUS_TONE[c.status];
  const log = useIM(() => buildActivity().filter(e => e.connector === c.id), [c.id]);
  return (
    <div style={{paddingTop:8}}>
      {/* breadcrumb / back */}
      <button onClick={onBack} className="ff-mono upper"
        style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)',background:'transparent',border:0,cursor:'pointer',padding:0,marginBottom:14}}>
        ← ALL CONNECTORS
      </button>

      {/* head */}
      <div style={{display:'flex',alignItems:'flex-start',gap:18,marginBottom:24}}>
        <div style={{width:48,height:48,background:c.color,boxShadow:'inset 0 0 0 1px rgba(0,0,0,.1)'}}/>
        <div style={{flex:1}}>
          <div style={{display:'flex',alignItems:'baseline',gap:14}}>
            <h1 style={{fontSize:32,fontWeight:500,letterSpacing:'-0.02em',margin:0}}>{c.name}</h1>
            <span style={{display:'inline-flex',alignItems:'center',gap:6,fontSize:11,color:tone.fg,padding:'3px 8px',border:'1px solid '+tone.fg}} className="ff-mono upper">
              <span style={{display:'inline-block',width:6,height:6,borderRadius:3,background:tone.bg}}/>{tone.l}
            </span>
          </div>
          <div className="ff-mono" style={{fontSize:11,color:'var(--ink-3)',marginTop:4}}>
            {c.vendor} · {c.ident}
          </div>
          {c.note && (
            <div style={{marginTop:10,padding:'10px 14px',background:'rgba(180,40,40,.06)',borderLeft:'3px solid var(--danger)',fontSize:12,color:'var(--ink)'}}>
              {c.note}
            </div>
          )}
        </div>
        <div style={{display:'flex',gap:8}}>
          {c.status === 'reauth' && (
            <button className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'8px 14px',background:'var(--danger)',color:'#fff',border:0,cursor:'pointer'}}>
              RE-AUTHORIZE →
            </button>
          )}
          <button className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'8px 14px',background:'var(--ink)',color:'var(--bg)',border:0,cursor:'pointer'}}>
            SYNC NOW ↻
          </button>
          <button className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'8px 14px',background:'transparent',color:'var(--ink-2)',border:'1px solid var(--rule)',cursor:'pointer'}}>
            CONFIGURE
          </button>
        </div>
      </div>

      {/* metrics row */}
      <div style={{display:'grid',gridTemplateColumns:'repeat(6, 1fr)',border:'1px solid var(--rule)'}}>
        <ICKpi l="LAST SYNC" v={c.lastSync} sub={c.freq}/>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="CALLS · 24H" v={fmtBig(c.calls24h)}/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="P95 LATENCY" v={c.p95ms == null ? '—' : c.p95ms+'ms'}/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="ERROR RATE" v={c.errorRate == null ? '—' : c.errorRate.toFixed(1)+'%'} danger={c.errorRate > 5}/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="DATA IN · 24H" v={fmtBig(c.traffic.in)} sub="records"/>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)'}}>
          <ICKpi l="DATA OUT · 24H" v={fmtBig(c.traffic.out)} sub="writes"/>
        </div>
      </div>

      {/* two-col body */}
      <div style={{display:'grid',gridTemplateColumns:'1.4fr 1fr',gap:30,marginTop:30}}>
        {/* capabilities */}
        <div>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:14}}>
            CAPABILITIES · WHAT FLOWS
          </div>
          <div style={{border:'1px solid var(--rule)'}}>
            {c.capabilities.map((cap, i) => (
              <div key={i} style={{display:'grid',gridTemplateColumns:'auto 1fr auto',gap:14,padding:'14px 16px',alignItems:'center',
                borderBottom: i<c.capabilities.length-1?'1px solid var(--rule)':'none'}}>
                <div className="ff-mono upper" style={{
                  fontSize:9,letterSpacing:'.1em',padding:'3px 8px',
                  color: cap.kind==='in'?'var(--ok)':'var(--info)',
                  border:'1px solid '+(cap.kind==='in'?'var(--ok)':'var(--info)'),
                  whiteSpace:'nowrap'
                }}>
                  {cap.kind === 'in' ? '↓ READ' : '↑ WRITE'}
                </div>
                <div style={{fontSize:13}}>{cap.l}</div>
                <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',whiteSpace:'nowrap'}}>→ {cap.surface}</div>
              </div>
            ))}
          </div>

          {/* recent activity */}
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginTop:30,marginBottom:14}}>
            RECENT ACTIVITY · LAST 24H
          </div>
          <div style={{border:'1px solid var(--rule)',maxHeight:360,overflowY:'auto'}}>
            {log.slice(0, 14).map((e,i) => <ActivityRow key={i} e={e}/>)}
          </div>
        </div>

        {/* auth + scopes + meta */}
        <div>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:14}}>
            AUTHENTICATION
          </div>
          <div style={{border:'1px solid var(--rule)',padding:16,marginBottom:24}}>
            <ICKvRow l="Method" v={c.auth}/>
            <ICKvRow l="Status" v={c.status === 'reauth' ? <span style={{color:'var(--danger)'}}>Expired</span> : <span style={{color:'var(--ok)'}}>Valid</span>}/>
            <ICKvRow l="Expires" v={c.authExpires || 'Never'} mono last/>
          </div>

          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:14}}>
            SCOPES GRANTED
          </div>
          <div style={{border:'1px solid var(--rule)',padding:16,marginBottom:24}}>
            <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
              {c.scopes.map(s => (
                <span key={s} className="ff-mono" style={{fontSize:11,padding:'3px 8px',background:'var(--bg-2)',color:'var(--ink-2)',border:'1px solid var(--rule)'}}>{s}</span>
              ))}
            </div>
          </div>

          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.12em',color:'var(--ink-3)',marginBottom:14}}>
            DEPENDENT SURFACES
          </div>
          <div style={{border:'1px solid var(--rule)',padding:16}}>
            <div style={{fontSize:12,color:'var(--ink-2)',lineHeight:1.6}}>
              {Array.from(new Set(c.capabilities.map(x=>x.surface))).map((s,i,arr) => (
                <span key={s}>
                  <span style={{color:'var(--ink)'}}>{s}</span>
                  {i<arr.length-1 && <span style={{color:'var(--ink-4)'}}> · </span>}
                </span>
              ))}
            </div>
            <div style={{fontSize:11,color:'var(--ink-3)',marginTop:10,paddingTop:10,borderTop:'1px solid var(--rule)',lineHeight:1.5}}>
              If this connector goes silent, these screens lose data within {c.freq.toLowerCase().includes('every') ? c.freq.toLowerCase() : 'a sync cycle'}.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function ICKvRow({ l, v, mono, last }) {
  return (
    <div style={{display:'flex',justifyContent:'space-between',gap:14,padding:'7px 0',fontSize:12,
      borderBottom: last ? 'none' : '1px solid var(--rule-soft, var(--bg-2))'}}>
      <span style={{color:'var(--ink-3)'}}>{l}</span>
      <span className={mono?'ff-mono':''} style={{color:'var(--ink)',textAlign:'right'}}>{v}</span>
    </div>
  );
}

// ─────────── activity row (used in detail + activity tab)
function ActivityRow({ e, withConnector }) {
  const KIND = {
    read:          { fg:'var(--ok)',     l:'READ',     symbol:'↓' },
    write:         { fg:'var(--info)',   l:'WRITE',    symbol:'↑' },
    error:         { fg:'var(--danger)', l:'ERROR',    symbol:'×' },
    'auth-expired':{ fg:'var(--danger)', l:'AUTH',     symbol:'⚠' },
  };
  const k = KIND[e.kind] || KIND.read;
  const ago = e.minutesAgo < 60 ? `${e.minutesAgo}m`
    : e.minutesAgo < 1440 ? `${Math.floor(e.minutesAgo/60)}h`
    : `${Math.floor(e.minutesAgo/1440)}d`;
  return (
    <div style={{display:'grid',gridTemplateColumns: withConnector ? '60px 130px 70px 1fr 80px 90px' : '60px 70px 1fr 80px 90px',
      gap:14,padding:'9px 14px',alignItems:'center',borderBottom:'1px solid var(--rule-soft, var(--bg-2))',fontSize:11}}>
      <span className="ff-mono" style={{color:'var(--ink-3)'}}>{ago}</span>
      {withConnector && (
        <span style={{display:'inline-flex',alignItems:'center',gap:6}}>
          <ConnSwatch color={e.color} size={9}/>
          <span style={{fontSize:11}}>{e.connectorName}</span>
        </span>
      )}
      <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.08em',color:k.fg,padding:'2px 6px',border:'1px solid '+k.fg,justifySelf:'start'}}>
        {k.symbol} {k.l}
      </span>
      <span style={{color:'var(--ink-2)'}}>{e.path}</span>
      <span className="ff-mono num" style={{textAlign:'right',color: e.status >= 400 ? 'var(--danger)' : 'var(--ink-2)'}}>
        {e.status}
      </span>
      <span className="ff-mono num" style={{textAlign:'right',color:'var(--ink-3)'}}>
        {e.kind === 'read' ? fmtBytes(e.bytesIn) : e.kind === 'write' ? fmtBytes(e.bytesOut) : e.ms+'ms'}
      </span>
    </div>
  );
}

// ─────────── 03 ACTIVITY
function ICActivityTab() {
  const [filter, setFilter] = useIS('all');
  const [conn, setConn] = useIS('all');
  const log = useIM(() => buildActivity(), []);
  const filtered = log.filter(e => {
    if (filter !== 'all' && e.kind !== filter) return false;
    if (conn !== 'all' && e.connector !== conn) return false;
    return true;
  });
  return (
    <div style={{paddingTop:8}}>
      {/* filter strip */}
      <div style={{display:'flex',gap:0,borderBottom:'1px solid var(--rule)',alignItems:'center',padding:'0 0 10px'}}>
        <div style={{display:'flex',gap:0}}>
          {['all','read','write','error','auth-expired'].map(k => (
            <button key={k} onClick={()=>setFilter(k)} className="ff-mono upper"
              style={{padding:'8px 12px',border:0,background: filter===k?'var(--ink)':'transparent',color:filter===k?'var(--bg)':'var(--ink-2)',
                fontSize:10,letterSpacing:'.06em',cursor:'pointer'}}>
              {k.replace('-',' ')}
            </button>
          ))}
        </div>
        <div style={{flex:1}}/>
        <select value={conn} onChange={e=>setConn(e.target.value)}
          style={{padding:'6px 10px',background:'var(--paper)',border:'1px solid var(--rule)',color:'var(--ink)',fontSize:11}}>
          <option value="all">All connectors</option>
          {CONNECTORS.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
        </select>
        <span className="ff-mono num" style={{fontSize:11,color:'var(--ink-3)',marginLeft:14}}>
          {filtered.length} events
        </span>
      </div>

      <div style={{border:'1px solid var(--rule)',maxHeight:680,overflowY:'auto'}}>
        {/* header */}
        <div style={{display:'grid',gridTemplateColumns:'60px 130px 70px 1fr 80px 90px',gap:14,padding:'8px 14px',
          background:'var(--bg-2)',borderBottom:'1px solid var(--rule)',position:'sticky',top:0}}>
          {['Ago','Connector','Kind','Path','Status','Size/MS'].map(h => (
            <span key={h} className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)'}}>{h}</span>
          ))}
        </div>
        {filtered.map((e,i) => <ActivityRow key={i} e={e} withConnector/>)}
      </div>
    </div>
  );
}

// ─────────── 04 PERMISSIONS — capability matrix
function ICPermissionsTab() {
  // Build a matrix: surfaces (rows) × connectors (cols), cell = read/write/both
  const surfaces = useIM(() => {
    const set = new Set();
    CONNECTORS.forEach(c => c.capabilities.forEach(cap => set.add(cap.surface)));
    return Array.from(set).sort();
  }, []);

  const cell = (connector, surface) => {
    const caps = connector.capabilities.filter(c => c.surface === surface);
    if (caps.length === 0) return null;
    const kinds = new Set(caps.map(c => c.kind));
    if (kinds.has('in') && kinds.has('out')) return 'both';
    if (kinds.has('in')) return 'read';
    return 'write';
  };

  return (
    <div style={{paddingTop:8}}>
      <div style={{fontSize:13,color:'var(--ink-2)',maxWidth:680,lineHeight:1.5,marginBottom:18}}>
        What each connector is permitted to <span style={{color:'var(--ok)'}}>read</span> from
        and <span style={{color:'var(--info)'}}>write</span> to inside ASTRO. Use this to audit
        scope creep before granting new credentials, and to trace where data on any given screen
        comes from.
      </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}}>
                <span className="ff-mono upper" style={{fontSize:9,letterSpacing:'.1em',color:'var(--ink-3)'}}>Surface</span>
              </th>
              {CONNECTORS.map(c => (
                <th key={c.id} style={{padding:'10px 8px',textAlign:'center',borderBottom:'1px solid var(--rule)',whiteSpace:'nowrap',minWidth:96}}>
                  <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:6}}>
                    <ConnSwatch color={c.color} size={10}/>
                    <span className="ff-mono" style={{fontSize:10,color:'var(--ink-2)'}}>{c.name.split(' ')[0]}</span>
                  </div>
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {surfaces.map((s, i) => (
              <tr key={s} style={{borderBottom: i<surfaces.length-1?'1px solid var(--rule-soft, var(--bg-2))':'none'}}>
                <td style={{padding:'10px 14px',position:'sticky',left:0,background:'var(--paper)',borderRight:'1px solid var(--rule)',fontSize:12}}>
                  {s}
                </td>
                {CONNECTORS.map(c => {
                  const v = cell(c, s);
                  return (
                    <td key={c.id} style={{padding:'10px 8px',textAlign:'center'}}>
                      {v === 'read' && <span style={{color:'var(--ok)',fontSize:14}}>↓</span>}
                      {v === 'write' && <span style={{color:'var(--info)',fontSize:14}}>↑</span>}
                      {v === 'both' && <span style={{color:'var(--ink)',fontSize:14}}>⇅</span>}
                      {!v && <span style={{color:'var(--ink-4)'}}>·</span>}
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* legend */}
      <div style={{marginTop:14,display:'flex',gap:18,fontSize:11,color:'var(--ink-2)'}}>
        <span><span style={{color:'var(--ok)',fontSize:14,marginRight:4}}>↓</span> Reads from this surface</span>
        <span><span style={{color:'var(--info)',fontSize:14,marginRight:4}}>↑</span> Writes to this surface</span>
        <span><span style={{color:'var(--ink)',fontSize:14,marginRight:4}}>⇅</span> Bidirectional</span>
      </div>
    </div>
  );
}

// ─────────── MAIN SCREEN
function ScreenIntegrations({ go, payload }) {
  const [tab, setTab] = useIS(payload?.tab || 'overview');
  const [openId, setOpenId] = useIS(payload?.connector || null);

  const onOpenConnector = (id) => { setTab('connectors'); setOpenId(id); };

  const stats = useIM(() => {
    const c = { healthy:0, degraded:0, reauth:0, silent:0 };
    CONNECTORS.forEach(x => c[x.status]++);
    return c;
  }, []);

  const TABS = [
    { k:'overview',    l:'Overview' },
    { k:'connectors',  l:'Connectors',  n: CONNECTORS.length },
    { k:'activity',    l:'Activity',    n: stats.degraded + stats.reauth, warn: (stats.degraded + stats.reauth) > 0 },
    { k:'permissions', l:'Permissions' },
  ];

  return (
    <div>
      <PageHeader
        eyebrow={['SETTINGS', 'API INTEGRATIONS', `${CONNECTORS.length} WIRED`]}
        title="Integrations"
        highlight="Integrations"
        sub="Every external API ASTRO talks to lives here — DSPs, rights orgs, social, workflow, market intel. One place to see what's healthy, what's expired, what each connector is permitted to do, and where data flows from on every screen of the app."
        actions={
          <div style={{display:'flex',gap:8,alignItems:'center'}}>
            <span style={{display:'inline-flex',alignItems:'center',gap:6,fontSize:11,color: stats.reauth ? 'var(--danger)' : 'var(--ink-2)'}}>
              <span style={{display:'inline-block',width:8,height:8,borderRadius:4,background: stats.reauth ? 'var(--danger)' : 'var(--ok)'}}/>
              {stats.healthy} healthy
              {stats.degraded > 0 && <span style={{color:'var(--warn, #d4881f)'}}> · {stats.degraded} degraded</span>}
              {stats.reauth > 0 && <span style={{color:'var(--danger)'}}> · {stats.reauth} re-auth</span>}
            </span>
          </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); if(t.k!=='connectors') setOpenId(null);}} 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 ? 'var(--danger)' : 'var(--ink-3)'}}>{t.n}</span>
            )}
          </button>
        ))}
      </div>

      {/* Body */}
      <div style={{paddingTop:8}}>
        {tab === 'overview'    && <ICOverviewTab onOpenConnector={onOpenConnector} setTab={setTab}/>}
        {tab === 'connectors'  && <ICConnectorsTab openId={openId} setOpenId={setOpenId}/>}
        {tab === 'activity'    && <ICActivityTab/>}
        {tab === 'permissions' && <ICPermissionsTab/>}
      </div>
    </div>
  );
}

Object.assign(window, { ScreenIntegrations });
