const { useState, useEffect, useRef } = React;

const API = '';

function cx(...xs) { return xs.filter(Boolean).join(' '); }

function fmtMoney(n) {
  return (n ?? 0).toLocaleString('en-US', { style: 'currency', currency: 'USD' });
}

function fmtNum(n) {
  if (n === 0 || n === null || n === undefined) return '';
  return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function fmtMoneyParens(n) {
  if (n === null || n === undefined || n === 0) return '0.00';
  if (n < 0) return `(${Math.abs(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })})`;
  return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function fmtPct(p) {
  if (p === null || p === undefined) return '';
  const sign = p > 0 ? '+' : '';
  return sign + (p * 100).toFixed(1) + '%';
}

function fmtMonth(ym) {
  const [y, m] = ym.split('-');
  const d = new Date(Number(y), Number(m) - 1, 1);
  return d.toLocaleString('en-US', { month: 'short' }) + ' ' + y.slice(2);
}

function Stat({ label, value, valueClass, big }) {
  return (
    <div className="flex flex-col gap-0.5">
      <div className={cx('uppercase tracking-widest text-dim', big ? 'text-[11px]' : 'text-[10px]')}>{label}</div>
      <div className={cx(big ? 'text-xl' : 'text-base', 'num', valueClass)}>{value}</div>
    </div>
  );
}

function DropZone({ label, hint, onFile, compact }) {
  const ref = useRef(null);
  return (
    <div
      onDragOver={e => e.preventDefault()}
      onDrop={e => { e.preventDefault(); const f = e.dataTransfer.files?.[0]; if (f) onFile(f); }}
      onClick={() => ref.current?.click()}
      className={cx(
        'border-2 border-dashed border-dim hover:border-accent rounded-2xl cursor-pointer text-center transition-colors',
        compact ? 'p-3' : 'p-5',
      )}
    >
      <div className={cx('text-fg font-medium', compact ? 'text-xs' : 'text-sm')}>{label}</div>
      <div className={cx('text-dim mt-1', compact ? 'text-[10px]' : 'text-[11px]')}>{hint}</div>
      <input
        ref={ref}
        type="file"
        accept=".csv,text/csv"
        className="hidden"
        onChange={e => { const f = e.target.files?.[0]; if (f) onFile(f); e.target.value = ''; }}
      />
    </div>
  );
}

function ImportBanner({ kind, result }) {
  if (!result) return null;
  return (
    <div className="border border-dim/70 rounded-xl p-3 grid grid-cols-4 gap-4">
      <div className="col-span-4 text-[10px] uppercase tracking-widest text-dim">
        {kind} import
      </div>
      <Stat label="Rows seen" value={result.rows_seen} />
      <Stat label="Imported" value={result.imported} valueClass="text-pos" />
      <Stat label="Updated / dupes" value={result.duplicates_skipped} valueClass="text-accent2" />
      <Stat
        label="Errors"
        value={result.errors.length}
        valueClass={result.errors.length ? 'text-neg' : 'text-fg'}
      />
      {result.errors.length > 0 && (
        <ul className="col-span-4 mt-1 text-xs text-neg space-y-0.5 max-h-24 overflow-auto">
          {result.errors.slice(0, 10).map((e, i) => <li key={i}>{e}</li>)}
        </ul>
      )}
    </div>
  );
}

function Sparkline({ points, width = 720, height = 110 }) {
  if (!points || points.length === 0) {
    return <div className="text-dim text-xs italic px-2 py-4">No attendance data yet.</div>;
  }
  const totals = points.map(p => p.total);
  const rolling = points.map(p => p.rolling_4wk);
  const lo = Math.min(...totals, ...rolling, 0);
  const hi = Math.max(...totals, ...rolling, 1);
  const range = hi - lo || 1;
  const padL = 40, padR = 10, padT = 8, padB = 22;
  const W = width - padL - padR;
  const H = height - padT - padB;
  const n = points.length;
  const x = i => padL + (n === 1 ? W / 2 : (i / (n - 1)) * W);
  const y = v => padT + H - ((v - lo) / range) * H;
  const barW = Math.max(2, (W / Math.max(n, 1)) * 0.55);
  const linePath = 'M ' + rolling.map((v, i) => `${x(i).toFixed(2)} ${y(v).toFixed(2)}`).join(' L ');

  return (
    <svg width={width} height={height} className="block w-full">
      <line x1={padL} y1={padT + H} x2={padL + W} y2={padT + H} stroke="#4A3A6A" strokeWidth="0.5" />
      <line x1={padL} y1={padT} x2={padL} y2={padT + H} stroke="#4A3A6A" strokeWidth="0.5" />
      {points.map((p, i) => {
        const cx = x(i);
        const yTop = y(p.total);
        return <rect key={i} x={cx - barW / 2} y={yTop} width={barW} height={padT + H - yTop} fill="#9945FF" fillOpacity="0.35" />;
      })}
      <path d={linePath} stroke="#14F195" strokeWidth="1.75" fill="none" />
      <text x={padL - 5} y={padT + 8} fontSize="9" fill="#4A3A6A" textAnchor="end" className="num">{Math.round(hi)}</text>
      <text x={padL - 5} y={padT + H} fontSize="9" fill="#4A3A6A" textAnchor="end" className="num">{Math.round(lo)}</text>
      <text x={padL} y={height - 6} fontSize="9" fill="#4A3A6A">{points[0].date}</text>
      <text x={padL + W} y={height - 6} fontSize="9" fill="#4A3A6A" textAnchor="end">{points[n - 1].date}</text>
    </svg>
  );
}

function DonorSummaryView({ summary, onDownload, onDownloadXlsx }) {
  const empty = !summary || summary.rows.length === 0;
  return (
    <>
      <div className="flex items-center justify-between border-t border-dim/40 pt-4">
        <div className="flex items-baseline gap-8">
          <Stat label="Total contributions" value={summary ? fmtMoney(summary.grand_total) : '$0.00'} valueClass="text-pos" big />
          <Stat label="Unique donors" value={summary?.donor_count ?? 0} big />
        </div>
        <div className="flex gap-2">
          <button
            onClick={onDownload}
            disabled={empty}
            className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-pos/80 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]"
          >
            Download PDF
          </button>
          <button
            onClick={onDownloadXlsx}
            disabled={empty}
            className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.4)]"
          >
            Excel
          </button>
        </div>
      </div>

      <div className="border border-dim/70 rounded-xl overflow-hidden overflow-x-auto">
        <table className="w-full text-sm">
          <thead className="bg-accent/20 text-fg uppercase text-[10px] tracking-widest">
            <tr>
              <th className="text-left px-3 py-2 font-semibold">Donor</th>
              <th className="text-left px-3 py-2 font-semibold">Family</th>
              <th className="text-right px-3 py-2 font-semibold">Gifts</th>
              <th className="text-right px-3 py-2 font-semibold">Last gift</th>
              <th className="text-right px-3 py-2 font-semibold">Total</th>
            </tr>
          </thead>
          <tbody>
            {(summary?.rows ?? []).map((r, i) => (
              <tr key={r.donor} className={i % 2 === 0 ? 'bg-bg' : 'bg-accent/[0.06]'}>
                <td className="px-3 py-1.5">{r.donor}</td>
                <td className="px-3 py-1.5 text-fg/70">{r.family_unit || ''}</td>
                <td className="px-3 py-1.5 num">{r.count}</td>
                <td className="px-3 py-1.5 num">{r.last_gift_date || ''}</td>
                <td className="px-3 py-1.5 num text-pos font-semibold">{fmtMoney(r.total)}</td>
              </tr>
            ))}
            {(!summary || summary.rows.length === 0) && (
              <tr>
                <td colSpan="5" className="px-3 py-8 text-center italic" style={{ color: '#4A3A6A' }}>
                  No contributions yet. Drop a contributions CSV above.
                </td>
              </tr>
            )}
            {summary && summary.rows.length > 0 && (
              <tr className="bg-accent/10 border-t border-accent">
                <td className="px-3 py-2 font-semibold uppercase text-[11px] tracking-widest text-accent" colSpan="2">Total</td>
                <td className="px-3 py-2 num">{summary.rows.reduce((a, r) => a + r.count, 0)}</td>
                <td className="px-3 py-2"></td>
                <td className="px-3 py-2 num text-pos font-bold">{fmtMoney(summary.grand_total)}</td>
              </tr>
            )}
          </tbody>
        </table>
      </div>
    </>
  );
}

