/* Attention / Shared Following View — dedicated page for cohort information diet + cohesion.
   Visualizes the following lists data (globalRanked from agg + per-member attention).
   Complements the Pedigree Map (past trajectory) with current "who we collectively follow".
*/

const { useState, useEffect, useMemo } = React;

const CAT_COLOR = {
  'in-cohort': 'var(--accent)',
  'ai-lab': '#f59e0b',
  'researcher': '#10b981',
  'tool': '#3b82f6',
  'media': '#8b5cf6',
  'vc': '#ec4899',
  'musk-orbit': '#dc2626',
  'founder': '#14b8a6',
  'other': 'var(--text-tertiary)'
};

const CAT_LABEL = {
  'in-cohort': 'In-cohort',
  'ai-lab': 'AI Labs',
  'researcher': 'Researchers',
  'tool': 'Tools / Infra',
  'media': 'Media / Papers',
  'vc': 'VC / Angels',
  'musk-orbit': 'Musk Orbit',
  'founder': 'Founders / Operators',
  'other': 'Other'
};

function xUrl(handle) {
  return 'https://x.com/' + String(handle || '').replace(/^@/, '');
}

function AttentionView({ onOpenFollows }) {
  const [agg, setAgg] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [openCat, setOpenCat] = useState(null);

  useEffect(function () {
    var tried = false;
    function done(d) {
      if (d) setAgg(d);
      setLoaded(true);
    }
    fetch('/data/xai-following-agg.json')
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(done)
      .catch(function () {
        // Fallback: compute a minimal global view from per-member attention in the loaded cohort
        if (!tried && window.COHORT && window.COHORT.members) {
          tried = true;
          var members = window.COHORT.members.filter(function (m) { return m.attention && m.attention.topFollowed && m.attention.topFollowed.length; });
          var by = {};
          members.forEach(function (m) {
            (m.attention.topFollowed || []).forEach(function (f) {
              var h = f.handle;
              if (!by[h]) by[h] = { handle: h, name: f.name || h, category: f.category || 'other', followedByCount: 0 };
              by[h].followedByCount++;
            });
          });
          var ranked = Object.values(by).sort(function (a, b) { return b.followedByCount - a.followedByCount; }).slice(0, 50);
          done({ globalRanked: ranked, internalVsExternalRatio: { internal: 0, external: 0, ratio: 0 }, categoryStats: {} });
        } else {
          done(null);
        }
      });
  }, []);

  const cohortMembers = (window.COHORT && window.COHORT.members) || [];
  const members = cohortMembers.filter(function (m) {
    return m.attention && m.attention.topFollowed && m.attention.topFollowed.length > 0;
  });

  const summary = useMemo(function () {
    if (!agg) return null;
    var internal = agg.internalVsExternalRatio ? agg.internalVsExternalRatio.internal : 0;
    var external = agg.internalVsExternalRatio ? agg.internalVsExternalRatio.external : 0;
    var ratio = external > 0 ? Math.round((internal / (internal + external)) * 100) : 0;
    return {
      members: members.length,
      totalRanked: (agg.globalRanked || []).length,
      internalRatio: ratio
    };
  }, [agg, members.length]);

  // Live top from members (in case agg not loaded or for freshness)
  const liveTop = useMemo(function () {
    var count = {};
    members.forEach(function (m) {
      (m.attention.topFollowed || []).slice(0, 30).forEach(function (f) {
        var k = f.handle;
        if (!count[k]) count[k] = { handle: k, name: f.name, category: f.category, c: 0 };
        count[k].c++;
      });
    });
    return Object.values(count).sort(function (a, b) { return b.c - a.c; }).slice(0, 30);
  }, [members]);

  var top = (agg && agg.globalRanked && agg.globalRanked.length > 0) ? agg.globalRanked : liveTop;

  // Category stats for bars (prefer agg, fallback live)
  var catStats = useMemo(function () {
    if (agg && agg.categoryStats && Object.keys(agg.categoryStats).length > 0) return agg.categoryStats;
    var s = { 'in-cohort': 0, 'ai-lab': 0, 'researcher': 0, 'tool': 0, 'media': 0, 'vc': 0, 'other': 0 };
    members.forEach(function (m) {
      var cs = m.attention.categoryCounts || {};
      Object.keys(s).forEach(function (k) { s[k] += (cs[k] || 0); });
    });
    return s;
  }, [agg, members]);

  // Scale bars to the largest *signal* category (exclude the catch-all 'other', which
  // dwarfs everything) so meaningful categories stay readable. 'other' clamps at 100%.
  var maxCat = Math.max.apply(null, Object.keys(catStats)
    .filter(function (k) { return k !== 'other'; })
    .map(function (k) { return catStats[k] || 0; })) || 1;

  // Per-category account lists for the expandable rows. Prefer the precomputed full
  // lists from the agg; otherwise group whatever ranked accounts we have by category.
  var catRanked = useMemo(function () {
    if (agg && agg.categoryRanked && Object.keys(agg.categoryRanked).length > 0) return agg.categoryRanked;
    var grouped = {};
    (top || []).forEach(function (t) {
      var cat = t.category || 'other';
      if (!grouped[cat]) grouped[cat] = [];
      grouped[cat].push({ handle: t.handle, name: t.name, followedByCount: t.followedByCount || t.c || 1 });
    });
    Object.keys(grouped).forEach(function (k) {
      grouped[k].sort(function (a, b) { return b.followedByCount - a.followedByCount; });
    });
    return grouped;
  }, [agg, top]);

  // High-overlap members (from current members)
  var highOverlap = useMemo(function () {
    return members
      .filter(function (m) { return m.attention.cohortOverlapScore > 0; })
      .sort(function (a, b) { return b.attention.cohortOverlapScore - a.attention.cohortOverlapScore; })
      .slice(0, 8);
  }, [members]);

  if (!loaded) {
    return <div style={{ padding: 32, color: 'var(--text-tertiary)' }}>Loading attention data…</div>;
  }

  // Render whenever we have ANY attention signal: either the precomputed global
  // aggregate (xai-following-agg.json) OR per-member attention on the cohort.
  // `top` falls back to member-derived liveTop, so a non-empty `top` means we
  // have something useful to show even when per-member .attention is absent
  // (e.g. the deployed snapshot only ships the agg file).
  const hasData = (top && top.length > 0) || members.length > 0;
  if (!hasData) {
    return (
      <div style={{ padding: 32, background: 'var(--bg-base)', minHeight: '100%' }}>
        <div style={{ fontSize: 15, fontWeight: 600, marginBottom: 8 }}>Shared Attention</div>
        <div style={{ color: 'var(--text-tertiary)', marginBottom: 12 }}>
          No following data yet in this data snapshot.
        </div>
        <div style={{ color: 'var(--text-tertiary)', fontSize: 12, lineHeight: 1.4 }}>
          To populate for the prototype: run e.g. <code>COHORT=xai-t1 pnpm fetch:followings</code> (or a T2 subset), then <code>pnpm export:data</code> (this attaches .attention to xai.json and builds the agg), commit the updated <code>public/data/*.json</code> files (or refresh the Railway /data volume), push, and redeploy.<br/><br/>
          Per-member attention (when present) is also visible in the Directory drawer.
        </div>
      </div>
    );
  }

  return (
    <div style={{ height: '100%', overflowY: 'auto', padding: '24px 32px 48px', background: 'var(--bg-base)' }}>
      {/* Hero */}
      <div style={{ marginBottom: 24 }}>
        <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Shared Attention</div>
        <div style={{ color: 'var(--text-tertiary)', marginTop: 4, fontSize: 14 }}>
          Current information diet of the cohort — who they follow, and where their collective attention overlaps.
          Complements the Pedigree Map (past) with live signals (present).
        </div>
        <div style={{ marginTop: 12, fontSize: 12, color: 'var(--text-tertiary)' }}>
          {members.length > 0 ? members.length + ' members with following data · ' : ''}{top.length} unique high-signal accounts surfaced
          {agg && agg.internalVsExternalRatio ? ' · ' + agg.internalVsExternalRatio.ratio + ' internal ratio' : ''}
        </div>
      </div>

      {/* Stats row */}
      <div style={{ display: 'flex', gap: 12, marginBottom: 24, flexWrap: 'wrap' }}>
        <div style={{ background: 'var(--bg-card)', border: '1px solid var(--border-subtle)', borderRadius: 10, padding: '12px 16px', minWidth: 160 }}>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Members with data</div>
          <div style={{ fontSize: 24, fontWeight: 700, fontFamily: 'var(--mono)' }}>{members.length}</div>
        </div>
        <div style={{ background: 'var(--bg-card)', border: '1px solid var(--border-subtle)', borderRadius: 10, padding: '12px 16px', minWidth: 160 }}>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Unique followed (top)</div>
          <div style={{ fontSize: 24, fontWeight: 700, fontFamily: 'var(--mono)' }}>{top.length}</div>
        </div>
        <div style={{ background: 'var(--bg-card)', border: '1px solid var(--border-subtle)', borderRadius: 10, padding: '12px 16px', minWidth: 180 }}>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Internal attention</div>
          <div style={{ fontSize: 24, fontWeight: 700, fontFamily: 'var(--mono)' }}>
            {agg && agg.internalVsExternalRatio ? agg.internalVsExternalRatio.ratio : '—'}
          </div>
          <div style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>of top follows are within cohort</div>
        </div>
      </div>

      {/* Top followed bubbles */}
      <div style={{ marginBottom: 28 }}>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 8 }}>
          Accounts the cohort follows most
          <span style={{ fontSize: 11, fontWeight: 400, color: 'var(--text-tertiary)' }}>(size = follows from active insiders)</span>
        </div>
        <div style={{ fontSize: 12, color: 'var(--text-tertiary)', lineHeight: 1.6, marginBottom: 12, maxWidth: 820 }}>
          Each bubble is an account that <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}>multiple xAI insiders independently follow</strong> — not who follows them, but who they pay attention to. What makes this high-signal isn't the raw count: following lists are only pulled for the members our scoring ranks as the <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}>most active, high-influence accounts</strong> on the team. The theory — someone who lives on X (high frequency, backed by the xAI halo) has the sharpest read on who's actually worth following, and heavy users refresh their follows often, so it's a fresh signal. Any one person carries social-media noise, but aggregating across many independent active insiders cancels it out. The names that keep recurring are who the team genuinely watches — and a quiet recruiting radar.
        </div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' }}>
          {top.slice(0, 20).map(function (t, idx) {
            var count = t.followedByCount || t.c || 1;
            var size = Math.max(38, Math.min(92, Math.round(38 + Math.sqrt(count) * 7)));
            var col = CAT_COLOR[t.category] || 'var(--text-tertiary)';
            return (
              <a key={idx} href={xUrl(t.handle)} target="_blank" rel="noopener noreferrer"
                title={'@' + t.handle + ' · followed by ' + count + ' members'} style={{
                width: size, height: size, borderRadius: '50%',
                background: col, opacity: 0.85,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                flexDirection: 'column', color: '#111', fontSize: Math.max(9, Math.min(12, size * 0.14)),
                textAlign: 'center', padding: 4, border: '1px solid rgba(0,0,0,0.15)',
                boxShadow: '0 1px 2px rgba(0,0,0,0.2)', textDecoration: 'none', cursor: 'pointer'
              }}>
                <div style={{ fontWeight: 700, lineHeight: 1.05, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: size - 8 }}>
                  {t.name || t.handle}
                </div>
                <div style={{ fontSize: Math.max(8, size * 0.12), opacity: 0.75, fontFamily: 'var(--mono)' }}>
                  {count}
                </div>
              </a>
            );
          })}
        </div>
        <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 8 }}>
          Showing top 20.{' '}
          <button onClick={onOpenFollows} style={{ background: 'none', border: 'none', padding: 0, color: 'var(--accent)', fontSize: 11, cursor: 'pointer', fontFamily: 'inherit', textDecoration: 'underline' }}>
            See the full ranked list with weights →
          </button>
        </div>
      </div>

      {/* Category breakdown */}
      <div style={{ marginBottom: 28 }}>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>Attention by category</div>
        <div style={{ fontSize: 11.5, color: 'var(--text-tertiary)', marginBottom: 10 }}>
          Click a category to see which accounts the cohort follows in it.
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {Object.keys(catStats).map(function (cat) {
            var v = catStats[cat] || 0;
            var w = Math.max(2, Math.min(100, Math.round((v / maxCat) * 100)));
            var accounts = (catRanked && catRanked[cat]) || [];
            var moreCount = v - accounts.length;
            var isOpen = openCat === cat;
            var clickable = accounts.length > 0;
            return (
              <div key={cat}>
                <div
                  onClick={clickable ? function () { setOpenCat(isOpen ? null : cat); } : undefined}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10, fontSize: 12,
                    cursor: clickable ? 'pointer' : 'default',
                    padding: '3px 4px', borderRadius: 4,
                    background: isOpen ? 'var(--bg-elevated)' : 'transparent'
                  }}
                >
                  <div style={{ width: 14, color: 'var(--text-tertiary)', fontSize: 10 }}>
                    {clickable ? (isOpen ? '▾' : '▸') : ''}
                  </div>
                  <div style={{ width: 92, color: CAT_COLOR[cat] || 'var(--text-tertiary)', fontWeight: 600 }}>{CAT_LABEL[cat]}</div>
                  <div style={{ flex: 1, background: 'var(--bg-elevated)', borderRadius: 3, height: 10, overflow: 'hidden' }}>
                    <div style={{ width: w + '%', height: '100%', background: CAT_COLOR[cat] || 'var(--text-tertiary)' }} />
                  </div>
                  <div style={{ width: 42, textAlign: 'right', fontFamily: 'var(--mono)', color: 'var(--text-tertiary)' }}>{v}</div>
                </div>
                {isOpen && (
                  <div style={{
                    display: 'flex', flexWrap: 'wrap', gap: 6,
                    padding: '10px 4px 12px 28px'
                  }}>
                    {accounts.map(function (acc, ai) {
                      return (
                        <a key={ai} href={xUrl(acc.handle)} target="_blank" rel="noopener noreferrer"
                          title={'@' + acc.handle + ' · followed by ' + acc.followedByCount + ' members'}
                          style={{
                            display: 'inline-flex', alignItems: 'center', gap: 6,
                            fontSize: 11, padding: '3px 8px', borderRadius: 12,
                            background: 'var(--bg-card)', border: '1px solid var(--border-subtle)',
                            color: 'var(--text-secondary)', textDecoration: 'none'
                          }}>
                          <span style={{ width: 7, height: 7, borderRadius: '50%', background: CAT_COLOR[cat] || 'var(--text-tertiary)' }} />
                          <span>{acc.name || acc.handle}</span>
                          <span style={{ fontFamily: 'var(--mono)', color: 'var(--text-tertiary)' }}>{acc.followedByCount}</span>
                        </a>
                      );
                    })}
                    {moreCount > 0 && (
                      <span style={{ alignSelf: 'center', fontSize: 11, color: 'var(--text-tertiary)' }}>
                        +{moreCount} more
                      </span>
                    )}
                  </div>
                )}
              </div>
            );
          })}
        </div>
        <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 6 }}>
          Concentration on researchers + ai-labs is the core cohesion signal.
        </div>
      </div>

      {/* High-overlap members from T1 / current data */}
      {highOverlap.length > 0 && (
        <div style={{ marginBottom: 20 }}>
          <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 6 }}>Most representative members</div>
          <div style={{ fontSize: 11.5, color: 'var(--text-tertiary)', marginBottom: 12, lineHeight: 1.5, maxWidth: 640, marginLeft: 16, paddingLeft: 12, borderLeft: '2px solid var(--border-subtle)' }}>
            Cohort members whose own follow list overlaps most with the group's collective attention —
            i.e. the people whose information diet best mirrors everyone else's.
            Higher overlap score = more of who they follow is also followed by ≥3 other members.
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(260px, 1fr))', gap: 8 }}>
            {highOverlap.map(function (m, i) {
              var a = m.attention;
              return (
                <div key={i} style={{ background: 'var(--bg-card)', border: '1px solid var(--border-subtle)', borderRadius: 8, padding: '10px 12px', fontSize: 12 }}>
                  <a href={xUrl(m.handle)} target="_blank" rel="noopener noreferrer"
                    style={{ fontFamily: 'var(--mono)', fontWeight: 600, color: 'var(--accent)', textDecoration: 'none' }}>
                    @{m.handle}
                  </a>
                  <div style={{ color: 'var(--text-tertiary)', margin: '2px 0 6px' }}>
                    overlap score: <strong>{a.cohortOverlapScore}</strong> · internal follows: {a.internalFollowCount}
                  </div>
                  <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                    {(a.topFollowed || []).slice(0, 5).map(function (f, fi) {
                      return (
                        <a key={fi} href={xUrl(f.handle)} target="_blank" rel="noopener noreferrer" title={'@' + f.handle} style={{
                          fontSize: 10, padding: '1px 6px', borderRadius: 3,
                          background: CAT_COLOR[f.category] || 'var(--bg-elevated)',
                          color: '#111', textDecoration: 'none'
                        }}>{f.name || f.handle}</a>
                      );
                    })}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      )}

      {/* Footer note */}
      <div style={{ marginTop: 20, fontSize: 11, color: 'var(--text-tertiary)' }}>
        Data from prioritized T1 fetch (high-activity + high-influence members) + previous runs. Total {members.length} members with following lists.
        Classification uses expanded seeds (researcher / vc / ai-lab etc.). Re-run fetch + export to refresh.
      </div>
    </div>
  );
}

