// Cap-table viewer.
//
// Single-mode "my return" simulator inspired by dilutionlab.canonical.cc.
// Rendered by Viewer when file.kind === 'captable'. Two fetches:
//   /api/captable/public  — anonymised tier totals + supply
//   /api/captable/me      — caller's own allocations + advisor grants
// Both run on mount; the component re-renders once data lands. Pre-
// migration, both endpoints return migration_required and the UI shows
// a polite "coming soon" stub instead of trying to render an empty grid.

function fmtUsd(n) {
  if (n == null || isNaN(n)) return '—';
  const x = Number(n);
  if (x >= 1e9) return `$${(x / 1e9).toFixed(2)}B`;
  if (x >= 1e6) return `$${(x / 1e6).toFixed(2)}M`;
  if (x >= 1e3) return `$${(x / 1e3).toFixed(1)}K`;
  return `$${x.toFixed(2)}`;
}
function fmtTok(n) {
  if (n == null || isNaN(n)) return '—';
  const x = Number(n);
  if (x >= 1e9) return `${(x / 1e9).toFixed(2)}B`;
  if (x >= 1e6) return `${(x / 1e6).toFixed(2)}M`;
  if (x >= 1e3) return `${(x / 1e3).toFixed(1)}K`;
  return x.toLocaleString();
}
function fmtPct(n) {
  if (n == null || isNaN(n)) return '—';
  const x = Number(n) * 100;
  if (x >= 10) return `${x.toFixed(1)}%`;
  if (x >= 1)  return `${x.toFixed(2)}%`;
  return `${x.toFixed(3)}%`;
}

// ── Canonical round model (source of truth: captable.md v2, 2026-06-11) ──
// FINAL model — source of truth: deck/tokenomics-methodology.md (§2/§3/§9).
// ARBCAP is the share-equivalent token of Arb Capital (Rivendale Corp), an
// equity-like, revenue-backed token (NOT a stablecoin, NOT wstGBP).
// Core reframe: do NOT index on the 1B cap. Index on the ALLOCATED (issued)
// base. Founders keep all 210M; their ownership % of the allocated base
// dilutes as each cohort is issued (100% -> 55.3% at 380M allocated -> 21%
// only at full deployment). Revenue is STAKED-ONLY: ownership != revenue.
// $1.00 is the first primary-sale tranche price, NOT a peg/floor/ceiling.

// The 1B cap, four lines. Detail (primary treasury / staking / team / POL)
// is deliberately collapsed: it is not needed to answer "how do founders
// dilute" and adds extraneous load.
const SUPPLY_BUCKETS = [
  { holder: 'Founders (3 × 70M)',            tokens: 210_000_000 },
  { holder: 'Advisors',                      tokens: 20_000_000 },
  { holder: 'SAFT investors',                tokens: 50_000_000 },
  { holder: 'Treasury, team, staking & POL', tokens: 720_000_000 },
];

// §3 — founder ownership % of the cumulative ALLOCATED (issued) base as each
// cohort is released. Founders hold 210M throughout; the % falls because the
// denominator grows. allocVal = price × allocated base; fdv = price × 1B.
const ALLOC_PATH = [
  { point: 'Founders',                   released: 210_000_000, alloc: 210_000_000, price: null, founderPct: 100.0, allocVal: 21,          fdv: 100 },
  { point: 'Advisors',                   released: 20_000_000,  alloc: 230_000_000, price: null, founderPct: 91.3,  allocVal: 23,          fdv: 100 },
  { point: 'SAFT — Pre',                 released: 10_000_000,  alloc: 240_000_000, price: 0.05, founderPct: 87.5,  allocVal: 12_000_000,  fdv: 50_000_000 },
  { point: 'SAFT — Seed',                released: 20_000_000,  alloc: 260_000_000, price: 0.10, founderPct: 80.8,  allocVal: 26_000_000,  fdv: 100_000_000 },
  { point: 'SAFT — Final',               released: 20_000_000,  alloc: 280_000_000, price: 0.25, founderPct: 75.0,  allocVal: 70_000_000,  fdv: 250_000_000 },
  { point: 'Market — 1st $1.00 tranche', released: 100_000_000, alloc: 380_000_000, price: 1.00, founderPct: 55.3,  allocVal: 380_000_000, fdv: 1_000_000_000 },
];

