/* Finance module — Nutri-style screens
   Mobile screens: Overview · Transactions · Add · Budgets · Insights
*/

function FinanceHome({ txns, accounts, onOpenTxn, onAdd, onEditAccount, loading }) {
  const t30 = nfTotalsForRange(txns, 30);
  const t7  = nfTotalsForRange(txns, 7);
  // Net worth = sum of all account egpBalances (converts foreign-currency accounts to EGP)
  const totalBalance = (accounts || []).reduce((s, a) => s + (a.egpBalance != null ? a.egpBalance : (a.balance || 0)), 0);
  const hasForeignAccts = (accounts || []).some(a => a.currency && a.currency !== 'EGP');

  // last 7 day spending sparkline-bars
  const today = new Date();
  const week = [];
  for (let i = 6; i >= 0; i--) {
    const d = new Date(today); d.setDate(today.getDate()-i);
    const iso = isoOf(d);
    const cal = txns.filter(t => t.date === iso && t.kind === 'expense').reduce((s,t) => s + Math.abs(t.amount), 0);
    week.push({
      iso, label: ['S','M','T','W','T','F','S'][d.getDay()], cal,
    });
  }
  const maxCal = Math.max(...week.map(w => w.cal), 1);
  // Y-axis ticks for weekly spending bar chart (inlined — _niceYTicks not yet available)
  const _wkRough = maxCal / 3;
  const _wkMag = Math.pow(10, Math.floor(Math.log10(_wkRough || 1)));
  const _wkStep = ([1, 2, 2.5, 5, 10].find(f => f * _wkMag >= _wkRough) || 10) * _wkMag;
  const wkTicks = Array.from({ length: Math.ceil(maxCal / _wkStep) + 1 }, (_, i) => i * _wkStep);
  const wkMaxY = Math.max(wkTicks[wkTicks.length - 1], 1);

  // top categories
  const cats = nfByCategory(txns, 30).slice(0, 4);
  const totalSpent30 = cats.reduce((s, c) => s + c.total, 0);

  // recent transactions
  const recent = [...txns].sort((a, b) => b.date.localeCompare(a.date) || (b.time||'').localeCompare(a.time||'')).slice(0, 5);

  if (!txns.length) {
    return (
      <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', paddingTop: 4 }}>
          <div>
            <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Finance</div>
            <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>{prettyDate(todayISO())}</div>
          </div>
          <button onClick={onAdd} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '7px 14px', borderRadius: 999,
            background: 'var(--accent)', color: 'var(--on-accent)',
            border: '1px solid var(--accent)', font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
          }}><PlusIcon size={14}/>Add</button>
        </div>
        {loading
          ? <div style={{ textAlign: 'center', padding: '40px 0', color: 'var(--muted)', fontSize: 14 }}>Loading your data…</div>
          : <EmptyState icon="💳" title="No transactions yet" subtitle="Tap + to log your first income or expense. Your balance, charts, and categories will appear here."/>
        }
      </div>
    );
  }

  return (
    <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* header */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', paddingTop: 4 }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Finance</div>
          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>{prettyDate(NUTRI_TODAY)}</div>
        </div>
        <button onClick={onAdd} style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '7px 14px', borderRadius: 999,
          background: 'var(--accent)', color: 'var(--on-accent)',
          border: '1px solid var(--accent)', font: 'inherit', fontSize: 12.5, fontWeight: 600,
          cursor: 'pointer',
        }}><PlusIcon size={14}/>Add</button>
      </div>

      {/* HERO — Net balance */}
      <div style={{
        ...nutriStyles.card, padding: 20,
        display: 'flex', flexDirection: 'column', gap: 14,
      }}>
        <div>
          <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Net worth</div>
          <div style={{ fontSize: 36, fontWeight: 600, letterSpacing: '-0.03em', marginTop: 4, lineHeight: 1 }}>
            {nfMoneyFmt(Math.round(totalBalance))}
            <span style={{ fontSize: 14, color: 'var(--muted)', fontWeight: 500, marginLeft: 6 }}>EGP{hasForeignAccts ? ' equiv.' : ''}</span>
          </div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <FinStatTile label="Income · 30d"  value={nfMoneyFmt(t30.income)}  color="var(--c-protein)" sign="+" />
          <FinStatTile label="Spent · 30d"   value={nfMoneyFmt(t30.expenses)} color="var(--c-carbs)"  sign="−" />
        </div>
      </div>

      {/* Accounts row */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 2 }}>
        <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.01em' }}>Accounts</div>
        <div style={{ fontSize: 12, color: 'var(--muted)' }}>{(accounts || NF_ACCOUNTS).length} total</div>
      </div>
      <div style={{ display: 'flex', gap: 10, overflowX: 'auto', margin: '0 -18px', padding: '0 18px 4px' }}>
        {(accounts || NF_ACCOUNTS).map(a => (
          <AccountCard key={a.id} account={a} onEdit={onEditAccount ? () => onEditAccount(a) : null}/>
        ))}
      </div>

      {/* 7-day spending */}
      <div style={{ ...nutriStyles.card, padding: 16 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 12 }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>Spending this week</div>
          <div style={{ fontSize: 11, color: 'var(--muted)' }}>{nfMoneyFmt(t7.expenses)} {NF_CURRENCY}</div>
        </div>
        <svg width="100%" viewBox="0 0 300 110" style={{ display: 'block', overflow: 'visible' }}>
          {wkTicks.map(v => {
            const gy = 14 + (1 - v / wkMaxY) * 78;
            const lbl = v >= 1000 ? (v/1000).toFixed(1)+'k' : String(Math.round(v));
            return (
              <g key={v}>
                <line x1={38} x2={294} y1={gy} y2={gy}
                  stroke="var(--border)" strokeWidth="0.6" strokeDasharray="3 3"/>
                <text x={34} y={gy + 3.5} textAnchor="end"
                  fontSize="9" fill="var(--muted)" fontFamily="inherit">{lbl}</text>
              </g>
            );
          })}
          {week.map((w, i) => {
            const slotW = 256 / 7;
            const bW = slotW * 0.6;
            const bH = Math.max((w.cal / wkMaxY) * 78, w.cal > 0 ? 2 : 0);
            const x = 38 + i * slotW + (slotW - bW) / 2;
            return (
              <g key={w.iso}>
                <rect x={x} y={92 - bH} width={bW} height={bH} rx={3}
                  fill={w.cal > 0 ? 'var(--text)' : 'var(--ring-track)'}/>
                <text x={38 + i * slotW + slotW / 2} y={105}
                  textAnchor="middle" fontSize="9" fill="var(--muted)">{w.label}</text>
              </g>
            );
          })}
        </svg>
      </div>

      {/* Top categories */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 2 }}>
        <div style={{ fontSize: 16, fontWeight: 600 }}>Top categories</div>
        <div style={{ fontSize: 12, color: 'var(--muted)' }}>Last 30 days</div>
      </div>
      <div style={{ ...nutriStyles.card, padding: 16, display:'flex', flexDirection:'column', gap: 12 }}>
        {cats.map(c => {
          const pct = c.total / Math.max(totalSpent30, 1);
          return (
            <div key={c.id} style={{ display:'flex', flexDirection:'column', gap: 6 }}>
              <div style={{ display:'flex', alignItems:'center', gap:10 }}>
                <FoodThumb emoji={c.icon} tint={c.color} size={36}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 600 }}>{c.name}</div>
                  <div style={{ fontSize: 11, color:'var(--muted)' }}>{Math.round(pct*100)}% of spending</div>
                </div>
                <div style={{ fontSize: 14, fontWeight: 600 }}>{nfMoneyFmt(c.total)}</div>
              </div>
              <div style={{ marginLeft: 46 }}>
                <MacroBar value={c.total} goal={cats[0].total} color={c.color}/>
              </div>
            </div>
          );
        })}
      </div>

      {/* Recent transactions */}
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 2 }}>
        <div style={{ fontSize: 16, fontWeight: 600 }}>Recent transactions</div>
        <div style={{ fontSize: 12, color: 'var(--muted)' }}>{recent.length} latest</div>
      </div>
      <div style={{ display:'flex', flexDirection:'column', gap: 8 }}>
        {recent.map(t => <TxnCard key={t.id} txn={t} onClick={() => onOpenTxn(t)} />)}
      </div>

      <div style={{ height: 60 }}/>
    </div>
  );
}

function FinStatTile({ label, value, color, sign, sub }) {
  return (
    <div style={{
      padding: 12, borderRadius: 14,
      background: 'var(--surface-2)', border: '1px solid var(--border)',
    }}>
      <div style={{ fontSize: 10.5, color:'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{label}</div>
      <div style={{ fontSize: 18, fontWeight: 600, color: color, letterSpacing: '-0.02em', marginTop: 4 }}>
        {sign ? <span style={{ color: color, marginRight: 2 }}>{sign}</span> : null}{value}
      </div>
      {sub ? <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 3 }}>{sub}</div> : null}
    </div>
  );
}

function AccountCard({ account, onEdit }) {
  const bal = account.balance != null ? account.balance : ((account.startingBalance || 0) + (account.income || 0) - (account.expenses || 0));
  const negative = bal < 0;
  const acctCurrency = account.currency || NF_CURRENCY;
  const showEgpEquiv = acctCurrency !== 'EGP' && account.egpBalance != null && Math.round(account.egpBalance) !== Math.round(bal);
  return (
    <div style={{
      flexShrink: 0, width: 190,
      ...nutriStyles.card, padding: 14,
      display: 'flex', flexDirection: 'column', gap: 8,
      background: 'var(--surface-2)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{
          width: 32, height: 32, borderRadius: 10,
          background: 'var(--surface)', border: '1px solid var(--border)',
          display: 'grid', placeItems: 'center', fontSize: 16,
        }}>{account.icon}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ fontSize: 10, color:'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{account.kind}</div>
          {onEdit && (
            <button onClick={onEdit} style={{
              width: 22, height: 22, borderRadius: 6,
              background: 'var(--surface)', border: '1px solid var(--border-2)',
              display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)',
            }}>
              <EditIcon size={11}/>
            </button>
          )}
        </div>
      </div>
      <div>
        <div style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 500 }}>{account.name}</div>
        <div style={{ fontSize: 20, fontWeight: 600, letterSpacing: '-0.02em', color: negative ? 'var(--c-carbs)' : 'var(--text)' }}>
          {nfMoneyFmt(bal)}
        </div>
        <div style={{ fontSize: 10, color: 'var(--muted-2)', fontWeight: 500 }}>{acctCurrency}</div>
        {showEgpEquiv && (
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 500 }}>≈ {nfMoneyFmt(Math.round(account.egpBalance))} EGP</div>
        )}
      </div>
      {(account.income > 0 || account.expenses > 0) && (
        <div style={{ display: 'flex', gap: 8, borderTop: '1px solid var(--border)', paddingTop: 8 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 9, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>In</div>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--c-protein)' }}>+{nfMoneyFmt(account.income || 0)}</div>
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 9, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Out</div>
            <div style={{ fontSize: 12, fontWeight: 600, color: 'var(--c-carbs)' }}>−{nfMoneyFmt(account.expenses || 0)}</div>
          </div>
        </div>
      )}
    </div>
  );
}

function TxnCard({ txn, onClick, compact = false }) {
  const isTransfer = txn.kind === 'transfer';
  const isIncome   = txn.kind === 'income';
  const cat = isTransfer ? null : (NF_CATEGORIES.find(c => c.id === txn.category) || NF_CATEGORIES[0]);
  const fromAcct = isTransfer ? (NF_ACCOUNTS.find(a => a.id === txn.fromAccount) || {}) : null;
  const toAcct   = isTransfer ? (NF_ACCOUNTS.find(a => a.id === txn.toAccount)   || {}) : null;
  return (
    <button onClick={onClick} style={{
      ...nutriStyles.card, padding: 12,
      display: 'flex', alignItems: 'center', gap: 12,
      width: '100%', textAlign: 'left', cursor: 'pointer',
      color: 'var(--text)', font: 'inherit',
      transition: 'transform .12s ease',
    }}
    onMouseDown={e=>e.currentTarget.style.transform='scale(.99)'}
    onMouseUp={e=>e.currentTarget.style.transform=''}
    onMouseLeave={e=>e.currentTarget.style.transform=''}
    >
      {isTransfer ? (
        <div style={{
          width: 44, height: 44, borderRadius: 12, flexShrink: 0,
          background: 'var(--c-fat-soft)', border: '1px solid rgba(87,132,216,0.18)',
          display: 'grid', placeItems: 'center', fontSize: 20,
        }}>↔</div>
      ) : (
        <FoodThumb emoji={cat.icon} tint={cat.color} size={44}/>
      )}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
          {isTransfer ? `${fromAcct.name || 'Account'} → ${toAcct.name || 'Account'}` : txn.title}
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 2 }}>
          {isTransfer
            ? `Transfer · ${prettyDateShort(txn.date)}`
            : `${cat.name} · ${prettyDateShort(txn.date)} · ${txn.time}`}
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 2 }}>
        <div style={{
          fontSize: 15, fontWeight: 700, letterSpacing: '-0.01em',
          color: isTransfer ? 'var(--c-fat)' : isIncome ? 'var(--c-protein)' : 'var(--text)',
          whiteSpace: 'nowrap',
        }}>
          {isTransfer ? '↔ ' : (isIncome ? '+' : '−')}{nfMoneyFmt(Math.abs(txn.amount))}
        </div>
        {!isTransfer && txn.currency && txn.currency !== 'EGP' && (
          <div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '0.04em', color: 'var(--c-fat)', background: 'var(--c-fat-soft)', padding: '1px 5px', borderRadius: 4 }}>
            {txn.currency}
          </div>
        )}
      </div>
    </button>
  );
}

function prettyDateShort(iso) {
  const d = new Date(iso + 'T00:00:00');
  if (iso === todayISO()) return 'Today';
  if (iso === daysAgo(1)) return 'Yest.';
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
}

/* ── TRANSACTIONS LIST ────────────────────────────────────────────── */
function FinanceTxns({ txns, categories, accounts, onOpenTxn, onAdd }) {
  const liveCats  = categories || NF_CATEGORIES;
  const liveAccts = accounts   || NF_ACCOUNTS;

  const [filter,      setFilter]      = React.useState('all');
  const [q,           setQ]           = React.useState('');
  const [filterCat,   setFilterCat]   = React.useState('');
  const [filterAcct,  setFilterAcct]  = React.useState('');
  const [filterType,  setFilterType]  = React.useState('');
  const [showFilters, setShowFilters] = React.useState(false);

  // lookup maps for search
  const catNameMap = {}, subcatNameMap = {}, acctNameMap = {};
  liveCats.forEach(c => {
    catNameMap[c.id] = c.name;
    (c.subcategories||[]).forEach(s => { subcatNameMap[s.id] = s.name; });
  });
  liveAccts.forEach(a => { acctNameMap[a.id] = a.name; });

  const filtered = txns
    .filter(t => filter === 'all' || t.kind === filter)
    .filter(t => !filterCat  || t.category === filterCat)
    .filter(t => !filterAcct || t.account === filterAcct || t.fromAccount === filterAcct || t.toAccount === filterAcct)
    .filter(t => {
      if (!filterType) return true;
      const catObj = liveCats.find(c => c.id === t.category);
      return catObj && catObj.spendingType === filterType;
    })
    .filter(t => {
      if (!q) return true;
      const ql = q.toLowerCase();
      return [
        t.title, t.category, catNameMap[t.category],
        subcatNameMap[t.subcategory], acctNameMap[t.account],
        acctNameMap[t.fromAccount], acctNameMap[t.toAccount],
        t.notes, t.subcategory,
        (liveCats.find(c => c.id === t.category)||{}).spendingType,
      ].some(v => v && v.toLowerCase().includes(ql));
    })
    .sort((a,b) => b.date.localeCompare(a.date) || (b.time||'').localeCompare(a.time||''));

  const groups = {};
  filtered.forEach(t => { (groups[t.date] = groups[t.date]||[]).push(t); });
  const hasFilters = filterCat || filterAcct || filterType;

  return (
    <div style={{ padding:'12px 18px 24px', display:'flex', flexDirection:'column', gap:14 }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start', paddingTop:4 }}>
        <div>
          <div style={{ fontSize:22, fontWeight:700, letterSpacing:'-0.02em' }}>Transactions</div>
          <div style={{ fontSize:12.5, color:'var(--muted)', marginTop:2, fontWeight:500 }}>{filtered.length} entries</div>
        </div>
        <div style={{ display:'flex', gap:6, alignItems:'center' }}>
          <button onClick={() => setShowFilters(v => !v)} style={{
            display:'inline-flex', alignItems:'center', gap:4,
            padding:'7px 12px', borderRadius:999,
            background: (showFilters||hasFilters) ? 'var(--text)' : 'var(--surface)',
            color:       (showFilters||hasFilters) ? 'var(--bg)'   : 'var(--text)',
            border:'1px solid var(--border-2)', font:'inherit', fontSize:12.5, fontWeight:500, cursor:'pointer',
          }}>⚙{hasFilters ? ' ·' : ''}</button>
          <button onClick={onAdd} style={{
            display:'inline-flex', alignItems:'center', gap:6, padding:'7px 14px', borderRadius:999,
            background:'var(--accent)', color:'var(--on-accent)', border:'1px solid var(--accent)',
            font:'inherit', fontSize:12.5, fontWeight:600, cursor:'pointer',
          }}><PlusIcon size={14}/>Add</button>
        </div>
      </div>

      {/* search */}
      <div style={{ ...nutriStyles.card, display:'flex', alignItems:'center', gap:10, padding:'10px 14px', borderRadius:14 }}>
        <SearchIcon size={16}/>
        <input value={q} onChange={e=>setQ(e.target.value)}
          placeholder="Search title, category, subcategory, account, notes…"
          style={{ flex:1, border:0, background:'transparent', outline:'none', font:'inherit', fontSize:13.5, color:'var(--text)' }}/>
        {q && <button onClick={() => setQ('')} style={{ background:'none', border:0, cursor:'pointer', color:'var(--muted)', fontSize:15, padding:'0 4px', font:'inherit' }}>✕</button>}
      </div>

      {/* kind pills */}
      <div style={{ display:'flex', gap:6, flexWrap:'wrap' }}>
        {[{ id:'all', label:'All' },{ id:'expense', label:'Expenses' },{ id:'income', label:'Income' },{ id:'transfer', label:'Transfers' }].map(k => (
          <button key={k.id} onClick={() => setFilter(k.id)} style={{
            padding:'7px 14px', borderRadius:999,
            background: filter===k.id ? 'var(--text)' : 'var(--surface)',
            color:       filter===k.id ? 'var(--bg)'   : 'var(--text)',
            border: filter===k.id ? '1px solid var(--text)' : '1px solid var(--border-2)',
            font:'inherit', fontSize:12.5, fontWeight:500, cursor:'pointer',
          }}>{k.label}</button>
        ))}
      </div>

      {/* filter panel */}
      {showFilters && (
        <div style={{ ...nutriStyles.card, padding:14, display:'flex', flexDirection:'column', gap:12 }}>
          <div>
            <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:6 }}>Category</div>
            <div style={{ display:'flex', gap:5, flexWrap:'wrap' }}>
              {[{ id:'', name:'All', icon:'', color:'var(--text)' }, ...liveCats.filter(c => c.id !== 'income')].map(c => (
                <button key={c.id||'all'} onClick={() => setFilterCat(c.id)} style={{
                  padding:'5px 10px', borderRadius:999, font:'inherit', fontSize:12, fontWeight:500, cursor:'pointer',
                  background: filterCat===c.id ? (c.color||'var(--text)') : 'var(--surface-2)',
                  color:       filterCat===c.id ? '#fff' : 'var(--text)',
                  border:'1px solid ' + (filterCat===c.id ? (c.color||'var(--text)') : 'var(--border)'),
                }}>{c.icon ? c.icon+' ' : ''}{c.name}</button>
              ))}
            </div>
          </div>
          <div>
            <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:6 }}>Spending type</div>
            <div style={{ display:'flex', gap:5, flexWrap:'wrap' }}>
              {['','need','want','waste','investment'].map(tp => (
                <button key={tp||'all'} onClick={() => setFilterType(tp)} style={{
                  padding:'5px 10px', borderRadius:999, font:'inherit', fontSize:12, fontWeight:500, cursor:'pointer',
                  background: filterType===tp ? 'var(--text)' : 'var(--surface-2)',
                  color:       filterType===tp ? 'var(--bg)'   : 'var(--text)',
                  border:'1px solid var(--border)',
                  textTransform: tp ? 'capitalize' : 'none',
                }}>{tp || 'All types'}</button>
              ))}
            </div>
          </div>
          <div>
            <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:6 }}>Account</div>
            <div style={{ display:'flex', gap:5, flexWrap:'wrap' }}>
              {[{ id:'', name:'All', icon:'' }, ...liveAccts].map(a => (
                <button key={a.id||'all'} onClick={() => setFilterAcct(a.id)} style={{
                  padding:'5px 10px', borderRadius:999, font:'inherit', fontSize:12, fontWeight:500, cursor:'pointer',
                  background: filterAcct===a.id ? 'var(--text)' : 'var(--surface-2)',
                  color:       filterAcct===a.id ? 'var(--bg)'   : 'var(--text)',
                  border:'1px solid var(--border)',
                }}>{a.icon ? a.icon+' ' : ''}{a.name}</button>
              ))}
            </div>
          </div>
          {hasFilters && (
            <button onClick={() => { setFilterCat(''); setFilterAcct(''); setFilterType(''); }} style={{
              alignSelf:'flex-start', padding:'5px 12px', borderRadius:999,
              background:'transparent', color:'#C25A4E', border:'1px solid rgba(194,90,78,.3)',
              font:'inherit', fontSize:12, fontWeight:500, cursor:'pointer',
            }}>Clear filters</button>
          )}
        </div>
      )}

      {/* groups */}
      {Object.keys(groups).length === 0 ? (
        <EmptyState icon="📭" title="No transactions found" subtitle="Try a different filter or search term."/>
      ) : Object.entries(groups).sort((a,b) => b[0].localeCompare(a[0])).map(([date, items]) => {
        const dayTotal = items.reduce((s,t) => t.kind==='income' ? s+t.amount : t.kind==='transfer' ? s : s-Math.abs(t.amount), 0);
        return (
          <div key={date} style={{ display:'flex', flexDirection:'column', gap:8 }}>
            <div style={{ display:'flex', alignItems:'baseline', justifyContent:'space-between', padding:'0 4px' }}>
              <div style={{ fontSize:12, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase' }}>{prettyDate(date)}</div>
              <div style={{ fontSize:12, fontWeight:600, color:dayTotal >= 0 ? 'var(--c-protein)' : 'var(--text-2)' }}>
                {dayTotal >= 0 ? '+' : '−'}{nfMoneyFmt(Math.abs(dayTotal))}
              </div>
            </div>
            {items.map(t => <TxnCard key={t.id} txn={t} onClick={() => onOpenTxn(t)} compact/>)}
          </div>
        );
      })}

      <div style={{ height:60 }}/>
    </div>
  );
}