function PnlView({ report, onDownload, onDownloadXlsx }) {
  const empty = !report || (report.income_rows.length === 0 && report.expense_rows.length === 0);
  const months = report?.months ?? [];

  function cell(v) {
    if (v === 0 || v === null || v === undefined) return <span className="text-dim">·</span>;
    return v < 0
      ? <span className="text-neg">({fmtNum(Math.abs(v))})</span>
      : fmtNum(v);
  }

  return (
    <>
      <div className="flex items-center justify-between border-t border-dim/40 pt-4 gap-4 flex-wrap">
        <div className="flex items-baseline gap-8 flex-wrap">
          <Stat label="Income (12mo)" value={report ? fmtMoney(report.income_grand_total) : '$0.00'} valueClass="text-pos" big />
          <Stat label="Expenses (12mo)" value={report ? fmtMoney(report.expense_grand_total) : '$0.00'} valueClass="text-neg" big />
          <Stat
            label="Net income"
            value={report ? fmtMoney(report.net_grand_total) : '$0.00'}
            valueClass={report && report.net_grand_total < 0 ? 'text-neg' : 'text-pos'}
            big
          />
        </div>
        <div className="flex gap-2">
          <button
            onClick={onDownload}
            disabled={empty}
            className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-pos/80 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]"
          >
            Download PDF
          </button>
          <button
            onClick={onDownloadXlsx}
            disabled={empty}
            className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.4)]"
          >
            Excel
          </button>
        </div>
      </div>

      <div className="border border-dim/70 rounded-xl overflow-hidden overflow-x-auto">
        <table className="w-full text-xs">
          <thead className="bg-accent/20 text-fg uppercase text-[10px] tracking-widest sticky top-0">
            <tr>
              <th className="text-left px-2 py-2 font-semibold">Account</th>
              {months.map(m => (
                <th key={m} className="text-right px-2 py-2 font-semibold num text-[10px]">{fmtMonth(m)}</th>
              ))}
              <th className="text-right px-2 py-2 font-semibold">Total</th>
            </tr>
          </thead>
          <tbody>
            {empty && (
              <tr>
                <td colSpan={months.length + 2} className="px-3 py-8 text-center italic" style={{ color: '#4A3A6A' }}>
                  No income or expense data in the trailing 12 months. Drop CSVs above.
                </td>
              </tr>
            )}

            {!empty && (
              <>
                <tr className="bg-accent/10">
                  <td colSpan={months.length + 2} className="px-2 py-1.5 text-accent text-[10px] uppercase tracking-widest font-bold">Income</td>
                </tr>
                {report.income_rows.map(r => (
                  <tr key={'inc-' + r.label} className="hover:bg-accent/[0.06]">
                    <td className="px-2 py-1">{r.label}</td>
                    {r.cells.map((v, i) => <td key={i} className="px-2 py-1 num text-pos">{cell(v)}</td>)}
                    <td className="px-2 py-1 num text-pos font-semibold">{fmtNum(r.total)}</td>
                  </tr>
                ))}
                <tr className="border-t border-dim/60 bg-accent/[0.04]">
                  <td className="px-2 py-1.5 font-semibold uppercase text-[10px] tracking-widest">Total income</td>
                  {report.income_totals.map((v, i) => <td key={i} className="px-2 py-1.5 num text-pos font-bold">{fmtNum(v)}</td>)}
                  <td className="px-2 py-1.5 num text-pos font-bold">{fmtNum(report.income_grand_total)}</td>
                </tr>

                <tr className="bg-accent/10">
                  <td colSpan={months.length + 2} className="px-2 py-1.5 text-accent text-[10px] uppercase tracking-widest font-bold">Expenses</td>
                </tr>
                {report.expense_rows.map(r => (
                  <tr key={'exp-' + r.label} className="hover:bg-accent/[0.06]">
                    <td className="px-2 py-1">{r.label}</td>
                    {r.cells.map((v, i) => <td key={i} className="px-2 py-1 num text-neg">{cell(v)}</td>)}
                    <td className="px-2 py-1 num text-neg font-semibold">{fmtNum(r.total)}</td>
                  </tr>
                ))}
                <tr className="border-t border-dim/60 bg-accent/[0.04]">
                  <td className="px-2 py-1.5 font-semibold uppercase text-[10px] tracking-widest">Total expenses</td>
                  {report.expense_totals.map((v, i) => <td key={i} className="px-2 py-1.5 num text-neg font-bold">{fmtNum(v)}</td>)}
                  <td className="px-2 py-1.5 num text-neg font-bold">{fmtNum(report.expense_grand_total)}</td>
                </tr>

                <tr className="border-t-2 border-accent bg-accent/20">
                  <td className="px-2 py-2 font-bold uppercase text-[11px] tracking-widest">Net income</td>
                  {report.net_income.map((v, i) => (
                    <td key={i} className={cx('px-2 py-2 num font-bold', v < 0 ? 'text-neg' : 'text-pos')}>
                      {fmtMoneyParens(v)}
                    </td>
                  ))}
                  <td className={cx('px-2 py-2 num font-bold', report.net_grand_total < 0 ? 'text-neg' : 'text-pos')}>
                    {fmtMoneyParens(report.net_grand_total)}
                  </td>
                </tr>
              </>
            )}
          </tbody>
        </table>
      </div>
    </>
  );
}