// Unlocked tokens at month m given a cliff (in months) + linear-vest
// period (in months). At m < cliff: 0. At cliff <= m < cliff+linear:
// linear ramp from 0 → total. At m >= cliff+linear: full total.
function unlockedAt(totalTokens, cliffMonths, linearMonths, m) {
  if (totalTokens <= 0) return 0;
  if (m < cliffMonths) return 0;
  if (linearMonths <= 0) return totalTokens;
  const progress = Math.min(1, (m - cliffMonths) / linearMonths);
  return totalTokens * progress;
}

// Crystal-clear canonical model: founder dilution path, valuation/FDV
// ladder, and the return grid — the answers to "what's the valuation /
// FDV / how do founders dilute" so a VC never has to ask. Static content
// from captable.md; rendered above the live-progress strip + simulator.
function ClarityBand() {
  const facts = [
    { k: 'FOUNDERS', v: '210M → 55%', s: '100% → 55% of issued tokens across the raise (→ 21% at full deployment)' },
    { k: 'RAISING',  v: '50M · $7.5M', s: 'SAFT: Pre $0.05 / Seed $0.10 / Final $0.25' },
    { k: 'THE REST', v: 'unissued treasury', s: 'non-dilutive until sold as growth capital' },
  ];
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: `repeat(${facts.length}, 1fr)`, gap: 1,
      background: 'var(--border)', border: '1px solid var(--border)', marginBottom: 28,
    }}>
      {facts.map((f, i) => (
        <div key={i} style={{ background: 'var(--bg-card)', padding: '14px 14px' }}>
          <div className="mono" style={{ fontSize: 9, letterSpacing: '0.12em', color: 'var(--text-tertiary)', marginBottom: 6 }}>{f.k}</div>
          <div style={{ fontSize: 17, fontWeight: 500, letterSpacing: '-0.02em', marginBottom: 4 }}>{f.v}</div>
          <div className="mono" style={{ fontSize: 9, color: 'var(--text-tertiary)', lineHeight: 1.4 }}>{f.s}</div>
        </div>
      ))}
    </div>
  );
}

function CanonSection({ n, title, sub, children }) {
  return (
    <div style={{ marginBottom: 32 }}>
      <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--gold-dark)', marginBottom: 4 }}>
        {n}
      </div>
      <h2 style={{ fontSize: 19, fontWeight: 500, letterSpacing: '-0.02em', margin: '0 0 4px' }}>{title}</h2>
      {sub && <p style={{ fontSize: 12, color: 'var(--text-secondary)', lineHeight: 1.55, margin: '0 0 14px', maxWidth: 760 }}>{sub}</p>}
      {children}
    </div>
  );
}