/* ── BUDGETS ──────────────────────────────────────────────────────── */
function FinanceBudgets({ txns }) {
  const spent = {};
  const cutoff = daysAgo(30);
  txns.filter(t => t.date >= cutoff && t.kind === 'expense').forEach(t => {
    spent[t.category] = (spent[t.category] || 0) + Math.abs(t.amount);
  });

  return (
    <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ paddingTop: 4 }}>
        <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Budgets</div>
        <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>This month · {NF_CURRENCY}</div>
      </div>

      <div style={{ display:'flex', flexDirection:'column', gap: 10 }}>
        {Object.entries(NF_BUDGETS).map(([catId, budget]) => {
          const cat = NF_CATEGORIES.find(c => c.id === catId);
          const used = spent[catId] || 0;
          const pct = Math.min(1, used / budget);
          const over = used > budget;
          return (
            <div key={catId} style={{ ...nutriStyles.card, padding: 14 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 10 }}>
                <FoodThumb emoji={cat.icon} tint={cat.color} size={36}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 14, fontWeight: 600 }}>{cat.name}</div>
                  <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2 }}>
                    <span style={{ color: over ? 'var(--c-carbs)' : 'var(--text-2)', fontWeight: 600 }}>{nfMoneyFmt(used)}</span> of {nfMoneyFmt(budget)}
                  </div>
                </div>
                <div style={{
                  fontSize: 13, fontWeight: 600,
                  color: over ? 'var(--c-carbs)' : pct > 0.85 ? 'var(--c-carbs)' : 'var(--muted)',
                }}>
                  {Math.round(pct*100)}%
                </div>
              </div>
              <MacroBar value={used} goal={budget} color={over ? 'var(--c-carbs)' : cat.color}/>
            </div>
          );
        })}
      </div>

      <div style={{ height: 60 }}/>
    </div>
  );
}

/* ── INSIGHTS HELPERS ─────────────────────────────────────────────── */
// Default Finance Insights filters (Part 2). Other presets are available as
// optional, user-managed filters via the Manage Filters sheet.
const _FIN_RANGE_PRESETS = [
  { id: 'month',      label: 'This Month' },
  { id: 'last_month', label: 'Last Month' },
  { id: 'year',       label: 'This Year'  },
  { id: 'custom',     label: 'Custom'     },
];
// Full catalog of built-in presets (used by the global filter manager so users
// can re-add the ones that aren't shown by default).
const _FIN_RANGE_CATALOG = [
  { id: 'today',      label: 'Today'        },
  { id: '7d',         label: 'Last 7 Days'  },
  { id: '14d',        label: 'Last 14 Days' },
  { id: '30d',        label: 'Last 30 Days' },
  { id: 'month',      label: 'This Month'   },
  { id: 'last_month', label: 'Last Month'   },
  { id: 'quarter',    label: 'This Quarter' },
  { id: 'year',       label: 'This Year'    },
  { id: 'all',        label: 'All Time'     },
];

function _niceYTicks(maxVal, targetTicks) {
  if (!maxVal || maxVal <= 0) return [0, 1];
  targetTicks = targetTicks || 6; // aim for ~6 ticks for readable scales (Part 5)
  const rough = maxVal / targetTicks;
  const mag   = Math.pow(10, Math.floor(Math.log10(rough)));
  const step  = ([1, 2, 2.5, 5, 10].find(f => f * mag >= rough) || 10) * mag;
  return Array.from({ length: Math.ceil(maxVal / step) + 1 }, (_, i) => i * step);
}
// Compact axis label: 1500 → "1.5k", 20000 → "20k", 0.5 → "0.5"
function _axisFmt(v) {
  if (v >= 1000000) return (v / 1000000).toFixed(v % 1000000 ? 1 : 0) + 'm';
  if (v >= 1000)    return (v / 1000).toFixed(v % 1000 ? 1 : 0) + 'k';
  return String(Math.round(v * 100) / 100);
}

function _computeFinanceRange(rangeId, customFrom, customTo, savedCustoms) {
  const today = todayISO();
  // Saved custom filter (id like 'cf_…') → resolve its stored from/to.
  if (rangeId && rangeId.indexOf('cf_') === 0 && savedCustoms) {
    const f = savedCustoms.find(c => c.id === rangeId);
    if (f) return { from: f.from, to: f.to };
  }
  const d = new Date(today + 'T00:00:00');
  // Use local date parts — toISOString() converts to UTC and shifts dates in UTC+N timezones.
  const fmt = dt => dt instanceof Date
    ? (dt.getFullYear() + '-' + String(dt.getMonth()+1).padStart(2,'0') + '-' + String(dt.getDate()).padStart(2,'0'))
    : String(dt);
  switch (rangeId) {
    case 'today':      return { from: today, to: today };
    case '7d':         return { from: daysAgo(6),  to: today };
    case '14d':        return { from: daysAgo(13), to: today };
    case '30d':        return { from: daysAgo(29), to: today };
    case 'month':      return { from: fmt(new Date(d.getFullYear(), d.getMonth(), 1)), to: today };
    case 'last_month': return { from: fmt(new Date(d.getFullYear(), d.getMonth()-1, 1)), to: fmt(new Date(d.getFullYear(), d.getMonth(), 0)) };
    case 'quarter':    return { from: fmt(new Date(d.getFullYear(), Math.floor(d.getMonth()/3)*3, 1)), to: today };
    case 'year':       return { from: d.getFullYear() + '-01-01', to: today };
    case 'all':        return { from: '2000-01-01', to: today };
    case 'custom':     return { from: customFrom || today, to: customTo || today };
    default:           return { from: daysAgo(29), to: today };
  }
}

function _buildChartBars(from, to, expenseTxns) {
  const fromD    = new Date(from + 'T00:00:00');
  const toD      = new Date(to   + 'T00:00:00');
  const dayCount = Math.round((toD - fromD) / 86400000) + 1;

  if (dayCount <= 14) {
    const bars = [];
    for (let i = 0; i < dayCount; i++) {
      const d   = new Date(fromD); d.setDate(fromD.getDate() + i);
      const iso = isoOf(d);
      const val = expenseTxns.filter(t => t.date === iso).reduce((s,t) => s + Math.abs(t.amount), 0);
      bars.push({ key: iso, label: ['S','M','T','W','T','F','S'][d.getDay()], value: val });
    }
    return { bars, granularity: 'daily' };
  }

  if (dayCount <= 90) {
    const wMap = {};
    expenseTxns.forEach(t => {
      const d   = new Date(t.date + 'T00:00:00');
      const dow = (d.getDay() + 6) % 7;
      const ws  = new Date(d); ws.setDate(d.getDate() - dow);
      const wk  = isoOf(ws);
      wMap[wk]  = (wMap[wk] || 0) + Math.abs(t.amount);
    });
    const bars = [];
    const startDow = (fromD.getDay() + 6) % 7;
    const first    = new Date(fromD); first.setDate(fromD.getDate() - startDow);
    for (let cur = new Date(first); isoOf(cur) <= to; cur.setDate(cur.getDate() + 7)) {
      const wk = isoOf(cur);
      bars.push({ key: wk, label: cur.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }), value: wMap[wk] || 0 });
    }
    return { bars, granularity: 'weekly' };
  }

  // Monthly
  const mMap = {};
  expenseTxns.forEach(t => { const mk = t.date.slice(0,7); mMap[mk] = (mMap[mk]||0) + Math.abs(t.amount); });
  const bars = [];
  for (
    let mc = new Date(fromD.getFullYear(), fromD.getMonth(), 1);
    mc.getFullYear() < toD.getFullYear() || (mc.getFullYear() === toD.getFullYear() && mc.getMonth() <= toD.getMonth());
    mc.setMonth(mc.getMonth() + 1)
  ) {
    const mk = mc.getFullYear() + '-' + String(mc.getMonth() + 1).padStart(2, '0');
    bars.push({ key: mk, label: mc.toLocaleDateString(undefined, { month: 'short', year: '2-digit' }), value: mMap[mk] || 0 });
  }
  return { bars, granularity: 'monthly' };
}

const _FIN_PALETTE = ['#4FA862','#E89B3C','#5784D8','#E05C6A','#9B6EC8','#3BAAD0','#D4833A','#6DBE6D','#C25A4E','#7A766C'];

function _buildIncomeMonthBars(from, to, incomeTxns) {
  const fromD = new Date(from + 'T00:00:00');
  const toD   = new Date(to   + 'T00:00:00');
  const mMap  = {};
  incomeTxns.forEach(t => { const mk = t.date.slice(0,7); mMap[mk] = (mMap[mk]||0) + t.amount; });
  const bars  = [];
  for (
    let mc = new Date(fromD.getFullYear(), fromD.getMonth(), 1);
    mc.getFullYear() < toD.getFullYear() || (mc.getFullYear() === toD.getFullYear() && mc.getMonth() <= toD.getMonth());
    mc.setMonth(mc.getMonth() + 1)
  ) {
    const mk = mc.getFullYear() + '-' + String(mc.getMonth()+1).padStart(2,'0');
    bars.push({ key: mk, label: mc.toLocaleDateString(undefined, { month:'short', year:'2-digit' }), value: mMap[mk] || 0 });
  }
  return bars;
}