function BvaView({ report, onDownload, onDownloadXlsx, onDropBudget }) {
  const empty = !report || (report.income_rows.length === 0 && report.expense_rows.length === 0);

  function variancePill(r) {
    if (r.variance_pct === null) return <span className="text-dim">—</span>;
    const cls = r.flagged ? 'text-neg font-bold' : (r.variance_pct >= 0 ? 'text-pos' : 'text-fg/70');
    return <span className={cls}>{fmtPct(r.variance_pct)}{r.flagged ? '  !' : ''}</span>;
  }

  function moneyDelta(v) {
    if (v === 0 || v === null) return <span className="text-dim">0.00</span>;
    return v < 0
      ? <span className="text-neg">({fmtNum(Math.abs(v))})</span>
      : <span className="text-pos">{fmtNum(v)}</span>;
  }

  return (
    <>
      <div className="flex items-center justify-between border-t border-dim/40 pt-4 gap-4 flex-wrap">
        <div className="flex items-baseline gap-8 flex-wrap">
          <Stat label={report ? `FY${report.fiscal_year}` : 'Fiscal year'} value={report ? `${report.months_elapsed} / 12 mo` : '0 / 12 mo'} big />
          <Stat
            label="Net (actual vs prorated)"
            value={report ? fmtMoney(report.net_actual - report.net_budget_prorated) : '$0.00'}
            valueClass={report && (report.net_actual - report.net_budget_prorated) < 0 ? 'text-neg' : 'text-pos'}
            big
          />
        </div>
        <div className="flex gap-2">
          <button
            onClick={onDownload}
            disabled={empty}
            className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-pos/80 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]"
          >
            Download PDF
          </button>
          <button
            onClick={onDownloadXlsx}
            disabled={empty}
            className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.4)]"
          >
            Excel
          </button>
        </div>
      </div>

      <DropZone
        label="Drop budget CSV"
        hint="Columns: fiscal_year, kind (income|expense), category, annual_amount, notes"
        onFile={onDropBudget}
        compact
      />

      <div className="border border-dim/70 rounded-xl overflow-hidden overflow-x-auto">
        <table className="w-full text-xs">
          <thead className="bg-accent/20 text-fg uppercase text-[10px] tracking-widest">
            <tr>
              <th className="text-left px-2 py-2 font-semibold">Category</th>
              <th className="text-right px-2 py-2 font-semibold">Annual budget</th>
              <th className="text-right px-2 py-2 font-semibold">Prorated</th>
              <th className="text-right px-2 py-2 font-semibold">Actual YTD</th>
              <th className="text-right px-2 py-2 font-semibold">Variance $</th>
              <th className="text-right px-2 py-2 font-semibold">Variance %</th>
            </tr>
          </thead>
          <tbody>
            {empty && (
              <tr>
                <td colSpan="6" className="px-3 py-8 text-center italic" style={{ color: '#4A3A6A' }}>
                  No budget rows for this fiscal year. Drop a budget CSV above.
                </td>
              </tr>
            )}

            {!empty && (
              <>
                <tr className="bg-accent/10">
                  <td colSpan="6" className="px-2 py-1.5 text-accent text-[10px] uppercase tracking-widest font-bold">Income</td>
                </tr>
                {report.income_rows.map(r => (
                  <tr key={'bi-' + r.category} className={cx(r.flagged && 'bg-neg/[0.08]')}>
                    <td className="px-2 py-1">{r.category}</td>
                    <td className="px-2 py-1 num">{fmtNum(r.annual_budget)}</td>
                    <td className="px-2 py-1 num">{fmtNum(r.prorated_budget)}</td>
                    <td className="px-2 py-1 num text-pos">{fmtNum(r.actual_ytd)}</td>
                    <td className="px-2 py-1 num">{moneyDelta(r.variance_dollar)}</td>
                    <td className="px-2 py-1 num">{variancePill(r)}</td>
                  </tr>
                ))}
                <tr className="border-t border-dim/60 bg-accent/[0.04]">
                  <td className="px-2 py-1.5 font-semibold uppercase text-[10px] tracking-widest">Total income</td>
                  <td className="px-2 py-1.5"></td>
                  <td className="px-2 py-1.5 num font-bold">{fmtNum(report.income_budget_prorated_total)}</td>
                  <td className="px-2 py-1.5 num font-bold text-pos">{fmtNum(report.income_actual_total)}</td>
                  <td className="px-2 py-1.5 num font-bold">{moneyDelta(report.income_actual_total - report.income_budget_prorated_total)}</td>
                  <td className="px-2 py-1.5"></td>
                </tr>

                <tr className="bg-accent/10">
                  <td colSpan="6" className="px-2 py-1.5 text-accent text-[10px] uppercase tracking-widest font-bold">Expenses</td>
                </tr>
                {report.expense_rows.map(r => (
                  <tr key={'be-' + r.category} className={cx(r.flagged && 'bg-neg/[0.08]')}>
                    <td className="px-2 py-1">{r.category}</td>
                    <td className="px-2 py-1 num">{fmtNum(r.annual_budget)}</td>
                    <td className="px-2 py-1 num">{fmtNum(r.prorated_budget)}</td>
                    <td className="px-2 py-1 num text-neg">{fmtNum(r.actual_ytd)}</td>
                    <td className="px-2 py-1 num">{moneyDelta(r.variance_dollar)}</td>
                    <td className="px-2 py-1 num">{variancePill(r)}</td>
                  </tr>
                ))}
                <tr className="border-t border-dim/60 bg-accent/[0.04]">
                  <td className="px-2 py-1.5 font-semibold uppercase text-[10px] tracking-widest">Total expenses</td>
                  <td className="px-2 py-1.5"></td>
                  <td className="px-2 py-1.5 num font-bold">{fmtNum(report.expense_budget_prorated_total)}</td>
                  <td className="px-2 py-1.5 num font-bold text-neg">{fmtNum(report.expense_actual_total)}</td>
                  <td className="px-2 py-1.5 num font-bold">{moneyDelta(report.expense_actual_total - report.expense_budget_prorated_total)}</td>
                  <td className="px-2 py-1.5"></td>
                </tr>

                <tr className="border-t-2 border-accent bg-accent/20">
                  <td className="px-2 py-2 font-bold uppercase text-[11px] tracking-widest">Net</td>
                  <td className="px-2 py-2"></td>
                  <td className="px-2 py-2 num font-bold">{fmtNum(report.net_budget_prorated)}</td>
                  <td className="px-2 py-2 num font-bold">{fmtNum(report.net_actual)}</td>
                  <td className={cx('px-2 py-2 num font-bold')}>{moneyDelta(report.net_actual - report.net_budget_prorated)}</td>
                  <td className="px-2 py-2"></td>
                </tr>
              </>
            )}
          </tbody>
        </table>
      </div>

      <div className="text-[11px] text-dim">
        Lines flagged in red exceed +/-15% variance against the prorated budget.
      </div>
    </>
  );
}