/* ── Full ranked-list sub-page (#attention/follows) ──
   A tabular complement to the bubble cloud: every external account the cohort
   follows, ranked, with its follow count (= "weight"), category, and a relative
   magnitude bar. Color encodes category (same legend as the bubbles). */
function AttentionFollowsView({ onBack }) {
  const [agg, setAgg] = useState(null);
  const [loaded, setLoaded] = useState(false);
  const [cat, setCat] = useState('all');
  const [sortKey, setSortKey] = useState('weight');

  useEffect(function () {
    fetch('/data/xai-following-agg.json')
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (d) { if (d) setAgg(d); setLoaded(true); })
      .catch(function () { setLoaded(true); });
  }, []);

  var rows = useMemo(function () {
    if (agg && agg.globalRanked && agg.globalRanked.length) {
      return agg.globalRanked.map(function (t) {
        var count = t.followedByCount || t.c || 0;
        return { handle: t.handle, name: t.name || t.handle, category: t.category || 'other', count: count, weight: t.weightedScore != null ? t.weightedScore : count };
      });
    }
    var members = ((window.COHORT && window.COHORT.members) || []).filter(function (m) {
      return m.attention && m.attention.topFollowed && m.attention.topFollowed.length;
    });
    var by = {};
    members.forEach(function (m) {
      (m.attention.topFollowed || []).forEach(function (f) {
        if (!by[f.handle]) by[f.handle] = { handle: f.handle, name: f.name || f.handle, category: f.category || 'other', count: 0 };
        by[f.handle].count++;
      });
    });
    return Object.values(by).map(function (r) { r.weight = r.count; return r; });
  }, [agg]);

  var maxWeight = useMemo(function () {
    return rows.reduce(function (m, r) { return Math.max(m, r.weight); }, 1);
  }, [rows]);

  var catCounts = useMemo(function () {
    var c = {};
    rows.forEach(function (r) { c[r.category] = (c[r.category] || 0) + 1; });
    return c;
  }, [rows]);

  var filtered = useMemo(function () {
    var list = cat === 'all' ? rows.slice() : rows.filter(function (r) { return r.category === cat; });
    list.sort(function (a, b) {
      if (sortKey === 'name') return (a.name || '').localeCompare(b.name || '');
      if (sortKey === 'count') return b.count - a.count;
      return b.weight - a.weight;
    });
    return list;
  }, [rows, cat, sortKey]);

  if (!loaded) {
    return <div style={{ padding: 32, color: 'var(--text-tertiary)' }}>Loading follows…</div>;
  }

  var cats = ['all'].concat(Object.keys(CAT_LABEL).filter(function (k) { return catCounts[k]; }));

  function Chip(props) {
    var active = props.active;
    return (
      <button onClick={props.onClick} style={{
        padding: '4px 11px', borderRadius: 8, cursor: 'pointer', fontSize: 11.5,
        border: active ? '1px solid var(--accent)' : '1px solid var(--border-default)',
        background: active ? 'var(--accent-muted)' : 'transparent',
        color: active ? 'var(--accent)' : 'var(--text-secondary)',
        fontFamily: 'var(--sans)', display: 'inline-flex', alignItems: 'center', gap: 6,
      }}>
        {props.dot && <span style={{ width: 8, height: 8, borderRadius: '50%', background: props.dot, flexShrink: 0 }}></span>}
        {props.label}
      </button>
    );
  }

  return (
    <div style={{ padding: '20px 32px', overflowY: 'auto', height: '100%' }}>
      <button onClick={onBack} style={{ background: 'none', border: 'none', padding: 0, color: 'var(--text-tertiary)', fontSize: 12, cursor: 'pointer', fontFamily: 'inherit', marginBottom: 12 }}>
        ← Back to Shared Attention
      </button>

      <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em', marginBottom: 6 }}>
        Accounts the cohort follows — full ranked list
      </div>
      <div style={{ fontSize: 12, color: 'var(--text-tertiary)', lineHeight: 1.6, marginBottom: 16, maxWidth: 820 }}>
        Every external account followed by our scoring-prioritized active insiders, ranked by
        <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}> follow count</strong> (the bar = weight relative to the top account).
        Color = category. {rows.length} accounts total.
      </div>

      {/* Category filter chips */}
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 12 }}>
        {cats.map(function (k) {
          return <Chip key={k} label={k === 'all' ? 'All · ' + rows.length : (CAT_LABEL[k] || k) + ' · ' + (catCounts[k] || 0)}
            dot={k === 'all' ? null : (CAT_COLOR[k] || 'var(--text-tertiary)')}
            active={cat === k} onClick={function () { setCat(k); }} />;
        })}
      </div>

      {/* Sort control (segmented tab) + weight explainer */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap', marginBottom: 12 }}>
        <span style={{ fontSize: 10.5, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Sort by</span>
        <div style={{ display: 'inline-flex', background: 'var(--bg-elevated)', borderRadius: 9, padding: 3, gap: 2 }}>
          {[['weight', 'Weight'], ['count', 'Follows'], ['name', 'Name']].map(function (o) {
            var active = sortKey === o[0];
            return (
              <button key={o[0]} onClick={function () { setSortKey(o[0]); }} style={{
                padding: '5px 14px', fontSize: 12, fontFamily: 'var(--sans)', border: 'none',
                background: active ? 'var(--bg-hover)' : 'transparent',
                color: active ? 'var(--text-primary)' : 'var(--text-tertiary)',
                cursor: 'pointer', borderRadius: 6, fontWeight: active ? 600 : 400,
                boxShadow: active ? '0 1px 3px rgba(0,0,0,0.4)' : 'none', transition: 'all .12s',
              }}>{o[1]}</button>
            );
          })}
        </div>
      </div>

      {/* How weight is calculated */}
      <div style={{ display: 'flex', gap: 10, alignItems: 'stretch', background: 'var(--bg-card)', border: '1px solid var(--border-subtle)', borderRadius: 8, padding: '11px 14px', marginBottom: 18, maxWidth: 860 }}>
        <div style={{ width: 3, borderRadius: 2, background: 'var(--accent)', flexShrink: 0 }}></div>
        <div style={{ fontSize: 11.5, color: 'var(--text-tertiary)', lineHeight: 1.65 }}>
          <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}>How "Weight" is calculated.</strong>{' '}
          Every follow is one vote — but votes aren't equal. Each member's vote is scaled by their own
          <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}> priority score</strong> (our activity + influence ranking),
          mean-normalized to 1, so{' '}
          <code style={{ fontFamily: 'var(--mono)', color: 'var(--text-secondary)', background: 'var(--bg-elevated)', padding: '1px 5px', borderRadius: 4 }}>weight = Σ (memberScore ÷ mean)</code>{' '}
          across everyone who follows that account. A uniform cohort → weight ≈ follows; but a follow from a more active,
          higher-reach insider counts for more. <strong style={{ color: 'var(--text-secondary)', fontWeight: 600 }}>Follows</strong> stays the honest head-count.
        </div>
      </div>

      {/* Table header */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '8px 12px', borderBottom: '1px solid var(--border-default)', fontSize: 10.5, fontWeight: 600, color: 'var(--text-tertiary)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
        <div style={{ width: 32, textAlign: 'right', flexShrink: 0 }}>#</div>
        <div style={{ flex: 1, minWidth: 160 }}>Account</div>
        <div style={{ width: 120, flexShrink: 0 }}>Category</div>
        <div style={{ width: 60, flexShrink: 0, textAlign: 'right', color: sortKey === 'count' ? 'var(--text-secondary)' : 'var(--text-tertiary)' }}>Follows{sortKey === 'count' ? ' ▾' : ''}</div>
        <div style={{ width: 56, flexShrink: 0, textAlign: 'right', color: sortKey === 'weight' ? 'var(--accent)' : 'var(--text-tertiary)' }}>Weight{sortKey === 'weight' ? ' ▾' : ''}</div>
        <div style={{ width: 170, flexShrink: 0 }}></div>
      </div>

      {/* Rows */}
      {filtered.map(function (r, i) {
        var col = CAT_COLOR[r.category] || 'var(--text-tertiary)';
        return (
          <div key={r.handle} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '8px 12px', borderBottom: '1px solid var(--border-subtle)', fontSize: 12.5 }}>
            <div style={{ width: 32, textAlign: 'right', flexShrink: 0, fontFamily: 'var(--mono)', color: 'var(--text-disabled)', fontFeatureSettings: "'tnum' 1" }}>{i + 1}</div>
            <div style={{ flex: 1, minWidth: 160, display: 'flex', alignItems: 'center', gap: 8, overflow: 'hidden' }}>
              <span style={{ width: 8, height: 8, borderRadius: '50%', background: col, flexShrink: 0 }}></span>
              <a href={xUrl(r.handle)} target="_blank" rel="noopener noreferrer" style={{ fontFamily: 'var(--mono)', color: 'var(--accent)', textDecoration: 'none', flexShrink: 0 }}>@{r.handle}</a>
              {r.name && r.name !== r.handle && <span style={{ color: 'var(--text-tertiary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name}</span>}
            </div>
            <div style={{ width: 120, flexShrink: 0, fontSize: 11, color: 'var(--text-secondary)' }}>{CAT_LABEL[r.category] || r.category}</div>
            <div style={{ width: 60, flexShrink: 0, textAlign: 'right', fontFamily: 'var(--mono)', color: 'var(--text-secondary)', fontFeatureSettings: "'tnum' 1" }}>{r.count}</div>
            <div style={{ width: 56, flexShrink: 0, textAlign: 'right', fontFamily: 'var(--mono)', fontWeight: 600, color: 'var(--text-primary)', fontFeatureSettings: "'tnum' 1" }}>{r.weight.toFixed(1)}</div>
            <div style={{ width: 170, flexShrink: 0, height: 7, background: 'rgba(255,255,255,0.06)', borderRadius: 4, overflow: 'hidden' }} title={'weighted ' + r.weight.toFixed(1) + ' · ' + r.count + ' followers'}>
              <div style={{ height: '100%', width: (r.weight / maxWeight * 100) + '%', background: col, opacity: 0.8, borderRadius: 4 }}></div>
            </div>
          </div>
        );
      })}

      {filtered.length === 0 && (
        <div style={{ padding: '32px 12px', color: 'var(--text-tertiary)', fontSize: 13 }}>No accounts in this category.</div>
      )}
    </div>
  );
}

window.AttentionView = AttentionView;
window.AttentionFollowsView = AttentionFollowsView;