/* Generic SVG donut + legend */
function _FinDonut({ data, size = 130 }) {
  const total = (data||[]).reduce((s,d) => s + (d.value||0), 0);
  if (!total) return null;
  const R = 36, SW = 18, C = 2 * Math.PI * R;
  let cum = 0;
  const slices = (data||[]).map(d => {
    const frac = d.value / total;
    const dash = frac * C;
    const sl   = { ...d, frac, dash, off: -cum };
    cum += dash;
    return sl;
  });
  return (
    <div>
      <svg viewBox="0 0 100 100" width={size} height={size} style={{ display:'block', margin:'0 auto' }}>
        <g transform="rotate(-90 50 50)">
          {slices.map((s,i) => (
            <circle key={i} cx={50} cy={50} r={R} fill="none"
              stroke={s.color} strokeWidth={SW}
              strokeDasharray={`${s.dash} ${C - s.dash}`}
              strokeDashoffset={s.off}/>
          ))}
        </g>
        <text x={50} y={47} textAnchor="middle" dominantBaseline="middle"
          fontSize="11" fontWeight="700" fill="var(--text)" fontFamily="inherit">
          {slices.length}
        </text>
        <text x={50} y={58} textAnchor="middle"
          fontSize="7" fill="var(--muted)" fontFamily="inherit">items</text>
      </svg>
      <div style={{ display:'flex', flexDirection:'column', gap:7, marginTop:6 }}>
        {slices.map((s,i) => (
          <div key={i} style={{ display:'flex', alignItems:'center', gap:8 }}>
            <div style={{ width:10, height:10, borderRadius:3, background:s.color, flexShrink:0 }}/>
            <div style={{ flex:1, fontSize:12.5, fontWeight:500, color:'var(--text)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{s.label}</div>
            <div style={{ fontSize:12, fontWeight:600, color:'var(--text)', whiteSpace:'nowrap' }}>{nfMoneyFmt(Math.round(s.value))}</div>
            <div style={{ fontSize:11, color:'var(--muted)', minWidth:34, textAlign:'right', whiteSpace:'nowrap' }}>{Math.round(s.frac*100)}%</div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ═══ GLOBAL TIME-FILTER BAR (reusable across projects) ═══════════════
   scope:        unique key per project (e.g. 'finance', 'calories')
   catalog:      [{id,label}] all selectable built-in presets
   defaultVisible: [ids] shown when the user hasn't customized
   value:        active filter id (preset id or saved-custom id 'cf_…')
   onChange(id): select a filter
   onManage():   open the manage sheet
   Reads saved config from Store (user-specific, persisted).               */
function TimeFilterBar({ scope, catalog, defaultVisible, value, onChange, onManage }) {
  const Store = window.Store;
  const cfg = (Store && Store.timeFilterConfig) ? Store.timeFilterConfig(scope) : {};
  const visibleIds = cfg.presets || defaultVisible;
  const customs = cfg.custom || [];
  const catMap = {}; catalog.forEach(p => { catMap[p.id] = p; });
  const pills = [];
  visibleIds.forEach(id => { if (catMap[id]) pills.push({ id, label: catMap[id].label }); });
  customs.forEach(c => pills.push({ id: c.id, label: c.label, custom: true }));
  return (
    <div style={{ display: 'flex', gap: 6, overflowX: 'auto', margin: '0 -18px', padding: '0 18px 4px', scrollbarWidth: 'none', WebkitOverflowScrolling: 'touch' }}>
      {pills.map(p => (
        <button key={p.id} onClick={() => onChange(p.id)} style={{
          flexShrink: 0, padding: '7px 14px', borderRadius: 999,
          background: value === p.id ? 'var(--text)' : 'var(--surface)',
          color: value === p.id ? 'var(--bg)' : 'var(--text)',
          border: value === p.id ? '1px solid var(--text)' : '1px solid var(--border-2)',
          font: 'inherit', fontSize: 12.5, fontWeight: 500, cursor: 'pointer', whiteSpace: 'nowrap',
        }}>{p.custom ? '★ ' : ''}{p.label}</button>
      ))}
      <button onClick={onManage} title="Manage filters" style={{
        flexShrink: 0, minWidth: 34, padding: '7px 9px', borderRadius: 999, display: 'grid', placeItems: 'center',
        background: 'var(--surface)', color: 'var(--muted)', border: '1px solid var(--border-2)', cursor: 'pointer', font: 'inherit', fontSize: 13,
      }}>⚙</button>
    </div>
  );
}

function ManageFiltersSheet({ scope, catalog, defaultVisible, onClose }) {
  const Store = window.Store;
  const cfg = (Store && Store.timeFilterConfig) ? Store.timeFilterConfig(scope) : {};
  const visibleIds = cfg.presets || defaultVisible;
  const customs = cfg.custom || [];
  const [editId, setEditId] = React.useState(null);
  const [label, setLabel]   = React.useState('');
  const [from, setFrom]     = React.useState(NUTRI_TODAY);
  const [to, setTo]         = React.useState(NUTRI_TODAY);
  const [adding, setAdding] = React.useState(false);

  function togglePreset(id) {
    if (id === 'custom') return; // 'Custom' range entry is always available
    const next = visibleIds.includes(id) ? visibleIds.filter(x => x !== id) : [...visibleIds, id];
    Store.setTimeFilterPresets(scope, next);
  }
  function startAdd() { setAdding(true); setEditId(null); setLabel(''); setFrom(NUTRI_TODAY); setTo(NUTRI_TODAY); }
  function startEdit(f) { setAdding(true); setEditId(f.id); setLabel(f.label); setFrom(f.from); setTo(f.to); }
  function saveCustom() {
    if (!label.trim()) return;
    if (editId) Store.updateCustomTimeFilter(scope, editId, { label: label.trim(), from, to });
    else Store.addCustomTimeFilter(scope, { id: 'cf_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 4), label: label.trim(), from, to });
    setAdding(false); setEditId(null); setLabel('');
  }

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)', animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)' }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>Manage filters</div>
        <button onClick={() => { Store.resetTimeFilters(scope); }} style={{
          background: 'none', border: 0, cursor: 'pointer', font: 'inherit', fontSize: 12, fontWeight: 600, color: 'var(--muted)' }}>Reset</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        {/* Default presets show/hide */}
        <div>
          <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', marginBottom: 8 }}>Default filters</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {catalog.map(p => {
              const on = visibleIds.includes(p.id) || p.id === 'custom';
              const locked = p.id === 'custom';
              return (
                <div key={p.id} style={{ ...nutriStyles.card, padding: '10px 12px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                  <span style={{ fontSize: 13, fontWeight: 500, color: on ? 'var(--text)' : 'var(--muted)' }}>{p.label}{locked ? ' (always on)' : ''}</span>
                  <button onClick={() => togglePreset(p.id)} disabled={locked} style={{
                    padding: '4px 12px', borderRadius: 999, font: 'inherit', fontSize: 11.5, fontWeight: 600,
                    cursor: locked ? 'default' : 'pointer', opacity: locked ? 0.5 : 1,
                    background: on ? 'var(--c-protein-soft)' : 'var(--surface-2)',
                    color: on ? 'var(--c-protein)' : 'var(--muted)',
                    border: '1px solid ' + (on ? 'rgba(79,168,98,.3)' : 'var(--border)'),
                  }}>{on ? 'Shown' : 'Hidden'}</button>
                </div>
              );
            })}
          </div>
        </div>

        {/* Saved custom filters */}
        <div>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase' }}>Saved custom filters</div>
            {!adding && <button onClick={startAdd} style={{ background: 'none', border: 0, cursor: 'pointer', font: 'inherit', fontSize: 11.5, color: 'var(--accent)', fontWeight: 600 }}>＋ Add</button>}
          </div>
          {customs.length === 0 && !adding && <div style={{ fontSize: 12, color: 'var(--muted)', fontStyle: 'italic' }}>No saved filters yet.</div>}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {customs.map(f => (
              <div key={f.id} style={{ ...nutriStyles.card, padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 8 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>★ {f.label}</div>
                  <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>{prettyDateShort(f.from)} – {prettyDateShort(f.to)}</div>
                </div>
                <button onClick={() => startEdit(f)} style={{ width: 30, height: 30, borderRadius: 8, background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)' }}><EditIcon size={13}/></button>
                <button onClick={() => Store.deleteCustomTimeFilter(scope, f.id)} style={{ width: 30, height: 30, borderRadius: 8, background: 'transparent', border: '1px solid rgba(194,90,78,.3)', display: 'grid', placeItems: 'center', cursor: 'pointer', color: '#C25A4E' }}>✕</button>
              </div>
            ))}
          </div>
          {adding && (
            <div style={{ ...nutriStyles.card, padding: 12, marginTop: 8, display: 'flex', flexDirection: 'column', gap: 10 }}>
              <input value={label} onChange={e => setLabel(e.target.value)} placeholder="Filter name (e.g. May Review)" autoFocus
                style={{ ..._fxInput }}/>
              <div style={{ display: 'flex', gap: 8 }}>
                <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 4 }}>
                  <span style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase' }}>From</span>
                  <input type="date" value={from} onChange={e => setFrom(e.target.value)} style={_fxInput}/>
                </label>
                <label style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 4 }}>
                  <span style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase' }}>To</span>
                  <input type="date" value={to} onChange={e => setTo(e.target.value)} style={_fxInput}/>
                </label>
              </div>
              <div style={{ display: 'flex', gap: 8 }}>
                <button onClick={saveCustom} disabled={!label.trim()} style={{
                  flex: 1, padding: '9px 0', borderRadius: 10, border: 0, font: 'inherit', fontSize: 13, fontWeight: 700,
                  background: label.trim() ? 'var(--accent)' : 'var(--surface-2)', color: label.trim() ? 'var(--on-accent)' : 'var(--muted)',
                  cursor: label.trim() ? 'pointer' : 'default' }}>{editId ? 'Update filter' : 'Save filter'}</button>
                <button onClick={() => { setAdding(false); setEditId(null); }} style={{
                  padding: '9px 14px', borderRadius: 10, background: 'var(--surface-2)', color: 'var(--muted)', border: '1px solid var(--border)', font: 'inherit', fontSize: 13, cursor: 'pointer' }}>Cancel</button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ── INSIGHTS SCREEN ──────────────────────────────────────────────── */
function FinanceInsights({ txns, accounts, categories, onCleanup }) {
  const liveCats  = categories || NF_CATEGORIES;
  const liveAccts = accounts   || NF_ACCOUNTS;

  const [range,       setRange]       = React.useState('month');
  const [customFrom,  setCustomFrom]  = React.useState(NUTRI_TODAY);
  const [customTo,    setCustomTo]    = React.useState(NUTRI_TODAY);
  const [chartType,   setChartType]   = React.useState('bar');
  const [showManage,  setShowManage]  = React.useState(false);

  const _tfCfg = (window.Store && window.Store.timeFilterConfig) ? window.Store.timeFilterConfig('finance') : {};
  const savedCustoms = _tfCfg.custom || [];
  const _tfCatalog = _FIN_RANGE_CATALOG.concat([{ id: 'custom', label: 'Custom' }]);
  const _tfDefault = _FIN_RANGE_PRESETS.map(p => p.id);
  const { from, to } = _computeFinanceRange(range, customFrom, customTo, savedCustoms);
  const dayCount = Math.max(1, Math.round((new Date(to+'T00:00:00') - new Date(from+'T00:00:00')) / 86400000) + 1);

  const inRange     = txns.filter(t => t.date >= from && t.date <= to);
  const expenseTxns = inRange.filter(t => t.kind === 'expense');
  const incomeTxns  = inRange.filter(t => t.kind === 'income');

  /* ── Expense stats */
  const totalExpense = expenseTxns.reduce((s,t) => s + Math.abs(t.amount), 0);
  const avgDaily     = totalExpense / dayCount;

  const dayTotals = {};
  expenseTxns.forEach(t => { dayTotals[t.date] = (dayTotals[t.date]||0) + Math.abs(t.amount); });
  const hdEntry  = Object.entries(dayTotals).sort((a,b) => b[1]-a[1])[0] || null;
  const hdDate   = hdEntry ? hdEntry[0] : null;
  const hdAmt    = hdEntry ? hdEntry[1] : 0;

  const highestExpTxn = expenseTxns.slice().sort((a,b) => Math.abs(b.amount) - Math.abs(a.amount))[0] || null;

  const catTotals = {};
  expenseTxns.forEach(t => { catTotals[t.category] = (catTotals[t.category]||0) + Math.abs(t.amount); });
  const topCatEntry = Object.entries(catTotals).sort((a,b) => b[1]-a[1])[0] || null;
  const topCatId    = topCatEntry ? topCatEntry[0] : null;
  const topCatObj   = topCatId ? (liveCats.find(c => c.id === topCatId) || null) : null;

  const subcatTotals = {};
  expenseTxns.forEach(t => { if (t.subcategory) subcatTotals[t.subcategory] = (subcatTotals[t.subcategory]||0) + Math.abs(t.amount); });
  const topSubEntry  = Object.entries(subcatTotals).sort((a,b) => b[1]-a[1])[0] || null;
  const topSubId     = topSubEntry ? topSubEntry[0] : null;
  const topSubAmt    = topSubEntry ? topSubEntry[1] : 0;
  let   topSubName   = null;
  if (topSubId) {
    for (const c of liveCats) {
      const s = (c.subcategories||[]).find(s => s.id === topSubId);
      if (s) { topSubName = s.name; break; }
    }
    if (!topSubName) topSubName = topSubId;
  }

  const typeBreakdown = { need: 0, want: 0, waste: 0, investment: 0 };
  expenseTxns.forEach(t => {
    const cat = liveCats.find(c => c.id === t.category);
    const type = cat && cat.spendingType;
    if (type && typeBreakdown.hasOwnProperty(type)) typeBreakdown[type] += Math.abs(t.amount);
  });

  const catBreakdown = Object.entries(catTotals)
    .map(([id, v]) => ({ ...(liveCats.find(c => c.id === id) || { id, name: id, color: '#7A766C', icon: '📦' }), total: v }))
    .sort((a,b) => b.total - a.total);

  /* ── Subcategory breakdown */
  const subcatBreakdown = Object.entries(subcatTotals)
    .map(([subId, v], i) => {
      let name = subId;
      for (const c of liveCats) {
        const s = (c.subcategories||[]).find(s => s.id === subId);
        if (s) { name = s.name; break; }
      }
      return { id: subId, name, value: v, color: _FIN_PALETTE[i % _FIN_PALETTE.length] };
    })
    .sort((a,b) => b.value - a.value);

  /* ── Account breakdown */
  const acctTotals = {};
  expenseTxns.forEach(t => { if (t.account) acctTotals[t.account] = (acctTotals[t.account]||0) + Math.abs(t.amount); });
  const acctBreakdown = Object.entries(acctTotals)
    .map(([acctId, v], i) => {
      const a = liveAccts.find(x => x.id === acctId);
      return { id: acctId, name: a ? (a.icon + ' ' + a.name) : acctId, value: v, color: _FIN_PALETTE[(i+3) % _FIN_PALETTE.length] };
    })
    .sort((a,b) => b.value - a.value);

  /* ── Top individual expenses */
  const topExpenses = expenseTxns.slice().sort((a,b) => Math.abs(b.amount)-Math.abs(a.amount)).slice(0, 8);

  /* ── Spending type donut data */
  const TYPE_COLORS = { need:'#5784D8', want:'#E89B3C', waste:'#C25A4E', investment:'#4FA862' };
  const spendTypeDonut = ['need','want','waste','investment']
    .map(k => ({ label: k.charAt(0).toUpperCase()+k.slice(1), value: typeBreakdown[k]||0, color: TYPE_COLORS[k] }))
    .filter(d => d.value > 0);

  /* ── Income stats */
  const totalIncome    = incomeTxns.reduce((s,t) => s + t.amount, 0);
  const highestIncTxn  = incomeTxns.slice().sort((a,b) => b.amount - a.amount)[0] || null;

  const incByMonth = {};
  incomeTxns.forEach(t => { const mk = t.date.slice(0,7); incByMonth[mk] = (incByMonth[mk]||0) + t.amount; });
  const mVals          = Object.values(incByMonth);
  const monthlyAvgInc  = mVals.length > 0 ? mVals.reduce((s,v) => s+v, 0) / mVals.length : 0;
  const bestMkEntry    = Object.entries(incByMonth).sort((a,b) => b[1]-a[1])[0] || null;
  const bestMonthLabel = bestMkEntry ? new Date(bestMkEntry[0]+'-15').toLocaleDateString(undefined,{month:'short',year:'numeric'}) : null;
  const bestMonthAmt   = bestMkEntry ? bestMkEntry[1] : 0;

  /* ── Income month bars + income type donut */
  const incMonthBars  = _buildIncomeMonthBars(from, to, incomeTxns);
  const incMonthMax   = Math.max(...incMonthBars.map(b => b.value), 1);
  const incMonthTicks = _niceYTicks(incMonthMax, 6);
  const incMonthMaxTick = incMonthTicks[incMonthTicks.length-1] || 1;
  const incSubcatTotals = {};
  incomeTxns.forEach(t => {
    const key = t.subcategory || '__other__';
    incSubcatTotals[key] = (incSubcatTotals[key]||0) + t.amount;
  });
  const incTypeCat = liveCats.find(c => c.id === 'income');
  const incSubcatNames = {};
  if (incTypeCat) (incTypeCat.subcategories||[]).forEach(s => { incSubcatNames[s.id] = s.name; });
  const incTypeDonut = Object.entries(incSubcatTotals)
    .map(([id, v], i) => ({ label: incSubcatNames[id] || (id === '__other__' ? 'Other' : id), value: v, color: _FIN_PALETTE[i % _FIN_PALETTE.length] }))
    .filter(d => d.value > 0)
    .sort((a,b) => b.value - a.value);

  /* ── Account stats (range in/out, all-time balance) */
  const acctStats  = liveAccts.map(a => {
    const ri = inRange.filter(t => t.account === a.id && t.kind === 'income').reduce((s,t) => s + (t.egpEquivalent != null ? t.egpEquivalent : t.amount), 0);
    const re = inRange.filter(t => t.account === a.id && t.kind === 'expense').reduce((s,t) => s + (t.egpEquivalent != null ? t.egpEquivalent : Math.abs(t.amount)), 0);
    return { ...a, rangeIncome: ri, rangeExpenses: re };
  });
  const totalBalance = liveAccts.reduce((s,a) => s + (a.egpBalance != null ? a.egpBalance : (a.balance||0)), 0);

  /* ── Adaptive chart */
  const { bars: chartBars, granularity: chartGran } = _buildChartBars(from, to, expenseTxns);
  const chartMax     = Math.max(...chartBars.map(b => b.value), 1);
  const chartTicks   = _niceYTicks(chartMax, 6);
  const chartMaxTick = chartTicks[chartTicks.length-1] || 1;
  const SVG_W=300, SVG_H=130, PAD_L=42, PAD_R=8, PAD_T=12, PAD_B=24;
  const CHART_W = SVG_W-PAD_L-PAD_R, CHART_H = SVG_H-PAD_T-PAD_B;
  const N_BARS  = chartBars.length;
  const SLOT_W  = CHART_W / Math.max(N_BARS, 1);
  const BAR_W   = Math.min(SLOT_W * 0.72, 28);
  const LBL_STEP = Math.max(1, Math.ceil(N_BARS / 7));

  const rangeLabel = (from === to) ? prettyDate(from) : (prettyDateShort(from) + ' – ' + prettyDateShort(to));

  function sectionHead(title) {
    return (
      <div style={{ display:'flex', alignItems:'center', gap:10, marginTop:4 }}>
        <div style={{ fontSize:14, fontWeight:700, letterSpacing:'-0.01em', color:'var(--text)' }}>{title}</div>
        <div style={{ flex:1, height:1, background:'var(--border-2)' }}/>
      </div>
    );
  }

  return (
    <div style={{ padding:'12px 18px 24px', display:'flex', flexDirection:'column', gap:14 }}>

      {/* Header */}
      <div style={{ paddingTop:4 }}>
        <div style={{ fontSize:22, fontWeight:700, letterSpacing:'-0.02em' }}>Insights</div>
        <div style={{ fontSize:12.5, color:'var(--muted)', marginTop:2, fontWeight:500 }}>{rangeLabel}</div>
      </div>

      {/* Range filter bar (managed / customizable) */}
      <TimeFilterBar scope="finance" catalog={_tfCatalog} defaultVisible={_tfDefault}
        value={range} onChange={setRange} onManage={() => setShowManage(true)}/>

      {/* Custom date inputs */}
      {range === 'custom' && (
        <div style={{ display:'flex', gap:8, alignItems:'center', flexWrap:'wrap' }}>
          <input type="date" value={customFrom} onChange={e => setCustomFrom(e.target.value)}
            style={{ flex:1, minWidth:120, border:'1px solid var(--border)', background:'var(--surface-2)', borderRadius:10, padding:'8px 10px', outline:'none', font:'inherit', fontSize:13, color:'var(--text)' }}/>
          <input type="date" value={customTo}   onChange={e => setCustomTo(e.target.value)}
            style={{ flex:1, minWidth:120, border:'1px solid var(--border)', background:'var(--surface-2)', borderRadius:10, padding:'8px 10px', outline:'none', font:'inherit', fontSize:13, color:'var(--text)' }}/>
          <button onClick={() => {
            const name = (window.prompt('Save this range as a reusable filter — name:') || '').trim();
            if (!name) return;
            const id = 'cf_' + Date.now().toString(36) + Math.random().toString(36).slice(2,4);
            window.Store.addCustomTimeFilter('finance', { id, label: name, from: customFrom, to: customTo });
            setRange(id);
          }} style={{ flexShrink:0, padding:'8px 12px', borderRadius:10, background:'var(--accent)', color:'var(--on-accent)', border:0, font:'inherit', fontSize:12.5, fontWeight:600, cursor:'pointer' }}>★ Save</button>
        </div>
      )}

      {inRange.length === 0 ? (
        <EmptyState icon="📊" title="No data for this period" subtitle="Try a wider time range or add transactions first."/>
      ) : (
        <>
          {/* ═══════════ EXPENSES ═══════════ */}
          {expenseTxns.length > 0 && (<>
            {sectionHead('Expenses')}

            {/* Key stats */}
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
              <FinStatTile label="Total"        value={nfMoneyFmt(Math.round(totalExpense))} color="var(--c-carbs)"   sign="−"/>
              <FinStatTile label="Avg / day"    value={nfMoneyFmt(Math.round(avgDaily))}      color="var(--text)"    sign=""/>
              <FinStatTile label="Highest day"  value={hdDate ? prettyDateShort(hdDate) : '—'}
                sub={hdAmt ? nfMoneyFmt(Math.round(hdAmt)) + ' ' + NF_CURRENCY : ''}         color="var(--text)"    sign=""/>
              <FinStatTile label="Entries"      value={String(expenseTxns.length)}             color="var(--text)"    sign=""/>
            </div>

            {/* Top category + subcategory */}
            {topCatObj && (
              <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
                <FinStatTile label="Top category"    value={topCatObj.name}
                  sub={nfMoneyFmt(catTotals[topCatId]) + ' ' + NF_CURRENCY}
                  color={topCatObj.color} sign={topCatObj.icon + ' '}/>
                {topSubName
                  ? <FinStatTile label="Top subcategory" value={topSubName}
                      sub={nfMoneyFmt(Math.round(topSubAmt)) + ' ' + NF_CURRENCY}
                      color="var(--muted)" sign=""/>
                  : <FinStatTile label="Avg / txn"
                      value={nfMoneyFmt(Math.round(totalExpense / Math.max(expenseTxns.length, 1)))}
                      sub={NF_CURRENCY} color="var(--text)" sign=""/>
                }
              </div>
            )}

            {/* Largest expense */}
            {highestExpTxn && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:8 }}>Largest expense</div>
                <TxnCard txn={highestExpTxn} onClick={() => {}} compact/>
              </div>
            )}

            {/* Spending type breakdown */}
            {totalExpense > 0 && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:10 }}>Spending type</div>
                <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:8 }}>
                  {[
                    { key:'need',       label:'Need',       color:'#5784D8' },
                    { key:'want',       label:'Want',       color:'#E89B3C' },
                    { key:'waste',      label:'Waste',      color:'#C25A4E' },
                    { key:'investment', label:'Investment', color:'#4FA862' },
                  ].map(t => {
                    const amt = typeBreakdown[t.key] || 0;
                    return (
                      <div key={t.key} style={{ padding:10, borderRadius:12, background:'var(--surface-2)', border:'1px solid var(--border)' }}>
                        <div style={{ fontSize:10, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase' }}>{t.label}</div>
                        <div style={{ fontSize:16, fontWeight:600, color:t.color, marginTop:4, letterSpacing:'-0.01em' }}>{nfMoneyFmt(Math.round(amt))}</div>
                        <div style={{ fontSize:10, color:'var(--muted)', marginTop:2 }}>{totalExpense > 0 ? Math.round(amt/totalExpense*100) : 0}%</div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Adaptive expense trend chart */}
            {chartBars.length > 0 && (() => {
              const pts = chartBars.map((b,i) => ({
                x: PAD_L + i*SLOT_W + SLOT_W/2,
                y: PAD_T + (1 - Math.max(b.value,0)/chartMaxTick)*CHART_H,
              }));
              const lineD = pts.map((p,i) => (i===0?`M${p.x},${p.y}`:`L${p.x},${p.y}`)).join(' ');
              const areaD = pts.length > 0 ? lineD + ` L${pts[pts.length-1].x},${PAD_T+CHART_H} L${pts[0].x},${PAD_T+CHART_H} Z` : '';
              return (
                <div style={{ ...nutriStyles.card, padding:14 }}>
                  <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
                    <div>
                      <div style={{ fontSize:13, fontWeight:600 }}>Expense trend</div>
                      <div style={{ fontSize:10.5, color:'var(--muted)' }}>
                        {chartGran==='daily'?'Daily':chartGran==='weekly'?'Weekly':'Monthly'}
                      </div>
                    </div>
                    <div style={{ display:'flex', gap:4, background:'var(--surface-2)', borderRadius:8, padding:3, border:'1px solid var(--border)' }}>
                      {['bar','line','area'].map(ct => (
                        <button key={ct} onClick={() => setChartType(ct)} style={{
                          padding:'4px 9px', borderRadius:6, border:0, cursor:'pointer', font:'inherit',
                          fontSize:10.5, fontWeight:600, textTransform:'capitalize',
                          background: chartType===ct ? 'var(--surface)' : 'transparent',
                          color: chartType===ct ? 'var(--text)' : 'var(--muted)',
                          boxShadow: chartType===ct ? 'var(--shadow-sm)' : 'none',
                        }}>{ct}</button>
                      ))}
                    </div>
                  </div>
                  <svg width="100%" viewBox={`0 0 ${SVG_W} ${SVG_H}`} style={{ display:'block', overflow:'visible' }}>
                    {/* Grid + Y-axis */}
                    {chartTicks.map(v => {
                      const gy  = PAD_T + (1 - v/chartMaxTick)*CHART_H;
                      const lbl = v>=1000000?(v/1000000).toFixed(1)+'M':v>=1000?(v>=10000?Math.round(v/1000):(v/1000).toFixed(1))+'k':String(v);
                      return (
                        <g key={v}>
                          <line x1={PAD_L} x2={SVG_W-PAD_R} y1={gy} y2={gy}
                            stroke="var(--border)" strokeWidth="0.8" strokeDasharray="3 3"/>
                          <text x={PAD_L-4} y={gy+3.5} textAnchor="end"
                            fontSize="9" fill="var(--muted)" fontFamily="inherit">{lbl}</text>
                        </g>
                      );
                    })}
                    {/* Bars */}
                    {chartType==='bar' && chartBars.map((b,i) => {
                      const bH = Math.max((b.value/chartMaxTick)*CHART_H, b.value>0?2:0);
                      const bx = PAD_L + i*SLOT_W + (SLOT_W-BAR_W)/2;
                      return (
                        <g key={b.key}>
                          <rect x={bx} y={PAD_T+CHART_H-bH} width={BAR_W} height={bH} rx={2}
                            fill={b.value>0?'var(--text)':'var(--ring-track)'}/>
                          {i%LBL_STEP===0 && (
                            <text x={PAD_L+i*SLOT_W+SLOT_W/2} y={SVG_H-6}
                              textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>
                          )}
                        </g>
                      );
                    })}
                    {/* Line */}
                    {chartType==='line' && <>
                      <path d={lineD} fill="none" stroke="var(--text)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
                      {pts.map((p,i) => (
                        <circle key={i} cx={p.x} cy={p.y} r="3" fill="var(--surface)" stroke="var(--text)" strokeWidth="1.5"/>
                      ))}
                      {chartBars.map((b,i) => i%LBL_STEP===0 && (
                        <text key={b.key} x={PAD_L+i*SLOT_W+SLOT_W/2} y={SVG_H-6}
                          textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>
                      ))}
                    </>}
                    {/* Area */}
                    {chartType==='area' && <>
                      <path d={areaD} fill="var(--text)" fillOpacity="0.12"/>
                      <path d={lineD} fill="none" stroke="var(--text)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
                      {chartBars.map((b,i) => i%LBL_STEP===0 && (
                        <text key={b.key} x={PAD_L+i*SLOT_W+SLOT_W/2} y={SVG_H-6}
                          textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>
                      ))}
                    </>}
                  </svg>
                </div>
              );
            })()}

            {/* By category — bar breakdown (names, amounts, %) */}
            <div style={{ ...nutriStyles.card, padding:14 }}>
              <div style={{ fontSize:13, fontWeight:600, marginBottom:12 }}>By category</div>
              {catBreakdown.length > 0 && totalExpense > 0
                ? <_FinBars items={catBreakdown.map(c => ({ key:c.id, label:`${c.icon} ${c.name}`, value:c.total, color:c.color }))} total={totalExpense}/>
                : <div style={{ fontSize:12.5, color:'var(--muted)', textAlign:'center', padding:'8px 0' }}>No category data for this range.</div>}
            </div>

            {/* By subcategory — bar breakdown */}
            <div style={{ ...nutriStyles.card, padding:14 }}>
              <div style={{ fontSize:13, fontWeight:600, marginBottom:12 }}>By subcategory</div>
              {subcatBreakdown.length > 0
                ? <_FinBars items={subcatBreakdown.map(s => ({ key: s.id, label: s.name, value: s.value, color: s.color }))} total={totalExpense}/>
                : <div style={{ fontSize:12.5, color:'var(--muted)', textAlign:'center', padding:'8px 0' }}>No subcategory data for this range.</div>}
            </div>

            {/* Spending type donut */}
            {spendTypeDonut.length > 0 && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:12 }}>Need · Want · Waste · Investment</div>
                <_FinDonut data={spendTypeDonut} size={130}/>
              </div>
            )}

            {/* Top expenses horizontal bar */}
            {topExpenses.length > 0 && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:10 }}>Top expenses</div>
                <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
                  {topExpenses.map((t,i) => {
                    const pct = totalExpense > 0 ? Math.abs(t.amount)/totalExpense : 0;
                    const catObj = liveCats.find(c => c.id === t.category) || {};
                    return (
                      <div key={t.id||i}>
                        <div style={{ display:'flex', justifyContent:'space-between', marginBottom:3 }}>
                          <div style={{ fontSize:12.5, fontWeight:500, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxWidth:'60%' }}>{t.title || catObj.name}</div>
                          <div style={{ fontSize:12.5, fontWeight:600 }}>{nfMoneyFmt(Math.abs(t.amount))}</div>
                        </div>
                        <div style={{ height:6, borderRadius:999, background:'var(--ring-track)', overflow:'hidden' }}>
                          <div style={{ height:'100%', width:`${pct*100}%`, background:catObj.color||'var(--text)', borderRadius:999 }}/>
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}

            {/* Account/payment donut */}
            {acctBreakdown.length > 0 && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:12 }}>By account</div>
                <_FinDonut data={acctBreakdown} size={130}/>
              </div>
            )}
          </>)}

          {/* ═══════════ INCOME ═══════════ */}
          {incomeTxns.length > 0 && (<>
            {sectionHead('Income')}
            <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:10 }}>
              <FinStatTile label="Total"        value={nfMoneyFmt(Math.round(totalIncome))}     color="var(--c-protein)" sign="+"/>
              <FinStatTile label="Monthly avg"  value={nfMoneyFmt(Math.round(monthlyAvgInc))}   color="var(--text)"      sign=""/>
              {bestMonthLabel
                ? <FinStatTile label="Best month"  value={bestMonthLabel}
                    sub={nfMoneyFmt(bestMonthAmt) + ' ' + NF_CURRENCY}
                    color="var(--c-protein)" sign=""/>
                : null}
              <FinStatTile   label="Entries"    value={String(incomeTxns.length)}               color="var(--text)"      sign=""/>
            </div>
            {highestIncTxn && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:8 }}>Highest income</div>
                <TxnCard txn={highestIncTxn} onClick={() => {}} compact/>
              </div>
            )}

            {/* Monthly income trend */}
            {incMonthBars.length > 0 && (() => {
              const IW=300, IH=130, IPL=42, IPR=8, IPT=12, IPB=24;
              const ICW=IW-IPL-IPR, ICH=IH-IPT-IPB;
              const NI=incMonthBars.length;
              const ISW=ICW/Math.max(NI,1), IBW=Math.min(ISW*0.72,28);
              const ILS=Math.max(1,Math.ceil(NI/7));
              const ipts = incMonthBars.map((b,i) => ({ x:IPL+i*ISW+ISW/2, y:IPT+(1-Math.max(b.value,0)/incMonthMaxTick)*ICH }));
              const ilineD = ipts.map((p,i) => (i===0?`M${p.x},${p.y}`:`L${p.x},${p.y}`)).join(' ');
              const iareaD = ipts.length>0 ? ilineD+` L${ipts[ipts.length-1].x},${IPT+ICH} L${ipts[0].x},${IPT+ICH} Z` : '';
              return (
                <div style={{ ...nutriStyles.card, padding:14 }}>
                  <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:10 }}>
                    <div style={{ fontSize:13, fontWeight:600 }}>Monthly income</div>
                    <div style={{ display:'flex', gap:4, background:'var(--surface-2)', borderRadius:8, padding:3, border:'1px solid var(--border)' }}>
                      {['bar','line','area'].map(ct => (
                        <button key={ct} onClick={() => setChartType(ct)} style={{
                          padding:'4px 9px', borderRadius:6, border:0, cursor:'pointer', font:'inherit',
                          fontSize:10.5, fontWeight:600, textTransform:'capitalize',
                          background: chartType===ct ? 'var(--surface)' : 'transparent',
                          color: chartType===ct ? 'var(--text)' : 'var(--muted)',
                          boxShadow: chartType===ct ? 'var(--shadow-sm)' : 'none',
                        }}>{ct}</button>
                      ))}
                    </div>
                  </div>
                  <svg width="100%" viewBox={`0 0 ${IW} ${IH}`} style={{ display:'block', overflow:'visible' }}>
                    {incMonthTicks.map(v => {
                      const gy=IPT+(1-v/incMonthMaxTick)*ICH;
                      const lbl=v>=1000000?(v/1000000).toFixed(1)+'M':v>=1000?(v>=10000?Math.round(v/1000):(v/1000).toFixed(1))+'k':String(v);
                      return (
                        <g key={v}>
                          <line x1={IPL} x2={IW-IPR} y1={gy} y2={gy} stroke="var(--border)" strokeWidth="0.8" strokeDasharray="3 3"/>
                          <text x={IPL-4} y={gy+3.5} textAnchor="end" fontSize="9" fill="var(--muted)" fontFamily="inherit">{lbl}</text>
                        </g>
                      );
                    })}
                    {chartType==='bar' && incMonthBars.map((b,i) => {
                      const bH=Math.max((b.value/incMonthMaxTick)*ICH,b.value>0?2:0);
                      const bx=IPL+i*ISW+(ISW-IBW)/2;
                      return (
                        <g key={b.key}>
                          <rect x={bx} y={IPT+ICH-bH} width={IBW} height={bH} rx={2} fill="var(--c-protein)"/>
                          {i%ILS===0 && <text x={IPL+i*ISW+ISW/2} y={IH-6} textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>}
                        </g>
                      );
                    })}
                    {chartType==='line' && <>
                      <path d={ilineD} fill="none" stroke="var(--c-protein)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
                      {ipts.map((p,i) => <circle key={i} cx={p.x} cy={p.y} r="3" fill="var(--surface)" stroke="var(--c-protein)" strokeWidth="1.5"/>)}
                      {incMonthBars.map((b,i) => i%ILS===0 && <text key={b.key} x={IPL+i*ISW+ISW/2} y={IH-6} textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>)}
                    </>}
                    {chartType==='area' && <>
                      <path d={iareaD} fill="var(--c-protein)" fillOpacity="0.15"/>
                      <path d={ilineD} fill="none" stroke="var(--c-protein)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round"/>
                      {incMonthBars.map((b,i) => i%ILS===0 && <text key={b.key} x={IPL+i*ISW+ISW/2} y={IH-6} textAnchor="middle" fontSize="9" fill="var(--muted)" fontFamily="inherit">{b.label}</text>)}
                    </>}
                  </svg>
                </div>
              );
            })()}

            {/* Income type donut */}
            {incTypeDonut.length > 0 && (
              <div style={{ ...nutriStyles.card, padding:14 }}>
                <div style={{ fontSize:13, fontWeight:600, marginBottom:12 }}>Income by type</div>
                <_FinDonut data={incTypeDonut} size={130}/>
              </div>
            )}
          </>)}

          {/* ═══════════ ACCOUNTS ═══════════ */}
          {sectionHead('Accounts')}
          <div style={{ ...nutriStyles.card, padding:14 }}>
            <div style={{ fontSize:11, color:'var(--muted)', fontWeight:600, letterSpacing:'0.04em', textTransform:'uppercase', marginBottom:4 }}>Total balance</div>
            <div style={{ fontSize:30, fontWeight:700, letterSpacing:'-0.03em', lineHeight:1.1 }}>
              {nfMoneyFmt(totalBalance)}
              <span style={{ fontSize:12, color:'var(--muted)', fontWeight:500, marginLeft:6 }}>{NF_CURRENCY}</span>
            </div>
          </div>
          <div style={{ ...nutriStyles.card, padding:'14px 14px 6px' }}>
            {acctStats.map((a, i) => (
              <div key={a.id} style={{
                display:'flex', alignItems:'center', gap:12,
                padding:'10px 0',
                borderBottom: i < acctStats.length-1 ? '1px solid var(--border)' : 'none',
              }}>
                <div style={{ fontSize:20, flexShrink:0 }}>{a.icon}</div>
                <div style={{ flex:1, minWidth:0 }}>
                  <div style={{ fontSize:13.5, fontWeight:600 }}>{a.name}</div>
                  <div style={{ fontSize:11, color:'var(--muted)', marginTop:1 }}>
                    {(a.rangeIncome > 0 || a.rangeExpenses > 0)
                      ? `+${nfMoneyFmt(a.rangeIncome)} · −${nfMoneyFmt(a.rangeExpenses)}`
                      : 'No activity this period'}
                  </div>
                </div>
                <div style={{ textAlign:'right' }}>
                  <div style={{ fontSize:15, fontWeight:700, color:(a.balance||0)<0?'var(--c-carbs)':'var(--text)' }}>
                    {nfMoneyFmt(a.balance||0)}
                  </div>
                  <div style={{ fontSize:9.5, color:'var(--muted)' }}>{a.currency || NF_CURRENCY}</div>
                  {a.currency && a.currency !== 'EGP' && a.egpBalance != null && (
                    <div style={{ fontSize:9, color:'var(--muted-2)' }}>≈ {nfMoneyFmt(Math.round(a.egpBalance))} EGP</div>
                  )}
                </div>
              </div>
            ))}
          </div>
        </>
      )}

      {onCleanup && (
        <div style={{ marginTop: 18, paddingTop: 14, borderTop: '1px solid var(--border)' }}>
          <button onClick={onCleanup} style={{
            display: 'inline-flex', alignItems: 'center', gap: 6, padding: '9px 14px', borderRadius: 10,
            background: 'transparent', color: '#C2554E', border: '1px solid rgba(194,85,78,0.3)',
            fontSize: 12.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
          }}>🧹 Clean up finance data…</button>
          <div style={{ fontSize: 11, color: 'var(--muted-2)', marginTop: 6 }}>Delete transactions by day, month, or range. Accounts, categories &amp; projects are kept.</div>
        </div>
      )}

      <div style={{ height:60 }}/>

      {showManage && (
        <div style={{ position:'fixed', inset:0, zIndex:200 }}>
          <ManageFiltersSheet scope="finance" catalog={_tfCatalog} defaultVisible={_tfDefault}
            onClose={() => setShowManage(false)}/>
        </div>
      )}
    </div>
  );
}

