// Value Investors Club — owner-only Markets Dashboard module.
//
// Mirrors VIC write-up metadata into a searchable list (company-name search +
// long/short + date filters), lets the owner open any write-up in a clean,
// structured reading view (body fetched on demand), and generate a Finch Bay
// thesis card off it — which lands in the searchable Idea Database and is
// emailed. All of it is driven by the `vic-sync` Edge Function; every table is
// owner-only (RLS), matching the FBC Portfolio section's privacy posture.
//
// Exposes window.VICIdeasPanel — the inner content; ForMe wraps it in a
// RailSection so the section numbering stays in one place.

const _vicInvoke = async (action, extra) => {
  const client = window._supabaseClient;
  if (!client) throw new Error('No backend connected.');
  const { data, error } = await client.functions.invoke('vic-sync', { body: { action, ...(extra || {}) } });
  if (error) {
    let payload = null;
    try { payload = await error.context.json(); } catch { /* ignore */ }
    if (payload && payload.error) throw new Error(payload.error);
    throw new Error(error.message || 'Edge function error');
  }
  if (data && data.error) throw new Error(data.error);
  return data;
};

const _vicDir = (d) => {
  if (d === 'long') return { label: 'LONG', color: tokens.green };
  if (d === 'short') return { label: 'SHORT', color: tokens.red };
  return { label: '—', color: tokens.inkMute };
};

// One row in the VIC list; expands to the reading view + Finch Bay controls.
// VIC is visible to anyone granted the Markets Dashboard page; the ACTIONS
// (sync, cookies, batch fills, Finch Bay card generation) stay owner-only —
// they spend the owner's tokens and use his VIC session, and vic-sync
// enforces the same line server-side.
const vicIsOwner = () => (window.SITE_ROLE || '') === 'owner';

