// Cortex — Tear Sheet page.
// One-ticker deep-dive: scores, fundamentals, signal timeline, model rationale.

function TickerSelector({ value, onChange }) {
  const [open, setOpen] = React.useState(false);
  const [query, setQuery] = React.useState('');
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    if (open) {
      setQuery('');
      requestAnimationFrame(() => inputRef.current?.focus());
    }
  }, [open]);

  const q = query.trim().toLowerCase();
  const matches = React.useMemo(() => {
    const xs = q
      ? ROWS.filter(r =>
          (r.ticker || '').toLowerCase().includes(q) ||
          (r.name   || '').toLowerCase().includes(q))
      : ROWS;
    return xs.slice(0, 200);
  }, [q]);

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

  return (
    <div ref={containerRef} style={{ position: 'relative' }}>
      <button onClick={() => setOpen(!open)} style={{
        appearance: 'none', cursor: 'pointer',
        display: 'flex', alignItems: 'center', gap: 12,
        padding: '10px 14px', background: CX.panel, border: `1px solid ${CX.lineHi}`,
        minWidth: 320,
      }}>
        <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.1em' }}>TICKER</Mono>
        <Mono style={{ fontSize: 14, color: CX.textHi }}>{value.ticker}</Mono>
        <span style={{ fontFamily: CX.display, fontSize: 13, color: CX.dim, flex: 1, textAlign: 'left' }}>{value.name}</span>
        <Mono style={{ fontSize: 10, color: CX.faint }}>▾</Mono>
      </button>
      {open && (
        <div style={{ position: 'absolute', top: '100%', left: 0, marginTop: 4, width: 420,
          background: CX.panelHi, border: `1px solid ${CX.lineHi}`, zIndex: 10,
          display: 'flex', flexDirection: 'column', maxHeight: 420 }}>
          <div style={{ padding: '10px 12px', borderBottom: `1px solid ${CX.line}`,
            display: 'flex', alignItems: 'center', gap: 10 }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.12em' }}>/</Mono>
            <input
              ref={inputRef}
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="search ticker or name (e.g. PLEJD or Plejd)…"
              style={{
                appearance: 'none', border: 'none', outline: 'none',
                background: 'transparent', color: CX.textHi,
                fontFamily: CX.mono, fontSize: 13, flex: 1,
              }}
            />
            <Mono style={{ fontSize: 10, color: CX.faint }}>
              {matches.length}{ROWS.length > matches.length ? `/${ROWS.length}` : ''}
            </Mono>
          </div>
          <div style={{ overflowY: 'auto' }}>
            {matches.length === 0 && (
              <div style={{ padding: '18px 14px' }}>
                <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.06em' }}>no matches</Mono>
              </div>
            )}
            {matches.map(r => {
              const bcolors = { compounder: CX.accent, cheap_decent: CX.blue, inflection: CX.amber, pullback: CX.dim, danger: CX.red };
              const bucketColor = r.bucket ? bcolors[r.bucket] : CX.veryFaint;
              return (
                <div key={r.security_id ?? r.ticker} onClick={() => { onChange(r); setOpen(false); }}
                  style={{ padding: '10px 14px', display: 'flex', gap: 12, cursor: 'pointer',
                    borderBottom: `1px solid ${CX.line}`, alignItems: 'center' }}>
                  <Mono style={{ fontSize: 12, color: CX.text, width: 80 }}>{r.ticker}</Mono>
                  <span style={{ fontFamily: CX.display, fontSize: 13, color: CX.dim, flex: 1 }}>{r.name}</span>
                  <Mono style={{ fontSize: 10, color: bucketColor, width: 80, textAlign: 'right', letterSpacing: '0.04em' }}>
                    ● {r.bucket || 'unflagged'}
                  </Mono>
                  <Mono style={{ fontSize: 10, color: CX.faint, width: 24, textAlign: 'right' }}>
                    {(r.country || '').slice(0, 2).toUpperCase()}
                  </Mono>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </div>
  );
}

// Trading-day window per horizon button.
const HORIZON_DAYS = { '1M': 21, '3M': 63, '6M': 126, '1Y': 252, '3Y': 756 };

// Price chart sourced from real cortex.prices_daily_v via window.PRICE_HISTORY.
function PriceChart({ ticker, horizon = '1Y', height = 220 }) {
  const ph = window.PRICE_HISTORY || { dates: [], closes: {} };
  const all = ph.closes?.[ticker] || [];
  const allDates = ph.dates || [];

  // Empty state when no prices for this ticker (e.g. brand-new listing).
  const W = 1200, H = height;
  if (!all.length) {
    return (
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        <text x={W / 2} y={H / 2} textAnchor="middle" fontFamily={CX.mono} fontSize="11" fill={CX.faint} letterSpacing="0.1em">
          NO PRICE HISTORY — {ticker}
        </text>
      </svg>
    );
  }

  const wantN = HORIZON_DAYS[horizon] || 252;
  const N = Math.min(wantN, all.length);
  const start = all.length - N;
  // Slice + ffill nulls forward so a missing day doesn't break the line.
  const ptsRaw = all.slice(start);
  const dates  = allDates.slice(start);
  const pts = [];
  let last = null;
  for (const v of ptsRaw) {
    if (v == null) pts.push(last);
    else { pts.push(v); last = v; }
  }
  // Drop leading nulls (ticker started trading later than the window begins).
  const firstReal = pts.findIndex(p => p != null);
  const visPts = firstReal === -1 ? [] : pts.slice(firstReal);
  const visDates = firstReal === -1 ? [] : dates.slice(firstReal);

  if (visPts.length < 2) {
    return (
      <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
        <text x={W / 2} y={H / 2} textAnchor="middle" fontFamily={CX.mono} fontSize="11" fill={CX.faint} letterSpacing="0.1em">
          INSUFFICIENT HISTORY FOR {horizon}
        </text>
      </svg>
    );
  }

  const min = Math.min(...visPts), max = Math.max(...visPts);
  const span = (max - min) || 1;
  const M = visPts.length;
  const xy = (i, p) => [50 + (i / (M - 1)) * (W - 70), 20 + ((max - p) / span) * (H - 40)];
  const path = visPts.map((p, i) => { const [x, y] = xy(i, p); return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)} ${y.toFixed(1)}`; }).join(' ');
  const area = path + ` L${xy(M - 1, visPts[M - 1])[0]} ${H - 20} L${xy(0, visPts[0])[0]} ${H - 20} Z`;

  // Period return for the legend annotation.
  const periodReturn = (visPts[M - 1] / visPts[0] - 1) * 100;
  const lineColor = periodReturn >= 0 ? CX.accent : CX.red;
  const areaColor = periodReturn >= 0 ? CX.accentVery : CX.redDim;

  // Build 4 evenly-spaced x-axis date labels from the actual date range.
  const labelIdxs = [0, Math.floor(M / 3), Math.floor((2 * M) / 3), M - 1];
  const fmtDate = (iso) => {
    // For sub-year ranges use "Jan 24"; for 1Y/3Y use "Jan 24" too — short is fine.
    if (!iso) return '';
    const [y, m] = iso.split('-');
    const months = ['', 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    return `${months[parseInt(m, 10)]} ${y.slice(2)}`;
  };

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
      {[0, 0.25, 0.5, 0.75, 1].map((g, i) => (
        <line key={i} x1="50" x2={W - 20} y1={20 + g * (H - 40)} y2={20 + g * (H - 40)} stroke={CX.line} strokeWidth="1" strokeDasharray={i === 0 || i === 4 ? '' : '2 4'} />
      ))}
      <path d={area} fill={areaColor} />
      <path d={path} fill="none" stroke={lineColor} strokeWidth="1.4" />
      {/* y labels */}
      {[max, (max + min) / 2, min].map((v, i) => (
        <text key={i} x="46" y={[24, H / 2, H - 24][i]} textAnchor="end" fontFamily={CX.mono} fontSize="10" fill={CX.faint}>
          {v >= 100 ? v.toFixed(0) : v.toFixed(2)}
        </text>
      ))}
      {/* x labels */}
      {labelIdxs.map((idx, i) => (
        <text key={i} x={xy(idx, visPts[idx])[0]} y={H - 4} textAnchor={i === 0 ? 'start' : i === 3 ? 'end' : 'middle'}
          fontFamily={CX.mono} fontSize="10" fill={CX.faint} letterSpacing="0.06em">
          {fmtDate(visDates[idx])}
        </text>
      ))}
      {/* period-return annotation */}
      <g transform={`translate(${W - 140}, 20)`}>
        <text x="0" y="14" fontFamily={CX.mono} fontSize="11" fill={lineColor} letterSpacing="0.04em">
          {horizon} · {periodReturn >= 0 ? '+' : ''}{periodReturn.toFixed(2)}%
        </text>
      </g>
    </svg>
  );
}

// Score radar (5-axis). All numbers 0..100.
function ScoreRadar({ row, size = 220 }) {
  const axes = [
    ['Q',   row.quality],
    ['V',   row.value],
    ['G',   row.growth],
    ['M',   row.momentum],
    ['I',   row.inflection],
  ];
  const cx = size / 2, cy = size / 2, R = size / 2 - 28;
  const pt = (i, v) => {
    const a = (Math.PI * 2 * i / axes.length) - Math.PI / 2;
    const r = (v / 100) * R;
    return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
  };
  const path = axes.map(([_, v], i) => `${i === 0 ? 'M' : 'L'}${pt(i, v).join(' ')}`).join(' ') + ' Z';
  return (
    <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size}>
      {[0.25, 0.5, 0.75, 1].map((g, i) => (
        <polygon key={i}
          points={axes.map((_, j) => {
            const a = (Math.PI * 2 * j / axes.length) - Math.PI / 2;
            return `${cx + Math.cos(a) * R * g},${cy + Math.sin(a) * R * g}`;
          }).join(' ')}
          fill="none" stroke={CX.line} strokeWidth="1" />
      ))}
      {axes.map((_, i) => {
        const a = (Math.PI * 2 * i / axes.length) - Math.PI / 2;
        return <line key={i} x1={cx} y1={cy} x2={cx + Math.cos(a) * R} y2={cy + Math.sin(a) * R} stroke={CX.line} strokeWidth="1" />;
      })}
      <path d={path} fill={CX.accentDim} stroke={CX.accent} strokeWidth="1.5" />
      {axes.map(([l, v], i) => {
        const [x, y] = pt(i, 110);
        return (
          <g key={i}>
            <text x={x} y={y} textAnchor="middle" dy="4" fontFamily={CX.mono} fontSize="11" fill={CX.dim} letterSpacing="0.06em">{l}</text>
          </g>
        );
      })}
      {/* center label */}
      <text x={cx} y={cy + 4} textAnchor="middle" fontFamily={CX.mono} fontSize="11" fill={CX.faint} letterSpacing="0.1em">RADAR</text>
    </svg>
  );
}

function PageTearsheet({ initialTicker }) {
  const [row, setRow] = React.useState(ROWS.find(r => r.ticker === initialTicker) || ROWS.find(r => r.ticker === 'SAGA B') || ROWS[0]);
  const [horizon, setHorizon] = React.useState('1Y');

  // Real "signals · last 30d" from the Claude AI document scores when we
  // have Cision coverage for this ticker; honest empty state otherwise.
  const aiScore = row.ai_text_score;
  const aiDocCount = row.ai_n_documents_90d;
  const aiLatestDoc = row.ai_latest_doc_date;
  const hasAi = aiScore != null && aiDocCount != null;

  // Risk read derived from the actual risk_score breakdown (component ranks
  // would be richer but we only have the aggregate). Cleaner empty state
  // than fabricated narrative.
  const riskFlags = [];
  if (row.risk >= 75) riskFlags.push(['risk score', `${row.risk} — danger band (cutoff 75)`]);
  else if (row.risk >= 50) riskFlags.push(['risk score', `${row.risk} — elevated`]);
  if (row.liquidity === 'thin' || row.liquidity === 'illiquid') {
    riskFlags.push(['liquidity', `${row.liquidity} — small float, sizing capped`]);
  }
  if (row.mcap_bucket === 'nano' || row.mcap_bucket === 'micro') {
    riskFlags.push(['market cap', `${row.mcap_bucket} — limited trading depth`]);
  }
  if (row.drawdown_from_52w_high != null && row.drawdown_from_52w_high <= -0.25) {
    riskFlags.push(['drawdown', `${(row.drawdown_from_52w_high * 100).toFixed(0)}% off 52w high`]);
  }

  return (
    <div style={{ padding: '32px 32px 64px', overflowY: 'auto', height: '100%' }}>
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 24 }}>
        <div>
          <Mono style={{ fontSize: 10, color: CX.accent, letterSpacing: '0.18em' }}>02 · /Q.TEARSHEET</Mono>
          <h1 style={{ fontFamily: CX.display, fontSize: 40, fontWeight: 400, letterSpacing: '-0.02em', margin: '8px 0 4px', color: CX.textHi }}>
            Tear sheet<span style={{ color: CX.accent }}>.</span>
          </h1>
          <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.08em' }}>
            queue as of {META.asof || '—'} · prices through {META.latest_price_date || '—'} · cortex {META.version || '—'}
          </Mono>
        </div>
        <TickerSelector value={row} onChange={setRow} />
      </div>

      {/* big identity bar */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 0.7fr 0.7fr 0.7fr 0.7fr', gap: 1, background: CX.line, border: `1px solid ${CX.line}`, marginBottom: 24 }}>
        <div style={{ background: CX.panel, padding: '22px 24px' }}>
          <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>{row.country.toUpperCase()} · {row.sector.toUpperCase()}</Mono>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 14, marginTop: 8 }}>
            <Mono style={{ fontSize: 36, color: CX.textHi, letterSpacing: '0.01em' }}>{row.ticker}</Mono>
            <h2 style={{ fontFamily: CX.display, fontSize: 28, fontWeight: 400, color: CX.text, margin: 0, letterSpacing: '-0.01em' }}>{row.name}</h2>
          </div>
          <div style={{ marginTop: 14, display: 'flex', alignItems: 'center', gap: 10 }}>
            {(() => {
              const bcolors = { compounder: CX.accent, cheap_decent: CX.blue, inflection: CX.amber, pullback: CX.dim, danger: CX.red };
              if (!row.bucket) {
                return (
                  <span style={{ display: 'inline-flex', alignItems: 'center' }}>
                    <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.08em' }}>● unflagged — no bucket cutoff met</Mono>
                    <Help id="primary_signal_bucket" />
                  </span>
                );
              }
              return (
                <span style={{ display: 'inline-flex', alignItems: 'center' }}>
                  <Mono style={{ fontSize: 11, color: bcolors[row.bucket], letterSpacing: '0.08em' }}>● {row.bucket} · #{row.rank} in bucket</Mono>
                  <Help id={`${row.bucket}_score`} />
                </span>
              );
            })()}
            {row.flag && (
              <span style={{ display: 'inline-flex', alignItems: 'center' }}>
                <Mono style={{ fontSize: 11, color: row.flag === 'AVOID' ? CX.red : CX.accent, letterSpacing: '0.1em' }}>flag · {row.flag}</Mono>
                <Help id={row.flag === 'AVOID' ? 'flag_avoid' : row.flag === 'BUY' ? 'flag_buy' : 'flag_new'} />
              </span>
            )}
            {row.held && <Mono style={{ fontSize: 11, color: CX.accent, letterSpacing: '0.1em' }}>● HELD</Mono>}
            {row.watch && <Mono style={{ fontSize: 11, color: CX.amber, letterSpacing: '0.1em' }}>● WATCH</Mono>}
          </div>
        </div>
        {[
          ['Mcap',      `€${row.mcap_eur}M`, CX.text, 'mcap_eur'],
          ['EV / EBIT', row.ev_ebit,         CX.text, 'ev_ebit'],
          ['FCF yield', `${row.fcf_yield}%`, parseFloat(row.fcf_yield) >= 0 ? CX.accent : CX.red, 'fcf_yield'],
          ['Δ 30D',     `${row.thirty_d}%`,  parseFloat(row.thirty_d) >= 0 ? CX.accent : CX.red, 'thirty_d'],
        ].map(([k, v, c, helpId], i) => (
          <div key={i} style={{ background: CX.panel, padding: '22px 20px', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>{k.toUpperCase()}</Mono>
              <Help id={helpId} />
            </div>
            <Mono style={{ fontSize: 28, color: c, marginTop: 8, letterSpacing: '-0.01em' }}>{v}</Mono>
          </div>
        ))}
      </div>

      {/* chart + scores */}
      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 24, marginBottom: 24 }}>
        <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>PRICE · {horizon} CLOSE</Mono>
              <Help id="price_chart" />
            </div>
            <div style={{ display: 'flex', gap: 4 }}>
              {['1M', '3M', '6M', '1Y', '3Y'].map((t) => {
                const on = horizon === t;
                return (
                  <button key={t} onClick={() => setHorizon(t)} style={{
                    appearance: 'none', cursor: 'pointer',
                    padding: '4px 10px',
                    border: `1px solid ${on ? CX.accent : CX.line}`,
                    color: on ? CX.accent : CX.dim,
                    fontFamily: CX.mono, fontSize: 10, letterSpacing: '0.06em',
                    background: on ? CX.accentVery : 'transparent',
                  }}>{t}</button>
                );
              })}
            </div>
          </div>
          <PriceChart ticker={row.ticker} horizon={horizon} />
          <div style={{ marginTop: 12, fontFamily: CX.mono, fontSize: 10, color: CX.faint, letterSpacing: '0.06em' }}>
            close prices from cortex.prices_daily_v — forward-filled across holidays
          </div>
        </div>
        <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px', display: 'flex', flexDirection: 'column' }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>FACTOR PROFILE</Mono>
            <Help id="primary_signal_bucket" />
          </div>
          <div style={{ display: 'flex', justifyContent: 'center', margin: '12px 0' }}>
            <ScoreRadar row={row} size={220} />
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {[
              ['Q · quality',    row.quality, false, 'quality_score'],
              ['V · value',      row.value,   false, 'value_score'],
              ['G · growth',     row.growth,  false, 'growth_score'],
              ['M · momentum',   row.momentum, false, 'momentum_score'],
              ['I · inflection', row.inflection, false, 'inflection_score'],
              ['R · risk',       row.risk, true, 'risk_score'],
            ].map((s, i) => (
              <div key={i} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div style={{ display: 'flex', alignItems: 'center' }}>
                  <Mono style={{ fontSize: 11, color: CX.dim, letterSpacing: '0.04em' }}>{s[0]}</Mono>
                  <Help id={s[3]} />
                </div>
                {s[2] ? (
                  <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 60, height: 4, background: CX.line, position: 'relative' }}>
                      <div style={{ position: 'absolute', inset: 0, width: `${s[1]}%`, background: s[1] > 70 ? CX.red : s[1] > 40 ? CX.amber : CX.accent }} />
                    </div>
                    <Mono style={{ fontSize: 11, color: CX.text, width: 22, textAlign: 'right' }}>{s[1]}</Mono>
                  </div>
                ) : <Score v={s[1]} w={60} />}
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* AI signal + risk derived from real cortex data, with honest empty states */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, marginBottom: 24 }}>
        <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px' }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>AI TEXT SIGNAL (CISION · 90D)</Mono>
            <Help id="ai_text_score" />
          </div>
          {hasAi ? (
            <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                <Mono style={{ fontSize: 11, color: CX.dim, letterSpacing: '0.04em' }}>composite (0–100)</Mono>
                <Mono style={{ fontSize: 18, color: aiScore >= 60 ? CX.accent : aiScore >= 40 ? CX.amber : CX.red }}>
                  {Number(aiScore).toFixed(1)}
                </Mono>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                <Mono style={{ fontSize: 11, color: CX.dim }}>docs in window</Mono>
                <Mono style={{ fontSize: 13, color: CX.text }}>{aiDocCount}</Mono>
              </div>
              {aiLatestDoc && (
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                  <Mono style={{ fontSize: 11, color: CX.dim }}>latest doc</Mono>
                  <Mono style={{ fontSize: 13, color: CX.text }}>{aiLatestDoc}</Mono>
                </div>
              )}
              <Mono style={{ marginTop: 6, fontSize: 11, color: CX.faint, letterSpacing: '0.04em', textWrap: 'pretty', display: 'block', whiteSpace: 'normal' }}>
                Mean of Claude's 6 directional subscores over Cision press releases in the last
                90 days. Risk fields pre-inverted (higher = lower risk).
              </Mono>
            </div>
          ) : (
            <Mono style={{ marginTop: 14, display: 'block', fontSize: 11, color: CX.faint, letterSpacing: '0.06em', whiteSpace: 'normal' }}>
              no Cision-scored documents in the 90-day window — AI coverage is Sweden-only
              and depends on the issuer publishing via Cision.
            </Mono>
          )}
        </div>
        <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px' }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>RISK FLAGS</Mono>
            <Help id="risk_score" />
          </div>
          {riskFlags.length ? (
            <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column' }}>
              {riskFlags.map(([k, v], i) => (
                <div key={i} style={{ display: 'grid', gridTemplateColumns: '14px 110px 1fr', gap: 12, padding: '12px 0', borderTop: i === 0 ? 'none' : `1px solid ${CX.line}`, alignItems: 'baseline' }}>
                  <Mono style={{ fontSize: 11, color: CX.amber }}>●</Mono>
                  <Mono style={{ fontSize: 11, color: CX.dim, letterSpacing: '0.04em' }}>{k}</Mono>
                  <Mono style={{ fontSize: 12, color: CX.text }}>{v}</Mono>
                </div>
              ))}
            </div>
          ) : (
            <Mono style={{ marginTop: 14, display: 'block', fontSize: 11, color: CX.faint, letterSpacing: '0.06em', whiteSpace: 'normal' }}>
              no risk flags raised — risk score in normal range, liquid, no major drawdown.
            </Mono>
          )}
        </div>
      </div>

      {/* PM notes — live from window.NOTES_BY_TICKER[ticker]; empty state otherwise */}
      <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px' }}>
        {(() => {
          const notes = (window.NOTES_BY_TICKER && window.NOTES_BY_TICKER[row.ticker]) || [];
          return (
            <>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 }}>
                <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>
                  PM NOTES — {notes.length} {notes.length === 1 ? 'ENTRY' : 'ENTRIES'}
                </Mono>
                <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.06em' }}>
                  read-only · notes are added via SQL (cortex.notes)
                </Mono>
              </div>
              {notes.length === 0 ? (
                <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.06em' }}>
                  no notes yet — add one to start the institutional memory loop
                </Mono>
              ) : notes.map((n, i) => (
                <div key={i} style={{ display: 'grid', gridTemplateColumns: '80px 90px 1fr', gap: 18, padding: '14px 0', borderTop: i === 0 ? 'none' : `1px solid ${CX.line}` }}>
                  <Mono style={{ fontSize: 11, color: CX.accent, letterSpacing: '0.08em' }}>{n.author}</Mono>
                  <Mono style={{ fontSize: 11, color: CX.faint }}>{n.date}</Mono>
                  <div style={{ fontFamily: CX.display, fontSize: 14, color: CX.text, lineHeight: 1.5, textWrap: 'pretty' }}>{n.body}</div>
                </div>
              ))}
            </>
          );
        })()}
      </div>
    </div>
  );
}

window.PageTearsheet = PageTearsheet;
window.TickerSelector = TickerSelector;