/* ── ADD TRANSACTION sheet ────────────────────────────────────────── */
// Reusable inline "＋ Add" control for quick-creating a managed resource from a form.
function _InlineAdd({ ctaLabel, placeholder, onSubmit }) {
  const [open, setOpen] = React.useState(false);
  const [val, setVal] = React.useState('');
  function submit() {
    const n = val.trim();
    if (!n) return;
    onSubmit(n);
    setVal(''); setOpen(false);
  }
  if (!open) {
    return (
      <button onClick={() => { setOpen(true); setVal(''); }} style={{
        background: 'none', border: 0, cursor: 'pointer', font: 'inherit',
        fontSize: 11, color: 'var(--accent)', fontWeight: 600, padding: '4px 0', alignSelf: 'flex-start',
      }}>＋ {ctaLabel}</button>
    );
  }
  return (
    <div style={{ display: 'flex', gap: 6, marginTop: 2 }}>
      <input autoFocus value={val} onChange={e => setVal(e.target.value)}
        onKeyDown={e => { if (e.key === 'Enter') submit(); if (e.key === 'Escape') setOpen(false); }}
        placeholder={placeholder || 'Name…'}
        style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '6px 10px', font: 'inherit', fontSize: 12.5, outline: 'none', color: 'var(--text)' }}/>
      <button onClick={submit} disabled={!val.trim()} style={{
        padding: '6px 12px', borderRadius: 8,
        background: val.trim() ? 'var(--accent)' : 'var(--surface-2)',
        color: val.trim() ? 'var(--on-accent)' : 'var(--muted)',
        border: 0, font: 'inherit', fontSize: 12, fontWeight: 600, cursor: val.trim() ? 'pointer' : 'default',
      }}>Add</button>
      <button onClick={() => setOpen(false)} style={{
        padding: '6px 10px', borderRadius: 8, background: 'var(--surface-2)', color: 'var(--muted)',
        border: '1px solid var(--border)', font: 'inherit', fontSize: 12, cursor: 'pointer',
      }}>✕</button>
    </div>
  );
}

/* Inline "create project" form for the expense sheet — name + optional notes/budget/start
   date, without leaving the form (so unsaved expense data is preserved). */
function _ProjectQuickAdd({ onSubmit }) {
  const [open, setOpen] = React.useState(false);
  const [name, setName] = React.useState('');
  const [notes, setNotes] = React.useState('');
  const [budget, setBudget] = React.useState('');
  const [startDate, setStartDate] = React.useState('');
  const fld = { width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '7px 10px', font: 'inherit', fontSize: 12.5, outline: 'none', color: 'var(--text)', boxSizing: 'border-box' };
  function submit() {
    const n = name.trim(); if (!n) return;
    onSubmit({ name: n, notes: notes.trim(), budget: budget !== '' ? +budget : undefined, startDate: startDate || undefined });
    setName(''); setNotes(''); setBudget(''); setStartDate(''); setOpen(false);
  }
  if (!open) {
    return (
      <button type="button" onClick={() => setOpen(true)} style={{
        background: 'none', border: 0, cursor: 'pointer', font: 'inherit',
        fontSize: 11.5, color: 'var(--accent)', fontWeight: 600, padding: '4px 0',
      }}>＋ Add project</button>
    );
  }
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: 10, background: 'var(--surface-2)', border: '1px solid var(--border)', borderRadius: 10 }}>
      <input autoFocus value={name} onChange={e => setName(e.target.value)} placeholder="Project name *"
        onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); submit(); } }} style={fld}/>
      <input value={notes} onChange={e => setNotes(e.target.value)} placeholder="Notes (optional)" style={fld}/>
      <div style={{ display: 'flex', gap: 6 }}>
        <input type="number" inputMode="decimal" value={budget} onChange={e => setBudget(e.target.value)} placeholder="Budget (optional)" style={fld}/>
        <input type="date" value={startDate} onChange={e => setStartDate(e.target.value)} title="Start date (optional)" style={fld}/>
      </div>
      <div style={{ display: 'flex', gap: 6 }}>
        <button type="button" onClick={submit} disabled={!name.trim()} style={{
          flex: 1, padding: '7px 12px', borderRadius: 8,
          background: name.trim() ? 'var(--accent)' : 'var(--surface)', color: name.trim() ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (name.trim() ? 'var(--accent)' : 'var(--border)'), font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: name.trim() ? 'pointer' : 'default',
        }}>Create &amp; select</button>
        <button type="button" onClick={() => setOpen(false)} style={{
          padding: '7px 12px', borderRadius: 8, background: 'var(--surface)', color: 'var(--muted)',
          border: '1px solid var(--border)', font: 'inherit', fontSize: 12.5, cursor: 'pointer',
        }}>Cancel</button>
      </div>
    </div>
  );
}

function FinanceAddSheet({ onClose, onSave, editTxn, categories: catsProp, accounts: acctsProp, onAddIncomeSource, usdRate, projects, onQuickAddCategory, onQuickAddSubcategory, onQuickAddAccount, onQuickAddProject }) {
  const liveCats  = catsProp  || NF_CATEGORIES;
  const liveAccts = acctsProp || NF_ACCOUNTS;
  const liveProjects = (projects || []).filter(p => (p.status || 'active') !== 'archived');
  const ed = editTxn || null;   // when present, this sheet EDITS an existing txn instead of adding

  const [kind, setKind]     = React.useState(ed ? (ed.kind === 'income' ? 'income' : 'expense') : 'expense');
  const [projectIds, setProjectIds] = React.useState(ed ? (ed.projectIds || []) : []);
  const [amount, setAmount] = React.useState(ed ? String(Math.abs(ed.amount)) : '');
  const [title, setTitle]   = React.useState(ed ? (ed.title || '') : '');
  const [cat, setCat]       = React.useState(() => {
    if (ed) return ed.category || 'food';
    const first = liveCats.find(c => c.id !== 'income');
    return first ? first.id : 'food';
  });
  const [subcat, setSubcat] = React.useState(ed ? (ed.subcategory || '') : '');
  const [acct, setAcct]     = React.useState(ed ? (ed.account || (liveAccts[0] || {}).id || 'cash') : ((liveAccts[0] || {}).id || 'cash'));
  const [date, setDate]     = React.useState(ed ? (ed.date || NUTRI_TODAY) : NUTRI_TODAY);
  const [notes, setNotes]   = React.useState(ed ? (ed.notes || '') : '');
  const [currency, setCurrency] = React.useState(() => {
    if (ed) return ed.currency || 'EGP';
    const a = liveAccts[0];
    return (a && a.currency) || 'EGP';
  });
  const [txnRate, setTxnRate] = React.useState(ed && ed.exchangeRate && ed.exchangeRate !== 1 ? String(ed.exchangeRate) : '');
  // Skips the auto-adjust effects below on the FIRST render so pre-filled edit values survive mount.
  const _initRef = React.useRef(false);
  const [showNewSource, setShowNewSource] = React.useState(false);
  const [newSourceName, setNewSourceName] = React.useState('');

  const CURRENCIES = ['EGP', 'USD', 'EUR', 'GBP', 'SAR', 'AED'];
  const isForeign = currency !== 'EGP';
  const numAmount = parseFloat(amount) || 0;
  const numRate   = parseFloat(txnRate) || 1;
  const egpEquiv  = isForeign && numAmount > 0 ? +(numAmount * numRate).toFixed(2) : numAmount;

  function handleAddSource() {
    const n = newSourceName.trim();
    if (!n || !onAddIncomeSource) return;
    onAddIncomeSource(n, (newId) => {
      setSubcat(newId);
      setShowNewSource(false);
      setNewSourceName('');
    });
  }

  React.useEffect(() => {
    if (!_initRef.current) return;
    if (kind === 'income' && cat !== 'income') {
      setCat('income');
      setSubcat('');
    }
    if (kind !== 'income' && cat === 'income') {
      const first = liveCats.find(c => c.id !== 'income');
      setCat(first ? first.id : 'food');
      setSubcat('');
    }
  }, [kind]);

  // Reset subcategory when category changes
  React.useEffect(() => { if (!_initRef.current) return; setSubcat(''); }, [cat]);

  // Sync currency with selected account
  React.useEffect(() => {
    if (!_initRef.current) return;
    const acctObj = liveAccts.find(a => a.id === acct);
    if (acctObj && acctObj.currency) setCurrency(acctObj.currency);
  }, [acct]);

  // Pre-fill exchange rate when switching to a known currency
  React.useEffect(() => {
    if (!_initRef.current) return;
    if (currency === 'USD' && usdRate) setTxnRate(String(usdRate.toFixed(2)));
    else if (currency === 'EGP') setTxnRate('');
  }, [currency]);

  // Runs LAST on mount (defined after the effects above) → flips the guard on so user-driven
  // changes still auto-adjust, while the initial pre-filled edit values are never wiped.
  React.useEffect(() => { _initRef.current = true; }, []);

  const activeCatObj = liveCats.find(c => c.id === cat);
  const subcatOptions = (activeCatObj && activeCatObj.subcategories) || [];

  const canSave = !!parseFloat(amount);

  function submit() {
    if (!canSave) return;
    onSave({
      id: ed ? ed.id : undefined,   // present ⇒ update existing txn (no duplicate)
      kind, amount, title, cat, subcategory: subcat, acct, date, notes,
      currency,
      projectIds: projectIds.length ? projectIds : undefined,
      exchangeRate: isForeign ? numRate : undefined,
      egpEquivalent: isForeign ? egpEquiv : undefined,
    });
  }

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display:'flex', flexDirection:'column', height:'100%', background:'var(--bg)',
      paddingTop: 'var(--safe-top)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{ed ? (kind === 'income' ? 'Edit income' : 'Edit expense') : 'New transaction'}</div>
        <button onClick={submit} disabled={!canSave} style={{
          background: canSave ? 'var(--accent)' : 'var(--surface-2)',
          color: canSave ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default', font: 'inherit',
        }}>{ed ? 'Save changes' : 'Save'}</button>
      </div>

      <div style={{ flex: 1, overflow: 'auto', padding: '12px 16px 60px' }}>
        <div style={{
          display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4,
          background: 'var(--surface-2)', borderRadius: 12, padding: 4, border: '1px solid var(--border)',
          marginBottom: 16,
        }}>
          {[
            { k: 'expense', label: 'Expense' },
            { k: 'income',  label: 'Income'  },
          ].map(t => (
            <button key={t.k} onClick={()=>setKind(t.k)} style={{
              padding: '7px 0', borderRadius: 9, border: 0,
              background: kind===t.k ? 'var(--surface)' : 'transparent',
              color: kind===t.k ? 'var(--text)' : 'var(--muted)',
              fontSize: 12, fontWeight: 600, cursor: 'pointer', font: 'inherit',
              boxShadow: kind===t.k ? 'var(--shadow-sm)' : 'none',
              gridColumn: 'span 1',
            }}>{t.label}</button>
          ))}
        </div>

        <div style={{ textAlign: 'center', padding: '16px 0 20px' }}>
          <input
            type="number" inputMode="decimal" placeholder="0"
            value={amount} onChange={e=>setAmount(e.target.value)}
            style={{
              border: 0, background: 'transparent', outline: 'none',
              font: 'inherit',
              fontSize: 44, fontWeight: 600, letterSpacing: '-0.04em',
              textAlign: 'center', width: '100%', color: 'var(--text)',
            }}
          />
          <div style={{ display: 'flex', gap: 6, justifyContent: 'center', flexWrap: 'wrap', marginTop: 8 }}>
            {CURRENCIES.map(c => (
              <button key={c} onClick={() => setCurrency(c)} style={{
                padding: '4px 10px', borderRadius: 999, font: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer',
                background: currency === c ? 'var(--text)' : 'var(--surface-2)',
                color:       currency === c ? 'var(--bg)'  : 'var(--muted)',
                border: '1px solid ' + (currency === c ? 'var(--text)' : 'var(--border)'),
              }}>{c}</button>
            ))}
          </div>
        </div>

        <div style={{ ...nutriStyles.card, padding: 14, display:'flex', flexDirection:'column', gap: 12 }}>
          {/* Exchange rate row (foreign currency only) */}
          {isForeign && (
            <div>
              <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>
                Exchange rate <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}>(1 {currency} = ? EGP)</span>
              </div>
              <input type="number" inputMode="decimal" value={txnRate} onChange={e => setTxnRate(e.target.value)} placeholder="e.g. 47.50"
                style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
              {numAmount > 0 && (
                <div style={{ marginTop: 8, padding: '8px 12px', borderRadius: 10, background: 'var(--c-fat-soft)', border: '1px solid rgba(87,132,216,0.2)', fontSize: 12.5, color: 'var(--text)' }}>
                  EGP equivalent: <strong>{nfMoneyFmt(egpEquiv)} EGP</strong>
                </div>
              )}
            </div>
          )}

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Title</span>
            <input value={title} onChange={e=>setTitle(e.target.value)} placeholder="e.g. Coffee at Costa" style={{
              width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
              borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)',
            }}/>
          </label>

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Date</span>
            <input type="date" value={date} onChange={e=>setDate(e.target.value)} style={{
              width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
              borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)',
            }}/>
          </label>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Category</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {liveCats.filter(c => kind === 'income' ? c.id === 'income' : c.id !== 'income').map(c => (
                <button key={c.id} onClick={()=>setCat(c.id)} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderRadius: 999,
                  background: cat === c.id ? 'var(--text)' : 'var(--surface-2)',
                  color: cat === c.id ? 'var(--bg)' : 'var(--text)',
                  border: cat === c.id ? '1px solid var(--text)' : '1px solid var(--border)',
                  font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
                }}>{c.icon} {c.name}</button>
              ))}
            </div>
            {kind !== 'income' && onQuickAddCategory && (
              <div style={{ marginTop: 8 }}>
                <_InlineAdd ctaLabel="New category" placeholder="Category name…" onSubmit={n => onQuickAddCategory(n, id => setCat(id))}/>
              </div>
            )}
          </div>

          {(subcatOptions.length > 0 || kind === 'income' || (kind !== 'income' && !!onQuickAddSubcategory)) && (
            <div>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
                <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>
                  {kind === 'income' ? 'Income Source' : 'Subcategory'} <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}>(optional)</span>
                </div>
                {kind === 'income' && !showNewSource && (
                  <button onClick={() => { setShowNewSource(true); setNewSourceName(''); }} style={{
                    background: 'none', border: 0, cursor: 'pointer', font: 'inherit',
                    fontSize: 11, color: 'var(--accent)', fontWeight: 600, padding: '2px 0',
                  }}>＋ New source</button>
                )}
              </div>
              {subcatOptions.length > 0 && (
                <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                  {subcatOptions.map(s => (
                    <button key={s.id} onClick={() => setSubcat(subcat === s.id ? '' : s.id)} style={{
                      display: 'inline-flex', alignItems: 'center', gap: 5, padding: '5px 10px', borderRadius: 999,
                      background: subcat === s.id ? 'var(--text)' : 'var(--surface-3)',
                      color: subcat === s.id ? 'var(--bg)' : 'var(--muted)',
                      border: subcat === s.id ? '1px solid var(--text)' : '1px solid var(--border)',
                      font: 'inherit', fontSize: 11.5, fontWeight: 500, cursor: 'pointer',
                    }}>{s.name}</button>
                  ))}
                </div>
              )}
              {kind === 'income' && subcatOptions.length === 0 && !showNewSource && (
                <div style={{ fontSize: 12, color: 'var(--muted)', fontStyle: 'italic' }}>No sources yet — tap ＋ New source to add one.</div>
              )}
              {kind === 'income' && showNewSource && (
                <div style={{ display: 'flex', gap: 6, marginTop: 8 }}>
                  <input autoFocus value={newSourceName} onChange={e=>setNewSourceName(e.target.value)}
                    onKeyDown={e => { if (e.key === 'Enter') handleAddSource(); if (e.key === 'Escape') setShowNewSource(false); }}
                    placeholder="Source name…"
                    style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '6px 10px', font: 'inherit', fontSize: 12.5, outline: 'none', color: 'var(--text)' }}/>
                  <button onClick={handleAddSource} disabled={!newSourceName.trim()} style={{
                    padding: '6px 12px', borderRadius: 8,
                    background: newSourceName.trim() ? 'var(--accent)' : 'var(--surface-2)',
                    color: newSourceName.trim() ? 'var(--on-accent)' : 'var(--muted)',
                    border: 0, font: 'inherit', fontSize: 12, fontWeight: 600, cursor: newSourceName.trim() ? 'pointer' : 'default',
                  }}>Add</button>
                  <button onClick={() => setShowNewSource(false)} style={{
                    padding: '6px 10px', borderRadius: 8, background: 'var(--surface-2)',
                    color: 'var(--muted)', border: '1px solid var(--border)', font: 'inherit', fontSize: 12, cursor: 'pointer',
                  }}>✕</button>
                </div>
              )}
              {kind !== 'income' && onQuickAddSubcategory && (
                <div style={{ marginTop: 8 }}>
                  <_InlineAdd ctaLabel="New subcategory" placeholder="Subcategory name…" onSubmit={n => onQuickAddSubcategory(cat, n, id => setSubcat(id))}/>
                </div>
              )}
            </div>
          )}

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Account</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {liveAccts.map(a => (
                <button key={a.id} onClick={()=>setAcct(a.id)} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderRadius: 999,
                  background: acct === a.id ? 'var(--text)' : 'var(--surface-2)',
                  color: acct === a.id ? 'var(--bg)' : 'var(--text)',
                  border: acct === a.id ? '1px solid var(--text)' : '1px solid var(--border)',
                  font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
                }}>{a.icon} {a.name}</button>
              ))}
            </div>
            {onQuickAddAccount && (
              <div style={{ marginTop: 8 }}>
                <_InlineAdd ctaLabel="New account" placeholder="Account name…" onSubmit={n => onQuickAddAccount(n, id => setAcct(id))}/>
              </div>
            )}
          </div>

          {(liveProjects.length > 0 || onQuickAddProject) && (
            <div>
              <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>
                Projects <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}>(optional · choose any number)</span>
              </div>
              {liveProjects.length > 0 && (
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                  {liveProjects.map(p => {
                    const on = projectIds.indexOf(p.id) >= 0;
                    return (
                      <button key={p.id} type="button"
                        onClick={() => setProjectIds(ids => on ? ids.filter(x => x !== p.id) : [...ids, p.id])}
                        style={{
                          display: 'inline-flex', alignItems: 'center', gap: 5, padding: '6px 11px', borderRadius: 999,
                          background: on ? 'var(--accent)' : 'var(--surface-2)', color: on ? 'var(--on-accent)' : 'var(--text)',
                          border: '1px solid ' + (on ? 'var(--accent)' : 'var(--border-2)'),
                          fontSize: 12.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
                        }}>
                        {on ? '✓ ' : ''}{p.name}
                      </button>
                    );
                  })}
                </div>
              )}
              {onQuickAddProject && (
                <div style={{ marginTop: 8 }}>
                  <_ProjectQuickAdd onSubmit={(payload) => onQuickAddProject(payload, id => setProjectIds(ids => ids.indexOf(id) >= 0 ? ids : [...ids, id]))}/>
                </div>
              )}
            </div>
          )}

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Notes (optional)</span>
            <textarea value={notes} onChange={e=>setNotes(e.target.value)} rows={2} placeholder="Optional" style={{
              width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
              borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 13, color: 'var(--text)', resize: 'vertical',
            }}/>
          </label>
        </div>
      </div>
    </div>
  );
}

