// reports.jsx — Report Generation
// ─────────────────────────────────────────────────────────────────
// Composable report builder: templates, blocks, scheduling, exports.
//
// Three views:
//   01 LIBRARY     — saved reports + scheduled runs
//   02 BUILDER     — drag-pick blocks, configure params, live preview
//   03 SCHEDULES   — recurring deliveries (email + drop)
//
// Block types (data-driven; pull from existing globals):
//   • SUMMARY      — KPI strip (configurable cells)
//   • EARNINGS     — period earnings by source / territory / type
//   • TOP-WORKS    — top N works by revenue / streams
//   • STATEMENT    — line-level statement extract
//   • REGISTRATIONS— CWR/CAF acks status
//   • ANOMALIES    — issues + leak signals
//   • ROYALTY-LIFT — optimize engine projected lift (requires royalty-optimize)
//   • PATTERNS     — pattern recognition top hits
//   • SUB-PUB      — sub-publisher performance summary
//   • TEXT         — markdown / commentary block
//   • PARAGRAPH    — auto-summary (uses claude.complete if available)
//
// Each report =
//   { id, name, blocks: [{ kind, params }], schedule, recipients, format }
//
// EXPORT:
//   window.ReportEngine.run(report)            — render report data
//   window.ScreenReports                       — full screen
// ─────────────────────────────────────────────────────────────────
(function () {
  if (typeof window === 'undefined' || !window.React) return;
  const _S = React.useState, _M = React.useMemo, _E = React.useEffect, _R = React.useRef;

  // ─── helpers ────────────────────────────────────────────────────
  const fmtUsd = (n) => {
    if (n == null || isNaN(n)) return '—';
    const a = Math.abs(n);
    if (a >= 1e6) return '$' + (n/1e6).toFixed(2) + 'M';
    if (a >= 1e3) return '$' + (n/1e3).toFixed(1) + 'k';
    return '$' + Math.round(n).toLocaleString();
  };
  const fmtNum = (n) => {
    if (n == null) return '—';
    const a = Math.abs(n);
    if (a >= 1e9) return (n/1e9).toFixed(1) + 'B';
    if (a >= 1e6) return (n/1e6).toFixed(1) + 'M';
    if (a >= 1e3) return (n/1e3).toFixed(1) + 'k';
    return Math.round(n).toLocaleString();
  };
  const fmtPct = (n, sign) => (sign && n > 0 ? '+' : '') + (n*100).toFixed(1) + '%';

  function Mono({ children, upper, size, color, style, ...rest }) {
    return <span className={'ff-mono' + (upper?' upper':'')} style={{ fontSize: size||11, color: color||'var(--ink)', letterSpacing: upper?'.08em':0, ...style }} {...rest}>{children}</span>;
  }

  function uid(p) {
    return (p || 'r') + '-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
  }

  function sumBy(list, fn) {
    return (list || []).reduce((s, x) => s + (fn(x) || 0), 0);
  }

  // ─── BLOCK CATALOG ─────────────────────────────────────────────
  const BLOCKS = {
    summary: {
      label: 'Summary KPIs',
      icon: '⊞',
      desc: '4-cell metric strip — total earnings, active deals, top territory, anomalies',
      params: { period: 'last_quarter', cells: 4 },
      run: (params) => {
        const stmts = (window.__STMT_INDEX && window.__STMT_INDEX.statements) || [];
        const ags = window.AGREEMENTS || [];
        const recs = window.RECORDINGS || [];
        const works = window.WORKS || [];
        const total = sumBy(stmts, s => s.grossUsd || s.gross || 0);
        const active = ags.filter(a => a.status === 'active' || a.status === 'live').length;
        // Top territory by source country count
        const terrSums = {};
        stmts.forEach(s => {
          const t = s.territory || s.sourceCountry || 'Unknown';
          terrSums[t] = (terrSums[t] || 0) + (s.grossUsd || s.gross || 0);
        });
        const topTerr = Object.entries(terrSums).sort((a, b) => b[1] - a[1])[0] || ['—', 0];
        return {
          cells: [
            { label: 'TOTAL EARNINGS', value: fmtUsd(total), sub: stmts.length + ' statements' },
            { label: 'ACTIVE DEALS',   value: fmtNum(active), sub: 'of ' + ags.length + ' total' },
            { label: 'WORKS · RECS',   value: fmtNum(works.length) + ' · ' + fmtNum(recs.length), sub: 'catalog' },
            { label: 'TOP TERRITORY',  value: topTerr[0],     sub: fmtUsd(topTerr[1]) },
          ],
        };
      },
    },
    earnings: {
      label: 'Earnings Breakdown',
      icon: '$',
      desc: 'Earnings by source / territory / type — table + sparkbars',
      params: { dim: 'source', period: 'last_quarter', limit: 10 },
      run: (params) => {
        const stmts = (window.__STMT_INDEX && window.__STMT_INDEX.statements) || [];
        const dim = params.dim || 'source';
        const buckets = {};
        stmts.forEach(s => {
          const k = dim === 'source' ? (s.sourceName || s.sourceId || 'Unknown')
                  : dim === 'territory' ? (s.territory || s.sourceCountry || 'Unknown')
                  : (s.kind || s.type || s.sourceKind || 'Unknown');
          buckets[k] = (buckets[k] || 0) + (s.grossUsd || s.gross || 0);
        });
        const rows = Object.entries(buckets)
          .map(([k, v]) => ({ key: k, value: v }))
          .sort((a, b) => b.value - a.value)
          .slice(0, params.limit || 10);
        const total = rows.reduce((s, r) => s + r.value, 0);
        return { rows, total, dim };
      },
    },
    topworks: {
      label: 'Top Works',
      icon: '★',
      desc: 'Top N works by revenue with ISWC + writer info',
      params: { limit: 10, by: 'revenue' },
      run: (params) => {
        const works = window.WORKS || [];
        const stmts = (window.__STMT_INDEX && window.__STMT_INDEX.statements) || [];
        // Map work id → revenue from statements (heuristic: match by title)
        const revByWork = {};
        stmts.forEach(s => {
          (s.lines || s.items || []).forEach(l => {
            const t = l.title || l.workTitle;
            if (t) revByWork[t] = (revByWork[t] || 0) + (l.amountUsd || l.amount || l.gross || 0);
          });
        });
        const rows = works.map(w => ({
          ...w, revenue: revByWork[w.title] || (w.revenue || 0)
        }))
        .sort((a, b) => (b.revenue || 0) - (a.revenue || 0))
        .slice(0, params.limit || 10);
        return { rows };
      },
    },
    statement: {
      label: 'Statement Extract',
      icon: '⌗',
      desc: 'Line-level extract from a single statement run',
      params: { statementId: null, limit: 25 },
      run: (params) => {
        const stmts = (window.__STMT_INDEX && window.__STMT_INDEX.statements) || [];
        const stmt = params.statementId ? stmts.find(s => s.id === params.statementId) : stmts[0];
        if (!stmt) return { rows: [], statement: null };
        const lines = stmt.lines || stmt.items || [];
        return { rows: lines.slice(0, params.limit || 25), statement: stmt };
      },
    },
    registrations: {
      label: 'Registrations Status',
      icon: '⬢',
      desc: 'CWR/CAF acks — accepted / rejected / pending',
      params: {},
      run: () => {
        const cwr = (window.CWR_TX && window.CWR_TX) || (window.CWR_TXS) || [];
        const counts = { accepted: 0, rejected: 0, pending: 0 };
        cwr.forEach(t => {
          const s = (t.ackStatus || t.status || '').toLowerCase();
          if (s.includes('accept')) counts.accepted++;
          else if (s.includes('reject') || s.includes('error')) counts.rejected++;
          else counts.pending++;
        });
        return { counts, total: cwr.length || 0 };
      },
    },
    anomalies: {
      label: 'Anomalies & Leaks',
      icon: '⚠',
      desc: 'Active issues from issues view + leak signals',
      params: { limit: 10 },
      run: (params) => {
        const issues = (window.ISSUES_INDEX || window.ISSUES || []).slice(0, params.limit || 10);
        let leakCount = 0;
        if (window.LeakEngine && window.LeakEngine.scan) {
          try { leakCount = (window.LeakEngine.scan().leaks || []).length; } catch (e) {}
        }
        return { issues, leakCount };
      },
    },
    royaltyLift: {
      label: 'Royalty Optimization',
      icon: '↗',
      desc: 'Projected lift from royalty-optimize engine',
      params: { limit: 6 },
      run: (params) => {
        if (window.RoyaltyOptimize && window.RoyaltyOptimize.scan) {
          try {
            const r = window.RoyaltyOptimize.scan();
            return { strategies: (r.strategies || []).slice(0, params.limit || 6), total: r.totalLiftUsd || 0 };
          } catch (e) {}
        }
        return { strategies: [], total: 0 };
      },
    },
    patterns: {
      label: 'Pattern Recognition',
      icon: '◫',
      desc: 'Top patterns from pattern engine, by severity',
      params: { limit: 8, severity: 'all' },
      run: (params) => {
        if (window.PatternEngine && window.PatternEngine.scan) {
          try {
            const r = window.PatternEngine.scan();
            let p = r.patterns || [];
            if (params.severity !== 'all') p = p.filter(x => x.severity === params.severity);
            return { patterns: p.slice(0, params.limit || 8) };
          } catch (e) {}
        }
        return { patterns: [] };
      },
    },
    subpub: {
      label: 'Sub-Pub Performance',
      icon: '◐',
      desc: 'Sub-publisher deal status, share collected, recoupment',
      params: { limit: 8 },
      run: (params) => {
        const deals = (window.SP_DEALS || []).slice(0, params.limit || 8);
        return { deals };
      },
    },
    text: {
      label: 'Commentary / Notes',
      icon: '¶',
      desc: 'Free-text markdown block',
      params: { content: 'Write your commentary here…' },
      run: (params) => ({ content: params.content || '' }),
    },
    paragraph: {
      label: 'Auto-Summary (LLM)',
      icon: '∿',
      desc: 'Claude-generated summary of report data so far',
      params: { prompt: 'Summarize the key takeaways from this report.' },
      run: async (params, context) => {
        if (!window.claude || !window.claude.complete) return { summary: '(LLM not available — set up Claude integration)', loading: false };
        try {
          const ctx = JSON.stringify(context || {}).slice(0, 2000);
          const text = await window.claude.complete(`${params.prompt}\n\nContext:\n${ctx}`);
          return { summary: text };
        } catch (e) { return { summary: '(error: ' + e.message + ')' }; }
      },
    },
  };

  // ─── REPORT TEMPLATES ──────────────────────────────────────────
  const TEMPLATES = [
    {
      id: 'tpl-board',
      name: 'Board Quarterly',
      desc: '4-page exec board report: KPIs, earnings, anomalies, optimization',
      blocks: [
        { kind: 'summary',     params: { period: 'last_quarter' } },
        { kind: 'earnings',    params: { dim: 'source', limit: 10 } },
        { kind: 'topworks',    params: { limit: 10 } },
        { kind: 'patterns',    params: { limit: 6, severity: 'all' } },
        { kind: 'royaltyLift', params: { limit: 5 } },
        { kind: 'paragraph',   params: { prompt: 'Write a 1-paragraph executive summary suitable for a board, in plain prose, no bullets.' } },
      ],
      schedule: 'Quarterly (last day of quarter)',
    },
    {
      id: 'tpl-writer',
      name: 'Writer Statement',
      desc: 'Per-writer statement: earnings breakdown + commentary',
      blocks: [
        { kind: 'summary',  params: {} },
        { kind: 'earnings', params: { dim: 'territory', limit: 12 } },
        { kind: 'topworks', params: { limit: 15 } },
        { kind: 'text',     params: { content: 'Royalty period commentary follows…' } },
      ],
      schedule: 'Quarterly (with statement run)',
    },
    {
      id: 'tpl-audit',
      name: 'Audit Pack',
      desc: 'Compliance + audit-prep: registrations, statement extracts, anomalies',
      blocks: [
        { kind: 'summary',       params: {} },
        { kind: 'registrations', params: {} },
        { kind: 'anomalies',     params: { limit: 20 } },
        { kind: 'statement',     params: { limit: 50 } },
      ],
      schedule: 'On-demand',
    },
    {
      id: 'tpl-subpub',
      name: 'Sub-Pub Monthly',
      desc: 'Sub-publisher activity report — per-deal performance',
      blocks: [
        { kind: 'subpub',   params: { limit: 12 } },
        { kind: 'earnings', params: { dim: 'territory', limit: 10 } },
        { kind: 'patterns', params: { limit: 4, severity: 'alert' } },
      ],
      schedule: 'Monthly (1st)',
    },
    {
      id: 'tpl-anomaly',
      name: 'Anomaly Watchlist',
      desc: 'Daily ops digest: pattern hits, leaks, issues',
      blocks: [
        { kind: 'patterns',  params: { limit: 12, severity: 'all' } },
        { kind: 'anomalies', params: { limit: 15 } },
        { kind: 'paragraph', params: { prompt: 'List the top 3 things ops should do today, in priority order.' } },
      ],
      schedule: 'Daily (8am)',
    },
  ];

  // ─── SAVED REPORTS (mock library) ──────────────────────────────
  const SAVED = [
    { id: 'rep-001', name: 'Q3 2025 Board Report', tplId: 'tpl-board',  blocks: TEMPLATES[0].blocks, lastRun: '2025-10-01', recipients: 'board@pluralis.com', schedule: 'Quarterly', status: 'sent', size: '4 pages' },
    { id: 'rep-002', name: 'October Sub-Pub Pack', tplId: 'tpl-subpub', blocks: TEMPLATES[3].blocks, lastRun: '2025-10-01', recipients: 'subpubs@pluralis.com', schedule: 'Monthly', status: 'sent', size: '6 pages' },
    { id: 'rep-003', name: 'Daily Anomaly Watch', tplId: 'tpl-anomaly', blocks: TEMPLATES[4].blocks, lastRun: '2025-11-04', recipients: 'ops@pluralis.com', schedule: 'Daily', status: 'sent', size: '2 pages' },
    { id: 'rep-004', name: 'Helado Negro · Writer Q3', tplId: 'tpl-writer', blocks: TEMPLATES[1].blocks, lastRun: '2025-10-15', recipients: 'rcl@helado.fm', schedule: 'Quarterly', status: 'draft', size: '3 pages' },
    { id: 'rep-005', name: 'Sept Audit Pack — A24 Sync', tplId: 'tpl-audit', blocks: TEMPLATES[2].blocks, lastRun: '2025-09-22', recipients: 'audit@pluralis.com', schedule: 'On-demand', status: 'sent', size: '12 pages' },
    { id: 'rep-006', name: 'Floating Points Quarterly', tplId: 'tpl-writer', blocks: TEMPLATES[1].blocks, lastRun: '2025-10-15', recipients: 'mgmt@floatingpoints.co', schedule: 'Quarterly', status: 'queued', size: '3 pages' },
  ];

  // ─── RUN A REPORT ──────────────────────────────────────────────
  async function run(report) {
    const out = [];
    let ctx = {};
    for (const b of (report.blocks || [])) {
      const def = BLOCKS[b.kind];
      if (!def) { out.push({ kind: b.kind, error: 'Unknown block type' }); continue; }
      try {
        const result = await Promise.resolve(def.run(b.params || {}, ctx));
        ctx[b.kind] = result;
        out.push({ kind: b.kind, label: def.label, params: b.params, data: result });
      } catch (e) {
        out.push({ kind: b.kind, error: e.message });
      }
    }
    return { report, blocks: out, generatedAt: new Date().toISOString() };
  }

  window.ReportEngine = { BLOCKS, TEMPLATES, SAVED, run };

  // ───────────────────────── BLOCK RENDERERS ─────────────────────
  function BlockSummary({ data }) {
    return (
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${data.cells.length}, 1fr)`, borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)' }}>
        {data.cells.map((c, i) => (
          <div key={i} style={{ padding: '16px 22px', borderRight: i < data.cells.length - 1 ? '1px solid var(--rule)' : 0 }}>
            <Mono upper size={9} color="var(--ink-3)">{c.label}</Mono>
            <div className="ff-display" style={{ fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 4 }}>{c.value}</div>
            <div style={{ fontSize: 10, color: 'var(--ink-2)', marginTop: 3 }}>{c.sub}</div>
          </div>
        ))}
      </div>
    );
  }

  function BlockEarnings({ data, params }) {
    const max = Math.max(...data.rows.map(r => r.value), 1);
    return (
      <div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 60px 1fr 80px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule)' }}>
          <Mono upper size={9} color="var(--ink-3)">{(params.dim || 'source').toUpperCase()}</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>SHARE</Mono>
          <span></span>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>AMOUNT</Mono>
        </div>
        {data.rows.map((r, i) => (
          <div key={i} style={{ display: 'grid', gridTemplateColumns: '1fr 60px 1fr 80px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center' }}>
            <span style={{ fontSize: 12 }}>{r.key}</span>
            <Mono size={11} style={{ textAlign: 'right' }}>{((r.value / data.total) * 100).toFixed(1)}%</Mono>
            <div style={{ height: 6, background: 'var(--bg-2)' }}>
              <div style={{ height: '100%', width: ((r.value / max) * 100) + '%', background: 'var(--ink)' }}/>
            </div>
            <Mono size={11} style={{ textAlign: 'right', fontWeight: 500 }}>{fmtUsd(r.value)}</Mono>
          </div>
        ))}
        <div style={{ display: 'flex', justifyContent: 'space-between', padding: '8px 0', marginTop: 4, borderTop: '1px solid var(--rule)' }}>
          <Mono upper size={10}>TOTAL</Mono>
          <Mono size={12} style={{ fontWeight: 600 }}>{fmtUsd(data.total)}</Mono>
        </div>
      </div>
    );
  }

  function BlockTopWorks({ data }) {
    return (
      <div>
        <div style={{ display: 'grid', gridTemplateColumns: '30px 1fr 1fr 100px 90px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule)' }}>
          <Mono upper size={9} color="var(--ink-3)">#</Mono>
          <Mono upper size={9} color="var(--ink-3)">WORK</Mono>
          <Mono upper size={9} color="var(--ink-3)">WRITERS</Mono>
          <Mono upper size={9} color="var(--ink-3)">ISWC</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>REV</Mono>
        </div>
        {data.rows.map((w, i) => (
          <div key={w.id || i} style={{ display: 'grid', gridTemplateColumns: '30px 1fr 1fr 100px 90px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <Mono size={11} color="var(--ink-3)">{(i+1).toString().padStart(2, '0')}</Mono>
            <span style={{ fontSize: 12, fontWeight: 500 }}>{w.title || '—'}</span>
            <span style={{ fontSize: 11, color: 'var(--ink-2)' }}>{(w.writers || []).map(x => x.name || x).join(', ').slice(0, 40) || '—'}</span>
            <Mono size={10} color="var(--ink-3)">{w.iswc || '—'}</Mono>
            <Mono size={11} style={{ textAlign: 'right', fontWeight: 500 }}>{fmtUsd(w.revenue)}</Mono>
          </div>
        ))}
        {data.rows.length === 0 && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11 }}>No works data.</div>}
      </div>
    );
  }

  function BlockStatement({ data }) {
    if (!data.statement) return <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11 }}>No statement found.</div>;
    return (
      <div>
        <div style={{ marginBottom: 10 }}>
          <Mono size={11}>Statement {data.statement.id} · {data.statement.sourceName || data.statement.sourceId} · {data.statement.period || '—'}</Mono>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 80px 80px 80px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule)' }}>
          <Mono upper size={9} color="var(--ink-3)">LINE</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>UNITS</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>RATE</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>AMOUNT</Mono>
        </div>
        {data.rows.slice(0, 25).map((l, i) => (
          <div key={i} style={{ display: 'grid', gridTemplateColumns: '1fr 80px 80px 80px', gap: 10, padding: '5px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <span style={{ fontSize: 11 }}>{l.title || l.workTitle || l.recordingTitle || '(unmatched)'}</span>
            <Mono size={10} style={{ textAlign: 'right' }} color="var(--ink-2)">{fmtNum(l.units || l.qty)}</Mono>
            <Mono size={10} style={{ textAlign: 'right' }} color="var(--ink-2)">{l.rate ? '$' + l.rate.toFixed(4) : '—'}</Mono>
            <Mono size={11} style={{ textAlign: 'right', fontWeight: 500 }}>{fmtUsd(l.amountUsd || l.amount || l.gross)}</Mono>
          </div>
        ))}
      </div>
    );
  }

  function BlockRegistrations({ data }) {
    const { counts, total } = data;
    const entries = [
      { k: 'accepted', l: 'Accepted', c: '#0a8754', v: counts.accepted },
      { k: 'rejected', l: 'Rejected', c: '#a32a18', v: counts.rejected },
      { k: 'pending',  l: 'Pending',  c: '#d4881f', v: counts.pending },
    ];
    return (
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
        {entries.map(e => (
          <div key={e.k} style={{ padding: '14px 18px', border: '1px solid var(--rule)', borderLeft: '3px solid ' + e.c }}>
            <Mono upper size={9} color="var(--ink-3)">{e.l.toUpperCase()}</Mono>
            <div className="ff-display" style={{ fontSize: 22, fontWeight: 600, marginTop: 4, color: e.c }}>{e.v}</div>
            <div style={{ fontSize: 10, color: 'var(--ink-2)', marginTop: 3 }}>{total > 0 ? ((e.v / total) * 100).toFixed(1) + '% of ' + total : '—'}</div>
          </div>
        ))}
      </div>
    );
  }

  function BlockAnomalies({ data }) {
    return (
      <div>
        <div style={{ marginBottom: 8 }}>
          <Mono size={11} color="var(--ink-2)">{data.issues.length} active issue{data.issues.length === 1 ? '' : 's'} · {data.leakCount} leak signal{data.leakCount === 1 ? '' : 's'}</Mono>
        </div>
        {data.issues.map((i, idx) => (
          <div key={idx} style={{ display: 'grid', gridTemplateColumns: '24px 1fr 60px 100px', gap: 10, padding: '7px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <Mono size={10} color="var(--ink-3)">{(idx+1).toString().padStart(2, '0')}</Mono>
            <span style={{ fontSize: 11 }}>{i.title || i.label || i.kind || '—'}</span>
            <Mono size={9} upper color={i.severity === 'high' ? '#a32a18' : i.severity === 'med' ? '#d4881f' : 'var(--ink-3)'}>{(i.severity || 'low').toUpperCase()}</Mono>
            <Mono size={10} color="var(--ink-3)">{i.entity || i.party || '—'}</Mono>
          </div>
        ))}
        {data.issues.length === 0 && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11, fontStyle: 'italic' }}>No active issues.</div>}
      </div>
    );
  }

  function BlockRoyaltyLift({ data }) {
    return (
      <div>
        <div style={{ padding: '12px 14px', background: 'var(--bg-2)', marginBottom: 10 }}>
          <Mono upper size={9} color="var(--ink-3)">PROJECTED ANNUAL LIFT</Mono>
          <div className="ff-display" style={{ fontSize: 24, fontWeight: 600, color: '#0a8754' }}>{fmtUsd(data.total)}</div>
        </div>
        {data.strategies.map((s, i) => (
          <div key={i} style={{ display: 'grid', gridTemplateColumns: '30px 1fr 80px 70px 70px', gap: 10, padding: '7px 0', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center' }}>
            <Mono size={10} color="var(--ink-3)">{(i+1).toString().padStart(2,'0')}</Mono>
            <span style={{ fontSize: 11, fontWeight: 500 }}>{s.name || s.title || '—'}</span>
            <Mono size={11} style={{ textAlign: 'right', color: '#0a8754', fontWeight: 500 }}>{fmtUsd(s.liftUsd || s.lift || 0)}</Mono>
            <Mono size={9} upper color="var(--ink-3)" style={{ textAlign: 'right' }}>{(s.effort || 'med').toUpperCase()}</Mono>
            <Mono size={9} upper color="var(--ink-3)" style={{ textAlign: 'right' }}>{Math.round((s.confidence || 0.7) * 100)}%</Mono>
          </div>
        ))}
        {data.strategies.length === 0 && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11, fontStyle: 'italic' }}>Royalty optimization engine not loaded.</div>}
      </div>
    );
  }

  function BlockPatterns({ data }) {
    const sevColor = (s) => s === 'critical' ? '#a32a18' : s === 'alert' ? '#d4881f' : s === 'watch' ? '#0a8754' : '#7a8590';
    return (
      <div>
        {data.patterns.map((p, i) => (
          <div key={i} style={{
            display: 'grid', gridTemplateColumns: '70px 1fr 60px 50px',
            gap: 10, padding: '8px 12px', borderLeft: '3px solid ' + sevColor(p.severity),
            background: 'var(--paper)', border: '1px solid var(--rule)', borderLeftWidth: 3, marginBottom: 4,
          }}>
            <Mono size={9} upper color="#fff" style={{ background: sevColor(p.severity), padding: '2px 5px', textAlign: 'center', alignSelf: 'flex-start' }}>{p.severity.toUpperCase()}</Mono>
            <div>
              <div style={{ fontSize: 12, fontWeight: 500 }}>{p.title}</div>
              <div style={{ fontSize: 10, color: 'var(--ink-2)', marginTop: 2 }}>{p.kicker}</div>
            </div>
            <Mono size={10} upper color="var(--ink-3)">{p.lens}</Mono>
            <Mono size={10} color="var(--ink-3)" style={{ textAlign: 'right' }}>{Math.round(p.conf * 100)}%</Mono>
          </div>
        ))}
        {data.patterns.length === 0 && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11, fontStyle: 'italic' }}>No patterns detected.</div>}
      </div>
    );
  }

  function BlockSubPub({ data }) {
    return (
      <div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 80px 80px 80px 60px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule)' }}>
          <Mono upper size={9} color="var(--ink-3)">DEAL · TERR.</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>FEE</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>COLLECTED</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>RECOUPED</Mono>
          <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>STATUS</Mono>
        </div>
        {data.deals.map((d, i) => (
          <div key={d.id || i} style={{ display: 'grid', gridTemplateColumns: '1fr 80px 80px 80px 60px', gap: 10, padding: '6px 0', borderBottom: '1px solid var(--rule-soft)' }}>
            <span style={{ fontSize: 11 }}>{d.partner || d.subpub || '—'} · {d.territory || '—'}</span>
            <Mono size={10} style={{ textAlign: 'right' }}>{(d.feePct != null ? d.feePct + '%' : '—')}</Mono>
            <Mono size={10} style={{ textAlign: 'right' }}>{fmtUsd(d.collected || d.collectedUsd || 0)}</Mono>
            <Mono size={10} style={{ textAlign: 'right' }}>{fmtUsd(d.recouped || 0)}</Mono>
            <Mono size={9} upper color="var(--ink-3)" style={{ textAlign: 'right' }}>{(d.status || 'live').toUpperCase()}</Mono>
          </div>
        ))}
        {data.deals.length === 0 && <div style={{ padding: 16, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11, fontStyle: 'italic' }}>No sub-pub deals.</div>}
      </div>
    );
  }

  function BlockText({ data }) {
    return (
      <div style={{ padding: '14px 18px', background: 'var(--bg-2)', fontSize: 13, lineHeight: 1.7, whiteSpace: 'pre-wrap' }}>
        {data.content || <span style={{ color: 'var(--ink-3)', fontStyle: 'italic' }}>(empty commentary)</span>}
      </div>
    );
  }

  function BlockParagraph({ data }) {
    return (
      <div style={{ padding: '14px 18px', borderLeft: '3px solid #1a4ed8', background: '#1a4ed808', fontSize: 12.5, lineHeight: 1.7 }}>
        <Mono upper size={9} color="#1a4ed8" style={{ display: 'block', marginBottom: 6 }}>AUTO-SUMMARY</Mono>
        {data.summary || <span style={{ color: 'var(--ink-3)' }}>generating…</span>}
      </div>
    );
  }

  const RENDERERS = {
    summary: BlockSummary,
    earnings: BlockEarnings,
    topworks: BlockTopWorks,
    statement: BlockStatement,
    registrations: BlockRegistrations,
    anomalies: BlockAnomalies,
    royaltyLift: BlockRoyaltyLift,
    patterns: BlockPatterns,
    subpub: BlockSubPub,
    text: BlockText,
    paragraph: BlockParagraph,
  };

  // ─── PREVIEW PANE ──────────────────────────────────────────────
  function ReportPreview({ report }) {
    const [out, setOut] = _S(null);
    const [running, setRunning] = _S(false);

    _E(() => {
      let cancelled = false;
      setRunning(true);
      run(report).then(r => { if (!cancelled) { setOut(r); setRunning(false); } });
      return () => { cancelled = true; };
    }, [JSON.stringify(report.blocks)]);

    if (!out) return <div style={{ padding: 40, textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>{running ? 'Rendering…' : 'No preview'}</div>;

    return (
      <div style={{ background: 'var(--paper)', padding: '38px 42px', border: '1px solid var(--rule)', maxWidth: 920, margin: '0 auto', boxShadow: '0 1px 3px rgba(0,0,0,0.04)' }}>
        {/* Header */}
        <div style={{ borderBottom: '2px solid var(--ink)', paddingBottom: 14, marginBottom: 22 }}>
          <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 4 }}>PLURALIS · ASTRO REPORT</Mono>
          <div className="ff-display" style={{ fontSize: 28, fontWeight: 600, letterSpacing: '-0.02em' }}>{report.name || '(untitled report)'}</div>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 8, fontSize: 11, color: 'var(--ink-2)' }}>
            <span>Generated {new Date(out.generatedAt).toLocaleString()}</span>
            <span>{out.blocks.length} block{out.blocks.length === 1 ? '' : 's'}</span>
          </div>
        </div>

        {out.blocks.map((b, i) => {
          const Renderer = RENDERERS[b.kind];
          return (
            <div key={i} style={{ marginBottom: 26 }}>
              <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 10, borderBottom: '1px solid var(--rule)', paddingBottom: 4 }}>
                <Mono upper size={10} color="var(--ink)">{(i+1).toString().padStart(2,'0')} · {(BLOCKS[b.kind] || {}).label || b.kind}</Mono>
              </div>
              {b.error
                ? <div style={{ padding: 12, color: '#a32a18', fontSize: 12 }}>Error: {b.error}</div>
                : Renderer ? <Renderer data={b.data} params={b.params}/> : <div style={{ fontSize: 12, color: 'var(--ink-3)' }}>(no renderer)</div>}
            </div>
          );
        })}

        <div style={{ borderTop: '1px solid var(--rule)', paddingTop: 12, marginTop: 30, display: 'flex', justifyContent: 'space-between', fontSize: 10, color: 'var(--ink-3)' }}>
          <span>Pluralis · ASTRO · Confidential</span>
          <span>Page {out.blocks.length} of {out.blocks.length}</span>
        </div>
      </div>
    );
  }

  // ─── BUILDER ───────────────────────────────────────────────────
  function ReportBuilder({ report, onChange, onSave, onCancel }) {
    const [name, setName] = _S(report.name || 'Untitled Report');
    const [blocks, setBlocks] = _S(report.blocks || []);
    const [recipients, setRecipients] = _S(report.recipients || '');
    const [schedule, setSchedule] = _S(report.schedule || 'On-demand');
    const [showAddPanel, setShowAddPanel] = _S(false);
    const [activeBlock, setActiveBlock] = _S(null);

    const current = { ...report, name, blocks, recipients, schedule };

    const addBlock = (kind) => {
      const def = BLOCKS[kind];
      setBlocks([...blocks, { kind, params: { ...(def.params || {}) } }]);
      setShowAddPanel(false);
      setActiveBlock(blocks.length);
    };
    const removeBlock = (i) => setBlocks(blocks.filter((_, idx) => idx !== i));
    const moveBlock = (i, dir) => {
      const j = i + dir;
      if (j < 0 || j >= blocks.length) return;
      const copy = [...blocks];
      [copy[i], copy[j]] = [copy[j], copy[i]];
      setBlocks(copy);
    };
    const updateParam = (i, key, val) => {
      const copy = [...blocks];
      copy[i] = { ...copy[i], params: { ...(copy[i].params || {}), [key]: val } };
      setBlocks(copy);
    };

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '380px 1fr', gap: 22, alignItems: 'flex-start' }}>
        {/* Left — config panel */}
        <div style={{ position: 'sticky', top: 20 }}>
          <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>NAME</Mono>
          <input value={name} onChange={(e) => setName(e.target.value)} style={{
            width: '100%', padding: '10px 14px', fontSize: 13,
            border: '1px solid var(--rule)', background: 'var(--paper)', color: 'var(--ink)',
            outline: 'none', fontFamily: 'inherit', marginBottom: 14,
          }}/>

          <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>BLOCKS · {blocks.length}</Mono>
          <div style={{ border: '1px solid var(--rule)', marginBottom: 10 }}>
            {blocks.map((b, i) => {
              const def = BLOCKS[b.kind] || {};
              return (
                <div key={i} style={{ borderBottom: i < blocks.length - 1 ? '1px solid var(--rule-soft)' : 0 }}>
                  <div onClick={() => setActiveBlock(activeBlock === i ? null : i)} style={{
                    display: 'grid', gridTemplateColumns: '24px 1fr auto', gap: 10, alignItems: 'center',
                    padding: '8px 12px', cursor: 'pointer',
                    background: activeBlock === i ? 'var(--bg-2)' : 'var(--paper)',
                  }}>
                    <Mono size={11} color="var(--ink-3)" style={{ textAlign: 'center' }}>{def.icon || '·'}</Mono>
                    <div>
                      <div style={{ fontSize: 12, fontWeight: 500 }}>{def.label}</div>
                      <Mono size={9} color="var(--ink-3)">block #{i+1}</Mono>
                    </div>
                    <div style={{ display: 'flex', gap: 2 }}>
                      <button onClick={(e) => { e.stopPropagation(); moveBlock(i, -1); }} disabled={i === 0} className="ff-mono" style={{ fontSize: 10, padding: '2px 6px', background: 'transparent', border: '1px solid var(--rule)', cursor: i===0?'not-allowed':'pointer', opacity: i===0?0.3:1 }}>↑</button>
                      <button onClick={(e) => { e.stopPropagation(); moveBlock(i, 1); }} disabled={i === blocks.length - 1} className="ff-mono" style={{ fontSize: 10, padding: '2px 6px', background: 'transparent', border: '1px solid var(--rule)', cursor: i===blocks.length-1?'not-allowed':'pointer', opacity: i===blocks.length-1?0.3:1 }}>↓</button>
                      <button onClick={(e) => { e.stopPropagation(); removeBlock(i); }} className="ff-mono" style={{ fontSize: 10, padding: '2px 6px', background: 'transparent', border: '1px solid var(--rule)', color: '#a32a18', cursor: 'pointer' }}>×</button>
                    </div>
                  </div>
                  {activeBlock === i && def.params && (
                    <div style={{ padding: '12px 14px', borderTop: '1px solid var(--rule-soft)', background: 'var(--bg-2)' }}>
                      <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 8 }}>PARAMETERS</Mono>
                      {Object.entries(def.params).map(([k, defaultVal]) => (
                        <div key={k} style={{ display: 'grid', gridTemplateColumns: '90px 1fr', gap: 8, marginBottom: 6, alignItems: 'center' }}>
                          <Mono size={10} color="var(--ink-2)">{k}</Mono>
                          {typeof defaultVal === 'number' ? (
                            <input type="number" value={(b.params && b.params[k]) ?? defaultVal} onChange={(e) => updateParam(i, k, parseInt(e.target.value) || 0)}
                              style={{ padding: '4px 8px', fontSize: 11, border: '1px solid var(--rule)', background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}/>
                          ) : (
                            <input value={(b.params && b.params[k]) ?? defaultVal} onChange={(e) => updateParam(i, k, e.target.value)}
                              style={{ padding: '4px 8px', fontSize: 11, border: '1px solid var(--rule)', background: 'var(--paper)', color: 'var(--ink)', fontFamily: 'inherit' }}/>
                          )}
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              );
            })}
            {blocks.length === 0 && (
              <div style={{ padding: 18, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11, fontStyle: 'italic' }}>No blocks. Add one below.</div>
            )}
          </div>

          {!showAddPanel && (
            <button onClick={() => setShowAddPanel(true)} className="ff-mono upper" style={{
              fontSize: 11, padding: '8px 14px', width: '100%',
              background: 'transparent', color: 'var(--ink)',
              border: '1px dashed var(--rule)', cursor: 'pointer', marginBottom: 14,
            }}>+ ADD BLOCK</button>
          )}
          {showAddPanel && (
            <div style={{ border: '1px solid var(--rule)', padding: 8, marginBottom: 14 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6, padding: '0 4px' }}>
                <Mono upper size={9} color="var(--ink-3)">PICK BLOCK TYPE</Mono>
                <button onClick={() => setShowAddPanel(false)} style={{ background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer', fontSize: 14 }}>×</button>
              </div>
              {Object.entries(BLOCKS).map(([k, def]) => (
                <button key={k} onClick={() => addBlock(k)} style={{
                  display: 'grid', gridTemplateColumns: '24px 1fr', gap: 10, alignItems: 'center',
                  padding: '8px 10px', width: '100%', textAlign: 'left',
                  background: 'transparent', border: 0, borderBottom: '1px solid var(--rule-soft)',
                  cursor: 'pointer', color: 'var(--ink)',
                }}>
                  <Mono size={11} color="var(--ink-3)" style={{ textAlign: 'center' }}>{def.icon}</Mono>
                  <div>
                    <div style={{ fontSize: 12, fontWeight: 500 }}>{def.label}</div>
                    <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>{def.desc}</div>
                  </div>
                </button>
              ))}
            </div>
          )}

          <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>RECIPIENTS</Mono>
          <input value={recipients} onChange={(e) => setRecipients(e.target.value)} placeholder="emails, comma-separated" style={{
            width: '100%', padding: '10px 14px', fontSize: 12,
            border: '1px solid var(--rule)', background: 'var(--paper)', color: 'var(--ink)',
            outline: 'none', fontFamily: 'inherit', marginBottom: 14,
          }}/>

          <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>SCHEDULE</Mono>
          <select value={schedule} onChange={(e) => setSchedule(e.target.value)} style={{
            width: '100%', padding: '10px 14px', fontSize: 12,
            border: '1px solid var(--rule)', background: 'var(--paper)', color: 'var(--ink)',
            fontFamily: 'inherit', marginBottom: 18,
          }}>
            {['On-demand', 'Daily', 'Weekly', 'Monthly', 'Quarterly', 'Annually'].map(s => <option key={s}>{s}</option>)}
          </select>

          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={() => onSave(current)} className="ff-mono upper" style={{
              fontSize: 11, padding: '10px 16px', flex: 1,
              background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer',
            }}>SAVE</button>
            <button onClick={onCancel} className="ff-mono upper" style={{
              fontSize: 11, padding: '10px 16px',
              background: 'transparent', color: 'var(--ink)',
              border: '1px solid var(--rule)', cursor: 'pointer',
            }}>CANCEL</button>
          </div>
        </div>

        {/* Right — live preview */}
        <div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
            <Mono upper size={9} color="var(--ink-3)">LIVE PREVIEW</Mono>
            <Mono size={10} color="var(--ink-3)">updates as you edit</Mono>
          </div>
          {window.ReportExportBar && (
            <div style={{ marginBottom: 12, padding: 10, border: '1px solid var(--rule)', background: 'var(--bg-2)' }}>
              {React.createElement(window.ReportExportBar, { report: current })}
            </div>
          )}
          <ReportPreview report={current}/>
        </div>
      </div>
    );
  }

  // ─── LIBRARY VIEW ──────────────────────────────────────────────
  function ReportsLibrary({ reports, onOpen, onNew, onFromTpl }) {
    const ExportBar = window.ReportExportBar;
    return (
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
          <Mono upper size={9} color="var(--ink-3)">YOUR REPORTS · {reports.length}</Mono>
          <button onClick={onNew} className="ff-mono upper" style={{
            fontSize: 11, padding: '8px 14px',
            background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer',
          }}>+ NEW REPORT</button>
        </div>

        <div style={{ border: '1px solid var(--rule)', marginBottom: 30 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 90px 100px 1fr 90px 60px 240px', gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--rule)', background: 'var(--bg-2)' }}>
            <Mono upper size={9} color="var(--ink-3)">NAME</Mono>
            <Mono upper size={9} color="var(--ink-3)">STATUS</Mono>
            <Mono upper size={9} color="var(--ink-3)">SCHEDULE</Mono>
            <Mono upper size={9} color="var(--ink-3)">RECIPIENTS</Mono>
            <Mono upper size={9} color="var(--ink-3)">LAST RUN</Mono>
            <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>SIZE</Mono>
            <Mono upper size={9} color="var(--ink-3)">EXPORT</Mono>
          </div>
          {reports.map(r => (
            <div key={r.id} style={{
              display: 'grid', gridTemplateColumns: '1fr 90px 100px 1fr 90px 60px 240px',
              gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--rule-soft)',
              alignItems: 'center',
            }}>
              <span onClick={() => onOpen(r)} style={{ fontSize: 13, fontWeight: 500, cursor: 'pointer' }}>{r.name}</span>
              <span className="ff-mono" style={{ fontSize: 9, padding: '2px 6px', letterSpacing: '0.08em', background: r.status === 'sent' ? '#0a875422' : r.status === 'queued' ? '#d4881f22' : 'var(--bg-2)', color: r.status === 'sent' ? '#0a8754' : r.status === 'queued' ? '#d4881f' : 'var(--ink-3)', display: 'inline-block', width: 'fit-content' }}>{(r.status || 'draft').toUpperCase()}</span>
              <Mono size={11} color="var(--ink-2)">{r.schedule}</Mono>
              <span style={{ fontSize: 11, color: 'var(--ink-2)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.recipients}</span>
              <Mono size={10} color="var(--ink-3)">{r.lastRun || '—'}</Mono>
              <Mono size={10} color="var(--ink-3)" style={{ textAlign: 'right' }}>{r.size || '—'}</Mono>
              <div onClick={(e) => e.stopPropagation()}>
                {ExportBar && <ExportBar report={r} compact />}
              </div>
            </div>
          ))}
        </div>

        <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 12 }}>
          START FROM TEMPLATE
        </Mono>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 10 }}>
          {TEMPLATES.map(t => (
            <button key={t.id} onClick={() => onFromTpl(t)} style={{
              padding: '14px 16px', textAlign: 'left',
              background: 'var(--paper)', border: '1px solid var(--rule)',
              cursor: 'pointer', fontFamily: 'inherit', color: 'var(--ink)',
            }}>
              <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>{t.schedule}</Mono>
              <div style={{ fontSize: 14, fontWeight: 500, marginBottom: 4 }}>{t.name}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-2)', lineHeight: 1.45, marginBottom: 8 }}>{t.desc}</div>
              <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                {t.blocks.map((b, i) => {
                  const def = BLOCKS[b.kind];
                  return def ? <Mono key={i} upper size={8} color="var(--ink-3)" style={{ background: 'var(--bg-2)', padding: '2px 5px' }}>{def.icon} {def.label.split(' ')[0]}</Mono> : null;
                })}
              </div>
            </button>
          ))}
        </div>
      </div>
    );
  }

  // ─── SCHEDULES VIEW ────────────────────────────────────────────
  function SchedulesView({ reports }) {
    // Build a 7-day timeline
    const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    const today = new Date();
    const upcoming = reports.filter(r => r.schedule !== 'On-demand');

    // Map each report → next-run estimate (mock)
    const queue = upcoming.map((r, i) => ({
      ...r,
      nextRun: r.schedule === 'Daily' ? '08:00 ' + days[(today.getDay() + 1) % 7]
             : r.schedule === 'Weekly' ? 'Mon 08:00'
             : r.schedule === 'Monthly' ? '1st of month'
             : r.schedule === 'Quarterly' ? 'Quarter end'
             : '—',
      slotDay: i % 7,
    }));

    return (
      <div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 6, marginBottom: 22 }}>
          {days.map((d, i) => (
            <div key={d} style={{ border: '1px solid var(--rule)', padding: '10px 12px', minHeight: 130, background: i === today.getDay() - 1 ? 'var(--bg-2)' : 'var(--paper)' }}>
              <Mono upper size={9} color={i === today.getDay() - 1 ? 'var(--ink)' : 'var(--ink-3)'} style={{ fontWeight: 500 }}>{d}</Mono>
              <div style={{ marginTop: 8 }}>
                {queue.filter(q => q.slotDay === i).map((q, j) => (
                  <div key={j} style={{ padding: '5px 7px', background: 'var(--bg)', border: '1px solid var(--rule-soft)', marginBottom: 4, fontSize: 10 }}>
                    <div style={{ fontWeight: 500, fontSize: 11 }}>{q.name}</div>
                    <Mono size={9} color="var(--ink-3)">{q.nextRun}</Mono>
                  </div>
                ))}
              </div>
            </div>
          ))}
        </div>

        <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 10 }}>SCHEDULED RUNS</Mono>
        <div style={{ border: '1px solid var(--rule)' }}>
          {upcoming.map(r => (
            <div key={r.id} style={{
              display: 'grid', gridTemplateColumns: '1fr 110px 1fr 140px 240px',
              gap: 10, padding: '10px 16px', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center',
            }}>
              <span style={{ fontSize: 13 }}>{r.name}</span>
              <Mono size={11} color="var(--ink-2)">{r.schedule}</Mono>
              <span style={{ fontSize: 11, color: 'var(--ink-2)' }}>{r.recipients}</span>
              <Mono size={10} color="var(--ink-3)">next: {r.schedule === 'Daily' ? 'tomorrow 08:00' : r.schedule}</Mono>
              <div>{window.ReportExportBar && React.createElement(window.ReportExportBar, { report: r, compact: true })}</div>
            </div>
          ))}
          {upcoming.length === 0 && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-3)', fontSize: 11 }}>No scheduled reports.</div>}
        </div>
      </div>
    );
  }

  // ─── MAIN SCREEN ───────────────────────────────────────────────
  function ScreenReports({ go, payload }) {
    const PageHeader = window.PageHeader;
    const [view, setView] = _S(payload?.tab || 'library');
    const [reports, setReports] = _S(SAVED.slice());
    const [editing, setEditing] = _S(null);

    const onOpen = (r) => { setEditing(r); setView('builder'); };
    const onNew = () => { setEditing({ id: uid(), name: 'Untitled Report', blocks: [], recipients: '', schedule: 'On-demand', status: 'draft' }); setView('builder'); };
    const onFromTpl = (t) => { setEditing({ id: uid(), name: t.name + ' · Copy', blocks: JSON.parse(JSON.stringify(t.blocks)), recipients: '', schedule: t.schedule, status: 'draft' }); setView('builder'); };
    const onSave = (r) => {
      const exists = reports.find(x => x.id === r.id);
      if (exists) setReports(reports.map(x => x.id === r.id ? { ...x, ...r } : x));
      else setReports([{ ...r, lastRun: '—', size: '— pages' }, ...reports]);
      setEditing(null);
      setView('library');
      if (window.toast) window.toast('Report saved', 'ok');
    };

    return (
      <div>
        {PageHeader && (
          <PageHeader
            eyebrow={['ANALYTICS', 'REPORTS', 'COMPOSER + DELIVERY']}
            title="reports."
            highlight="reports."
            sub="Composable reports across catalog, statements, registrations, and ML signals. Save, schedule, and deliver to stakeholders."
          />
        )}

        <div style={{ borderBottom: '1px solid var(--rule)', display: 'flex', gap: 0, marginBottom: 24 }}>
          {[
            { k: 'library',   l: 'Library' },
            { k: 'builder',   l: 'Builder' },
            { k: 'schedules', l: 'Schedules' },
          ].map(t => (
            <button key={t.k} onClick={() => setView(t.k)} style={{
              padding: '14px 22px', background: 'transparent', border: 0,
              borderBottom: '2px solid ' + (view === t.k ? 'var(--ink)' : 'transparent'),
              cursor: 'pointer', color: view === t.k ? 'var(--ink)' : 'var(--ink-3)',
              fontSize: 13, fontWeight: view === t.k ? 600 : 400,
            }}>{t.l}</button>
          ))}
        </div>

        {view === 'library'   && <ReportsLibrary reports={reports} onOpen={onOpen} onNew={onNew} onFromTpl={onFromTpl}/>}
        {view === 'builder'   && (
          editing
            ? <ReportBuilder report={editing} onSave={onSave} onCancel={() => { setEditing(null); setView('library'); }}/>
            : <div style={{ padding: 60, textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>
                <Mono size={11} color="var(--ink-3)">No report selected.</Mono>
                <div style={{ marginTop: 16 }}>
                  <button onClick={onNew} className="ff-mono upper" style={{ fontSize: 11, padding: '10px 20px', background: 'var(--ink)', color: 'var(--bg)', border: 0, cursor: 'pointer' }}>+ NEW REPORT</button>
                </div>
              </div>
        )}
        {view === 'schedules' && <SchedulesView reports={reports}/>}
      </div>
    );
  }

  window.ScreenReports = ScreenReports;

  console.log('[Reports] loaded · ' + Object.keys(BLOCKS).length + ' block types · ' + TEMPLATES.length + ' templates · ' + SAVED.length + ' saved');
})();