function AttendanceView({ trend, onDownload, onDownloadXlsx, onDropCsv, onAddEntry, onDelete }) {
  const [form, setForm] = useState({ date: '', adults: '', children: '', notes: '' });
  const empty = !trend || trend.total_points === 0;

  function submit(e) {
    e.preventDefault();
    if (!form.date || form.adults === '') return;
    onAddEntry({
      date: form.date,
      adults: Number(form.adults),
      children: Number(form.children || 0),
      notes: form.notes || null,
    });
    setForm({ date: '', adults: '', children: '', notes: '' });
  }

  const dirColor = trend?.direction === 'up' ? 'text-pos' : trend?.direction === 'down' ? 'text-neg' : 'text-fg/70';
  const dirLabel = trend?.direction === 'up' ? 'trending up' : trend?.direction === 'down' ? 'trending down' : 'flat';

  return (
    <>
      <div className="flex items-center justify-between border-t border-dim/40 pt-4 gap-4 flex-wrap">
        <div className="flex items-baseline gap-8 flex-wrap">
          <Stat label="Current 4wk avg" value={trend ? trend.avg_4wk_current.toFixed(0) : '0'} valueClass="text-pos" big />
          <Stat label="Prior 4wk avg" value={trend ? trend.avg_4wk_prior.toFixed(0) : '0'} big />
          <Stat label="Direction" value={dirLabel} valueClass={dirColor} big />
          <Stat label="Services recorded" value={trend?.total_points ?? 0} big />
        </div>
        <div className="flex gap-2">
          <button
            onClick={onDownload}
            disabled={empty}
            className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-pos/80 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]"
          >
            Download PDF
          </button>
          <button
            onClick={onDownloadXlsx}
            disabled={empty}
            className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.4)]"
          >
            Excel
          </button>
        </div>
      </div>

      <div className="border border-dim/70 rounded-xl p-4 bg-accent/[0.04]">
        <Sparkline points={trend?.points ?? []} />
        <div className="flex justify-end mt-2 gap-4 text-[10px] uppercase tracking-widest text-dim">
          <span><span className="inline-block w-3 h-3 align-middle mr-1" style={{ background: '#9945FF', opacity: 0.4 }}></span>weekly total</span>
          <span><span className="inline-block w-3 h-[2px] align-middle mr-1" style={{ background: '#14F195' }}></span>4-week rolling avg</span>
        </div>
      </div>

      <div className="grid grid-cols-12 gap-4">
        <form onSubmit={submit} className="col-span-7 border border-dim/70 rounded-xl p-3 flex flex-wrap items-end gap-2">
          <div className="flex flex-col gap-1">
            <label className="text-[10px] uppercase tracking-widest text-dim">Date</label>
            <input type="date" value={form.date} onChange={e => setForm({ ...form, date: e.target.value })}
              className="bg-bg border border-dim rounded-md text-fg px-2 py-1 text-sm focus:outline-none focus:border-accent" />
          </div>
          <div className="flex flex-col gap-1">
            <label className="text-[10px] uppercase tracking-widest text-dim">Adults</label>
            <input type="number" min="0" value={form.adults} onChange={e => setForm({ ...form, adults: e.target.value })}
              className="bg-bg border border-dim rounded-md text-fg px-2 py-1 text-sm w-20 num focus:outline-none focus:border-accent" />
          </div>
          <div className="flex flex-col gap-1">
            <label className="text-[10px] uppercase tracking-widest text-dim">Children</label>
            <input type="number" min="0" value={form.children} onChange={e => setForm({ ...form, children: e.target.value })}
              className="bg-bg border border-dim rounded-md text-fg px-2 py-1 text-sm w-20 num focus:outline-none focus:border-accent" />
          </div>
          <div className="flex flex-col gap-1 flex-1 min-w-[160px]">
            <label className="text-[10px] uppercase tracking-widest text-dim">Notes</label>
            <input type="text" value={form.notes} onChange={e => setForm({ ...form, notes: e.target.value })}
              placeholder="optional"
              className="bg-bg border border-dim rounded-md text-fg px-2 py-1 text-sm focus:outline-none focus:border-accent" />
          </div>
          <button type="submit"
            disabled={!form.date || form.adults === ''}
            className="bg-accent text-white px-3 py-1.5 rounded-md text-sm font-semibold disabled:opacity-40 hover:bg-accent/80">
            Save service
          </button>
        </form>

        <div className="col-span-5">
          <DropZone
            label="Drop attendance CSV"
            hint="Columns: date, adults, children, notes"
            onFile={onDropCsv}
            compact
          />
        </div>
      </div>

      <div className="border border-dim/70 rounded-xl overflow-hidden overflow-x-auto">
        <table className="w-full text-sm">
          <thead className="bg-accent/20 text-fg uppercase text-[10px] tracking-widest">
            <tr>
              <th className="text-left px-3 py-2 font-semibold">Date</th>
              <th className="text-right px-3 py-2 font-semibold">Adults</th>
              <th className="text-right px-3 py-2 font-semibold">Children</th>
              <th className="text-right px-3 py-2 font-semibold">Total</th>
              <th className="text-right px-3 py-2 font-semibold">4wk avg</th>
              <th className="text-left px-3 py-2 font-semibold">Notes</th>
              <th className="text-right px-3 py-2 font-semibold"></th>
            </tr>
          </thead>
          <tbody>
            {empty && (
              <tr><td colSpan="7" className="px-3 py-8 text-center italic" style={{ color: '#4A3A6A' }}>No attendance yet. Add a service above or drop a CSV.</td></tr>
            )}
            {(trend?.points ?? []).slice().reverse().map(p => (
              <tr key={p.date} className="hover:bg-accent/[0.06]">
                <td className="px-3 py-1.5">{p.date}</td>
                <td className="px-3 py-1.5 num">{p.adults}</td>
                <td className="px-3 py-1.5 num">{p.children}</td>
                <td className="px-3 py-1.5 num font-semibold">{p.total}</td>
                <td className="px-3 py-1.5 num text-pos">{p.rolling_4wk.toFixed(1)}</td>
                <td className="px-3 py-1.5 text-fg/60">{p.notes || ''}</td>
                <td className="px-3 py-1.5 text-right">
                  <button onClick={() => onDelete(p.date)} className="text-dim hover:text-neg text-[10px] uppercase tracking-widest">delete</button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </>
  );
}

function TaxLettersView({ bundle, year, onChangeYear, onDownloadPdf, onDownloadZip, onDownloadXlsx, onEditProfile }) {
  const empty = !bundle || bundle.eligible_donors === 0;
  const profileComplete = bundle?.profile_complete;
  const yearOptions = [];
  const currentYear = new Date().getFullYear();
  for (let y = currentYear; y >= currentYear - 5; y--) yearOptions.push(y);

  return (
    <>
      <div className="flex items-center justify-between border-t border-dim/40 pt-4 gap-4 flex-wrap">
        <div className="flex items-baseline gap-8 flex-wrap">
          <div className="flex flex-col gap-0.5">
            <div className="text-[10px] uppercase tracking-widest text-dim">Tax year</div>
            <select value={year} onChange={e => onChangeYear(Number(e.target.value))}
              className="bg-bg border border-dim rounded-md text-fg px-2 py-1 text-base num focus:outline-none focus:border-accent">
              {yearOptions.map(y => <option key={y} value={y}>{y}</option>)}
            </select>
          </div>
          <Stat label="Eligible donors" value={bundle?.eligible_donors ?? 0} valueClass="text-pos" big />
          <Stat label="Total acknowledged" value={bundle ? fmtMoney(bundle.grand_total) : '$0.00'} valueClass="text-pos" big />
          <Stat label="Below threshold" value={bundle?.excluded_donors_below_threshold ?? 0} valueClass="text-dim" big />
        </div>
        <div className="flex gap-2 flex-wrap">
          <button onClick={onDownloadPdf} disabled={empty}
            className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-pos/80 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]">
            Single PDF
          </button>
          <button onClick={onDownloadZip} disabled={empty}
            className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.5)]">
            ZIP (one PDF per donor)
          </button>
          <button onClick={onDownloadXlsx} disabled={empty}
            className="bg-accent/70 text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/60 shadow-[0_0_0_1px_rgba(153,69,255,0.4)]">
            Excel (mail merge)
          </button>
        </div>
      </div>

      {!profileComplete && bundle && (
        <div className="border border-neg/50 rounded-xl p-4 bg-neg/[0.06]">
          <div className="flex items-center justify-between gap-4 flex-wrap">
            <div>
              <div className="text-[10px] uppercase tracking-widest text-neg font-semibold mb-1">Church profile incomplete</div>
              <div className="text-sm text-fg">
                IRS-compliant letters require {!bundle.ein && <span className="text-neg">EIN</span>}
                {!bundle.ein && (!bundle.address || !bundle.signature_name) && ', '}
                {!bundle.address && <span className="text-neg">mailing address</span>}
                {!bundle.address && !bundle.signature_name && ', '}
                {!bundle.signature_name && <span className="text-neg">signature name</span>}
                . You can still generate, but the letters will look unfinished to your donors.
              </div>
            </div>
            <button onClick={onEditProfile}
              className="bg-neg text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-neg/85">
              Fix church profile
            </button>
          </div>
        </div>
      )}

      <div className="border border-dim/70 rounded-xl overflow-hidden overflow-x-auto">
        <table className="w-full text-sm">
          <thead className="bg-accent/20 text-fg uppercase text-[10px] tracking-widest">
            <tr>
              <th className="text-left px-3 py-2 font-semibold">Donor</th>
              <th className="text-left px-3 py-2 font-semibold">Family</th>
              <th className="text-right px-3 py-2 font-semibold">Gifts</th>
              <th className="text-right px-3 py-2 font-semibold">First gift</th>
              <th className="text-right px-3 py-2 font-semibold">Last gift</th>
              <th className="text-right px-3 py-2 font-semibold">Total</th>
            </tr>
          </thead>
          <tbody>
            {empty && (
              <tr><td colSpan="6" className="px-3 py-8 text-center italic" style={{ color: '#4A3A6A' }}>
                No donors met the ${bundle?.threshold ?? 250} threshold for {year}. Drop contributions for that year or change the year.
              </td></tr>
            )}
            {bundle?.letters?.map(d => (
              <tr key={d.donor} className="hover:bg-accent/[0.06]">
                <td className="px-3 py-1.5">{d.donor}</td>
                <td className="px-3 py-1.5 text-fg/70">{d.family_unit || ''}</td>
                <td className="px-3 py-1.5 num">{d.count}</td>
                <td className="px-3 py-1.5 num">{d.first_gift}</td>
                <td className="px-3 py-1.5 num">{d.last_gift}</td>
                <td className="px-3 py-1.5 num text-pos font-semibold">{fmtMoney(d.total)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div className="text-[11px] text-dim leading-relaxed">
        Letters include IRS section 170(f)(8) acknowledgement language. Default threshold is $250 (the IRS written-acknowledgement requirement). Each letter has the donor's contribution detail + your church name, EIN, and signature.
      </div>
    </>
  );
}

function App() {
  const [churches, setChurches] = useState([]);
  const [selected, setSelected] = useState(null);
  const [newChurchName, setNewChurchName] = useState('');
  const [tab, setTab] = useState('donor');
  const [summary, setSummary] = useState(null);
  const [pnl, setPnl] = useState(null);
  const [bva, setBva] = useState(null);
  const [attendance, setAttendance] = useState(null);
  const [contribImport, setContribImport] = useState(null);
  const [expenseImport, setExpenseImport] = useState(null);
  const [budgetImport, setBudgetImport] = useState(null);
  const [attendanceImport, setAttendanceImport] = useState(null);
  const [chmsSources, setChmsSources] = useState([]);
  const [contribSource, setContribSource] = useState('auto');
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState(null);
  const [me, setMe] = useState(null);
  const [signinModal, setSigninModal] = useState({ open: false, email: '', status: 'idle' });
  const [tierInfo, setTierInfo] = useState(null);
  const [taxBundle, setTaxBundle] = useState(null);
  const [taxYear, setTaxYear] = useState(new Date().getFullYear() - 1);
  const [profileModal, setProfileModal] = useState({ open: false, ein: '', address: '', signature_name: '' });

  useEffect(() => {
    fetch(`${API}/api/churches`)
      .then(r => r.json()).then(setChurches).catch(e => setError(String(e)));
    fetch(`${API}/api/auth/me`).then(r => r.json()).then(setMe).catch(() => {});
  }, []);

  useEffect(() => {
    if (!me) { setTierInfo(null); return; }
    fetch(`${API}/api/billing/my-tier`)
      .then(r => r.ok ? r.json() : null)
      .then(setTierInfo)
      .catch(() => {});
  }, [me?.id]);

  async function startSignin(e) {
    e?.preventDefault?.();
    const email = signinModal.email.trim();
    if (!email) return;
    setSigninModal(m => ({ ...m, status: 'sending' }));
    try {
      const res = await fetch(`${API}/api/auth/start`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email }),
      });
      if (!res.ok) {
        setSigninModal(m => ({ ...m, status: 'error' }));
        setError(`Signin failed: ${await res.text()}`);
      } else {
        setSigninModal(m => ({ ...m, status: 'sent' }));
      }
    } catch (e) {
      setSigninModal(m => ({ ...m, status: 'error' }));
      setError(String(e));
    }
  }

  async function logout() {
    await fetch(`${API}/api/auth/logout`, { method: 'POST' });
    setMe(null);
    setTierInfo(null);
  }

  async function startCheckout() {
    setBusy(true);
    try {
      const res = await fetch(`${API}/api/billing/checkout`, { method: 'POST' });
      if (!res.ok) {
        setError(`Checkout failed: ${await res.text()}`);
        setBusy(false);
        return;
      }
      const body = await res.json();
      window.location = body.checkout_url;
    } catch (e) {
      setError(String(e));
      setBusy(false);
    }
  }

  useEffect(() => {
    if (!selected) {
      setSummary(null); setPnl(null); setBva(null); setAttendance(null);
      setContribImport(null); setExpenseImport(null); setBudgetImport(null); setAttendanceImport(null);
      return;
    }
    refreshAll();
  }, [selected?.id]);

  function refreshAll() {
    if (!selected) return;
    const cid = selected.id;
    fetch(`${API}/api/churches/${cid}/reports/donor-summary`).then(r => r.json()).then(setSummary).catch(e => setError(String(e)));
    fetch(`${API}/api/churches/${cid}/reports/pnl`).then(r => r.json()).then(setPnl).catch(e => setError(String(e)));
    fetch(`${API}/api/churches/${cid}/reports/budget-vs-actual`).then(r => r.json()).then(setBva).catch(e => setError(String(e)));
    fetch(`${API}/api/churches/${cid}/reports/attendance`).then(r => r.json()).then(setAttendance).catch(e => setError(String(e)));
    fetch(`${API}/api/churches/${cid}/reports/tax-letters?year=${taxYear}`).then(r => r.json()).then(setTaxBundle).catch(e => setError(String(e)));
    if (chmsSources.length === 0) {
      fetch(`${API}/api/churches/${cid}/contributions/sources`).then(r => r.json()).then(setChmsSources).catch(e => setError(String(e)));
    }
  }

  useEffect(() => {
    if (!selected) return;
    fetch(`${API}/api/churches/${selected.id}/reports/tax-letters?year=${taxYear}`)
      .then(r => r.json()).then(setTaxBundle).catch(() => {});
  }, [taxYear, selected?.id]);

  async function saveChurchProfile(e) {
    e?.preventDefault?.();
    if (!selected) return;
    setBusy(true);
    try {
      const res = await fetch(`${API}/api/churches/${selected.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ein: profileModal.ein || null,
          address: profileModal.address || null,
          signature_name: profileModal.signature_name || null,
        }),
      });
      if (!res.ok) {
        setError(`Save failed: ${await res.text()}`);
      } else {
        const updated = await res.json();
        setChurches(prev => prev.map(c => c.id === updated.id ? updated : c));
        setSelected(updated);
        setProfileModal({ open: false, ein: '', address: '', signature_name: '' });
        refreshAll();
      }
    } catch (e) { setError(String(e)); }
    setBusy(false);
  }

  function openProfileModal() {
    setProfileModal({
      open: true,
      ein: selected?.ein || '',
      address: selected?.address || '',
      signature_name: selected?.signature_name || '',
    });
  }

  function downloadTaxLettersPdf()  { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/tax-letters.pdf?year=${taxYear}`; }
  function downloadTaxLettersZip()  { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/tax-letters.zip?year=${taxYear}`; }
  function downloadTaxLettersXlsx() { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/tax-letters.xlsx?year=${taxYear}`; }

  async function createChurch(e) {
    e.preventDefault();
    const name = newChurchName.trim();
    if (!name) return;
    setBusy(true);
    try {
      const res = await fetch(`${API}/api/churches`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name }),
      });
      if (!res.ok) {
        setError(`Create failed: ${await res.text()}`);
      } else {
        const c = await res.json();
        setChurches(prev => [...prev, c]);
        setSelected(c);
        setNewChurchName('');
      }
    } catch (e) { setError(String(e)); }
    setBusy(false);
  }

  async function uploadCsv(kind, file, extraData) {
    if (!file || !selected) return;
    setBusy(true);
    const fd = new FormData();
    fd.append('file', file);
    if (extraData) {
      for (const [k, v] of Object.entries(extraData)) {
        if (v !== null && v !== undefined && v !== '') fd.append(k, v);
      }
    }
    const cid = selected.id;
    const url = {
      contrib: `${API}/api/churches/${cid}/contributions/import`,
      expense: `${API}/api/churches/${cid}/expenses/import`,
      budget: `${API}/api/churches/${cid}/budget/import`,
      attendance: `${API}/api/churches/${cid}/attendance/import`,
    }[kind];
    const setter = {
      contrib: setContribImport,
      expense: setExpenseImport,
      budget: setBudgetImport,
      attendance: setAttendanceImport,
    }[kind];
    try {
      const res = await fetch(url, { method: 'POST', body: fd });
      const text = await res.text();
      if (!res.ok) {
        setError(`${kind} import failed: ${text}`);
      } else {
        setter(JSON.parse(text));
        refreshAll();
      }
    } catch (e) { setError(String(e)); }
    setBusy(false);
  }

  async function addAttendance(payload) {
    if (!selected) return;
    setBusy(true);
    try {
      const res = await fetch(`${API}/api/churches/${selected.id}/attendance`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
      });
      if (!res.ok) {
        setError(`Save failed: ${await res.text()}`);
      } else {
        refreshAll();
      }
    } catch (e) { setError(String(e)); }
    setBusy(false);
  }

  async function deleteAttendance(dateStr) {
    if (!selected || !attendance) return;
    const match = attendance.points.find(p => p.date === dateStr);
    if (!match) return;
    const records = await fetch(`${API}/api/churches/${selected.id}/attendance`).then(r => r.json());
    const target = records.find(r => r.date === dateStr);
    if (!target) return;
    setBusy(true);
    try {
      const res = await fetch(`${API}/api/churches/${selected.id}/attendance/${target.id}`, { method: 'DELETE' });
      if (!res.ok) setError(`Delete failed: ${await res.text()}`);
      else refreshAll();
    } catch (e) { setError(String(e)); }
    setBusy(false);
  }

  function downloadDonor()      { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/donor-summary.pdf`; }
  function downloadDonorXlsx()  { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/donor-summary.xlsx`; }
  function downloadPnl()        { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/pnl.pdf`; }
  function downloadPnlXlsx()    { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/pnl.xlsx`; }
  function downloadBva()        { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/budget-vs-actual.pdf`; }
  function downloadBvaXlsx()    { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/budget-vs-actual.xlsx`; }
  function downloadAttPdf()     { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/attendance.pdf`; }
  function downloadAttXlsx()    { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/attendance.xlsx`; }
  function downloadPacket()     { if (selected) window.location = `${API}/api/churches/${selected.id}/reports/board-packet.zip`; }

  const tabs = [
    { key: 'donor', label: 'Donor summary' },
    { key: 'pnl', label: 'Monthly P&L' },
    { key: 'bva', label: 'Budget vs Actual' },
    { key: 'attendance', label: 'Attendance' },
    { key: 'tax', label: 'Tax letters' },
  ];

  const hasAny = summary || pnl || bva || attendance;
  const packetReady = selected && hasAny &&
    ((summary?.rows?.length ?? 0) > 0
     || (pnl?.income_rows?.length ?? 0) > 0
     || (pnl?.expense_rows?.length ?? 0) > 0
     || (bva?.income_rows?.length ?? 0) > 0
     || (bva?.expense_rows?.length ?? 0) > 0
     || (attendance?.total_points ?? 0) > 0);

  return (
    <div className="min-h-screen flex flex-col">
      <header className="border-b border-dim/70 px-6 py-3 flex items-center justify-between">
        <div className="flex items-center gap-3">
          <span className="text-accent font-bold tracking-wide text-lg">FaithReports</span>
          <span className="text-dim text-[10px] uppercase tracking-widest">board reporting</span>
        </div>
        <div className="flex items-center gap-4">
          {me ? (
            <div className="flex items-center gap-3 text-xs">
              <span className="text-dim uppercase tracking-widest text-[10px]">signed in as</span>
              <span className="text-fg font-medium">{me.email}</span>
              <button onClick={logout} className="text-dim hover:text-neg text-[10px] uppercase tracking-widest">logout</button>
            </div>
          ) : (
            <button
              onClick={() => setSigninModal({ open: true, email: '', status: 'idle' })}
              className="bg-accent text-white px-3 py-1.5 rounded-md text-xs font-bold hover:bg-accent/85 uppercase tracking-widest"
            >
              Sign in
            </button>
          )}
          <div className="text-[10px] uppercase tracking-widest text-dim">v0.4.0</div>
        </div>
      </header>

      <main className="flex-1 grid grid-cols-12">
        <aside className="col-span-3 border-r border-dim/70 p-4 flex flex-col gap-5">
          <div>
            <div className="text-[10px] uppercase tracking-widest text-dim mb-2">Churches</div>
            <ul className="flex flex-col">
              {churches.map(c => (
                <li key={c.id}>
                  <button
                    onClick={() => setSelected(c)}
                    className={cx(
                      'w-full text-left px-2 py-1.5 text-sm border-l-2 transition-colors',
                      selected?.id === c.id
                        ? 'border-accent text-fg bg-accent/10'
                        : 'border-transparent text-fg/80 hover:text-fg hover:border-dim'
                    )}
                  >
                    {c.name}
                  </button>
                </li>
              ))}
              {churches.length === 0 && (
                <li className="text-sm italic px-2 py-1.5" style={{ color: '#4A3A6A' }}>No churches yet</li>
              )}
            </ul>
          </div>

          <form onSubmit={createChurch} className="border-t border-dim/70 pt-4">
            <div className="text-[10px] uppercase tracking-widest text-dim mb-2">New church</div>
            <input
              type="text"
              value={newChurchName}
              onChange={e => setNewChurchName(e.target.value)}
              placeholder="First Church"
              className="w-full bg-bg border border-dim rounded-md text-fg px-2 py-1.5 text-sm focus:outline-none focus:border-accent"
            />
            <button
              type="submit"
              disabled={busy || !newChurchName.trim()}
              className="mt-2 w-full bg-accent text-white px-2 py-1.5 rounded-md text-sm font-semibold disabled:opacity-40 hover:bg-accent/80"
            >
              Create church
            </button>
          </form>
        </aside>

        <section className="col-span-9 p-6 flex flex-col gap-5">
          {!selected && (
            <div className="text-sm" style={{ color: '#4A3A6A' }}>Select or create a church on the left to begin.</div>
          )}

          {selected && (
            <>
              <div className="flex items-center justify-between gap-4 flex-wrap">
                <div>
                  <h1 className="text-2xl font-semibold">{selected.name}</h1>
                  <div className="text-[11px] uppercase tracking-widest text-dim">Board reporting workspace</div>
                </div>
                <button
                  onClick={downloadPacket}
                  disabled={!packetReady}
                  className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold disabled:opacity-30 hover:bg-accent/80 shadow-[0_0_0_1px_rgba(153,69,255,0.5)]"
                >
                  Download Board Packet (ZIP)
                </button>
              </div>

              {me && tierInfo && (
                <div className="border border-accent/40 rounded-2xl p-4 bg-accent/[0.06] flex items-center justify-between gap-4 flex-wrap">
                  <div>
                    <div className="text-[10px] uppercase tracking-widest text-accent">
                      {tierInfo.waitlist_position ? `Founder position #${tierInfo.waitlist_position}` : 'No founder slot on file'}
                    </div>
                    <div className="text-base font-semibold mt-0.5">
                      {tierInfo.label} <span className="text-dim font-normal">— ${tierInfo.price_monthly}/mo locked for life</span>
                    </div>
                  </div>
                  <button
                    onClick={startCheckout}
                    disabled={busy}
                    className="bg-pos text-black px-4 py-2 rounded-lg text-sm font-bold hover:bg-pos/85 disabled:opacity-40 shadow-[0_0_0_1px_rgba(20,241,149,0.4)]"
                  >
                    {tierInfo.tier === 'standard' ? 'Subscribe at $49/mo' : `Claim my ${tierInfo.label} slot`}
                  </button>
                </div>
              )}


              <div className="grid grid-cols-2 gap-4">
                <div className="flex flex-col gap-2">
                  <div className="flex items-center gap-3">
                    <label className="text-[10px] uppercase tracking-widest text-dim whitespace-nowrap">Source</label>
                    <select
                      value={contribSource}
                      onChange={e => setContribSource(e.target.value)}
                      className="flex-1 bg-bg border border-dim rounded-md text-fg px-2 py-1 text-xs focus:outline-none focus:border-accent"
                    >
                      {chmsSources.length === 0 && <option value="auto">Auto-detect</option>}
                      {chmsSources.map(s => (
                        <option key={s.key} value={s.key}>
                          {s.label}{s.validated ? '' : '  (best effort)'}
                        </option>
                      ))}
                    </select>
                  </div>
                  <DropZone
                    label="Drop contributions CSV"
                    hint={(chmsSources.find(s => s.key === contribSource)?.description) || "Columns: date, donor, family unit, amount, type, memo"}
                    onFile={f => uploadCsv('contrib', f, { source: contribSource })}
                  />
                </div>
                <DropZone
                  label="Drop expenses CSV"
                  hint="Columns: date, vendor, category, amount, memo"
                  onFile={f => uploadCsv('expense', f)}
                />
              </div>

              {(contribImport || expenseImport || budgetImport || attendanceImport) && (
                <div className="grid grid-cols-2 gap-4">
                  <ImportBanner kind="Contributions" result={contribImport} />
                  <ImportBanner kind="Expenses" result={expenseImport} />
                  <ImportBanner kind="Budget" result={budgetImport} />
                  <ImportBanner kind="Attendance" result={attendanceImport} />
                </div>
              )}

              <div className="border-b border-dim/70 flex gap-1 overflow-x-auto">
                {tabs.map(t => (
                  <button
                    key={t.key}
                    onClick={() => setTab(t.key)}
                    className={cx(
                      'px-4 py-2 text-sm uppercase tracking-widest border-b-2 -mb-px transition-colors whitespace-nowrap',
                      tab === t.key
                        ? 'border-accent text-accent font-semibold'
                        : 'border-transparent text-fg/60 hover:text-fg'
                    )}
                  >
                    {t.label}
                  </button>
                ))}
              </div>

              {tab === 'donor' && <DonorSummaryView summary={summary} onDownload={downloadDonor} onDownloadXlsx={downloadDonorXlsx} />}
              {tab === 'pnl' && <PnlView report={pnl} onDownload={downloadPnl} onDownloadXlsx={downloadPnlXlsx} />}
              {tab === 'bva' && (
                <BvaView
                  report={bva}
                  onDownload={downloadBva}
                  onDownloadXlsx={downloadBvaXlsx}
                  onDropBudget={f => uploadCsv('budget', f)}
                />
              )}
              {tab === 'attendance' && (
                <AttendanceView
                  trend={attendance}
                  onDownload={downloadAttPdf}
                  onDownloadXlsx={downloadAttXlsx}
                  onDropCsv={f => uploadCsv('attendance', f)}
                  onAddEntry={addAttendance}
                  onDelete={deleteAttendance}
                />
              )}
              {tab === 'tax' && (
                <TaxLettersView
                  bundle={taxBundle}
                  year={taxYear}
                  onChangeYear={setTaxYear}
                  onDownloadPdf={downloadTaxLettersPdf}
                  onDownloadZip={downloadTaxLettersZip}
                  onDownloadXlsx={downloadTaxLettersXlsx}
                  onEditProfile={openProfileModal}
                />
              )}
            </>
          )}
        </section>
      </main>

      {signinModal.open && (
        <div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-4"
          onClick={() => setSigninModal({ open: false, email: '', status: 'idle' })}>
          <div onClick={e => e.stopPropagation()}
            className="bg-bg border border-accent/40 rounded-2xl p-6 max-w-md w-full shadow-[0_0_0_1px_rgba(153,69,255,0.5),0_24px_80px_-16px_rgba(153,69,255,0.4)]">
            <div className="text-[10px] uppercase tracking-[0.25em] text-accent mb-2">Sign in to FaithReports</div>
            <h2 className="text-xl font-bold mb-1">Magic link, no password</h2>
            <p className="text-sm text-dim mb-4">We email you a one-click sign-in link. 30-minute expiry. Single use.</p>
            {signinModal.status === 'sent' ? (
              <div className="text-sm text-pos border border-pos/30 bg-pos/[0.06] rounded-lg p-4">
                Check <span className="font-semibold">{signinModal.email}</span>. The link is on its way. Close this when you've clicked it.
              </div>
            ) : (
              <form onSubmit={startSignin} className="flex flex-col gap-3">
                <input type="email" required autoFocus value={signinModal.email}
                  onChange={e => setSigninModal(m => ({ ...m, email: e.target.value }))}
                  placeholder="you@yourchurch.org"
                  className="w-full bg-bg border border-dim rounded-lg text-fg px-3 py-2.5 text-sm focus:outline-none focus:border-accent" />
                <button type="submit"
                  disabled={signinModal.status === 'sending' || !signinModal.email.trim()}
                  className="bg-accent text-white px-4 py-2.5 rounded-lg text-sm font-bold hover:bg-accent/85 disabled:opacity-40">
                  {signinModal.status === 'sending' ? 'Sending…' : 'Send sign-in link'}
                </button>
              </form>
            )}
            <button onClick={() => setSigninModal({ open: false, email: '', status: 'idle' })}
              className="mt-4 text-[10px] uppercase tracking-widest text-dim hover:text-fg">close</button>
          </div>
        </div>
      )}

      {profileModal.open && (
        <div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center z-50 p-4"
          onClick={() => setProfileModal({ ...profileModal, open: false })}>
          <div onClick={e => e.stopPropagation()}
            className="bg-bg border border-accent/40 rounded-2xl p-6 max-w-lg w-full shadow-[0_0_0_1px_rgba(153,69,255,0.5),0_24px_80px_-16px_rgba(153,69,255,0.4)]">
            <div className="text-[10px] uppercase tracking-[0.25em] text-accent mb-2">Church profile</div>
            <h2 className="text-xl font-bold mb-1">Required for IRS-compliant tax letters</h2>
            <p className="text-sm text-dim mb-4">These appear on every donor acknowledgement letter. Save them once.</p>
            <form onSubmit={saveChurchProfile} className="flex flex-col gap-3">
              <div className="flex flex-col gap-1">
                <label className="text-[10px] uppercase tracking-widest text-dim">EIN (Employer Identification Number)</label>
                <input type="text" value={profileModal.ein}
                  onChange={e => setProfileModal({ ...profileModal, ein: e.target.value })}
                  placeholder="12-3456789"
                  className="bg-bg border border-dim rounded-md text-fg px-3 py-2 text-sm num focus:outline-none focus:border-accent" />
              </div>
              <div className="flex flex-col gap-1">
                <label className="text-[10px] uppercase tracking-widest text-dim">Mailing address (multi-line OK)</label>
                <textarea value={profileModal.address}
                  onChange={e => setProfileModal({ ...profileModal, address: e.target.value })}
                  placeholder="123 Faith Way&#10;Springfield, IL 62701"
                  rows="3"
                  className="bg-bg border border-dim rounded-md text-fg px-3 py-2 text-sm focus:outline-none focus:border-accent" />
              </div>
              <div className="flex flex-col gap-1">
                <label className="text-[10px] uppercase tracking-widest text-dim">Signature name (treasurer or pastor)</label>
                <input type="text" value={profileModal.signature_name}
                  onChange={e => setProfileModal({ ...profileModal, signature_name: e.target.value })}
                  placeholder="Pat Jones, Treasurer"
                  className="bg-bg border border-dim rounded-md text-fg px-3 py-2 text-sm focus:outline-none focus:border-accent" />
              </div>
              <div className="flex gap-2 mt-2">
                <button type="submit" disabled={busy}
                  className="bg-accent text-white px-4 py-2 rounded-lg text-sm font-bold hover:bg-accent/85 disabled:opacity-40">
                  Save church profile
                </button>
                <button type="button" onClick={() => setProfileModal({ ...profileModal, open: false })}
                  className="text-dim hover:text-fg text-xs uppercase tracking-widest">cancel</button>
              </div>
            </form>
          </div>
        </div>
      )}

      {busy && (
        <div className="fixed top-3 right-3 text-[10px] uppercase tracking-widest text-accent">working</div>
      )}

      {error && (
        <div className="fixed bottom-4 right-4 max-w-md bg-bg border border-neg rounded-lg text-neg text-sm px-3 py-2 flex items-start gap-3 shadow-lg">
          <span className="flex-1">{error}</span>
          <button onClick={() => setError(null)} className="text-fg/60 hover:text-fg text-xs uppercase tracking-widest">close</button>
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