/* ── TRANSFER ADD SHEET ───────────────────────────────────────────── */
function TransferAddSheet({ onClose, onSave, editTxn, accounts: acctsProp }) {
  const liveAccts = (acctsProp || NF_ACCOUNTS).filter(a => !a.archived);
  const ed = editTxn || null;   // when present, EDIT this transfer instead of adding
  const [date,   setDate]   = React.useState(ed ? (ed.date || NUTRI_TODAY) : NUTRI_TODAY);
  const [fromId, setFromId] = React.useState(ed ? (ed.fromAccount || (liveAccts[0] || {}).id || '') : ((liveAccts[0] || {}).id || ''));
  const [toId,   setToId]   = React.useState(ed ? (ed.toAccount || (liveAccts[1] || liveAccts[0] || {}).id || '') : ((liveAccts[1] || liveAccts[0] || {}).id || ''));
  const [amount, setAmount] = React.useState(ed ? String(Math.abs(ed.amount)) : '');
  const [rate,   setRate]   = React.useState(ed && ed.exchangeRate ? String(ed.exchangeRate) : '1');
  const [notes,  setNotes]  = React.useState(ed ? (ed.notes || '') : '');

  const fromAcct  = liveAccts.find(a => a.id === fromId) || {};
  const toAcct    = liveAccts.find(a => a.id === toId)   || {};
  const fromCur   = fromAcct.currency || NF_CURRENCY;
  const toCur     = toAcct.currency   || NF_CURRENCY;
  const isCross   = fromId && toId && fromCur !== toCur;
  const numAmount = parseFloat(amount) || 0;
  const numRate   = parseFloat(rate)   || 1;
  const converted = isCross ? +(numAmount * numRate).toFixed(4) : numAmount;

  const canSave = numAmount > 0 && fromId && toId && fromId !== toId;

  // If fromId changes to equal toId, pick another account for toId
  React.useEffect(() => {
    if (fromId === toId) {
      const other = liveAccts.find(a => a.id !== fromId);
      if (other) setToId(other.id);
    }
  }, [fromId]);

  function submit() {
    if (!canSave) return;
    onSave({
      id: ed ? ed.id : undefined,   // present ⇒ update existing transfer (no duplicate)
      fromAccount: fromId,
      toAccount: toId,
      amount: numAmount,
      date,
      currency: fromCur,
      toCurrency: toCur,
      exchangeRate: isCross ? numRate : undefined,
      convertedAmount: converted,
      notes,
    });
  }

  const pillStyle = (selected) => ({
    display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderRadius: 999,
    background: selected ? 'var(--text)' : 'var(--surface-2)',
    color: selected ? 'var(--bg)' : 'var(--text)',
    border: '1px solid ' + (selected ? 'var(--text)' : 'var(--border)'),
    font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
  });

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg)',
      paddingTop: 'var(--safe-top)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{ed ? 'Edit transfer' : 'Transfer'}</div>
        <button onClick={submit} disabled={!canSave} style={{
          background: canSave ? 'var(--accent)' : 'var(--surface-2)',
          color: canSave ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default', font: 'inherit',
        }}>{ed ? 'Save changes' : 'Save'}</button>
      </div>

      <div style={{ flex: 1, overflow: 'auto', padding: '12px 16px 60px' }}>
        {/* Amount */}
        <div style={{ textAlign: 'center', padding: '16px 0 20px' }}>
          <input
            type="number" inputMode="decimal" placeholder="0"
            value={amount} onChange={e => setAmount(e.target.value)}
            style={{
              border: 0, background: 'transparent', outline: 'none', font: 'inherit',
              fontSize: 44, fontWeight: 600, letterSpacing: '-0.04em',
              textAlign: 'center', width: '100%', color: 'var(--text)',
            }}
          />
          <div style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 600, marginTop: -2, letterSpacing: '0.06em' }}>{fromCur}</div>
        </div>

        <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 14 }}>
          {/* Date */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Date</span>
            <input type="date" value={date} onChange={e => setDate(e.target.value)} style={{
              width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
              borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)',
            }}/>
          </label>

          {/* From */}
          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>From account</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {liveAccts.map(a => (
                <button key={a.id} onClick={() => setFromId(a.id)} style={pillStyle(fromId === a.id)}>
                  {a.icon} {a.name}{a.currency && a.currency !== NF_CURRENCY ? ` (${a.currency})` : ''}
                </button>
              ))}
            </div>
          </div>

          {/* To */}
          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>To account</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {liveAccts.filter(a => a.id !== fromId).map(a => (
                <button key={a.id} onClick={() => setToId(a.id)} style={pillStyle(toId === a.id)}>
                  {a.icon} {a.name}{a.currency && a.currency !== NF_CURRENCY ? ` (${a.currency})` : ''}
                </button>
              ))}
            </div>
          </div>

          {/* Cross-currency: exchange rate */}
          {isCross && (
            <div>
              <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>
                Exchange rate <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}>(1 {fromCur} = ? {toCur})</span>
              </div>
              <input type="number" inputMode="decimal" value={rate} onChange={e => setRate(e.target.value)} placeholder="1"
                style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
            </div>
          )}

          {/* Converted preview */}
          {isCross && numAmount > 0 && (
            <div style={{
              padding: '10px 14px', borderRadius: 12,
              background: 'var(--c-fat-soft)', border: '1px solid rgba(87,132,216,0.2)',
              fontSize: 13, fontWeight: 500, color: 'var(--text)',
              display: 'flex', alignItems: 'center', gap: 8,
            }}>
              <span style={{ color: 'var(--muted)' }}>Recipient gets:</span>
              <span style={{ fontWeight: 700 }}>{nfMoneyFmt(converted)} {toCur}</span>
            </div>
          )}

          {/* Same-currency summary */}
          {!isCross && numAmount > 0 && fromId && toId && fromId !== toId && (
            <div style={{
              padding: '10px 14px', borderRadius: 12,
              background: 'var(--surface-2)', border: '1px solid var(--border)',
              fontSize: 13, color: 'var(--muted)', display: 'flex', alignItems: 'center', gap: 6,
            }}>
              <span>↔</span>
              <span>{fromAcct.name} → {toAcct.name} · {nfMoneyFmt(numAmount)} {fromCur}</span>
            </div>
          )}

          {/* Notes */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Notes (optional)</span>
            <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2} placeholder="Optional"
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 13, color: 'var(--text)', resize: 'vertical' }}/>
          </label>
        </div>
      </div>
    </div>
  );
}

/* ── TRANSACTION DETAIL sheet ─────────────────────────────────────── */
function TxnDetailSheet({ txn, onClose, onDelete, onEdit }) {
  if (!txn) return null;
  // Fixing Data records carry special balance-adjustment metadata — don't offer normal edit.
  const canEdit = !!onEdit && !txn.fixingData;
  const isTransfer = txn.kind === 'transfer';
  const isIncome   = txn.kind === 'income';
  const cat  = isTransfer ? null : (NF_CATEGORIES.find(c => c.id === txn.category) || NF_CATEGORIES[0]);
  const acct = isTransfer ? null : (NF_ACCOUNTS.find(a => a.id === txn.account) || NF_ACCOUNTS[0] || {});
  const fromAcct = isTransfer ? (NF_ACCOUNTS.find(a => a.id === txn.fromAccount) || {}) : null;
  const toAcct   = isTransfer ? (NF_ACCOUNTS.find(a => a.id === txn.toAccount)   || {}) : null;
  const isCross  = isTransfer && txn.exchangeRate && txn.exchangeRate !== 1 && txn.toCurrency && txn.toCurrency !== txn.currency;
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 30,
      display: 'flex', flexDirection: 'column',
      background: 'var(--bg)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
      paddingTop: 'var(--safe-top)',
    }}>
      <div style={{
        position: 'relative', height: 180,
        background: isTransfer
          ? 'linear-gradient(135deg, rgba(87,132,216,0.25), rgba(87,132,216,0.55))'
          : `linear-gradient(135deg, ${cat.color}55, ${cat.color}aa)`,
        display: 'grid', placeItems: 'center', fontSize: 64, lineHeight: 1,
        flexShrink: 0,
      }}>
        <button onClick={onClose} style={{
          position: 'absolute', top: 14, left: 14,
          width: 36, height: 36, borderRadius: '50%',
          border: '1px solid rgba(0,0,0,0.1)', background: 'rgba(255,255,255,0.9)',
          color: '#14140F', display: 'grid', placeItems: 'center', cursor: 'pointer',
        }}><XIcon size={18}/></button>
        <span style={{ filter: 'drop-shadow(0 6px 16px rgba(20,20,15,0.18))' }}>
          {isTransfer ? '↔' : cat.icon}
        </span>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '16px 18px 80px' }}>
        <div style={{ textAlign: 'center', marginBottom: 6 }}>
          <div style={{ fontSize: 34, fontWeight: 600, letterSpacing: '-0.03em',
            color: isTransfer ? 'var(--c-fat)' : isIncome ? 'var(--c-protein)' : 'var(--text)' }}>
            {isTransfer ? '↔ ' : (isIncome ? '+' : '−')}{nfMoneyFmt(Math.abs(txn.amount))}
          </div>
          <div style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 600, marginTop: -2 }}>
            {isTransfer ? (txn.currency || NF_CURRENCY) : (txn.currency || NF_CURRENCY)}
          </div>
          {!isTransfer && txn.currency && txn.currency !== 'EGP' && txn.egpEquivalent != null && (
            <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 4 }}>
              ≈ {nfMoneyFmt(Math.round(txn.egpEquivalent))} EGP
            </div>
          )}
        </div>
        <div style={{ ...nutriStyles.card, padding: 16, marginTop: 14 }}>
          {isTransfer ? (
            <>
              <DetailRow label="From"  value={`${fromAcct.icon || ''} ${fromAcct.name || 'Account'}`}/>
              <DetailRow label="To"    value={`${toAcct.icon || ''} ${toAcct.name || 'Account'}`}/>
              <DetailRow label="Amount" value={`${nfMoneyFmt(txn.amount)} ${txn.currency || NF_CURRENCY}`}/>
              {isCross && <DetailRow label="Rate" value={`1 ${txn.currency} = ${txn.exchangeRate} ${txn.toCurrency}`}/>}
              {isCross && <DetailRow label="Received" value={`${nfMoneyFmt(txn.convertedAmount || txn.amount)} ${txn.toCurrency}`}/>}
              <DetailRow label="Date"  value={prettyDate(txn.date)}/>
              <DetailRow label="Type"  value="Transfer" last/>
            </>
          ) : (
            <>
              <DetailRow label="Title"    value={txn.title}/>
              <DetailRow label="Category" value={cat.name}/>
              <DetailRow label="Account"  value={acct.name + ' ' + (acct.icon||'')}/>
              <DetailRow label="Amount"   value={`${nfMoneyFmt(Math.abs(txn.amount))} ${txn.currency || 'EGP'}`}/>
              {txn.currency && txn.currency !== 'EGP' && txn.egpEquivalent != null && (
                <DetailRow label="EGP equiv." value={`≈ ${nfMoneyFmt(Math.round(txn.egpEquivalent))} EGP`}/>
              )}
              {txn.currency && txn.currency !== 'EGP' && txn.exchangeRate && (
                <DetailRow label="Rate" value={`1 ${txn.currency} = ${txn.exchangeRate} EGP`}/>
              )}
              <DetailRow label="Date"     value={prettyDate(txn.date)}/>
              {txn.time && <DetailRow label="Time" value={txn.time}/>}
              <DetailRow label="Type"     value={txn.kind} last/>
            </>
          )}
        </div>
        {txn.notes && (
          <div style={{ ...nutriStyles.card, padding: 14, marginTop: 12 }}>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Notes</div>
            <div style={{ fontSize: 13.5, lineHeight: 1.5 }}>{txn.notes}</div>
          </div>
        )}
        {canEdit && (
          <button onClick={() => onEdit(txn)} style={{
            width: '100%', marginTop: 18, padding: '12px', borderRadius: 999,
            background: 'var(--accent)', color: 'var(--on-accent)', border: '1px solid var(--accent)',
            fontSize: 13.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
          }}><span style={{ fontSize: 15 }}>✏️</span> Edit</button>
        )}
        <div style={{ display: 'flex', gap: 10, marginTop: canEdit ? 10 : 18 }}>
          <button onClick={onDelete} style={{
            flex: 1, padding: '12px', borderRadius: 999,
            background: 'transparent', color: '#A33',
            border: '1px solid rgba(170,51,51,.35)',
            fontSize: 13.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
          }}>Delete</button>
          <button onClick={onClose} style={{
            flex: 1, padding: '12px', borderRadius: 999,
            background: 'var(--text)', color: 'var(--bg)',
            border: '1px solid var(--text)',
            fontSize: 13.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
          }}>Done</button>
        </div>
      </div>
    </div>
  );
}

function DetailRow({ label, value, last }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between',
      padding: '10px 0',
      borderBottom: last ? 'none' : '1px solid var(--border)',
    }}>
      <div style={{ fontSize: 12, color: 'var(--muted)', textTransform: 'capitalize' }}>{label}</div>
      <div style={{ fontSize: 13.5, fontWeight: 600 }}>{value}</div>
    </div>
  );
}

/* ── GOLD PRICE WIDGET ────────────────────────────────────────────── */
function GoldPriceWidget({ prices, updatedAt, refreshing, error, onRefresh, onManualSave }) {
  const [showManual, setShowManual] = React.useState(false);
  const [selectedKarat, setSelectedKarat] = React.useState('21k');
  const [manualPrice, setManualPrice] = React.useState('');

  React.useEffect(() => {
    if (prices && prices[selectedKarat]) setManualPrice(String(Math.round(prices[selectedKarat])));
  }, [selectedKarat, prices]);

  const updatedDisplay = updatedAt ? new Date(updatedAt).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : null;

  return (
    <div style={{ ...nutriStyles.card, padding: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 10.5, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>🥇 Gold Prices / gram</div>
          {prices && Object.keys(prices).length > 0 ? (
            <div style={{ marginTop: 6, display: 'flex', flexDirection: 'column', gap: 3 }}>
              {GOLD_KARATS.filter(k => prices[k.id]).map(k => (
                <div key={k.id} style={{ fontSize: 13.5, fontWeight: 600, letterSpacing: '-0.01em' }}>
                  {k.label}: <span style={{ color: '#E89B3C' }}>{nfMoneyFmt(Math.round(prices[k.id]))}</span>
                  <span style={{ fontSize: 11, fontWeight: 400, color: 'var(--muted)', marginLeft: 4 }}>EGP/g</span>
                </div>
              ))}
            </div>
          ) : (
            <div style={{ fontSize: 13, color: 'var(--muted)', marginTop: 3 }}>Prices not set — tap Refresh or enter manually</div>
          )}
          {updatedDisplay && (
            <div style={{ fontSize: 10, color: 'var(--muted-2)', marginTop: 4 }}>Last updated: {updatedDisplay}</div>
          )}
        </div>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexShrink: 0 }}>
          <button onClick={onRefresh} disabled={refreshing} style={{
            padding: '6px 12px', borderRadius: 999,
            background: '#E89B3C', color: '#fff',
            border: 0, font: 'inherit', fontSize: 12, fontWeight: 600,
            cursor: refreshing ? 'default' : 'pointer', opacity: refreshing ? 0.6 : 1,
            whiteSpace: 'nowrap',
          }}>{refreshing ? '…' : '⟳ Refresh'}</button>
          <button onClick={() => setShowManual(v => !v)} style={{
            padding: '6px 10px', borderRadius: 999,
            background: showManual ? 'var(--text)' : 'var(--surface-2)',
            color: showManual ? 'var(--bg)' : 'var(--muted)',
            border: '1px solid var(--border)', font: 'inherit', fontSize: 12, cursor: 'pointer',
            whiteSpace: 'nowrap',
          }}>Manual</button>
        </div>
      </div>
      {error && (
        <div style={{ fontSize: 11.5, color: '#C25A4E', padding: '6px 0', borderTop: '1px solid var(--border)', marginTop: 8 }}>
          ⚠ {error}
        </div>
      )}
      {showManual && (
        <div style={{ marginTop: 10, borderTop: '1px solid var(--border)', paddingTop: 10, display: 'flex', flexDirection: 'column', gap: 8 }}>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {GOLD_KARATS.map(k => (
              <button key={k.id} onClick={() => setSelectedKarat(k.id)} style={{
                padding: '5px 10px', borderRadius: 999, font: 'inherit', fontSize: 11.5, fontWeight: 600, cursor: 'pointer',
                background: selectedKarat === k.id ? 'var(--text)' : 'var(--surface-2)',
                color: selectedKarat === k.id ? 'var(--bg)' : 'var(--muted)',
                border: '1px solid ' + (selectedKarat === k.id ? 'var(--text)' : 'var(--border)'),
              }}>{k.label}</button>
            ))}
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
            <input
              type="number" inputMode="decimal"
              value={manualPrice} onChange={e => setManualPrice(e.target.value)}
              placeholder="Price per gram (EGP)"
              style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '7px 10px', font: 'inherit', fontSize: 13, outline: 'none', color: 'var(--text)' }}
            />
            <button onClick={() => {
              const p = parseFloat(manualPrice);
              if (p > 0) { onManualSave && onManualSave(selectedKarat, p); setShowManual(false); }
            }} style={{
              padding: '7px 14px', borderRadius: 8,
              background: parseFloat(manualPrice) > 0 ? '#E89B3C' : 'var(--surface-2)',
              color: parseFloat(manualPrice) > 0 ? '#fff' : 'var(--muted)',
              border: 0, font: 'inherit', fontSize: 12.5, fontWeight: 600,
              cursor: parseFloat(manualPrice) > 0 ? 'pointer' : 'default',
            }}>Set {selectedKarat}</button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ── GOLD STATS CARD ──────────────────────────────────────────────── */
function GoldStatsCard({ holdings, goldPrices }) {
  const stats = goldTotalStats(holdings, goldPrices);
  const plPositive = stats.profitLoss != null && stats.profitLoss >= 0;
  return (
    <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
      <div style={{ fontSize: 13, fontWeight: 600 }}>Gold Summary</div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Total weight</div>
          <div style={{ fontSize: 17, fontWeight: 700, marginTop: 3, letterSpacing: '-0.02em' }}>
            {stats.totalWeight.toFixed(1)}<span style={{ fontSize: 11, fontWeight: 500, color: 'var(--muted)', marginLeft: 3 }}>g</span>
          </div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Current value</div>
          <div style={{ fontSize: 17, fontWeight: 700, marginTop: 3, letterSpacing: '-0.02em', color: '#E89B3C' }}>
            {stats.totalCurrentValue != null ? nfMoneyFmt(Math.round(stats.totalCurrentValue)) : '—'}
            <span style={{ fontSize: 11, fontWeight: 500, color: 'var(--muted)', marginLeft: 3 }}>EGP</span>
          </div>
        </div>
        {stats.totalPurchaseValue != null && (
          <div>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Purchase value</div>
            <div style={{ fontSize: 16, fontWeight: 600, marginTop: 3 }}>
              {nfMoneyFmt(Math.round(stats.totalPurchaseValue))} <span style={{ fontSize: 11, color: 'var(--muted)' }}>EGP</span>
            </div>
          </div>
        )}
        {stats.profitLoss != null && (
          <div>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Profit / Loss</div>
            <div style={{ fontSize: 16, fontWeight: 700, marginTop: 3, color: plPositive ? 'var(--c-protein)' : '#C25A4E' }}>
              {plPositive ? '+' : ''}{nfMoneyFmt(Math.round(stats.profitLoss))} <span style={{ fontSize: 11, fontWeight: 500, color: 'var(--muted)' }}>EGP</span>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

/* ── GOLD HOLDING CARD ────────────────────────────────────────────── */
function GoldHoldingCard({ holding, goldPrices, onEdit }) {
  const currentValue = goldHoldingCurrentValue(holding, goldPrices);
  const purchaseValue = goldHoldingPurchaseValue(holding);
  const pl = (currentValue != null && purchaseValue != null) ? currentValue - purchaseValue : null;
  const plPositive = pl != null && pl >= 0;
  const pricePerGram = goldPriceForKarat(goldPrices, holding.karat);

  return (
    <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 8 }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 38, height: 38, borderRadius: 10, background: 'rgba(232,155,60,0.15)', display: 'grid', placeItems: 'center', fontSize: 20, flexShrink: 0 }}>🥇</div>
          <div>
            <div style={{ fontSize: 14, fontWeight: 700 }}>{holding.karat || '?'} Gold</div>
            <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 1 }}>
              {(holding.weightGrams || 0).toFixed(2)} g · {holding.date || ''}
            </div>
          </div>
        </div>
        {onEdit && (
          <button onClick={onEdit} style={{
            width: 30, height: 30, borderRadius: 8,
            background: 'var(--surface-2)', border: '1px solid var(--border-2)',
            display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)', flexShrink: 0,
          }}><EditIcon size={13}/></button>
        )}
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, paddingTop: 6, borderTop: '1px solid var(--border)' }}>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Current value</div>
          <div style={{ fontSize: 15, fontWeight: 700, marginTop: 2, color: '#E89B3C' }}>
            {currentValue != null ? nfMoneyFmt(Math.round(currentValue)) : '—'} <span style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 400 }}>EGP</span>
          </div>
          {pricePerGram && (
            <div style={{ fontSize: 10, color: 'var(--muted-2)', marginTop: 1 }}>{nfMoneyFmt(Math.round(pricePerGram))} EGP/g</div>
          )}
        </div>
        {purchaseValue != null ? (
          <div>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Purchase</div>
            <div style={{ fontSize: 15, fontWeight: 600, marginTop: 2 }}>{nfMoneyFmt(Math.round(purchaseValue))} <span style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 400 }}>EGP</span></div>
            {pl != null && (
              <div style={{ fontSize: 10.5, fontWeight: 700, color: plPositive ? 'var(--c-protein)' : '#C25A4E', marginTop: 2 }}>
                {plPositive ? '▲' : '▼'} {nfMoneyFmt(Math.abs(Math.round(pl)))} EGP
              </div>
            )}
          </div>
        ) : (
          <div>
            <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Purchase</div>
            <div style={{ fontSize: 12, color: 'var(--muted-2)', marginTop: 2 }}>Not recorded</div>
          </div>
        )}
      </div>
      {holding.notes ? (
        <div style={{ fontSize: 11.5, color: 'var(--muted)', borderTop: '1px solid var(--border)', paddingTop: 6 }}>{holding.notes}</div>
      ) : null}
    </div>
  );
}

/* ── GOLD ADD / EDIT SHEET ────────────────────────────────────────── */
function GoldAddEditSheet({ holding, goldPrices, onClose, onSave, onDelete }) {
  const isNew = !holding;
  const [date, setDate]     = React.useState(holding ? (holding.date || NUTRI_TODAY) : NUTRI_TODAY);
  const [karat, setKarat]   = React.useState(holding ? (holding.karat || '21k') : '21k');
  const [weight, setWeight] = React.useState(holding ? String(holding.weightGrams || '') : '');
  const [buyPricePerGram, setBuyPricePerGram] = React.useState(holding ? String(holding.purchasePricePerGram || '') : '');
  const [notes, setNotes]   = React.useState(holding ? (holding.notes || '') : '');

  const numWeight   = parseFloat(weight) || 0;
  const numBuyPpg   = parseFloat(buyPricePerGram) || 0;
  const totalBuy    = numWeight > 0 && numBuyPpg > 0 ? numWeight * numBuyPpg : null;
  const currentPpg  = goldPriceForKarat(goldPrices, karat);
  const currentVal  = currentPpg && numWeight > 0 ? currentPpg * numWeight : null;
  const pl          = currentVal != null && totalBuy != null ? currentVal - totalBuy : null;
  const canSave     = numWeight > 0;

  function submit() {
    if (!canSave) return;
    onSave({
      id: holding ? holding.id : null,
      date,
      karat,
      weightGrams: numWeight,
      purchasePricePerGram: numBuyPpg > 0 ? numBuyPpg : undefined,
      totalPurchaseValue:   totalBuy != null ? +totalBuy.toFixed(2) : undefined,
      notes: notes.trim() || undefined,
    });
  }

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg)',
      paddingTop: 'var(--safe-top)', animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{isNew ? 'Add gold holding' : 'Edit gold holding'}</div>
        <button onClick={submit} disabled={!canSave} style={{
          background: canSave ? 'var(--accent)' : 'var(--surface-2)',
          color: canSave ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px', fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default', font: 'inherit',
        }}>Save</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {/* Karat selector */}
          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 8 }}>Karat / Type</div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {GOLD_KARATS.map(k => (
                <button key={k.id} onClick={() => setKarat(k.id)} style={{
                  padding: '7px 14px', borderRadius: 999, font: 'inherit', fontSize: 12.5, fontWeight: 700, cursor: 'pointer',
                  background: karat === k.id ? '#E89B3C' : 'var(--surface-2)',
                  color: karat === k.id ? '#fff' : 'var(--muted)',
                  border: '1px solid ' + (karat === k.id ? '#E89B3C' : 'var(--border)'),
                }}>{k.label}</button>
              ))}
            </div>
          </div>

          {/* Date */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Date</span>
            <input type="date" value={date} onChange={e => setDate(e.target.value)} style={finInputStyle}/>
          </label>

          {/* Weight */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Weight (grams)</span>
            <input type="number" inputMode="decimal" value={weight} onChange={e => setWeight(e.target.value)} placeholder="e.g. 21.5" style={finInputStyle}/>
          </label>

          {/* Purchase price per gram */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Purchase price / gram (EGP) — optional</span>
            <input type="number" inputMode="decimal" value={buyPricePerGram} onChange={e => setBuyPricePerGram(e.target.value)} placeholder="e.g. 3800" style={finInputStyle}/>
          </label>

          {/* Purchase total (auto-computed) */}
          {totalBuy != null && (
            <div style={{ padding: '8px 12px', borderRadius: 10, background: 'var(--surface-2)', border: '1px solid var(--border)', fontSize: 12.5 }}>
              Total purchase value: <strong>{nfMoneyFmt(Math.round(totalBuy))} EGP</strong>
            </div>
          )}

          {/* Current value preview */}
          {currentVal != null && numWeight > 0 && (
            <div style={{ padding: '8px 12px', borderRadius: 10, background: 'rgba(232,155,60,0.1)', border: '1px solid rgba(232,155,60,0.3)', fontSize: 12.5 }}>
              Current value at {nfMoneyFmt(Math.round(currentPpg))} EGP/g:
              <strong style={{ color: '#E89B3C', marginLeft: 4 }}>{nfMoneyFmt(Math.round(currentVal))} EGP</strong>
              {pl != null && (
                <span style={{ marginLeft: 8, fontWeight: 700, color: pl >= 0 ? 'var(--c-protein)' : '#C25A4E' }}>
                  {pl >= 0 ? '▲ +' : '▼ '}{nfMoneyFmt(Math.abs(Math.round(pl)))} EGP
                </span>
              )}
            </div>
          )}

          {/* Notes */}
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Notes (optional)</span>
            <textarea value={notes} onChange={e => setNotes(e.target.value)} placeholder="e.g. Inherited, gift, jewelry…" rows={2}
              style={{ ...finInputStyle, resize: 'vertical', minHeight: 60 }}/>
          </label>
        </div>

        {!isNew && onDelete && (
          <button onClick={onDelete} style={{
            width: '100%', padding: '12px', borderRadius: 999,
            background: 'transparent', color: '#A33', border: '1px solid rgba(170,51,51,.35)',
            fontSize: 13.5, fontWeight: 600, cursor: 'pointer', font: 'inherit',
          }}>Delete holding</button>
        )}
      </div>
    </div>
  );
}

const finInputStyle = {
  width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
  borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)',
};