const VICIdeaRow = ({ idea, isMobile }) => {
  const [open, setOpen] = React.useState(false);
  const [row, setRow] = React.useState(idea);           // gains body_html once opened
  const [loadingBody, setLoadingBody] = React.useState(false);
  const [card, setCard] = React.useState(null);         // generated Finch Bay card
  const [analyzing, setAnalyzing] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  const dir = _vicDir(row.direction);

  // Fetch the write-up body on first open (cached server-side after the first fetch).
  const ensureBody = React.useCallback(async () => {
    if (row.body_html) return;
    setLoadingBody(true); setMsg('');
    try { const r = await _vicInvoke('get_body', { id: row.id }); if (r && r.idea) setRow(r.idea); }
    catch (e) { setMsg(e.message); }
    setLoadingBody(false);
  }, [row.id, row.body_html]);

  const toggle = () => { const n = !open; setOpen(n); if (n) ensureBody(); };

  // Load a previously-generated Finch Bay card for this write-up, if any.
  React.useEffect(() => {
    if (!open || card || !row.analysis_idea_id) return;
    const client = window._supabaseClient;
    if (!client) return;
    client.from('screen_ideas').select('ticker, name, why, narrative, questions, pros, cons, score, generated_at')
      .eq('id', row.analysis_idea_id).maybeSingle()
      .then(({ data }) => { if (data) setCard(data); });
  }, [open, row.analysis_idea_id, card]);

  const generate = async () => {
    setAnalyzing(true); setMsg('');
    try {
      const r = await _vicInvoke('analyze', { id: row.id });
      if (r && r.idea) {
        setCard(r.idea);
        setRow((p) => ({ ...p, analyzed_at: r.generated_at }));
        setMsg(`✓ Saved to the Idea Database${r.emailed ? ' · emailed' : ''}${r.fit_score != null ? ` · Finch Bay fit ${r.fit_score}/100` : ''}`);
      }
    } catch (e) { setMsg(`✗ ${e.message}`); }
    setAnalyzing(false);
  };

  return (
    <div style={{ borderBottom: `1px solid ${tokens.inkLineSoft}` }}>
      <div onClick={toggle} className="case-row" style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr auto' : '86px 1fr 120px 58px 24px', gap: isMobile ? '6px 10px' : '14px', alignItems: 'center', padding: '11px 4px', cursor: 'pointer' }}>
        <span style={{ fontFamily: fontMono, fontSize: '10px', color: tokens.inkMute, letterSpacing: '0.04em', gridColumn: isMobile ? '1 / 2' : 'auto' }}>{row.posted_at || '—'}</span>
        <span style={{ minWidth: 0 }}>
          <span style={{ fontSize: '14px', letterSpacing: '-0.01em', color: tokens.ink }}>{row.company || '(untitled)'}</span>
          {row.ticker && <span style={{ fontFamily: fontMono, fontSize: '10px', color: tokens.inkMute, marginLeft: '8px' }}>{row.ticker}</span>}
          {row.analyzed_at && <span title="Finch Bay card generated" style={{ fontFamily: fontMono, fontSize: '8.5px', letterSpacing: '0.1em', color: '#C9622E', border: `1px solid #C9622E`, borderRadius: '999px', padding: '1px 6px', marginLeft: '8px', whiteSpace: 'nowrap' }}>FB ✓</span>}
          {isMobile && row.author && <span style={{ fontFamily: fontMono, fontSize: '9px', color: tokens.inkMute, marginLeft: '8px' }}>· {row.author}</span>}
        </span>
        {!isMobile && <span style={{ fontFamily: fontMono, fontSize: '10px', color: tokens.inkMute, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{row.author}</span>}
        <span style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.08em', color: dir.color, textAlign: isMobile ? 'left' : 'right' }}>{dir.label}</span>
        {!isMobile && <span style={{ fontFamily: fontMono, fontSize: '12px', color: tokens.inkMute, textAlign: 'center' }}>{open ? '▾' : '▸'}</span>}
      </div>

      {open && (
        <div style={{ padding: isMobile ? '6px 4px 22px' : '8px 4px 26px', background: 'rgba(0,0,0,0.012)' }}>
          {/* Controls */}
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', alignItems: 'center', marginBottom: '14px' }}>
            {vicIsOwner() && <button onClick={generate} disabled={analyzing || loadingBody}
              style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.1em', padding: '8px 14px', background: analyzing ? 'none' : tokens.ink, color: analyzing ? tokens.inkMute : tokens.paper, border: `1px solid ${tokens.ink}`, cursor: analyzing ? 'default' : 'pointer' }}>
              {analyzing ? 'GENERATING…' : card ? '↻ RE-GENERATE FINCH BAY CARD' : '⚡ GENERATE FINCH BAY CARD'}
            </button>}
            <a href={row.url} target="_blank" rel="noopener noreferrer" style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.08em', color: tokens.inkMute, borderBottom: `1px solid ${tokens.inkLine}`, paddingBottom: '2px' }}>OPEN ON VIC ↗</a>
            {msg && <span style={{ fontFamily: fontMono, fontSize: '10px', color: msg.startsWith('✗') ? tokens.red : tokens.green }}>{msg}</span>}
          </div>

          {/* Finch Bay card (reuses the site-wide IdeaCard) */}
          {card && (
            <div style={{ marginBottom: '18px' }}>
              {typeof window.IdeaCard === 'function'
                ? <window.IdeaCard idea={card} rank={0} />
                : <div style={{ fontFamily: fontMono, fontSize: '11px', color: tokens.inkMute }}>Card saved to the Idea Database.</div>}
            </div>
          )}

          {/* The write-up, structured */}
          {loadingBody ? (
            <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', color: tokens.inkMute }}>LOADING WRITE-UP…</div>
          ) : row.body_html ? (
            <div className="vic-writeup" style={{ fontSize: '14px', lineHeight: 1.65, color: tokens.ink, maxWidth: '820px', overflowWrap: 'anywhere' }}
              dangerouslySetInnerHTML={{ __html: row.body_html }} />
          ) : (
            <div style={{ fontFamily: fontMono, fontSize: '11px', color: tokens.inkMute }}>Open on VIC to read — the body could not be parsed here.</div>
          )}
        </div>
      )}
    </div>
  );
};

const VICIdeasPanel = () => {
  const isMobile = useIsMobile();
  const [rows, setRows] = React.useState(undefined);    // undefined loading · [] none
  const [q, setQ] = React.useState('');
  const [dir, setDir] = React.useState('all');
  const [busy, setBusy] = React.useState('');           // '', 'sync', 'backfill'
  const [state, setState] = React.useState(null);
  const [err, setErr] = React.useState('');
  const [page, setPage] = React.useState(0);            // 15 ideas per page, newest first

  const load = React.useCallback(async () => {
    const client = window._supabaseClient;
    if (!client) { setRows(null); return; }
    let query = client.from('vic_ideas')
      .select('id, vic_id, url, company, ticker, author, posted_at, direction, teaser, body_html, analysis_idea_id, analyzed_at')
      .order('posted_at', { ascending: false, nullsFirst: false })
      .order('first_seen', { ascending: false })
      .limit(400);
    if (dir !== 'all') query = query.eq('direction', dir);
    if (q.trim()) {
      const term = q.trim().replace(/[%,]/g, ' ');
      query = query.or(`company.ilike.%${term}%,ticker.ilike.%${term}%,author.ilike.%${term}%`);
    }
    const { data, error } = await query;
    if (error) { setErr(error.message); setRows([]); return; }
    setErr(''); setRows(data || []); setPage(0);
  }, [q, dir]);

  // vic_state is server-only (no client RLS), so read non-sensitive status
  // through the edge function instead of the table directly.
  const loadStatus = React.useCallback(async () => {
    if (!vicIsOwner()) { setState(null); return; }   // status/sync are owner-only server-side
    try { const s = await _vicInvoke('status'); setState(s); } catch { setState(null); }
  }, []);
  React.useEffect(() => { loadStatus(); }, [loadStatus, busy]);

  // Session cookie (owner pastes it after logging into VIC in their browser —
  // VIC's Cloudflare login CAPTCHA blocks server-side sign-in).
  const [cookieText, setCookieText] = React.useState('');
  const [savingCookie, setSavingCookie] = React.useState(false);
  const [cookieMsg, setCookieMsg] = React.useState('');
  const [showCookie, setShowCookie] = React.useState(false);
  const saveCookie = async () => {
    if (!cookieText.trim()) return;
    setSavingCookie(true); setCookieMsg('');
    try {
      const r = await _vicInvoke('set_cookie', { cookie: cookieText.trim() });
      if (r && r.ok) { setCookieMsg('✓ ' + (r.detail || 'Saved')); setCookieText(''); setShowCookie(false); await loadStatus(); }
      else setCookieMsg('✗ ' + ((r && r.error) || 'Could not verify the session'));
    } catch (e) { setCookieMsg('✗ ' + e.message); }
    setSavingCookie(false);
  };

  // Debounced reload on search / filter change.
  React.useEffect(() => { const t = setTimeout(load, q ? 280 : 0); return () => clearTimeout(t); }, [load, q]);

  const run = async (action) => {
    setBusy(action); setErr('');
    try { await _vicInvoke(action); await load(); }
    catch (e) { setErr(e.message); }
    setBusy('');
  };

  // Connection test — runs the `probe` action (writes nothing) and shows the
  // result so setup can be confirmed without touching a browser console.
  const [probe, setProbe] = React.useState(null);
  const [copied, setCopied] = React.useState(false);
  const runProbe = async () => {
    setBusy('probe'); setErr(''); setProbe(null); setCopied(false);
    try { const r = await _vicInvoke('probe'); setProbe(r); }
    catch (e) { setProbe({ error: e.message }); }
    setBusy('');
  };
  const copyProbe = async () => {
    const txt = JSON.stringify(probe, null, 2);
    try { await navigator.clipboard.writeText(txt); setCopied(true); setTimeout(() => setCopied(false), 2500); }
    catch { /* clipboard blocked — the text is visible below to copy manually */ }
  };

  // Enrichment fills ticker/date/author/L-S from each idea's page. VIC caps how
  // many pages an account can open per day, so this is a SINGLE small batch on
  // click (newest posts first) — the daily cron trickles the rest over time.
  const [enriching, setEnriching] = React.useState(false);
  const [enrichMsg, setEnrichMsg] = React.useState('');
  const runEnrich = async () => {
    setEnriching(true); setEnrichMsg('Filling a batch…');
    try {
      const r = await _vicInvoke('enrich', { limit: 15 });
      if (r && r.rate_limited) setEnrichMsg('⚠ Hit VIC’s daily view limit — it resets within 24h. The daily job continues automatically.');
      else if (r) setEnrichMsg(`✓ Filled ${r.filled || 0}. ${r.remaining || 0} left — the daily job fills the rest, newest first.`);
      await loadStatus(); await load();
    } catch (e) { setEnrichMsg('✗ ' + e.message); }
    setEnriching(false);
  };

  const count = Array.isArray(rows) ? rows.length : 0;

  return (
    <IntelShell collapseKey="vicideas" title="VALUE INVESTORS CLUB · IDEA MIRROR"
      right={state && state.last_sync_at ? `SYNCED ${new Date(state.last_sync_at).toLocaleDateString()}` : ''}
      onAction={vicIsOwner() ? () => run('sync') : undefined} actionLabel={vicIsOwner() ? (busy === 'sync' ? 'SYNCING…' : '⟳ SYNC NOW') : undefined} running={busy === 'sync'}>
      <div style={{ padding: isMobile ? '16px' : '18px 20px' }}>
        {err && <div style={{ fontFamily: fontMono, fontSize: '10px', color: tokens.red, marginBottom: '12px' }}>{err}</div>}

        {/* Session status + cookie paste (VIC login is CAPTCHA-walled) — owner only */}
        {vicIsOwner() && (() => {
          const ok = state && state.session_ok;
          const open = showCookie || (state && !ok);
          return (
            <div style={{ border: `1px solid ${ok ? tokens.green : tokens.ochre}`, borderRadius: '4px', padding: '11px 13px', marginBottom: '16px', background: 'rgba(0,0,0,0.012)' }}>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', alignItems: 'center', justifyContent: 'space-between' }}>
                <span style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.08em', color: ok ? tokens.green : tokens.ochre }}>
                  {state == null ? '· CHECKING SESSION…' : ok ? '● VIC SESSION ACTIVE' : '○ VIC SESSION NEEDED — PASTE YOUR COOKIE'}
                </span>
                <button onClick={() => setShowCookie((v) => !v)} style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', padding: '5px 11px', background: 'none', color: tokens.inkMute, border: `1px solid ${tokens.inkLine}`, cursor: 'pointer' }}>
                  {open ? 'HIDE' : (ok ? 'UPDATE COOKIE' : 'SET COOKIE')}
                </button>
              </div>
              {open && (
                <div style={{ marginTop: '10px' }}>
                  <div style={{ fontFamily: fontMono, fontSize: '9px', color: tokens.inkMute, lineHeight: 1.7, marginBottom: '8px' }}>
                    Logged into valueinvestorsclub.com, with DevTools → <b>Network</b> tab open:<br />
                    <b>Easiest —</b> right-click the top request (Type <code>document</code>, e.g. the page's number) → <b>Copy</b> → <b>Copy as cURL</b>, and paste the whole thing below. The cookie is pulled out automatically.<br />
                    <b>Or —</b> click that request → <b>Headers</b> → <b>Request Headers</b> → copy the value after <code>Cookie:</code>.<br />
                    (“Remember me” keeps it valid for weeks. The paste is stored server-side, never shown again.)
                  </div>
                  <textarea value={cookieText} onChange={(e) => setCookieText(e.target.value)} rows={3}
                    placeholder="Paste 'Copy as cURL' output, or the raw Cookie header value…"
                    style={{ width: '100%', boxSizing: 'border-box', fontFamily: fontMono, fontSize: '11px', padding: '8px 10px', border: `1px solid ${tokens.inkLine}`, background: '#FDFBF7', color: tokens.ink, outline: 'none', resize: 'vertical' }} />
                  <div style={{ display: 'flex', gap: '10px', alignItems: 'center', marginTop: '8px' }}>
                    <button onClick={saveCookie} disabled={savingCookie || !cookieText.trim()}
                      style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.1em', padding: '8px 14px', background: savingCookie ? 'none' : tokens.ink, color: savingCookie ? tokens.inkMute : tokens.paper, border: `1px solid ${tokens.ink}`, cursor: savingCookie ? 'default' : 'pointer' }}>
                      {savingCookie ? 'VERIFYING…' : 'SAVE SESSION'}
                    </button>
                    {cookieMsg && <span style={{ fontFamily: fontMono, fontSize: '10px', color: cookieMsg.startsWith('✗') ? tokens.red : tokens.green }}>{cookieMsg}</span>}
                  </div>
                </div>
              )}
            </div>
          );
        })()}

        {/* Search + filters — SYNC (the shell action) pulls the full A-Z archive */}
        <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 160px', gap: '10px', alignItems: 'center', marginBottom: '14px' }}>
          <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search by company, ticker, or author…"
            style={{ width: '100%', boxSizing: 'border-box', fontFamily: fontBody, fontSize: '13px', padding: '9px 12px', border: `1px solid ${tokens.inkLine}`, background: '#FDFBF7', color: tokens.ink, outline: 'none' }} />
          <select value={dir} onChange={(e) => setDir(e.target.value)}
            style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.06em', padding: '9px 10px', border: `1px solid ${tokens.inkLine}`, background: '#FDFBF7', color: tokens.ink, outline: 'none' }}>
            <option value="all">ALL DIRECTIONS</option>
            <option value="long">LONG ONLY</option>
            <option value="short">SHORT ONLY</option>
          </select>
        </div>

        {/* Connection test — one-time setup check; no console needed. Owner only. */}
        {vicIsOwner() && <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', alignItems: 'center', marginBottom: '14px' }}>
          <button onClick={runProbe} disabled={!!busy}
            title="Log in to VIC and report what it can see — run this once after setup to confirm the connection works"
            style={{ fontFamily: fontMono, fontSize: '10px', letterSpacing: '0.1em', padding: '9px 14px', background: 'none', color: tokens.inkMute, border: `1px dashed ${tokens.inkLine}`, cursor: busy ? 'default' : 'pointer', whiteSpace: 'nowrap' }}>
            {busy === 'probe' ? 'TESTING…' : '⚙ TEST VIC CONNECTION'}
          </button>
          <span style={{ fontFamily: fontMono, fontSize: '9px', color: tokens.inkMute, letterSpacing: '0.04em' }}>Run this once after setup to confirm login + parsing.</span>
        </div>}

        {probe && (
          <div style={{ border: `1px solid ${probe.error || probe.login_ok === false ? tokens.red : tokens.green}`, borderRadius: '4px', padding: '12px 14px', marginBottom: '16px', background: 'rgba(0,0,0,0.015)' }}>
            {probe.error ? (
              <div style={{ fontFamily: fontMono, fontSize: '11px', color: tokens.red, lineHeight: 1.6 }}>✗ TEST FAILED — {probe.error}</div>
            ) : (
              <React.Fragment>
                <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.06em', color: probe.login_ok ? tokens.green : tokens.red, marginBottom: '8px' }}>
                  {probe.login_ok ? '✓ LOGIN OK' : '✗ LOGIN FAILED'} · {probe.login_detail || ''} · PARSED {probe.parsed_count ?? 0} IDEAS
                </div>
                {Array.isArray(probe.parsed_sample) && probe.parsed_sample.length > 0 && (
                  <div style={{ fontSize: '12px', color: tokens.inkSoft, lineHeight: 1.6, marginBottom: '8px' }}>
                    Sample: {probe.parsed_sample.map((p, i) => <span key={i}>{i ? ', ' : ''}{p.company || '(blank)'}{p.ticker ? ` (${p.ticker})` : ''}</span>)}
                  </div>
                )}
                <div style={{ fontFamily: fontMono, fontSize: '9px', color: tokens.inkMute, lineHeight: 1.6 }}>
                  {probe.login_ok && (probe.parsed_count ?? 0) > 0
                    ? 'Looks good — hit ⟳ SYNC NOW, then ⤓ BACKFILL YTD. If the sample names look wrong, copy the full result and send it to Claude to tune the parser.'
                    : 'Something needs tuning — copy the full result below and send it to Claude to fix the login/parser.'}
                </div>
              </React.Fragment>
            )}
            <div style={{ display: 'flex', gap: '10px', alignItems: 'center', marginTop: '10px' }}>
              <button onClick={copyProbe}
                style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', padding: '6px 12px', background: tokens.ink, color: tokens.paper, border: 'none', cursor: 'pointer' }}>
                {copied ? '✓ COPIED' : '⧉ COPY FULL RESULT'}
              </button>
              <button onClick={() => setProbe(null)}
                style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', padding: '6px 12px', background: 'none', color: tokens.inkMute, border: `1px solid ${tokens.inkLine}`, cursor: 'pointer' }}>DISMISS</button>
            </div>
            <details style={{ marginTop: '10px' }}>
              <summary style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.08em', color: tokens.inkMute, cursor: 'pointer' }}>SHOW RAW RESULT (for Claude)</summary>
              <pre style={{ marginTop: '8px', maxHeight: '260px', overflow: 'auto', background: '#FDFBF7', border: `1px solid ${tokens.inkLine}`, borderRadius: '3px', padding: '10px', fontFamily: fontMono, fontSize: '10px', lineHeight: 1.5, color: tokens.ink, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{JSON.stringify(probe, null, 2)}</pre>
            </details>
          </div>
        )}

        {state && (
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px 14px', alignItems: 'center', marginBottom: '14px' }}>
            <span style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.06em', color: tokens.inkMute }}>
              {typeof state.idea_count === 'number' ? `${state.idea_count} IN ARCHIVE · ` : ''}{count} LOADED · 15/PAGE{q || dir !== 'all' ? ' (FILTERED)' : ''}
            </span>
            {vicIsOwner() && typeof state.unenriched === 'number' && (state.unenriched > 0 || enriching) && (
              <React.Fragment>
                <button onClick={runEnrich} disabled={enriching}
                  title="Fill a small batch now (newest posts first). VIC limits how many pages can be opened per day, so the daily job fills the rest over time."
                  style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', padding: '6px 12px', background: 'none', color: tokens.inkMute, border: `1px solid ${tokens.inkLine}`, cursor: enriching ? 'default' : 'pointer', whiteSpace: 'nowrap' }}>
                  {enriching ? 'FILLING…' : `⏳ FILL A BATCH (${state.unenriched} left)`}
                </button>
                {enrichMsg && <span style={{ fontFamily: fontMono, fontSize: '9px', color: enrichMsg.startsWith('✗') ? tokens.red : tokens.inkMute }}>{enrichMsg}</span>}
              </React.Fragment>
            )}
          </div>
        )}

        {/* List header */}
        {!isMobile && count > 0 && (
          <div style={{ display: 'grid', gridTemplateColumns: '86px 1fr 120px 58px 24px', gap: '14px', padding: '0 4px 8px', borderBottom: `1px solid ${tokens.inkLine}`, fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', color: tokens.inkMute }}>
            <span>POSTED</span><span>COMPANY</span><span>AUTHOR</span><span style={{ textAlign: 'right' }}>L/S</span><span />
          </div>
        )}

        {/* Rows */}
        {rows === undefined ? (
          <div style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.1em', color: tokens.inkMute, padding: '24px 4px' }}>LOADING…</div>
        ) : count === 0 ? (
          <div style={{ fontFamily: fontMono, fontSize: '11px', color: tokens.inkMute, lineHeight: 1.8, padding: '24px 4px' }}>
            {q || dir !== 'all' ? 'No write-ups match your search.' : (state && !state.session_ok ? (
              <React.Fragment>Set your VIC session cookie above, then click <span style={{ color: tokens.ink }}>⟳ SYNC NOW</span>.</React.Fragment>
            ) : (
              <React.Fragment>
                No VIC ideas yet. Click <span style={{ color: tokens.ink }}>⟳ SYNC NOW</span> — it pulls VIC's full A-to-Z archive (a few thousand ideas), searchable by company. Dates, tickers, and long/short fill in as you open each one.
              </React.Fragment>
            ))}
          </div>
        ) : (
          <React.Fragment>
            <div>{rows.slice(page * 15, page * 15 + 15).map((r) => <VICIdeaRow key={r.id} idea={r} isMobile={isMobile} />)}</div>
            {count > 15 && (() => {
              const last = Math.ceil(count / 15) - 1;
              return (
                <div style={{ display: 'flex', gap: '16px', alignItems: 'center', justifyContent: 'flex-end', fontFamily: fontMono, fontSize: '9.5px', letterSpacing: '0.08em', color: tokens.inkMute, marginTop: '10px', userSelect: 'none' }}>
                  <span onClick={() => setPage(Math.max(0, page - 1))} style={{ cursor: page > 0 ? 'pointer' : 'default', opacity: page > 0 ? 1 : 0.3, color: page > 0 ? tokens.ink : tokens.inkMute }}>‹ NEWER</span>
                  <span>{page * 15 + 1}–{Math.min(count, (page + 1) * 15)} OF {count}</span>
                  <span onClick={() => setPage(Math.min(last, page + 1))} style={{ cursor: page < last ? 'pointer' : 'default', opacity: page < last ? 1 : 0.3, color: page < last ? tokens.ink : tokens.inkMute }}>OLDER ›</span>
                </div>
              );
            })()}
          </React.Fragment>
        )}

        <div style={{ fontFamily: fontMono, fontSize: '9px', color: tokens.inkMute, marginTop: '14px', lineHeight: 1.6 }}>
          Private research aid. Write-ups are members' copyrighted work, mirrored here for personal reference only — never republished. Open a write-up to read it and generate an on-demand Finch Bay card (saved to the Idea Database + emailed).
        </div>
      </div>
    </IntelShell>
  );
};

const VICIdeasSection = () => {
  const isMobile = useIsMobile();
  const [collapsed, toggle] = useCollapsed('section.vic');
  return (
    <div>
      <div onClick={toggle} style={{ display: 'flex', alignItems: 'center', gap: '6px', marginBottom: collapsed ? '0' : '8px', cursor: 'pointer', userSelect: 'none' }}>
        <Chevron collapsed={collapsed} onClick={(e) => { e.stopPropagation(); toggle(); }} size={13} />
        <span style={{ fontFamily: fontMono, fontSize: '11px', letterSpacing: '0.18em', color: tokens.inkMute }}>VALUE INVESTORS CLUB</span>
        <span style={{ fontFamily: fontMono, fontSize: '9px', letterSpacing: '0.1em', color: tokens.ochre, border: `1px solid ${tokens.ochre}`, padding: '1px 6px' }}>PRIVATE</span>
      </div>
      {!collapsed && <VICIdeasPanel />}
    </div>
  );
};

window.VICIdeasPanel = VICIdeasPanel;
window.VICIdeasSection = VICIdeasSection;