// Bordered table with mono header + right-aligned numeric cells.
function CapTable({ head, rows, highlightLast }) {
  return (
    <div style={{ overflowX: 'auto', border: '1px solid var(--border)' }}>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12.5 }}>
        <thead>
          <tr style={{ background: 'var(--bg)' }}>
            {head.map((h, i) => (
              <th key={i} className="mono" style={{
                textAlign: i === 0 ? 'left' : 'right', padding: '9px 12px',
                fontSize: 9.5, letterSpacing: '0.08em', color: 'var(--text-tertiary)',
                fontWeight: 500, borderBottom: '1px solid var(--border)', whiteSpace: 'nowrap',
              }}>{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((r, ri) => {
            const isLast = highlightLast && ri === rows.length - 1;
            return (
              <tr key={ri} style={{ background: isLast ? 'rgba(212,175,55,0.06)' : 'transparent' }}>
                {r.map((c, ci) => (
                  <td key={ci} style={{
                    textAlign: ci === 0 ? 'left' : 'right', padding: '9px 12px',
                    borderBottom: ri === rows.length - 1 ? 'none' : '1px solid var(--border)',
                    fontFamily: ci === 0 ? 'inherit' : 'var(--font-mono)',
                    fontWeight: isLast ? 600 : 400,
                    color: isLast ? 'var(--gold-dark)' : 'var(--text-primary)',
                    whiteSpace: 'nowrap',
                  }}>{c}</td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

function CanonicalModel() {
  const note = { fontSize: 11, color: 'var(--text-tertiary)', lineHeight: 1.5, margin: '10px 0 0' };
  const fmtAllocVal = v => (v >= 1000 ? fmtUsd(v) : `$${v}`); // tiny par values shown literally
  const pctCap = n => `${((n / 1_000_000_000) * 100).toFixed(0)}%`;

  const allocRows = ALLOC_PATH.map(r => ([
    r.point,
    r.price == null ? 'nominal' : `$${r.price.toFixed(2)}`,
    fmtTok(r.alloc),
    `${r.founderPct.toFixed(1)}%`,
    fmtAllocVal(r.allocVal),
    fmtUsd(r.fdv),
  ]));
  const bucketRows = SUPPLY_BUCKETS.map(b => [b.holder, fmtTok(b.tokens), pctCap(b.tokens)]);
  const bucketTotal = ['Total', fmtTok(1_000_000_000), '100%'];

  return (
    <div>
      <CanonSection
        n="FOUNDER DILUTION"
        title="Founders keep all 210M; their share falls as we issue"
        sub="Founders never sell. Their % drops only because each new cohort is issued and the denominator (tokens actually issued) grows, like a standard equity cap table. Measured on issued tokens, not the 1B cap."
      >
        <CapTable
          head={['Release', 'Price', 'Issued tokens', 'Founder %', 'Allocated value', 'FDV (×1B)']}
          rows={allocRows}
          highlightLast
        />
        <p style={note}>
          $1.00 is the first primary-sale price, not a peg or floor; below-$1 secondary trading is expected. Ownership share, not revenue: revenue is staked-only (see the Tokenomics doc). Model your own position at any price with the slider at the top.
        </p>
      </CanonSection>

      <CanonSection
        n="THE FULL 1B"
        title="Where the supply goes"
        sub="SAFT investors are their own 50M bucket, not carved from founders. The 720M of treasury, team, staking and liquidity is unissued: non-dilutive until sold, and any sale is growth capital, not free dilution."
      >
        <CapTable head={['Bucket', 'Tokens', '% of cap']} rows={[...bucketRows, bucketTotal]} highlightLast />
      </CanonSection>
    </div>
  );
}

function CaptableBody({ file, identity }) {
  const [pub, setPub] = React.useState(null);
  const [me, setMe] = React.useState(null);
  const [error, setError] = React.useState(null);

  // Single cap-table doc ('captable'): the Overview / Founder-dilution
  // toggle is always shown.
  const [view, setView] = React.useState('overview');

  // Simulator state.
  const [tierId, setTierId] = React.useState(null);
  const [usd, setUsd] = React.useState(50000);
  const [exitFdv, setExitFdv] = React.useState(1_000_000_000); // $1B FDV default

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const [p, m] = await Promise.all([
          fetch('/api/captable/public', { credentials: 'include' }).then(r => r.ok ? r.json() : Promise.reject(`HTTP ${r.status}`)),
          fetch('/api/captable/me',     { credentials: 'include' }).then(r => r.ok ? r.json() : Promise.reject(`HTTP ${r.status}`)),
        ]);
        if (cancelled) return;
        setPub(p); setMe(m);
        // Default the simulator's tier to the user's largest allocation, else Pre.
        const largest = (m.allocations || []).slice().sort((a, b) => b.tokens - a.tokens)[0];
        if (largest) {
          setTierId(largest.tier_id);
          setUsd(Number(largest.usd_committed) || 0);
        } else if ((p.tiers || []).length) {
          setTierId(p.tiers[0].id);
        }
      } catch (e) {
        if (!cancelled) setError(typeof e === 'string' ? e : (e.message || 'fetch failed'));
      }
    })();
    return () => { cancelled = true; };
  }, []);

  if (error) {
    return (
      <div style={{ padding: 40, color: 'var(--error)', fontSize: 13 }}>
        Couldn't load cap table: {error}
      </div>
    );
  }
  if (!pub || !me) {
    return (
      <div style={{ padding: 40, textAlign: 'center', color: 'var(--text-tertiary)', fontFamily: 'var(--font-mono)', fontSize: 11, letterSpacing: '0.14em' }}>
        LOADING…
      </div>
    );
  }
  if (pub.migration_required) {
    return (
      <div style={{ padding: 56, background: 'var(--bg-card)', border: '1px solid var(--border)' }}>
        <div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary)', letterSpacing: '0.14em', marginBottom: 12 }}>
          CAP_TABLE · COMING SOON
        </div>
        <h1 style={{ fontSize: 28, fontWeight: 400, letterSpacing: '-0.02em', margin: '0 0 12px' }}>
          Cap table & dilution
        </h1>
        <p style={{ fontSize: 14, color: 'var(--text-secondary)', lineHeight: 1.6, maxWidth: 560 }}>
          The cap-table data is being prepared. This view will show the round structure across Pre / Seed / Final / Public tiers, advisor grants, and let you model your return at a chosen exit valuation. Check back shortly.
        </p>
      </div>
    );
  }

  const tiers = pub.tiers || [];
  const tier = tiers.find(t => t.id === tierId) || tiers[0];
  const myAllocs = me.allocations || [];
  const myAdvisor = (me.advisor_grants || [])[0] || null;
  const myTotalTokens = myAllocs.reduce((s, a) => s + Number(a.tokens || 0), 0) + (myAdvisor ? Number(myAdvisor.tokens || 0) : 0);
  const myTotalUsd = myAllocs.reduce((s, a) => s + Number(a.usd_committed || 0), 0);

  // Simulator math.
  const simTokens = tier && tier.price_usd > 0 ? usd / tier.price_usd : 0;
  const simExitPrice = pub.total_supply > 0 ? exitFdv / pub.total_supply : 0;
  const simExitValue = simTokens * simExitPrice;
  const simMoic = usd > 0 ? simExitValue / usd : 0;

  return (
    <div style={{ padding: 32, background: 'var(--bg-card)', border: '1px solid var(--border)' }}>
      {/* Header */}
      <div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary)', letterSpacing: '0.14em', marginBottom: 8 }}>
        FINANCIALS · CAP TABLE
      </div>
      <h1 style={{ fontSize: 32, fontWeight: 400, letterSpacing: '-0.025em', margin: '0 0 8px' }}>
        Arb Capital cap table & dilution model
      </h1>
      <p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, margin: '0 0 16px', maxWidth: 720 }}>
        Total token supply <strong>{fmtTok(pub.total_supply)}</strong> · Treasury <strong>{fmtTok(pub.treasury_pool)}</strong> · Founders <strong>{fmtTok(pub.founder_pool)}</strong>.
        Pick a tier and exit FDV to model your return.
      </p>

      {/* Overview / Founder dilution toggle. */}
      {(
        <div style={{ display: 'inline-flex', border: '1px solid var(--border)', borderRadius: 6, overflow: 'hidden', marginBottom: 28 }}>
          {[['overview', 'Overview'], ['dilution', 'Founder dilution']].map(([k, label]) => (
            <button
              key={k}
              onClick={() => setView(k)}
              className="mono"
              style={{
                padding: '8px 18px', fontSize: 11, letterSpacing: '0.1em', cursor: 'pointer',
                border: 'none', borderRight: k === 'overview' ? '1px solid var(--border)' : 'none',
                background: view === k ? 'var(--gold)' : 'transparent',
                color: view === k ? '#1a1a1a' : 'var(--text-secondary)',
                fontWeight: view === k ? 600 : 400,
              }}
            >
              {label}
            </button>
          ))}
        </div>
      )}

      {/* Tier strip */}
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${tiers.length + 1}, 1fr)`, gap: 12, marginBottom: 32 }}>
        {tiers.map(t => {
          const pctFilled = t.target_raise_usd > 0 ? Math.min(1, t.committed_usd / t.target_raise_usd) : 0;
          const mine = myAllocs.find(a => a.tier_id === t.id);
          return (
            <div key={t.id} style={{
              padding: 14,
              border: '1px solid var(--border)',
              background: mine ? 'rgba(212,175,55,0.06)' : 'var(--bg)',
              borderColor: mine ? 'var(--gold)' : 'var(--border)',
            }}>
              <div className="mono" style={{ fontSize: 9, letterSpacing: '0.16em', color: 'var(--text-tertiary)', marginBottom: 6 }}>
                {t.name.toUpperCase()} · ${Number(t.price_usd).toFixed(2)}
              </div>
              <div style={{ fontSize: 18, fontWeight: 500, letterSpacing: '-0.02em', marginBottom: 4 }}>
                {t.committed_masked
                  ? <span style={{ color: 'var(--text-tertiary)' }}>Pooled</span>
                  : fmtUsd(t.committed_usd)}
                {' '}<span style={{ color: 'var(--text-tertiary)', fontSize: 11 }}>/ {fmtUsd(t.target_raise_usd)}</span>
              </div>
              <div style={{ height: 3, background: 'var(--border)', marginBottom: 6, position: 'relative' }}>
                <div style={{ height: '100%', width: `${(t.committed_masked ? 0 : pctFilled) * 100}%`, background: 'var(--gold)' }} />
              </div>
              <div className="mono" style={{ fontSize: 9, color: 'var(--text-tertiary)', letterSpacing: '0.08em' }}>
                {t.committed_masked ? '—' : fmtTok(t.committed_tokens)} / {fmtTok(t.target_tokens)} TOK · {t.allocation_count || 0} alloc
              </div>
              {mine && (
                <div className="mono" style={{ fontSize: 9, color: 'var(--gold-dark)', letterSpacing: '0.1em', marginTop: 6 }}>
                  ⭑ YOU: {fmtTok(mine.tokens)} TOK
                </div>
              )}
            </div>
          );
        })}
        {/* Advisors card */}
        <div style={{ padding: 14, border: '1px solid var(--border)', background: 'var(--bg)' }}>
          <div className="mono" style={{ fontSize: 9, letterSpacing: '0.16em', color: 'var(--text-tertiary)', marginBottom: 6 }}>
            ADVISORS · ${Number(pub.advisors.reference_price_usd).toFixed(2)} REF
          </div>
          <div style={{ fontSize: 18, fontWeight: 500, letterSpacing: '-0.02em', marginBottom: 4 }}>
            {pub.advisors.committed_masked
              ? <span style={{ color: 'var(--text-tertiary)' }}>Pooled</span>
              : fmtTok(pub.advisors.total_tokens)}
          </div>
          <div className="mono" style={{ fontSize: 9, color: 'var(--text-tertiary)', letterSpacing: '0.08em' }}>
            {pub.advisors.grant_count} GRANT{pub.advisors.grant_count === 1 ? '' : 'S'}
          </div>
          {myAdvisor && (
            <div className="mono" style={{ fontSize: 9, color: 'var(--gold-dark)', letterSpacing: '0.1em', marginTop: 6 }}>
              ⭑ YOU: {fmtTok(myAdvisor.tokens)} TOK
            </div>
          )}
        </div>
      </div>

      {/* Personalised banner */}
      {myTotalTokens > 0 ? (
        <div style={{ padding: 16, marginBottom: 24, border: '1px solid var(--gold)', background: 'rgba(212,175,55,0.05)' }}>
          <div className="mono" style={{ fontSize: 9, letterSpacing: '0.16em', color: 'var(--gold-dark)', marginBottom: 4 }}>
            YOUR POSITION
          </div>
          <div style={{ fontSize: 16 }}>
            <strong>{fmtTok(myTotalTokens)}</strong> tokens across {myAllocs.length} allocation{myAllocs.length === 1 ? '' : 's'}{myAdvisor ? ' + advisor grant' : ''} · <strong>{fmtUsd(myTotalUsd)}</strong> committed.
          </div>
        </div>
      ) : (
        <div style={{ padding: 14, marginBottom: 24, border: '1px solid var(--border)', background: 'var(--bg)', fontSize: 13, color: 'var(--text-secondary)' }}>
          No allocation on this room yet. Use the simulator below to model a position. Reply to JB at <a href="mailto:jb@arb.capital" style={{ color: 'var(--gold-dark)' }}>jb@arb.capital</a> if you're considering one.
        </div>
      )}

      {/* Simulator */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, marginBottom: 24 }}>
        <div>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--text-tertiary)', marginBottom: 14 }}>
            SIMULATE
          </div>
          <div style={{ marginBottom: 14 }}>
            <div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary)', letterSpacing: '0.1em', marginBottom: 6 }}>TIER</div>
            <select className="input" value={tierId || ''} onChange={e => setTierId(e.target.value)} style={{ width: '100%', height: 36 }}>
              {tiers.map(t => (
                <option key={t.id} value={t.id}>{t.name} · ${Number(t.price_usd).toFixed(2)} / token</option>
              ))}
            </select>
          </div>
          <div style={{ marginBottom: 14 }}>
            <div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary)', letterSpacing: '0.1em', marginBottom: 6 }}>$ INVESTED</div>
            <input className="input" type="number" min="0" step="1000" value={usd}
              onChange={e => setUsd(Math.max(0, Number(e.target.value) || 0))}
              style={{ width: '100%', height: 36, fontFamily: 'var(--font-mono)' }} />
          </div>
          <div>
            <div className="mono" style={{ fontSize: 10, color: 'var(--text-tertiary)', letterSpacing: '0.1em', marginBottom: 6 }}>
              EXIT FDV · {fmtUsd(exitFdv)} ({fmtUsd(exitFdv / pub.total_supply)} / TOK)
            </div>
            <input type="range" min={pub.total_supply * 0.10} max={pub.total_supply * 10} step={pub.total_supply * 0.05} value={exitFdv}
              onChange={e => setExitFdv(Number(e.target.value))}
              style={{ width: '100%' }} />
            <div className="mono" style={{ display: 'flex', justifyContent: 'space-between', fontSize: 9, color: 'var(--text-tertiary)', letterSpacing: '0.08em', marginTop: 4 }}>
              <span>{fmtUsd(pub.total_supply * 0.10)}</span>
              <span>par {fmtUsd(pub.total_supply)}</span>
              <span>{fmtUsd(pub.total_supply * 10)}</span>
            </div>
          </div>
        </div>

        <div style={{ background: 'var(--bg)', border: '1px solid var(--border)', padding: 20 }}>
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--text-tertiary)', marginBottom: 14 }}>
            PROJECTED RETURN
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            <Stat label="TOKENS" value={fmtTok(simTokens)} />
            <Stat label="EXIT PRICE / TOK" value={fmtUsd(simExitPrice)} />
            <Stat label="EXIT VALUE" value={fmtUsd(simExitValue)} highlight />
            <Stat label="MOIC" value={simMoic > 0 ? `${simMoic.toFixed(1)}×` : '—'} highlight />
          </div>
          <div className="mono" style={{ fontSize: 9, color: 'var(--text-tertiary)', letterSpacing: '0.06em', marginTop: 14, lineHeight: 1.5 }}>
            BASED ON {fmtTok(pub.total_supply)} FIXED SUPPLY · TIER PRICE ${tier ? Number(tier.price_usd).toFixed(2) : '—'} · YOUR % OF SUPPLY {fmtPct(simTokens / pub.total_supply)}
          </div>
        </div>
      </div>

      {/* Vesting chart — only if user has a position */}
      {(myAllocs.length > 0 || myAdvisor) && (
        <VestingChart
          tiers={tiers}
          allocations={myAllocs}
          advisorGrant={myAdvisor}
        />
      )}

      {/* ── The founder-dilution rationale, after the live view ── */}
      {view === 'dilution' && (
        <React.Fragment>
          <div style={{ borderTop: '1px solid var(--border)', margin: '40px 0 24px' }} />
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--text-tertiary)', marginBottom: 18 }}>
            THE STRUCTURE · HOW FOUNDERS DILUTE &amp; WHAT IT IS WORTH
          </div>
          <ClarityBand />
          <CanonicalModel />
        </React.Fragment>
      )}
    </div>
  );
}

function Stat({ label, value, highlight }) {
  return (
    <div>
      <div className="mono" style={{ fontSize: 9, letterSpacing: '0.12em', color: 'var(--text-tertiary)', marginBottom: 4 }}>{label}</div>
      <div style={{ fontSize: highlight ? 22 : 17, fontWeight: 500, letterSpacing: '-0.02em', color: highlight ? 'var(--gold-dark)' : 'var(--text-primary)' }}>{value}</div>
    </div>
  );
}

// Tiny SVG area chart of total unlocked tokens across the caller's
// positions, month by month for 48 months. Per-position colours layered.
function VestingChart({ tiers, allocations, advisorGrant }) {
  const months = 48;
  const tierById = Object.fromEntries((tiers || []).map(t => [t.id, t]));
  const positions = [];
  for (const a of (allocations || [])) {
    const t = tierById[a.tier_id];
    positions.push({
      label: t ? t.name : 'Allocation',
      tokens: Number(a.tokens) || 0,
      cliff: a.cliff_months ?? (t?.cliff_months || 0),
      linear: a.linear_months ?? (t?.linear_months || 0),
    });
  }
  if (advisorGrant) {
    positions.push({
      label: 'Advisor',
      tokens: Number(advisorGrant.tokens) || 0,
      cliff: advisorGrant.cliff_months || 0,
      linear: advisorGrant.linear_months || 0,
    });
  }

  const total = positions.reduce((s, p) => s + p.tokens, 0);
  if (total === 0) return null;

  // Per-month total unlocked across all positions.
  const series = [];
  for (let m = 0; m <= months; m++) {
    let u = 0;
    for (const p of positions) u += unlockedAt(p.tokens, p.cliff, p.linear, m);
    series.push(u);
  }

  const W = 760, H = 200, pad = 24;
  const xFor = m => pad + (m / months) * (W - pad * 2);
  const yFor = v => H - pad - (v / total) * (H - pad * 2);

  const pts = series.map((v, m) => `${xFor(m).toFixed(1)},${yFor(v).toFixed(1)}`);
  const area = `${pad},${H - pad} ${pts.join(' ')} ${W - pad},${H - pad}`;

  return (
    <div style={{ marginTop: 8, background: 'var(--bg)', border: '1px solid var(--border)', padding: 16 }}>
      <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', color: 'var(--text-tertiary)', marginBottom: 10 }}>
        YOUR VESTING · 48-MONTH UNLOCK
      </div>
      <svg width="100%" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ display: 'block' }}>
        {/* Gridlines at 25/50/75% */}
        {[0.25, 0.5, 0.75].map(f => (
          <line key={f} x1={pad} x2={W - pad} y1={yFor(total * f)} y2={yFor(total * f)} stroke="var(--border)" strokeDasharray="2 4" />
        ))}
        <polygon points={area} fill="rgba(212,175,55,0.12)" />
        <polyline points={pts.join(' ')} fill="none" stroke="#D4AF37" strokeWidth="1.5" />
        {/* Month labels at 0/12/24/36/48 */}
        {[0, 12, 24, 36, 48].map(m => (
          <g key={m}>
            <line x1={xFor(m)} x2={xFor(m)} y1={H - pad} y2={H - pad + 4} stroke="var(--text-tertiary)" />
            <text x={xFor(m)} y={H - pad + 14} fontSize="9" fill="var(--text-tertiary)" fontFamily="var(--font-mono)" textAnchor="middle">m{m}</text>
          </g>
        ))}
        {/* Y axis labels */}
        <text x={pad} y={yFor(total) - 4} fontSize="9" fill="var(--text-tertiary)" fontFamily="var(--font-mono)">{fmtTok(total)}</text>
        <text x={pad} y={H - pad - 4} fontSize="9" fill="var(--text-tertiary)" fontFamily="var(--font-mono)">0</text>
      </svg>
      <div className="mono" style={{ fontSize: 9, color: 'var(--text-tertiary)', letterSpacing: '0.06em', marginTop: 8 }}>
        TOTAL: {fmtTok(total)} TOK · POSITIONS: {positions.map(p => `${p.label} ${fmtTok(p.tokens)}`).join(' · ')}
      </div>
    </div>
  );
}

Object.assign(window, { CaptableBody });