/* ── USD/EGP RATE WIDGET ──────────────────────────────────────────── */
function UsdEgpRateWidget({ rate, updatedAt, refreshing, error, onRefresh, onManualSave }) {
  const [showManual, setShowManual] = React.useState(false);
  const [manualRate, setManualRate] = React.useState(rate ? String(rate.toFixed(2)) : '');
  React.useEffect(() => {
    if (rate && !manualRate) setManualRate(String(rate.toFixed(2)));
  }, [rate]);

  const rateDisplay = rate ? rate.toFixed(2) : null;
  const updatedDisplay = updatedAt ? new Date(updatedAt).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : null;

  return (
    <div style={{ ...nutriStyles.card, padding: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 10.5, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>USD / EGP Rate</div>
          {rateDisplay ? (
            <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.01em', marginTop: 3 }}>
              1 USD = {rateDisplay} <span style={{ fontSize: 12, fontWeight: 500, color: 'var(--muted)' }}>EGP</span>
            </div>
          ) : (
            <div style={{ fontSize: 13, color: 'var(--muted)', marginTop: 3 }}>Rate not set — tap Refresh or enter manually</div>
          )}
          {updatedDisplay && (
            <div style={{ fontSize: 10, color: 'var(--muted-2)', marginTop: 2 }}>Last updated: {updatedDisplay}</div>
          )}
        </div>
        <div style={{ display: 'flex', gap: 6, alignItems: 'center', flexShrink: 0 }}>
          <button onClick={onRefresh} disabled={refreshing} style={{
            padding: '6px 12px', borderRadius: 999,
            background: 'var(--accent)', color: 'var(--on-accent)',
            border: 0, font: 'inherit', fontSize: 12, fontWeight: 600,
            cursor: refreshing ? 'default' : 'pointer', opacity: refreshing ? 0.6 : 1,
            whiteSpace: 'nowrap',
          }}>{refreshing ? '…' : '⟳ Refresh'}</button>
          <button onClick={() => setShowManual(v => !v)} style={{
            padding: '6px 10px', borderRadius: 999,
            background: showManual ? 'var(--text)' : 'var(--surface-2)',
            color: showManual ? 'var(--bg)' : 'var(--muted)',
            border: '1px solid var(--border)', font: 'inherit', fontSize: 12, cursor: 'pointer',
            whiteSpace: 'nowrap',
          }}>Manual</button>
        </div>
      </div>
      {error && (
        <div style={{ fontSize: 11.5, color: '#C25A4E', padding: '6px 0', borderTop: '1px solid var(--border)', marginTop: 8 }}>
          ⚠ {error}
        </div>
      )}
      {showManual && (
        <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 10, borderTop: '1px solid var(--border)', paddingTop: 10 }}>
          <input
            type="number" inputMode="decimal"
            value={manualRate} onChange={e => setManualRate(e.target.value)}
            placeholder="e.g. 47.50"
            style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '7px 10px', font: 'inherit', fontSize: 13, outline: 'none', color: 'var(--text)' }}
          />
          <button onClick={() => {
            const r = parseFloat(manualRate);
            if (r > 0) { onManualSave && onManualSave(r); setShowManual(false); }
          }} style={{
            padding: '7px 14px', borderRadius: 8,
            background: parseFloat(manualRate) > 0 ? 'var(--accent)' : 'var(--surface-2)',
            color: parseFloat(manualRate) > 0 ? 'var(--on-accent)' : 'var(--muted)',
            border: 0, font: 'inherit', fontSize: 12.5, fontWeight: 600,
            cursor: parseFloat(manualRate) > 0 ? 'pointer' : 'default',
          }}>Set rate</button>
        </div>
      )}
    </div>
  );
}

/* ── ACCOUNTS MANAGEMENT TAB ──────────────────────────────────────── */
function FinanceAccountsTab({ accounts, txns, onAddAccount, onEditAccount, onAdjustAccount, usdRate, usdRateUpdatedAt, rateRefreshing, rateError, onRefreshRate, onManualRate, goldHoldings, goldPrices, goldPricesUpdatedAt, goldRefreshing, goldError, onRefreshGold, onManualGoldPrice, onAddGolding, onEditGolding }) {
  const accts = accounts || NF_ACCOUNTS;
  const totalNet = accts.reduce((s, a) => s + (a.egpBalance != null ? a.egpBalance : (a.balance || 0)), 0);
  const totalIn  = accts.reduce((s, a) => s + (a.income || 0), 0);
  const totalOut = accts.reduce((s, a) => s + (a.expenses || 0), 0);
  const hasForeignAccts = accts.some(a => a.currency && a.currency !== 'EGP');

  return (
    <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', paddingTop: 4 }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Accounts</div>
          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>{accts.length} accounts</div>
        </div>
        <button onClick={onAddAccount} style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '7px 14px', borderRadius: 999,
          background: 'var(--accent)', color: 'var(--on-accent)',
          border: '1px solid var(--accent)', font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
        }}><PlusIcon size={14}/>Add</button>
      </div>

      {/* Net worth summary */}
      <div style={{ ...nutriStyles.card, padding: 16, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Net worth{hasForeignAccts ? ' (EGP)' : ''}</div>
          <div style={{ fontSize: 18, fontWeight: 700, letterSpacing: '-0.02em', marginTop: 3 }}>{nfMoneyFmt(Math.round(totalNet))}</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Total in</div>
          <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--c-protein)', marginTop: 3 }}>+{nfMoneyFmt(totalIn)}</div>
        </div>
        <div>
          <div style={{ fontSize: 10, color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' }}>Total out</div>
          <div style={{ fontSize: 16, fontWeight: 600, color: 'var(--c-carbs)', marginTop: 3 }}>−{nfMoneyFmt(totalOut)}</div>
        </div>
      </div>

      {/* USD/EGP Rate Widget */}
      <UsdEgpRateWidget
        rate={usdRate}
        updatedAt={usdRateUpdatedAt}
        refreshing={rateRefreshing}
        error={rateError}
        onRefresh={onRefreshRate}
        onManualSave={onManualRate}
      />

      {/* Account cards — horizontal scroll */}
      <div style={{ display: 'flex', gap: 10, overflowX: 'auto', margin: '0 -18px', padding: '0 18px 6px' }}>
        {accts.map(a => (
          <AccountCard key={a.id} account={a} onEdit={onEditAccount ? () => onEditAccount(a) : null}/>
        ))}
      </div>

      {/* Account list — detail rows */}
      <div style={{ ...nutriStyles.card, padding: 16, display: 'flex', flexDirection: 'column', gap: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 12 }}>All accounts</div>
        {accts.map((a, i) => (
          <div key={a.id} style={{
            display: 'flex', alignItems: 'center', gap: 12,
            padding: '12px 0',
            borderBottom: i < accts.length - 1 ? '1px solid var(--border)' : 'none',
          }}>
            <div style={{
              width: 36, height: 36, borderRadius: 10,
              background: 'var(--surface-2)', border: '1px solid var(--border)',
              display: 'grid', placeItems: 'center', fontSize: 18, flexShrink: 0,
            }}>{a.icon}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13.5, fontWeight: 600 }}>{a.name}</div>
              <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>
                {a.kind} · Start: {nfMoneyFmt(a.startingBalance || 0)}
              </div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontSize: 15, fontWeight: 700, letterSpacing: '-0.01em', color: (a.balance || 0) < 0 ? 'var(--c-carbs)' : 'var(--text)' }}>
                {nfMoneyFmt(a.balance || 0)}
              </div>
              <div style={{ fontSize: 10, color: 'var(--muted)', marginTop: 1 }}>{a.currency || NF_CURRENCY}</div>
              {a.currency && a.currency !== 'EGP' && a.egpBalance != null && (
                <div style={{ fontSize: 9.5, color: 'var(--muted-2)', marginTop: 1 }}>≈ {nfMoneyFmt(Math.round(a.egpBalance))} EGP</div>
              )}
            </div>
            <button onClick={() => onAdjustAccount && onAdjustAccount(a)} title="Fixing Data — adjust balance" style={{
              padding: '0 9px', height: 30, borderRadius: 8,
              background: 'var(--surface-2)', border: '1px solid var(--border-2)',
              display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)',
              flexShrink: 0, fontSize: 11.5, fontWeight: 600, font: 'inherit', whiteSpace: 'nowrap',
            }}>🛠 Fix</button>
            <button onClick={() => onEditAccount && onEditAccount(a)} style={{
              width: 30, height: 30, borderRadius: 8,
              background: 'var(--surface-2)', border: '1px solid var(--border-2)',
              display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)',
              flexShrink: 0,
            }}><EditIcon size={14}/></button>
          </div>
        ))}
      </div>

      {/* ── Gold Holdings Section ── */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 8 }}>
        <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: '-0.01em' }}>🥇 Gold Holdings</div>
        <button onClick={onAddGolding} style={{
          display: 'inline-flex', alignItems: 'center', gap: 5,
          padding: '6px 12px', borderRadius: 999,
          background: '#E89B3C', color: '#fff',
          border: 0, font: 'inherit', fontSize: 12, fontWeight: 600, cursor: 'pointer',
        }}><PlusIcon size={12}/>Add</button>
      </div>

      <GoldPriceWidget
        prices={goldPrices}
        updatedAt={goldPricesUpdatedAt}
        refreshing={goldRefreshing}
        error={goldError}
        onRefresh={onRefreshGold}
        onManualSave={onManualGoldPrice}
      />

      {(goldHoldings || []).length > 0 ? (
        <>
          <GoldStatsCard holdings={goldHoldings} goldPrices={goldPrices}/>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {[...(goldHoldings || [])].sort((a, b) => (b.date || '').localeCompare(a.date || '')).map(h => (
              <GoldHoldingCard key={h.id} holding={h} goldPrices={goldPrices} onEdit={onEditGolding ? () => onEditGolding(h) : null}/>
            ))}
          </div>
        </>
      ) : (
        <div style={{ ...nutriStyles.card, padding: 24, textAlign: 'center', color: 'var(--muted)' }}>
          <div style={{ fontSize: 28, marginBottom: 8 }}>🥇</div>
          <div style={{ fontSize: 13, fontWeight: 600 }}>No gold holdings yet</div>
          <div style={{ fontSize: 12, marginTop: 4 }}>Tap Add to record a holding</div>
        </div>
      )}

      <div style={{ height: 60 }}/>
    </div>
  );
}

/* ── ACCOUNT ADD / EDIT SHEET ─────────────────────────────────────── */
function AccountAddEditSheet({ account, onClose, onSave }) {
  const isNew = !account;
  const [name, setName]   = React.useState(account ? account.name : '');
  const [kind, setKind]   = React.useState(account ? (account.kind || 'cash') : 'cash');
  const [icon, setIcon]   = React.useState(account ? (account.icon || '💵') : '💵');
  const [startBal, setStartBal] = React.useState(account ? String(account.startingBalance || 0) : '0');
  const [currency, setCurrency] = React.useState(account ? (account.currency || 'EGP') : 'EGP');
  const [acctNotes, setAcctNotes] = React.useState(account ? (account.notes || '') : '');

  const KINDS = ['cash', 'bank', 'card', 'wallet', 'savings', 'other'];
  const ICONS = ['💵','🏦','💳','🪙','👜','💼','🏧','🎒','💰','🪄'];
  const CURRENCIES = ['EGP', 'USD', 'EUR', 'GBP', 'SAR', 'AED'];

  function submit() {
    if (!name.trim()) return;
    onSave({
      id:  account ? account.id : null,
      name: name.trim(),
      kind,
      icon,
      startingBalance: parseFloat(startBal) || 0,
      currency,
      notes: acctNotes.trim() || undefined,
    });
  }

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{isNew ? 'New account' : 'Edit account'}</div>
        <button onClick={submit} disabled={!name.trim()} style={{
          background: name.trim() ? 'var(--accent)' : 'var(--surface-2)',
          color: name.trim() ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (name.trim() ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: name.trim() ? 'pointer' : 'default', font: 'inherit',
        }}>Save</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Account name</span>
            <input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. Bank Account"
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
          </label>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Type</div>
            <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
              {KINDS.map(k => (
                <button key={k} onClick={()=>setKind(k)} style={{
                  padding: '6px 12px', borderRadius: 999,
                  background: kind === k ? 'var(--text)' : 'var(--surface-2)',
                  color: kind === k ? 'var(--bg)' : 'var(--text)',
                  border: kind === k ? '1px solid var(--text)' : '1px solid var(--border)',
                  font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
                  textTransform: 'capitalize',
                }}>{k}</button>
              ))}
            </div>
          </div>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Icon</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {ICONS.map(ic => (
                <button key={ic} onClick={()=>setIcon(ic)} style={{
                  width: 38, height: 38, borderRadius: 10, fontSize: 18,
                  background: icon === ic ? 'var(--text)' : 'var(--surface-2)',
                  border: '1px solid var(--border)', cursor: 'pointer', font: 'inherit',
                }}>{ic}</button>
              ))}
            </div>
          </div>

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Starting balance ({NF_CURRENCY})</span>
            <input type="number" inputMode="decimal" value={startBal} onChange={e=>setStartBal(e.target.value)}
              placeholder="0"
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
          </label>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Currency</div>
            <div style={{ display: 'flex', gap: 5, flexWrap: 'wrap' }}>
              {CURRENCIES.map(c => (
                <button key={c} onClick={() => setCurrency(c)} style={{
                  padding: '6px 12px', borderRadius: 999,
                  background: currency === c ? 'var(--text)' : 'var(--surface-2)',
                  color: currency === c ? 'var(--bg)' : 'var(--text)',
                  border: currency === c ? '1px solid var(--text)' : '1px solid var(--border)',
                  font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
                }}>{c}</button>
              ))}
            </div>
          </div>

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Notes (optional)</span>
            <textarea value={acctNotes} onChange={e=>setAcctNotes(e.target.value)} rows={2} placeholder="e.g. Business account"
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 13, color: 'var(--text)', resize: 'vertical' }}/>
          </label>
        </div>
      </div>
    </div>
  );
}

/* ── FIXING DATA — adjust an account's actual balance (creates a visible record) ── */
function AccountAdjustSheet({ account, onClose, onSave }) {
  const current = account ? (account.balance != null ? account.balance : (account.startingBalance || 0)) : 0;
  const curr = (account && account.currency) || 'EGP';
  const [actual, setActual] = React.useState(String(Math.round(current)));
  const [date, setDate]     = React.useState(NUTRI_TODAY);
  const [note, setNote]     = React.useState('');
  const parsed = (actual !== '' && !isNaN(+actual)) ? +actual : null;
  const diff = parsed != null ? +(parsed - current).toFixed(2) : 0;
  const canSave = parsed != null && diff !== 0;
  const dirLabel = diff < 0 ? 'Expense' : diff > 0 ? 'Income' : '';

  function submit() {
    if (!canSave) return;
    if (!confirm('Create a Fixing Data ' + dirLabel.toLowerCase() + ' of ' + nfMoneyFmt(Math.abs(diff)) + ' ' + curr + ' to set ' + (account.name || 'this account') + '’s balance to ' + nfMoneyFmt(parsed) + '?')) return;
    onSave({ accountId: account.id, fromBalance: current, toBalance: parsed, date, note, currency: curr });
  }

  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', height: '100%', background: 'var(--bg)', paddingTop: 'var(--safe-top)', animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)' }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>🛠 Fixing Data</div>
        <button onClick={submit} disabled={!canSave} style={{
          background: canSave ? 'var(--accent)' : 'var(--surface-2)', color: canSave ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'), borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default', font: 'inherit' }}>Save</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '16px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <div style={{ ...nutriStyles.card, padding: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 36, height: 36, borderRadius: 10, background: 'var(--surface-2)', border: '1px solid var(--border)', display: 'grid', placeItems: 'center', fontSize: 18 }}>{account && account.icon}</div>
            <div>
              <div style={{ fontSize: 14, fontWeight: 600 }}>{account && account.name}</div>
              <div style={{ fontSize: 11.5, color: 'var(--muted)' }}>Current calculated balance</div>
            </div>
            <div style={{ marginLeft: 'auto', fontSize: 18, fontWeight: 700 }}>{nfMoneyFmt(current)} <span style={{ fontSize: 11, color: 'var(--muted)' }}>{curr}</span></div>
          </div>
        </div>

        <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Actual balance</span>
          <input type="number" inputMode="decimal" value={actual} onChange={e => setActual(e.target.value)} autoFocus
            style={{ width: '100%', border: '1px solid var(--border-2)', background: 'var(--surface)', borderRadius: 10, padding: '11px 12px', outline: 'none', font: 'inherit', fontSize: 18, fontWeight: 700, color: 'var(--text)' }}/>
        </label>

        {/* live difference */}
        <div style={{ ...nutriStyles.card, padding: 14, background: diff === 0 ? 'var(--surface-2)' : (diff < 0 ? 'rgba(232,155,60,0.10)' : 'rgba(79,168,98,0.10)'),
          border: '1px solid ' + (diff === 0 ? 'var(--border)' : (diff < 0 ? 'rgba(232,155,60,0.4)' : 'rgba(79,168,98,0.4)')) }}>
          {parsed == null ? (
            <div style={{ fontSize: 13, color: 'var(--muted)' }}>Enter the real balance to see the adjustment.</div>
          ) : diff === 0 ? (
            <div style={{ fontSize: 13, color: 'var(--muted)', fontWeight: 600 }}>No change — nothing will be recorded.</div>
          ) : (
            <>
              <div style={{ fontSize: 12, color: 'var(--muted)', fontWeight: 600 }}>Will record a <b style={{ color: diff < 0 ? 'var(--c-carbs)' : 'var(--c-protein)' }}>{dirLabel}</b> “Fixing Data”</div>
              <div style={{ fontSize: 22, fontWeight: 700, marginTop: 4, color: diff < 0 ? 'var(--c-carbs)' : 'var(--c-protein)' }}>{diff < 0 ? '−' : '+'}{nfMoneyFmt(Math.abs(diff))} {curr}</div>
              <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 4 }}>{nfMoneyFmt(current)} → {nfMoneyFmt(parsed)}</div>
            </>
          )}
        </div>

        <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Date</span>
          <input type="date" value={date} onChange={e => setDate(e.target.value)} style={{ width: '100%', border: '1px solid var(--border-2)', background: 'var(--surface)', borderRadius: 10, padding: '10px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
        </label>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Note (optional)</span>
          <textarea value={note} onChange={e => setNote(e.target.value)} rows={2} placeholder="e.g. Missed cash spending"
            style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 13, color: 'var(--text)', resize: 'vertical' }}/>
        </label>
        <div style={{ fontSize: 11, color: 'var(--muted-2)', lineHeight: 1.6 }}>Fixing Data creates a normal, searchable finance record (category “Fixing Data”) — it never silently overwrites your balance, and your starting balance is left unchanged.</div>
      </div>
    </div>
  );
}

/* ── INCOME SOURCES MANAGEMENT TAB ───────────────────────────────── */
function IncomeSourcesTab({ categories, txns, onAddSource, onRenameSource, onDeleteSource, onBack }) {
  const incCat = (categories || NF_CATEGORIES).find(c => c.id === 'income');
  const sources = (incCat && incCat.subcategories) || [];
  const [adding, setAdding] = React.useState(false);
  const [newName, setNewName] = React.useState('');
  const [editingId, setEditingId] = React.useState(null);
  const [editName, setEditName] = React.useState('');

  function usedCount(subId) { return (txns || []).filter(t => t.subcategory === subId).length; }

  function handleAdd() {
    const n = newName.trim();
    if (!n) return;
    onAddSource && onAddSource(n, () => { setNewName(''); setAdding(false); });
  }

  function startEdit(s) { setEditingId(s.id); setEditName(s.name); }

  function saveEdit(s) {
    if (editName.trim() && editName.trim() !== s.name) {
      onRenameSource && onRenameSource(s.id, editName.trim());
    }
    setEditingId(null);
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onBack} style={iconBtn}><BackIcon size={18}/></button>
        <div style={{ flex: 1, fontSize: 16, fontWeight: 700 }}>Income Sources</div>
        <button onClick={() => { setAdding(true); setNewName(''); }} style={{
          display: 'flex', alignItems: 'center', gap: 5, padding: '7px 14px', borderRadius: 999,
          background: 'var(--accent)', color: 'var(--on-accent)', border: 0,
          font: 'inherit', fontSize: 13, fontWeight: 600, cursor: 'pointer',
        }}><PlusIcon size={13}/>Add</button>
      </div>

      <div style={{ flex: 1, overflow: 'auto', padding: '12px 16px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {adding && (
          <div style={{ ...nutriStyles.card, padding: 12, display: 'flex', gap: 8, alignItems: 'center' }}>
            <input autoFocus value={newName} onChange={e=>setNewName(e.target.value)}
              onKeyDown={e => { if (e.key==='Enter') handleAdd(); if (e.key==='Escape') setAdding(false); }}
              placeholder="Source name…"
              style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '8px 12px', font: 'inherit', fontSize: 13.5, outline: 'none', color: 'var(--text)' }}/>
            <button onClick={handleAdd} disabled={!newName.trim()} style={{
              padding: '8px 14px', borderRadius: 8,
              background: newName.trim() ? 'var(--accent)' : 'var(--surface-2)',
              color: newName.trim() ? 'var(--on-accent)' : 'var(--muted)',
              border: 0, font: 'inherit', fontSize: 13, fontWeight: 600, cursor: newName.trim() ? 'pointer' : 'default',
            }}>Add</button>
            <button onClick={() => setAdding(false)} style={iconBtn}><XIcon size={16}/></button>
          </div>
        )}

        {sources.length === 0 && !adding && (
          <div style={{ textAlign: 'center', padding: '48px 0', color: 'var(--muted)', fontSize: 13 }}>
            No income sources yet.<br/>Tap Add to create one.
          </div>
        )}

        {sources.map(s => {
          const used = usedCount(s.id);
          return (
            <div key={s.id} style={{ ...nutriStyles.card, padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{
                width: 34, height: 34, borderRadius: 10,
                background: 'var(--c-protein-soft)', display: 'grid', placeItems: 'center', fontSize: 17, flexShrink: 0,
              }}>💰</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                {editingId === s.id ? (
                  <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
                    <input autoFocus value={editName} onChange={e=>setEditName(e.target.value)}
                      onKeyDown={e => { if (e.key==='Enter') saveEdit(s); if (e.key==='Escape') setEditingId(null); }}
                      style={{ flex: 1, border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 8, padding: '5px 10px', font: 'inherit', fontSize: 13, outline: 'none', color: 'var(--text)' }}/>
                    <button onClick={() => saveEdit(s)} style={{
                      padding: '5px 10px', borderRadius: 8, background: 'var(--accent)', color: 'var(--on-accent)',
                      border: 0, font: 'inherit', fontSize: 12, fontWeight: 600, cursor: 'pointer',
                    }}>Save</button>
                    <button onClick={() => setEditingId(null)} style={iconBtn}><XIcon size={13}/></button>
                  </div>
                ) : (
                  <>
                    <div style={{ fontSize: 14, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.name}</div>
                    <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>{used} transaction{used !== 1 ? 's' : ''}</div>
                  </>
                )}
              </div>
              {editingId !== s.id && (
                <div style={{ display: 'flex', gap: 4 }}>
                  <button onClick={() => startEdit(s)} style={{ ...iconBtn, width: 30, height: 30, borderRadius: 8 }}><EditIcon size={14}/></button>
                  <button onClick={() => onDeleteSource && onDeleteSource('income', s.id)} style={{
                    width: 30, height: 30, borderRadius: 8,
                    background: 'var(--surface-2)', border: '1px solid var(--border-2)',
                    display: 'grid', placeItems: 'center', cursor: 'pointer',
                    color: used > 0 ? 'var(--muted-2)' : '#C25A4E', fontSize: 14,
                    opacity: 1,
                  }}>✕</button>
                </div>
              )}
            </div>
          );
        })}
        <div style={{ height: 40 }}/>
      </div>
    </div>
  );
}

/* ── CATEGORIES MANAGEMENT TAB ────────────────────────────────────── */
function FinanceCategories({ categories, txns, onAddCategory, onEditCategory, onDeleteCategory, onAddSubcategory, onEditSubcategory, onDeleteSubcategory, onManageIncomeSources }) {
  const cats = categories || NF_CATEGORIES;
  const [expanded, setExpanded] = React.useState(null);

  function usedCount(catId) {
    return (txns || []).filter(t => t.category === catId).length;
  }
  function subUsedCount(subId) {
    return (txns || []).filter(t => t.subcategory === subId).length;
  }

  const incCat = cats.find(c => c.id === 'income');
  const incSourceCount = incCat ? (incCat.subcategories || []).length : 0;

  return (
    <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', paddingTop: 4 }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Categories</div>
          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>{cats.length} categories</div>
        </div>
        <button onClick={onAddCategory} style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '7px 14px', borderRadius: 999,
          background: 'var(--accent)', color: 'var(--on-accent)',
          border: '1px solid var(--accent)', font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
        }}><PlusIcon size={14}/>Add</button>
      </div>

      {onManageIncomeSources && (
        <button onClick={onManageIncomeSources} style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '12px 14px', borderRadius: 14,
          background: 'var(--c-protein-soft)', border: '1px solid rgba(79,168,98,0.2)',
          cursor: 'pointer', font: 'inherit', textAlign: 'left', width: '100%',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ fontSize: 20 }}>💰</div>
            <div>
              <div style={{ fontSize: 13.5, fontWeight: 700, color: 'var(--text)' }}>Income Sources</div>
              <div style={{ fontSize: 11.5, color: 'var(--muted)', marginTop: 1 }}>{incSourceCount} source{incSourceCount !== 1 ? 's' : ''} — salary, freelance…</div>
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--accent)', fontWeight: 700 }}>Manage →</div>
        </button>
      )}

      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {cats.map(cat => {
          const isOpen = expanded === cat.id;
          const txnCount = usedCount(cat.id);
          const subs = cat.subcategories || [];
          return (
            <div key={cat.id} style={{ ...nutriStyles.card, overflow: 'hidden' }}>
              {/* Category row */}
              <div style={{ padding: '12px 14px', display: 'flex', alignItems: 'center', gap: 12 }}>
                <button onClick={() => setExpanded(isOpen ? null : cat.id)} style={{
                  flex: 1, display: 'flex', alignItems: 'center', gap: 10,
                  background: 'none', border: 0, cursor: 'pointer', font: 'inherit', textAlign: 'left', padding: 0, color: 'var(--text)',
                }}>
                  <div style={{
                    width: 34, height: 34, borderRadius: 10, flexShrink: 0,
                    background: cat.color + '22', border: '1px solid ' + cat.color + '44',
                    display: 'grid', placeItems: 'center', fontSize: 16,
                  }}>{cat.icon}</div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 14, fontWeight: 600 }}>{cat.name}</div>
                    <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>
                      {subs.length} subcategories · {txnCount} txns
                    </div>
                  </div>
                  <div style={{ fontSize: 14, color: 'var(--muted)', marginRight: 4 }}>{isOpen ? '▲' : '▼'}</div>
                </button>
                <button onClick={() => onEditCategory(cat)} style={{
                  width: 30, height: 30, borderRadius: 8, flexShrink: 0,
                  background: 'var(--surface-2)', border: '1px solid var(--border)',
                  display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)',
                }}><EditIcon size={13}/></button>
                <button onClick={() => onDeleteCategory(cat.id)} style={{
                  width: 30, height: 30, borderRadius: 8, flexShrink: 0,
                  background: 'transparent', border: '1px solid rgba(194,90,78,.3)',
                  display: 'grid', placeItems: 'center', cursor: 'pointer', color: '#C25A4E',
                }}>✕</button>
              </div>

              {/* Expanded: subcategories */}
              {isOpen && (
                <div style={{ borderTop: '1px solid var(--border)', background: 'var(--surface-2)' }}>
                  {subs.map((s, si) => (
                    <div key={s.id} style={{
                      display: 'flex', alignItems: 'center', gap: 10,
                      padding: '10px 14px 10px 52px',
                      borderBottom: si < subs.length - 1 ? '1px solid var(--border)' : 'none',
                    }}>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 13, fontWeight: 500 }}>{s.name}</div>
                        <div style={{ fontSize: 10.5, color: 'var(--muted)', marginTop: 1 }}>
                          {subUsedCount(s.id)} txns
                        </div>
                      </div>
                      <button onClick={() => onEditSubcategory(cat.id, s)} style={{
                        width: 26, height: 26, borderRadius: 6,
                        background: 'var(--surface)', border: '1px solid var(--border)',
                        display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--muted)',
                      }}><EditIcon size={11}/></button>
                      <button onClick={() => onDeleteSubcategory(cat.id, s.id)} style={{
                        width: 26, height: 26, borderRadius: 6,
                        background: 'transparent', border: '1px solid rgba(194,90,78,.3)',
                        display: 'grid', placeItems: 'center', cursor: 'pointer', color: '#C25A4E',
                        fontSize: 11,
                      }}>✕</button>
                    </div>
                  ))}
                  <button onClick={() => onAddSubcategory(cat.id)} style={{
                    width: '100%', padding: '10px 14px 10px 52px', textAlign: 'left',
                    background: 'none', border: 0, cursor: 'pointer', font: 'inherit',
                    fontSize: 12.5, color: 'var(--accent)', fontWeight: 600,
                    display: 'flex', alignItems: 'center', gap: 6,
                  }}>
                    <PlusIcon size={13}/>Add subcategory
                  </button>
                </div>
              )}
            </div>
          );
        })}
      </div>
      <div style={{ height: 60 }}/>
    </div>
  );
}

/* ── CATEGORY ADD / EDIT SHEET ────────────────────────────────────── */
function CategoryAddEditSheet({ category, onClose, onSave }) {
  const isNew = !category;
  const [name,         setName]         = React.useState(category ? category.name         : '');
  const [color,        setColor]        = React.useState(category ? category.color        : '#7A766C');
  const [icon,         setIcon]         = React.useState(category ? category.icon         : '📦');
  const [spendingType, setSpendingType] = React.useState(category ? (category.spendingType || null) : null);

  const COLORS = ['#E89B3C','#5784D8','#A06CC0','#4FA862','#D8745A','#7A766C','#C25A4E','#A99CE0','#1F3D2E'];
  const ICONS  = ['🍽️','🚗','⚡','💊','🎮','🛍️','💰','🏠','✈️','📚','💄','🎵','🐾','🌿','📦','💡','🎁','🏋️'];
  const SPENDING_TYPES = [
    { id: 'need',       label: 'Need',       color: '#5784D8' },
    { id: 'want',       label: 'Want',       color: '#E89B3C' },
    { id: 'waste',      label: 'Waste',      color: '#C25A4E' },
    { id: 'investment', label: 'Investment', color: '#4FA862' },
  ];

  function submit() {
    if (!name.trim()) return;
    onSave({ id: category ? category.id : null, name: name.trim(), color, icon, spendingType: spendingType || undefined });
  }

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{isNew ? 'New category' : 'Edit category'}</div>
        <button onClick={submit} disabled={!name.trim()} style={{
          background: name.trim() ? 'var(--accent)' : 'var(--surface-2)',
          color: name.trim() ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (name.trim() ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: name.trim() ? 'pointer' : 'default', font: 'inherit',
        }}>Save</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {/* Preview */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '8px 12px', borderRadius: 12, background: 'var(--surface-2)', border: '1px solid var(--border)' }}>
            <div style={{
              width: 40, height: 40, borderRadius: 12,
              background: color + '22', border: '1px solid ' + color + '66',
              display: 'grid', placeItems: 'center', fontSize: 20,
            }}>{icon}</div>
            <div style={{ fontSize: 16, fontWeight: 600 }}>{name || 'Category name'}</div>
          </div>

          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Name</span>
            <input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. Education"
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
          </label>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>
              Spending type <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}>(optional)</span>
            </div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {SPENDING_TYPES.map(t => (
                <button key={t.id} onClick={() => setSpendingType(spendingType === t.id ? null : t.id)} style={{
                  padding: '6px 12px', borderRadius: 999,
                  background: spendingType === t.id ? t.color : 'var(--surface-2)',
                  color:      spendingType === t.id ? '#fff'  : 'var(--text)',
                  border: '1px solid ' + (spendingType === t.id ? t.color : 'var(--border)'),
                  font: 'inherit', fontSize: 12, fontWeight: 500, cursor: 'pointer',
                }}>{t.label}</button>
              ))}
            </div>
          </div>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Color</div>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
              {COLORS.map(c => (
                <button key={c} onClick={() => setColor(c)} style={{
                  width: 30, height: 30, borderRadius: '50%', background: c,
                  border: color === c ? '3px solid var(--text)' : '3px solid var(--surface)',
                  cursor: 'pointer',
                }}/>
              ))}
            </div>
          </div>

          <div>
            <div style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', marginBottom: 6 }}>Icon</div>
            <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
              {ICONS.map(ic => (
                <button key={ic} onClick={() => setIcon(ic)} style={{
                  width: 38, height: 38, borderRadius: 10, fontSize: 18,
                  background: icon === ic ? 'var(--text)' : 'var(--surface-2)',
                  border: '1px solid var(--border)', cursor: 'pointer', font: 'inherit',
                }}>{ic}</button>
              ))}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ── SUBCATEGORY ADD / EDIT SHEET ─────────────────────────────────── */
function SubcategoryAddEditSheet({ catId, subcategory, onClose, onSave }) {
  const isNew = !subcategory;
  const [name, setName] = React.useState(subcategory ? subcategory.name : '');

  function submit() {
    if (!name.trim()) return;
    onSave({ catId, id: subcategory ? subcategory.id : null, name: name.trim() });
  }

  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)',
      animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)',
    }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{isNew ? 'New subcategory' : 'Edit subcategory'}</div>
        <button onClick={submit} disabled={!name.trim()} style={{
          background: name.trim() ? 'var(--accent)' : 'var(--surface-2)',
          color: name.trim() ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (name.trim() ? 'var(--accent)' : 'var(--border-2)'),
          borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: name.trim() ? 'pointer' : 'default', font: 'inherit',
        }}>Save</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px' }}>
        <div style={{ ...nutriStyles.card, padding: 14 }}>
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Subcategory name</span>
            <input value={name} onChange={e=>setName(e.target.value)} placeholder="e.g. Groceries"
              autoFocus
              style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)', borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14, color: 'var(--text)' }}/>
          </label>
        </div>
      </div>
    </div>
  );
}

/* ═══════════════════════════════════════════════════════════════════
   PROJECTS  +  FIXED EXPENSES / INSTALLMENTS  (Step 9)
   ═══════════════════════════════════════════════════════════════════ */

const _fxInput = {
  width: '100%', border: '1px solid var(--border)', background: 'var(--surface-2)',
  borderRadius: 10, padding: '8px 12px', outline: 'none', font: 'inherit', fontSize: 14,
  color: 'var(--text)', boxSizing: 'border-box',
};
function _FxLabel({ children, optional }) {
  return (
    <span style={{ fontSize: 11, color: 'var(--muted)', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase' }}>
      {children}{optional ? <span style={{ fontWeight: 400, textTransform: 'none', fontSize: 10 }}> (optional)</span> : null}
    </span>
  );
}
function _FxField({ label, optional, children }) {
  return <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}><_FxLabel optional={optional}>{label}</_FxLabel>{children}</label>;
}
function _FxSelect({ value, onChange, options, placeholder }) {
  return (
    <select value={value} onChange={e => onChange(e.target.value)} style={{ ..._fxInput, cursor: 'pointer' }}>
      {placeholder != null && <option value="">{placeholder}</option>}
      {options.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
    </select>
  );
}
function _FxPills({ value, onChange, options }) {
  return (
    <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
      {options.map(o => (
        <button key={o.value} onClick={() => onChange(o.value)} style={{
          padding: '6px 12px', borderRadius: 999, font: 'inherit', fontSize: 12, fontWeight: 600, cursor: 'pointer',
          background: value === o.value ? 'var(--text)' : 'var(--surface-2)',
          color: value === o.value ? 'var(--bg)' : 'var(--muted)',
          border: '1px solid ' + (value === o.value ? 'var(--text)' : 'var(--border)'),
        }}>{o.label}</button>
      ))}
    </div>
  );
}
function _SheetShell({ title, onClose, onSave, canSave, saveLabel, children }) {
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)', animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)' }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>{title}</div>
        <button onClick={onSave} disabled={!canSave} style={{
          background: canSave ? 'var(--accent)' : 'var(--surface-2)', color: canSave ? 'var(--on-accent)' : 'var(--muted)',
          border: '1px solid ' + (canSave ? 'var(--accent)' : 'var(--border-2)'), borderRadius: 999, padding: '6px 14px',
          fontSize: 13, fontWeight: 600, cursor: canSave ? 'pointer' : 'default', font: 'inherit' }}>{saveLabel || 'Save'}</button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 60px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        {children}
      </div>
    </div>
  );
}

/* Horizontal bar breakdown — readable labels + visible values + % (no hover-only). */
function _FinBars({ items, total }) {
  if (!items || !items.length) return null;
  const max = Math.max(...items.map(i => i.value), 1);
  const sum = total != null ? total : items.reduce((s, i) => s + i.value, 0);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {items.map((it, i) => {
        const pct = sum > 0 ? it.value / sum : 0;
        const w = Math.max((it.value / max) * 100, it.value > 0 ? 4 : 0);
        return (
          <div key={it.key || i}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 3, gap: 8 }}>
              <span style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{it.label}</span>
              <span style={{ fontSize: 12, fontWeight: 600, flexShrink: 0 }}>
                {nfMoneyFmt(Math.round(it.value))} <span style={{ color: 'var(--muted)', fontWeight: 500 }}>· {Math.round(pct * 100)}%</span>
              </span>
            </div>
            <div style={{ height: 8, borderRadius: 99, background: 'var(--ring-track)', overflow: 'hidden' }}>
              <div style={{ width: w + '%', height: '100%', borderRadius: 99, background: it.color || 'var(--text)' }}/>
            </div>
          </div>
        );
      })}
    </div>
  );
}

const _FX_STATUS_META = {
  active:    { label: 'Active',    color: 'var(--c-protein)' },
  paused:    { label: 'Paused',    color: '#E89B3C' },
  completed: { label: 'Completed', color: 'var(--muted)' },
  archived:  { label: 'Archived',  color: 'var(--muted-2)' },
};
function _StatusChip({ status }) {
  const m = _FX_STATUS_META[status || 'active'] || _FX_STATUS_META.active;
  return (
    <span style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase',
      padding: '2px 7px', borderRadius: 999, color: m.color, background: 'color-mix(in srgb, ' + 'transparent' + ', transparent)',
      border: '1px solid ' + m.color + '55' }}>{m.label}</span>
  );
}

/* ── PROJECTS SECTION ─────────────────────────────────────────────── */
function FinanceProjectsSection({ projects, txns, fixed, categories, onAdd, onEdit, onOpen }) {
  const list = projects || [];
  const active = list.filter(p => (p.status || 'active') !== 'archived');
  const archived = list.filter(p => (p.status || 'active') === 'archived');

  const Header = (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
      <div style={{ fontSize: 16, fontWeight: 700, letterSpacing: '-0.01em' }}>Projects</div>
      <button onClick={onAdd} style={{
        display: 'inline-flex', alignItems: 'center', gap: 6, padding: '7px 14px', borderRadius: 999,
        background: 'var(--accent)', color: 'var(--on-accent)', border: '1px solid var(--accent)',
        font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer' }}><PlusIcon size={14}/>Add</button>
    </div>
  );

  if (!list.length) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {Header}
        <EmptyState icon="📁" title="No projects yet" subtitle="Group related expenses, income and installments under a project — e.g. Apartment, Car, Travel."/>
      </div>
    );
  }

  function Card(p) {
    const st = window.financeProjectStats(p, txns);
    const fxCount = (fixed || []).filter(f => f.project === p.id).length;
    return (
      <div key={p.id} style={{ ...nutriStyles.card, padding: 0, overflow: 'hidden' }}>
        <button onClick={() => onOpen(p)} style={{ width: '100%', textAlign: 'left', background: 'none', border: 0, cursor: 'pointer', font: 'inherit', color: 'var(--text)', padding: 14 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <div style={{ fontSize: 14.5, fontWeight: 700, flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{p.name}</div>
            <_StatusChip status={p.status}/>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontSize: 12.5 }}>
            <span style={{ color: 'var(--muted)' }}>{st.count} expense{st.count !== 1 ? 's' : ''}{fxCount ? ' · ' + fxCount + ' fixed' : ''}</span>
            <span style={{ fontWeight: 700 }}>{nfMoneyFmt(Math.round(st.spent))} <span style={{ color: 'var(--muted)', fontWeight: 500, fontSize: 11 }}>EGP spent</span></span>
          </div>
          {st.budget != null && (
            <div style={{ marginTop: 8 }}>
              <MacroBar value={st.spent} goal={st.budget} color={st.spent > st.budget ? 'var(--c-carbs)' : 'var(--c-protein)'}/>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 4, fontSize: 11, color: 'var(--muted)' }}>
                <span>Budget {nfMoneyFmt(Math.round(st.budget))}</span>
                <span style={{ color: st.remaining < 0 ? 'var(--c-carbs)' : 'var(--c-protein)', fontWeight: 600 }}>
                  {st.remaining < 0 ? 'Over by ' : 'Left '}{nfMoneyFmt(Math.abs(Math.round(st.remaining)))}
                </span>
              </div>
            </div>
          )}
        </button>
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {Header}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
        {active.map(Card)}
      </div>
      {archived.length > 0 && (
        <>
          <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--muted)', textTransform: 'uppercase', letterSpacing: '0.05em', marginTop: 6 }}>Archived</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10, opacity: 0.7 }}>{archived.map(Card)}</div>
        </>
      )}
    </div>
  );
}

function ProjectAddEditSheet({ project, onClose, onSave, onDelete }) {
  const isNew = !project;
  const [name, setName]       = React.useState(project ? project.name : '');
  const [notes, setNotes]     = React.useState(project ? (project.notes || '') : '');
  const [startDate, setStart] = React.useState(project ? (project.startDate || NUTRI_TODAY) : NUTRI_TODAY);
  const [endDate, setEnd]     = React.useState(project ? (project.endDate || '') : '');
  const [budget, setBudget]   = React.useState(project && project.budget != null ? String(project.budget) : '');
  const [status, setStatus]   = React.useState(project ? (project.status || 'active') : 'active');
  const canSave = !!name.trim();
  function submit() {
    if (!canSave) return;
    onSave({ id: project ? project.id : null, name: name.trim(), notes: notes.trim(),
      startDate, endDate: endDate || undefined, budget: budget !== '' ? +budget : undefined, status });
  }
  return (
    <_SheetShell title={isNew ? 'New project' : 'Edit project'} onClose={onClose} onSave={submit} canSave={canSave}>
      <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <_FxField label="Project name">
          <input value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Apartment, Car, Travel" style={_fxInput} autoFocus/>
        </_FxField>
        <div>
          <_FxLabel>Status</_FxLabel>
          <div style={{ marginTop: 6 }}>
            <_FxPills value={status} onChange={setStatus} options={[
              { value: 'active', label: 'Active' }, { value: 'completed', label: 'Completed' }, { value: 'archived', label: 'Archived' } ]}/>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          <_FxField label="Start date"><input type="date" value={startDate} onChange={e => setStart(e.target.value)} style={_fxInput}/></_FxField>
          <_FxField label="End date" optional><input type="date" value={endDate} onChange={e => setEnd(e.target.value)} style={_fxInput}/></_FxField>
        </div>
        <_FxField label="Budget (EGP)" optional>
          <input type="number" inputMode="decimal" value={budget} onChange={e => setBudget(e.target.value)} placeholder="e.g. 50000" style={_fxInput}/>
        </_FxField>
        <_FxField label="Description / notes" optional>
          <textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2} placeholder="Optional" style={{ ..._fxInput, resize: 'vertical' }}/>
        </_FxField>
      </div>
      {!isNew && onDelete && (
        <button onClick={() => onDelete(project)} style={{
          background: 'none', border: '1px solid rgba(194,90,78,.35)', borderRadius: 12, padding: '11px 16px',
          font: 'inherit', fontSize: 13, fontWeight: 600, cursor: 'pointer', color: '#C25A4E' }}>
          Delete project
        </button>
      )}
    </_SheetShell>
  );
}

function ProjectDetailSheet({ project, txns, fixed, categories, onClose, onEdit, onOpenTxn }) {
  const st = window.financeProjectStats(project, txns);
  const linked = (txns || []).filter(t => txnProjectIds(t).indexOf(project.id) >= 0).sort((a, b) => (b.date || '').localeCompare(a.date || ''));
  const linkedFixed = (fixed || []).filter(f => f.project === project.id);
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', height: '100%',
      background: 'var(--bg)', paddingTop: 'var(--safe-top)', animation: 'sheetIn .25s cubic-bezier(.2,.7,.2,1)' }}>
      <div style={{ padding: '12px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
        <button onClick={onClose} style={iconBtn}><XIcon size={18}/></button>
        <div style={{ fontSize: 15, fontWeight: 600 }}>Project</div>
        <button onClick={() => onEdit(project)} style={{ ...iconBtn, width: 'auto', padding: '0 6px' }}><EditIcon size={16}/></button>
      </div>
      <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 80px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '-0.02em' }}>{project.name}</div>
            <_StatusChip status={project.status}/>
          </div>
          {(project.startDate || project.endDate) && (
            <div style={{ fontSize: 12, color: 'var(--muted)', marginTop: 2 }}>
              {project.startDate ? prettyDateShort(project.startDate) : ''}{project.endDate ? ' → ' + prettyDateShort(project.endDate) : ''}
            </div>
          )}
          {project.notes && <div style={{ fontSize: 12.5, color: 'var(--text-2)', marginTop: 6, lineHeight: 1.5 }}>{project.notes}</div>}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <FinStatTile label="Spent" value={nfMoneyFmt(Math.round(st.spent))} color="var(--c-carbs)"/>
          {st.income > 0 && <FinStatTile label="Income" value={nfMoneyFmt(Math.round(st.income))} color="var(--c-protein)"/>}
          {st.budget != null && <FinStatTile label="Budget" value={nfMoneyFmt(Math.round(st.budget))}/>}
          {st.remaining != null && <FinStatTile label={st.remaining < 0 ? 'Over budget' : 'Remaining'} value={nfMoneyFmt(Math.round(st.remaining))} color={st.remaining < 0 ? 'var(--c-carbs)' : 'var(--c-protein)'}/>}
        </div>
        {st.budget != null && <MacroBar value={st.spent} goal={st.budget} color={st.spent > st.budget ? 'var(--c-carbs)' : 'var(--c-protein)'}/>}

        {linkedFixed.length > 0 && (
          <div>
            <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8 }}>Linked fixed expenses</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {linkedFixed.map(f => (
                <div key={f.id} style={{ ...nutriStyles.card, padding: '10px 12px', display: 'flex', justifyContent: 'space-between', fontSize: 12.5 }}>
                  <span style={{ fontWeight: 600 }}>{f.name}</span>
                  <span style={{ color: 'var(--muted)' }}>{nfMoneyFmt(Math.round(+f.amount || 0))} {f.currency || 'EGP'} · {f.frequency || 'monthly'}</span>
                </div>
              ))}
            </div>
          </div>
        )}

        <div>
          <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8 }}>Linked expenses ({linked.length})</div>
          {linked.length === 0
            ? <div style={{ fontSize: 12.5, color: 'var(--muted)', padding: '8px 0' }}>No expenses linked yet. Pick this project when adding an expense.</div>
            : <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>{linked.map(t => <TxnCard key={t.id} txn={t} onClick={() => onOpenTxn && onOpenTxn(t)}/>)}</div>}
        </div>
      </div>
    </div>
  );
}

/* ── FIXED EXPENSES / INSTALLMENTS SECTION ────────────────────────── */
function FixedExpensesSection({ fixed, projects, categories, accounts, usdRate, onAdd, onEdit, onMarkPaid }) {
  const list = fixed || [];
  const cats = categories || NF_CATEGORIES;
  const today = todayISO();
  const catById = {}; cats.forEach(c => { catById[c.id] = c; });
  const projById = {}; (projects || []).forEach(p => { projById[p.id] = p; });

  const Header = (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
      <div style={{ fontSize: 16, fontWeight: 700, letterSpacing: '-0.01em' }}>Fixed &amp; Installments</div>
      <button onClick={onAdd} style={{
        display: 'inline-flex', alignItems: 'center', gap: 6, padding: '7px 14px', borderRadius: 999,
        background: 'var(--accent)', color: 'var(--on-accent)', border: '1px solid var(--accent)',
        font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer' }}><PlusIcon size={14}/>Add</button>
    </div>
  );

  if (!list.length) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {Header}
        <EmptyState icon="🧾" title="No fixed expenses yet" subtitle="Track rent, installments, subscriptions and recurring bills. Mark each due payment as paid to log it as an expense."/>
      </div>
    );
  }

  const stats = window.fixedExpenseStats(list, { todayISO: today, usdRate });
  const schedule = window.fixedScheduleList(list, { todayISO: today });
  const timeline = schedule.filter(r => r.status !== 'paid').slice(0, 20);
  const catBars = Object.entries(stats.byCategory)
    .map(([cid, v]) => ({ key: cid, label: (catById[cid] && catById[cid].name) || cid, value: v, color: (catById[cid] && catById[cid].color) || 'var(--text)' }))
    .sort((a, b) => b.value - a.value);
  const paidUnpaid = [
    { key: 'paid', label: 'Paid', value: stats.monthPaid, color: 'var(--c-protein)' },
    { key: 'unpaid', label: 'Unpaid', value: stats.monthUnpaid, color: '#E89B3C' },
  ].filter(x => x.value > 0);

  function statusChip(status) {
    const map = { overdue: { t: 'Overdue', c: '#C25A4E' }, due: { t: 'Due today', c: '#E89B3C' }, upcoming: { t: 'Upcoming', c: 'var(--muted)' }, paid: { t: 'Paid', c: 'var(--c-protein)' } };
    const m = map[status] || map.upcoming;
    return <span style={{ fontSize: 10, fontWeight: 700, color: m.c }}>{m.t}</span>;
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      {Header}

      {/* Stats */}
      <div style={{ ...nutriStyles.card, padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>This month</div>
          <div style={{ fontSize: 11, color: 'var(--muted)' }}>{stats.activeCount} active</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <FinStatTile label="Due this month" value={nfMoneyFmt(Math.round(stats.monthTotal))}/>
          <FinStatTile label="Monthly commitment" value={nfMoneyFmt(Math.round(stats.monthlyCommitment))} color="var(--c-fat)"/>
          <FinStatTile label="Paid" value={nfMoneyFmt(Math.round(stats.monthPaid))} color="var(--c-protein)"/>
          <FinStatTile label="Unpaid" value={nfMoneyFmt(Math.round(stats.monthUnpaid))} color="#E89B3C"/>
          <FinStatTile label={'Overdue' + (stats.overdueCount ? ' (' + stats.overdueCount + ')' : '')} value={nfMoneyFmt(Math.round(stats.overdue))} color="var(--c-carbs)"/>
          <FinStatTile label={'Upcoming' + (stats.upcomingCount ? ' (' + stats.upcomingCount + ')' : '')} value={nfMoneyFmt(Math.round(stats.upcoming))}/>
        </div>
        {stats.remainingInstallments > 0 && (
          <div style={{ fontSize: 12, color: 'var(--muted)' }}>
            Remaining installments value: <strong style={{ color: 'var(--text)' }}>{nfMoneyFmt(Math.round(stats.remainingInstallments))} EGP</strong>
          </div>
        )}
        {paidUnpaid.length > 0 && (
          <div style={{ borderTop: '1px solid var(--border)', paddingTop: 10 }}>
            <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 8 }}>Paid vs unpaid · this month</div>
            <_FinBars items={paidUnpaid}/>
          </div>
        )}
      </div>

      {catBars.length > 0 && (
        <div style={{ ...nutriStyles.card, padding: 16 }}>
          <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 12 }}>Fixed by category · this month</div>
          <_FinBars items={catBars}/>
        </div>
      )}

      {/* Timeline */}
      <div style={{ fontSize: 14, fontWeight: 600 }}>Upcoming &amp; overdue</div>
      {timeline.length === 0
        ? <div style={{ ...nutriStyles.card, padding: 16, fontSize: 12.5, color: 'var(--muted)', textAlign: 'center' }}>Nothing due in this window. 🎉</div>
        : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {timeline.map(r => {
              const cat = catById[r.category];
              const proj = projById[r.project];
              const overdue = r.status === 'overdue';
              return (
                <div key={r.fixedId + '_' + r.date} style={{ ...nutriStyles.card, padding: 12, display: 'flex', alignItems: 'center', gap: 12,
                  border: overdue ? '1px solid rgba(194,90,78,.35)' : '1px solid var(--border)' }}>
                  <FoodThumb emoji={(cat && cat.icon) || '🧾'} tint={(cat && cat.color) || 'var(--c-fat)'} size={40}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13.5, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name}</div>
                    <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>
                      {prettyDateShort(r.date)} · {statusChip(r.status)}{proj ? ' · ' + proj.name : ''}
                    </div>
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 4 }}>
                    <div style={{ fontSize: 14, fontWeight: 700 }}>{nfMoneyFmt(Math.round(+r.amount || 0))}{r.currency && r.currency !== 'EGP' ? ' ' + r.currency : ''}</div>
                    <button onClick={() => onMarkPaid(r.fixedId, r.date)} style={{
                      padding: '4px 10px', borderRadius: 999, font: 'inherit', fontSize: 11, fontWeight: 700, cursor: 'pointer',
                      background: 'var(--accent)', color: 'var(--on-accent)', border: '1px solid var(--accent)' }}>Mark paid</button>
                  </div>
                </div>
              );
            })}
          </div>
        )}

      {/* All fixed expenses */}
      <div style={{ fontSize: 14, fontWeight: 600, marginTop: 4 }}>All fixed expenses</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {list.map(fx => {
          const cat = catById[fx.category];
          const paid = window.fixedPaidCount(fx);
          const remaining = window.fixedRemainingCount(fx);
          const nd = window.fixedNextDue(fx, today);
          return (
            <button key={fx.id} onClick={() => onEdit(fx)} style={{ ...nutriStyles.card, padding: 12, display: 'flex', alignItems: 'center', gap: 12,
              width: '100%', textAlign: 'left', cursor: 'pointer', font: 'inherit', color: 'var(--text)' }}>
              <FoodThumb emoji={(cat && cat.icon) || '🧾'} tint={(cat && cat.color) || 'var(--c-fat)'} size={40}/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <span style={{ fontSize: 13.5, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{fx.name}</span>
                  {(fx.status && fx.status !== 'active') && <_StatusChip status={fx.status}/>}
                </div>
                <div style={{ fontSize: 11, color: 'var(--muted)', marginTop: 1 }}>
                  {fx.frequency || 'monthly'}{fx.installmentsTotal ? ' · ' + paid + '/' + fx.installmentsTotal + ' paid' : (paid ? ' · ' + paid + ' paid' : '')}
                  {nd ? ' · next ' + prettyDateShort(nd.date) : ''}
                </div>
              </div>
              <div style={{ fontSize: 14, fontWeight: 700 }}>{nfMoneyFmt(Math.round(+fx.amount || 0))}{fx.currency && fx.currency !== 'EGP' ? ' ' + fx.currency : ''}</div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function FixedAddEditSheet({ fixed, categories, accounts, projects, usdRate, onClose, onSave, onDelete, onQuickAddCategory, onQuickAddSubcategory, onQuickAddAccount, onQuickAddProject }) {
  const isNew = !fixed;
  const cats = (categories || NF_CATEGORIES).filter(c => c.id !== 'income');
  const accts = accounts || NF_ACCOUNTS;
  const defaultCat = (cats.find(c => c.id === 'fixed_expenses_installments') || cats[0] || {}).id || '';
  const [name, setName]       = React.useState(fixed ? fixed.name : '');
  const [catId, setCatId]     = React.useState(fixed ? (fixed.category || defaultCat) : defaultCat);
  const [subId, setSubId]     = React.useState(fixed ? (fixed.subcategory || '') : '');
  const [amount, setAmount]   = React.useState(fixed ? String(fixed.amount) : '');
  const [currency, setCurrency] = React.useState(fixed ? (fixed.currency || 'EGP') : 'EGP');
  const [account, setAccount] = React.useState(fixed ? (fixed.account || (accts[0] || {}).id) : (accts[0] || {}).id || 'cash');
  const [startDate, setStart] = React.useState(fixed ? (fixed.startDate || NUTRI_TODAY) : NUTRI_TODAY);
  const [endDate, setEnd]     = React.useState(fixed ? (fixed.endDate || '') : '');
  const [frequency, setFreq]  = React.useState(fixed ? (fixed.frequency || 'monthly') : 'monthly');
  const [intervalDays, setIv] = React.useState(fixed && fixed.intervalDays ? String(fixed.intervalDays) : '');
  const [dueDay, setDueDay]   = React.useState(fixed && fixed.dueDay ? String(fixed.dueDay) : '');
  const [installments, setInst] = React.useState(fixed && fixed.installmentsTotal ? String(fixed.installmentsTotal) : '');
  const [project, setProject] = React.useState(fixed ? (fixed.project || '') : '');
  const [status, setStatus]   = React.useState(fixed ? (fixed.status || 'active') : 'active');
  const [notes, setNotes]     = React.useState(fixed ? (fixed.notes || '') : '');

  const activeCat = cats.find(c => c.id === catId);
  const subOptions = (activeCat && activeCat.subcategories) || [];
  React.useEffect(() => { if (subId && !subOptions.some(s => s.id === subId)) setSubId(''); }, [catId]);
  const showInterval = frequency === 'everyX' || frequency === 'custom';
  const CUR = ['EGP', 'USD', 'EUR', 'GBP', 'SAR', 'AED'];
  const canSave = !!name.trim() && parseFloat(amount) > 0 && !!startDate;

  function submit() {
    if (!canSave) return;
    onSave({
      id: fixed ? fixed.id : null,
      name: name.trim(), category: catId, subcategory: subId || '',
      amount: Math.abs(parseFloat(amount)), currency, account,
      startDate, endDate: endDate || undefined,
      frequency, intervalDays: showInterval ? (+intervalDays || 30) : undefined,
      dueDay: dueDay ? +dueDay : undefined,
      installmentsTotal: installments ? +installments : undefined,
      project: project || undefined, status, notes: notes.trim(),
      ...(fixed ? { paidDates: fixed.paidDates || [] } : {}),
    });
  }

  return (
    <_SheetShell title={isNew ? 'New fixed expense' : 'Edit fixed expense'} onClose={onClose} onSave={submit} canSave={canSave}>
      <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <_FxField label="Name"><input value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Car installment, Netflix, Rent" style={_fxInput} autoFocus/></_FxField>
        <_FxField label="Amount">
          <div style={{ display: 'flex', gap: 8 }}>
            <input type="number" inputMode="decimal" value={amount} onChange={e => setAmount(e.target.value)} placeholder="0" style={{ ..._fxInput, flex: 1 }}/>
          </div>
        </_FxField>
        <div>
          <_FxLabel>Currency</_FxLabel>
          <div style={{ marginTop: 6 }}><_FxPills value={currency} onChange={setCurrency} options={CUR.map(c => ({ value: c, label: c }))}/></div>
        </div>
        <_FxField label="Category">
          <_FxSelect value={catId} onChange={setCatId} options={cats.map(c => ({ value: c.id, label: (c.icon ? c.icon + ' ' : '') + c.name }))}/>
          {onQuickAddCategory && <_InlineAdd ctaLabel="New category" placeholder="Category name…" onSubmit={n => onQuickAddCategory(n, id => setCatId(id))}/>}
        </_FxField>
        {(subOptions.length > 0 || onQuickAddSubcategory) && (
          <_FxField label="Subcategory" optional>
            {subOptions.length > 0 && <_FxSelect value={subId} onChange={setSubId} placeholder="— none —" options={subOptions.map(s => ({ value: s.id, label: s.name }))}/>}
            {onQuickAddSubcategory && <_InlineAdd ctaLabel="New subcategory" placeholder="Subcategory name…" onSubmit={n => onQuickAddSubcategory(catId, n, id => setSubId(id))}/>}
          </_FxField>
        )}
        <div>
          <_FxLabel>Frequency</_FxLabel>
          <div style={{ marginTop: 6 }}>
            <_FxPills value={frequency} onChange={setFreq} options={[
              { value: 'daily', label: 'Daily' }, { value: 'weekly', label: 'Weekly' }, { value: 'monthly', label: 'Monthly' },
              { value: 'yearly', label: 'Yearly' }, { value: 'everyX', label: 'Every X days' }, { value: 'custom', label: 'Custom' } ]}/>
          </div>
        </div>
        {showInterval && (
          <_FxField label="Interval (days)"><input type="number" inputMode="numeric" value={intervalDays} onChange={e => setIv(e.target.value)} placeholder="e.g. 90" style={_fxInput}/></_FxField>
        )}
        <div style={{ display: 'flex', gap: 10 }}>
          <_FxField label="Start date"><input type="date" value={startDate} onChange={e => setStart(e.target.value)} style={_fxInput}/></_FxField>
          <_FxField label="End date" optional><input type="date" value={endDate} onChange={e => setEnd(e.target.value)} style={_fxInput}/></_FxField>
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          <_FxField label="Due day" optional><input type="number" inputMode="numeric" value={dueDay} onChange={e => setDueDay(e.target.value)} placeholder="1–31" style={_fxInput}/></_FxField>
          <_FxField label="# Installments" optional><input type="number" inputMode="numeric" value={installments} onChange={e => setInst(e.target.value)} placeholder="e.g. 12" style={_fxInput}/></_FxField>
        </div>
        <_FxField label="Account / payment method">
          <_FxSelect value={account} onChange={setAccount} options={accts.map(a => ({ value: a.id, label: (a.icon ? a.icon + ' ' : '') + a.name }))}/>
          {onQuickAddAccount && <_InlineAdd ctaLabel="New account" placeholder="Account name…" onSubmit={n => onQuickAddAccount(n, id => setAccount(id))}/>}
        </_FxField>
        <_FxField label="Project" optional>
          <_FxSelect value={project} onChange={setProject} placeholder="— none —" options={(projects || []).filter(p => (p.status || 'active') !== 'archived').map(p => ({ value: p.id, label: p.name }))}/>
          {onQuickAddProject && <_InlineAdd ctaLabel="New project" placeholder="Project name…" onSubmit={n => onQuickAddProject(n, id => setProject(id))}/>}
        </_FxField>
        <div>
          <_FxLabel>Status</_FxLabel>
          <div style={{ marginTop: 6 }}>
            <_FxPills value={status} onChange={setStatus} options={[
              { value: 'active', label: 'Active' }, { value: 'paused', label: 'Paused' }, { value: 'completed', label: 'Completed' }, { value: 'archived', label: 'Archived' } ]}/>
          </div>
        </div>
        <_FxField label="Notes" optional><textarea value={notes} onChange={e => setNotes(e.target.value)} rows={2} placeholder="Optional" style={{ ..._fxInput, resize: 'vertical' }}/></_FxField>
      </div>
      {!isNew && onDelete && (
        <button onClick={() => onDelete(fixed)} style={{
          background: 'none', border: '1px solid rgba(194,90,78,.35)', borderRadius: 12, padding: '11px 16px',
          font: 'inherit', fontSize: 13, fontWeight: 600, cursor: 'pointer', color: '#C25A4E' }}>
          Delete fixed expense
        </button>
      )}
    </_SheetShell>
  );
}

function MarkPaidSheet({ fixed, dueDate, accounts, usdRate, onClose, onConfirm }) {
  const accts = accounts || NF_ACCOUNTS;
  const [account, setAccount] = React.useState(fixed.account || (accts[0] || {}).id || 'cash');
  const [date, setDate] = React.useState(dueDate || NUTRI_TODAY);
  const isForeign = fixed.currency && fixed.currency !== 'EGP';
  const [rate, setRate] = React.useState(isForeign && fixed.currency === 'USD' && usdRate ? String(usdRate) : '');
  const amt = Math.abs(+fixed.amount || 0);
  const numRate = parseFloat(rate) || 0;
  const egpEquivalent = isForeign && numRate > 0 ? +(amt * numRate).toFixed(2) : (isForeign ? null : amt);
  function confirm() {
    onConfirm({ account, date, ...(isForeign && numRate > 0 ? { exchangeRate: numRate, egpEquivalent } : {}) });
  }
  return (
    <_SheetShell title="Mark as paid" onClose={onClose} onSave={confirm} canSave={true} saveLabel="Confirm">
      <div style={{ ...nutriStyles.card, padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div>
          <div style={{ fontSize: 16, fontWeight: 700 }}>{fixed.name}</div>
          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2 }}>
            {nfMoneyFmt(amt)} {fixed.currency || 'EGP'} · due {prettyDateShort(dueDate)}
          </div>
        </div>
        <_FxField label="Pay date"><input type="date" value={date} onChange={e => setDate(e.target.value)} style={_fxInput}/></_FxField>
        <_FxField label="Account / payment method">
          <_FxSelect value={account} onChange={setAccount} options={accts.map(a => ({ value: a.id, label: (a.icon ? a.icon + ' ' : '') + a.name }))}/>
        </_FxField>
        {isForeign && (
          <_FxField label={'Rate (1 ' + fixed.currency + ' = ? EGP)'}>
            <input type="number" inputMode="decimal" value={rate} onChange={e => setRate(e.target.value)} placeholder="e.g. 50" style={_fxInput}/>
          </_FxField>
        )}
        {isForeign && egpEquivalent != null && (
          <div style={{ padding: '6px 12px', borderRadius: 8, background: 'var(--c-fat-soft)', border: '1px solid rgba(87,132,216,0.2)', fontSize: 12.5 }}>
            ≈ <strong>{Math.round(egpEquivalent).toLocaleString()} EGP</strong>
          </div>
        )}
        <div style={{ fontSize: 11.5, color: 'var(--muted)' }}>
          This creates one expense for {prettyDateShort(dueDate)}. The same due date can't be paid twice.
        </div>
      </div>
    </_SheetShell>
  );
}

/* ── PLAN TAB — segmented Fixed / Projects ────────────────────────── */
function FinancePlanTab(props) {
  const [view, setView] = React.useState('fixed');
  return (
    <div style={{ padding: '12px 18px 24px', display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', paddingTop: 4 }}>
        <div>
          <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '-0.02em' }}>Plan</div>
          <div style={{ fontSize: 12.5, color: 'var(--muted)', marginTop: 2, fontWeight: 500 }}>Fixed expenses, installments &amp; projects</div>
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4, background: 'var(--surface-2)', borderRadius: 12, padding: 4, border: '1px solid var(--border)' }}>
        {[{ k: 'fixed', label: 'Fixed & Installments' }, { k: 'projects', label: 'Projects' }].map(t => (
          <button key={t.k} onClick={() => setView(t.k)} style={{
            padding: '8px 0', borderRadius: 9, border: 0, font: 'inherit', fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
            background: view === t.k ? 'var(--surface)' : 'transparent', color: view === t.k ? 'var(--text)' : 'var(--muted)',
            boxShadow: view === t.k ? 'var(--shadow-sm)' : 'none' }}>{t.label}</button>
        ))}
      </div>
      {view === 'fixed'
        ? <FixedExpensesSection fixed={props.fixed} projects={props.projects} categories={props.categories} accounts={props.accounts}
            usdRate={props.usdRate} onAdd={props.onAddFixed} onEdit={props.onEditFixed} onMarkPaid={props.onMarkPaid}/>
        : <FinanceProjectsSection projects={props.projects} txns={props.txns} fixed={props.fixed} categories={props.categories}
            onAdd={props.onAddProject} onEdit={props.onEditProject} onOpen={props.onOpenProject}/>}
      <div style={{ height: 60 }}/>
    </div>
  );
}

Object.assign(window, {
  FinanceHome, FinanceTxns, FinanceBudgets, FinanceInsights, FinanceAddSheet, TransferAddSheet, TxnDetailSheet,
  TxnCard, AccountCard, FinStatTile, prettyDateShort, UsdEgpRateWidget,
  FinanceAccountsTab, AccountAddEditSheet, FinanceCategories, CategoryAddEditSheet, SubcategoryAddEditSheet,
  GoldPriceWidget, GoldStatsCard, GoldHoldingCard, GoldAddEditSheet,
  FinancePlanTab, FinanceProjectsSection, ProjectAddEditSheet, ProjectDetailSheet,
  FixedExpensesSection, FixedAddEditSheet, MarkPaidSheet, _FinBars,
  TimeFilterBar, ManageFiltersSheet, _niceYTicks, _axisFmt,
});
