/* Clear Mint — Core pages: Dashboard, Accounts, Transactions, Budget */
const { MetricCard, ChartCard, DataTable, DonutChart, TrendChart, ProgressBar, Legend } = window;
const { KpiRow, ChangeCell, StatusPill, CategoryBar, ActivityList, Tabs, VBars, CMC } = window;
const Ip = window.CMIcon;
const { G, PU, AM, BL, SL, RD, TEAL, PINK, INDIGO } = CMC;

const nwTrend = [620,628,640,668,662,690,700,712,735,742,758,770,772,790,798,810,822,818,835,845];
const nwX = ['May','Jun','Jul','Aug','Sep','Oct','Nov','Dec','Jan','Feb','Mar','Apr','May'];

/* ============================ DASHBOARD ============================ */
/* botanical plant for AI card */
function Plant() {
  return (
    <svg viewBox="0 0 130 140" width="100%" style={{display:'block'}}>
      <g fill="none" stroke="#2E7D5B" strokeWidth="2" strokeLinecap="round">
        <path d="M65 128 C65 96 63 70 60 52"/>
        <path d="M63 84 C46 80 38 66 38 52"/>
        <path d="M64 70 C82 68 92 56 94 42"/>
        <path d="M62 100 C48 98 41 88 40 76"/>
      </g>
      <g fill="#3E8C66">
        <path d="M38 52 C30 44 30 30 40 22 C50 32 50 46 38 52 Z" opacity=".9"/>
        <path d="M94 42 C104 36 106 22 98 12 C86 20 84 34 94 42 Z" opacity=".95"/>
        <path d="M40 76 C30 72 24 60 28 48 C40 54 46 66 40 76 Z" opacity=".8"/>
      </g>
      <g fill="#6BA886">
        <path d="M60 52 C52 40 54 26 66 18 C74 30 72 46 60 52 Z"/>
        <path d="M62 100 C74 96 84 100 88 110 C76 114 64 110 62 100 Z" opacity=".85"/>
      </g>
      <g fill="#D4A95D">
        <circle cx="100" cy="30" r="1.6"/><circle cx="34" cy="40" r="1.4"/>
        <circle cx="92" cy="58" r="1.4"/><circle cx="48" cy="24" r="1.2"/>
      </g>
    </svg>
  );
}

/* cash-flow stacked bar chart (income up / expenses down) */
function CashFlowChart({ days }) {
  const max = Math.max.apply(null, days.map(function(d){ return Math.max(d.inc||0, d.exp||0); })) || 1;
  return (
    <div>
      <div className="cf-chart">
        <div className="cf-base"/>
        {days.map((d,i)=>(
          <div className="cf-col" key={i}>
            <div className="cf-half"><div className="cf-up" style={{height:`${(d.inc||0)/max*100}%`}}/></div>
            <div className="cf-half bot"><div className="cf-dn" style={{height:`${(d.exp||0)/max*100}%`}}/></div>
          </div>
        ))}
      </div>
      <div className="cf-xlabels">{days.map((d,i)=><span key={i}>{d.label||d.d}</span>)}</div>
    </div>
  );
}

/* category → icon for recent-activity rows */
const CATICON = {
  'Income':<Ip.Dollar size={16}/>,'Salary':<Ip.Bank size={16}/>,'Housing':<Ip.House size={16}/>,
  'Groceries':<Ip.Cart size={16}/>,'Food & Dining':<Ip.Dining size={16}/>,'Dining':<Ip.Dining size={16}/>,
  'Transportation':<Ip.Transport size={16}/>,'Utilities':<Ip.Bolt size={16}/>,'Insurance':<Ip.Shield size={16}/>,
  'Shopping':<Ip.Cart size={16}/>,'Entertainment':<Ip.Film size={16}/>,'Subscriptions':<Ip.Repeat size={16}/>,
  'Kids':<Ip.Heart size={16}/>,'Transfer':<Ip.Transfer size={16}/>,'Credit Card Payment':<Ip.Card size={16}/>,
  'Benefits':<Ip.Dollar size={16}/>,'Other':<Ip.Wallet size={16}/>
};
function catIcon(c){ return CATICON[c] || <Ip.Wallet size={16}/>; }

const SPEND = [
  {color:'#1B5E4A',label:'Housing',pct:'32%',value:'$1,650'},
  {color:'#2E7D5B',label:'Groceries',pct:'18%',value:'$930'},
  {color:'#6BA886',label:'Transportation',pct:'14%',value:'$720'},
  {color:'#D4A95D',label:'Dining',pct:'11%',value:'$570'},
  {color:'#B7C2A8',label:'Entertainment',pct:'8%',value:'$410'},
  {color:'#C9B68A',label:'Others',pct:'17%',value:'$880'},
];
const CFDAYS = [
  {d:'May 12',inc:6.2,exp:4.1},{d:'May 13',inc:5.8,exp:3.6},{d:'May 14',inc:6.0,exp:4.4},
  {d:'May 15',inc:7.4,exp:4.0},{d:'May 16',inc:5.6,exp:3.4},{d:'May 17',inc:6.8,exp:4.6},{d:'May 18',inc:6.2,exp:3.8},
];

/* Dashboard quick-import: Upload OFX / QFX / CSV statement straight from the overview */
function DashUpload() {
  const CM = window.useStore ? window.useStore() : window.CM;
  const accts = CM.S.accounts || [];
  const [acct, setAcct] = React.useState(0);
  const [status, setStatus] = React.useState(null); // {kind:'ok'|'err'|'busy', msg}
  const fileRef = React.useRef(null);
  const [confirm, setConfirm] = React.useState(null);
  const doneMsg = (res) => { const at = res.auto && res.auto.total; setStatus({kind:'ok', msg:'Added '+res.added+' transaction'+(res.added!==1?'s':'')+(res.dup?(' · '+res.dup+' duplicate'+(res.dup!==1?'s':'')+' skipped'):'')+(at?(' · '+at+' recurring item'+(at!==1?'s':'')+' detected'):'')}); };
  const onFile = (e) => {
    const f = e.target.files && e.target.files[0]; if(!f) return;
    const nm = (f.name||'').toLowerCase();
    // OFX / QFX → parse first and pop up the bank-account info to confirm before importing.
    if((nm.endsWith('.ofx')||nm.endsWith('.qfx')) && window.CMimport && window.CMimport.parseOFX){
      setStatus({kind:'busy', msg:'Reading '+f.name+'…'});
      const fr = new FileReader();
      fr.onload = (ev)=>{ try{ const parsed = window.CMimport.parseOFX(ev.target.result);
        if(!parsed.rows.length){ setStatus({kind:'err', msg:'No transactions found in this file.'}); return; }
        setConfirm({ rows:parsed.rows, meta:parsed.meta, fname:f.name }); setStatus(null);
      }catch(err){ setStatus({kind:'err', msg:(err&&err.message)||'Could not read OFX'}); } };
      fr.onerror = ()=>setStatus({kind:'err', msg:'Could not read file.'});
      fr.readAsText(f); e.target.value=''; return;
    }
    setStatus({kind:'busy', msg:'Importing '+f.name+'…'});
    window.CMimport.importFile(f, accts.length ? 'bank' : null, accts.length ? parseInt(acct) : null).then(function(res){
      const acctName = (accts[acct] && accts[acct].name) || 'account';
      doneMsg(res);
      try { CM.logActivity && CM.logActivity('imported '+res.added+' transaction'+(res.added!==1?'s':''), acctName+' · '+f.name, {cat:'import'}); } catch(_e){}
    }).catch(function(err){ setStatus({kind:'err', msg:(err&&err.message)||'Import failed'}); });
    e.target.value='';
  };
  return (
    <div className="dash-import cm-mb-24">
      <span className="di-ic"><Ip.ArrowUp size={22}/></span>
      <div className="di-copy">
        <div className="di-title">Upload OFX / CSV</div>
        <div className="di-sub">Import an OFX, QFX or CSV statement from your bank — transactions are categorized automatically.</div>
      </div>
      {status && <span className={`di-status di-status--${status.kind}`}>
        {status.kind==='ok' && <Ip.Check size={14}/>}
        {status.kind==='err' && <Ip.Info size={14}/>}
        {status.msg}
      </span>}
      <div className="di-controls">
        {accts.length>0 && <select className="cm-select" value={acct} onChange={e=>setAcct(e.target.value)} aria-label="Destination account">
          {accts.map((a,i)=><option key={i} value={i}>{a.name}{a.last4?' ····'+a.last4:''}</option>)}
        </select>}
        <button className="cm-btn cm-btn-primary" onClick={()=>fileRef.current&&fileRef.current.click()}>
          <Ip.ArrowUp size={16}/>Upload OFX / CSV
        </button>
        <input ref={fileRef} type="file" accept=".ofx,.qfx,.csv,.qif,.txt,.pdf" style={{display:'none'}} onChange={onFile}/>
      </div>
      {confirm && <ImportConfirmModal CM={CM} rows={confirm.rows} meta={confirm.meta} fname={confirm.fname} defaultType="bank"
        onCancel={()=>{ setConfirm(null); setStatus({kind:'err', msg:'Import cancelled.'}); }}
        onDone={(res)=>{ const fn=confirm.fname; setConfirm(null); doneMsg(res); try{ CM.logActivity && CM.logActivity('imported '+res.added+' transaction'+(res.added!==1?'s':''), fn, {cat:'import'}); }catch(e){} }}/>}
    </div>
  );
}

/* Upcoming Bills — dashboard feed: bills, loan/mortgage payments, credit-card due dates,
   subscriptions and insurance renewals over the next 30 days (real data only). */
function UpcomingBillsCard({ CM }) {
  const fmt = CM.M;
  const t0 = new Date(); t0.setHours(0,0,0,0); const t30 = t0.getTime() + 30*864e5;
  const items = [];
  (CM.S.bills||[]).forEach(function(b){ if(b.paid||!b.dueDate) return; const ts=CM.toTs(b.dueDate); if(ts==null||ts>t30) return;
    items.push({ name:b.desc||b.name||'Bill', due:ts, amt:+b.amount||0, src:b._loan?'Loan / Mortgage':(b.cat==='Insurance'?'Insurance':'Bill'), overdue:ts<t0.getTime() }); });
  (CM.S.cards||[]).forEach(function(c){ if(!c.dueDate) return; const ts=CM.toTs(c.dueDate); if(ts==null||ts>t30||ts<t0.getTime()-60*864e5) return;
    items.push({ name:(c.name||'Credit Card')+(c.last4?' ····'+c.last4:''), due:ts, amt:c.minPayment!=null?+c.minPayment:null, src:'Credit card', overdue:ts<t0.getTime() }); });
  (CM.S.subscriptions||[]).forEach(function(s){ if(s.active===false||!s.nextDate) return; const ts=CM.toTs(s.nextDate); if(ts==null||ts>t30||ts<t0.getTime()) return;
    items.push({ name:s.name||'Subscription', due:ts, amt:+s.price||0, src:'Subscription', overdue:false }); });
  (CM.S.insurance||[]).forEach(function(p){ const ts=CM.toTs(p.renewal); if(ts==null||ts>t30||ts<t0.getTime()) return;
    items.push({ name:(p.name||p.type||'Policy')+' renewal', due:ts, amt:p.premium!=null?+p.premium:null, src:'Insurance', overdue:false }); });
  items.sort(function(a,b){ return a.due-b.due; });
  if(!items.length) return null;
  return (
    <ChartCard title="Upcoming Bills" className="cm-mb-24" action={<a href="#/bills" className="t-label text-green" style={{cursor:'pointer'}}>Manage bills</a>}>
      <div style={{overflowX:'auto'}}><table className="cm-table" style={{minWidth:560}}>
        <thead><tr><th>Due</th><th>Name</th><th>Source</th><th className="right">Amount</th><th>Status</th></tr></thead>
        <tbody>{items.slice(0,8).map(function(u,i){ return (<tr key={i}>
          <td><span className="num" style={{fontWeight:600,color:'var(--ink-600)',whiteSpace:'nowrap'}}>{CM.FDshort(new Date(u.due))}</span></td>
          <td><span style={{fontWeight:700,color:'var(--ink-900)'}}>{u.name}</span></td>
          <td><span className="cm-pill cm-pill-slate" style={{fontSize:11,fontWeight:700}}>{u.src}</span></td>
          <td style={{textAlign:'right'}}><span className="num" style={{fontWeight:700}}>{u.amt!=null?fmt(u.amt):'—'}</span></td>
          <td>{u.overdue?<StatusPill tone="red">Overdue</StatusPill>:<StatusPill tone="amber">Upcoming</StatusPill>}</td>
        </tr>); })}</tbody>
      </table></div>
    </ChartCard>
  );
}

function PageDashboard() {
  const CM = window.useStore ? window.useStore() : window.CM;
  const d = CM && CM.dashboard ? CM.dashboard() : null;
  if (!d) return <div className="t-body">Loading…</div>;
  const fmt = CM.M0, fmt2 = CM.M;
  // net worth delta from series
  const ser = d.nwSeries || [];
  const nwPrev = ser.length>1 ? ser[ser.length-2] : (d.netWorth||1);
  const nwPct = nwPrev ? ((d.netWorth-nwPrev)/Math.abs(nwPrev)*100) : 0;
  const cfMonthTotal = d.cashflow6.length ? d.cashflow6[d.cashflow6.length-1] : {inc:0,exp:0};
  const spendItems = d.spending.items.map(s=>({color:s.color,label:s.label,pct:s.pct+'%',value:fmt2(s.value)}));
  const score = d.score;
  const scoreLabel = score>=85?'Excellent':score>=70?'Strong':score>=55?'Good':'Building';
  const xl = d.cashflow6.map(m=>m.mo);
  return (
    <div>
      {/* Quick import */}
      <DashUpload/>
      {/* KPI strip */}
      {/* secondary KPI strip — cash, flow, upcoming, assets & liabilities */}
      {(() => {
        const totalCash = CM.totalCash ? CM.totalCash() : (CM.S.accounts||[]).reduce((s,a)=>s+(+a.balance||0),0);
        const totalAssets = CM.grossAssets ? CM.grossAssets() : (totalCash + (d.investments||0));
        const totalLiab = CM.totalLiabilities ? CM.totalLiabilities() : (d.totalLiab||0);
        const today = CM.today ? CM.today() : new Date();
        let upBills = 0, upBillCount = 0;
        try { const ub = CM.upcomingBills ? CM.upcomingBills() : (CM.S.bills||[]); upBillCount = ub.length; upBills = ub.reduce((s,b)=>s+(+b.amount||0),0); } catch(e){}
        // upcoming earnings: future-dated earnings, else recurring monthly income
        let upEarn = 0, upEarnCount = 0;
        try {
          const fut = (CM.S.earnings||[]).filter(e=>{ const dt=e.date?new Date(e.date):null; return dt && dt>=today; });
          upEarnCount = fut.length;
          upEarn = fut.reduce((s,e)=>s+(+e.amount||0),0);
          if (!upEarn && CM.monthlyIncome) upEarn = CM.monthlyIncome();
        } catch(e){}
        return (
          <div className="cm-grid-3 cm-mb-24" style={{display:'grid',gridTemplateColumns:'repeat(6,1fr)',gap:16}}>
            <MetricCard label="Total Cash" value={fmt(totalCash)} sub={(CM.S.accounts||[]).length+' accounts'} icon={<Ip.Wallet size={20}/>}/>
            <MetricCard label="Net Cash Flow" value={(d.cashFlow>=0?'+':'-')+fmt(Math.abs(d.cashFlow)).slice(1)} labelColor={d.cashFlow>=0?'var(--ink-700)':'var(--red-500)'}
              delta={{dir:d.cashFlow>=0?'pos':'neg',text:d.cashFlow>=0?'surplus':'deficit'}} sub="this month" icon={<Ip.Transfer size={20}/>}/>
            <MetricCard label="Upcoming Bills & Expenses" value={fmt(upBills)} sub={upBillCount+' due soon'} icon={<Ip.Wallet size={20}/>}/>
            <MetricCard label="Upcoming Earnings" value={fmt(upEarn)} sub={upEarnCount?upEarnCount+' expected':'monthly income'} icon={<Ip.Dollar size={20}/>}/>
            <MetricCard label="Total Assets" value={fmt(totalAssets)} sub="cash, investments & property" icon={<Ip.Grow size={20}/>}/>
            <MetricCard label="Total Liabilities" value={fmt(totalLiab)} sub="loans & card debt" icon={<Ip.Bank size={20}/>}/>
          </div>
        );
      })()}

      <div className="cm-grid-4 cm-mb-24">
        <MetricCard label="Net Worth" value={fmt(d.netWorth)} delta={{dir:nwPct>=0?'pos':'neg',text:Math.abs(nwPct).toFixed(1)+'%'}} sub="vs last month"
          icon={<Ip.Grow size={20}/>}/>
        <MetricCard label="Monthly Cash Flow" value={(d.cashFlow>=0?'+':'-')+fmt(Math.abs(d.cashFlow)).slice(1)} labelColor={d.cashFlow>=0?'var(--ink-700)':'var(--red-500)'}
          delta={{dir:d.cashFlow>=0?'pos':'neg',text:d.cashFlow>=0?'surplus':'deficit'}} sub="this month"
          icon={<Ip.Wallet size={20}/>}/>
        <MetricCard label="Investments" value={fmt(d.investments)} sub={(CM.S.investments||[]).length+' holdings · portfolio value'}
          icon={<Ip.Investments size={20}/>}/>
        <MetricCard label="Total Debt" value={fmt(d.totalLiab)} sub={((CM.S.liabilities||[]).length+(CM.S.cards||[]).length)+' accounts owed'}
          icon={<Ip.Bank size={20}/>}/>
      </div>

      
      {/* quick actions · popular reports · accounts · recent transactions · subscriptions */}
      {window.DashExtras ? <window.DashExtras/> : null}

      <UpcomingBillsCard CM={CM}/>

      {/* main + rail */}
      <div className="dash-grid">
        <div className="cm-stack">
          <div className="dash-cf">
            <ChartCard title="Cash Flow">
              <div className="cf-head">
                <div>
                  <span className="num" style={{fontFamily:'var(--font-serif)',fontSize:30,fontWeight:700,color:'var(--ink-900)'}}>{(d.cashFlow>=0?'+':'-')+fmt(Math.abs(d.cashFlow)).slice(1)}</span>
                  <span className="t-caption" style={{marginLeft:8}}>this month</span>
                </div>
                <div className="cf-legend">
                  <span><span className="cf-dot" style={{background:'var(--forest)'}}/>Income</span>
                  <span><span className="cf-dot" style={{background:'var(--gold)'}}/>Expenses</span>
                </div>
              </div>
              {window.CMChart ? <window.CMChart.CMBars height={210}
                x={d.cashflow6.map(m=>m.mo)}
                series={[{name:'Income',data:d.cashflow6.map(m=>Math.round(m.inc||0)),color:'#16A34A'},{name:'Expenses',data:d.cashflow6.map(m=>Math.round(m.exp||0)),color:'#D4AF37'}]}/>
                : <CashFlowChart days={d.cashflow6.map(m=>({label:m.mo,inc:m.inc,exp:m.exp}))}/>}
            </ChartCard>

            <div className="ai-card">
              {(() => {
                const cf = d.cashFlow, sv = d.savingsRate!=null?d.savingsRate:(d.income?cf/d.income*100:0);
                // mood + robot pose + vertical position driven by the finance situation
                let mood, robot, lift, ring;
                if (cf > 0 && sv >= 15) { mood='thriving'; robot='assets/robot-cheer.png'; lift=-18; ring='rgba(212,175,55,.30)'; }
                else if (cf >= 0) { mood='steady'; robot='assets/robot-lotus.png'; lift=-4; ring='rgba(126,216,174,.26)'; }
                else if (cf > -(d.income||1)*0.1) { mood='watch'; robot='assets/robot-sheet.png'; lift=6; ring='rgba(212,175,55,.22)'; }
                else { mood='alert'; robot='assets/robot-scan.png'; lift=14; ring='rgba(195,91,91,.26)'; }
                const head = cf>=0 ? "You're on track to save" : "You're running a deficit of";
                const note = mood==='thriving' ? 'Excellent — a strong savings month. Keep the momentum going.'
                  : mood==='steady' ? 'this month — a healthy, balanced month.'
                  : mood==='watch' ? 'this month — trim a little discretionary spending to get ahead.'
                  : 'this month — review your largest expenses with Mint.';
                return (
                  <React.Fragment>
                    <div style={{flex:1}}>
                      <div className="ai-head"><Ip.Spark size={18}/>AI Insight</div>
                      <div className="ai-text">{head}</div>
                      <div className="ai-amt">{fmt(Math.abs(cf))}</div>
                      <div className="ai-text" style={{marginTop:6,marginBottom:16}}>{note}</div>
                      <a href="#/ai" className="cm-btn cm-btn-primary" style={{padding:'9px 16px',textDecoration:'none'}}>Ask Mint</a>
                    </div>
                    <div className="ai-robot-wrap" data-mood={mood}>
                      <div className="ai-robot-ring" style={{background:'radial-gradient(circle at 50% 40%,'+ring+',transparent 70%)'}}/>
                      <img className="ai-robot" src={robot} alt="Mint AI" style={{transform:'translateY('+lift+'px)'}}/>
                      <div className="ai-podium"/>
                    </div>
                  </React.Fragment>
                );
              })()}
            </div>
          </div>

          <div className="cm-grid-3">
            <ChartCard title="Top Spending" action={<a href="#/analytics" className="t-label text-green" style={{cursor:'pointer'}}>View All</a>}>
              <div style={{display:'flex',justifyContent:'center',marginBottom:16,position:'relative'}}>
                <div style={{position:'absolute',inset:0,display:'flex',alignItems:'center',justifyContent:'center',pointerEvents:'none'}}>
                  <div style={{width:118,height:118,borderRadius:'50%',background:'radial-gradient(circle at 50% 38%,rgba(16,145,95,.08),transparent 70%)'}}/>
                </div>
              <div style={{marginBottom:8}}>
                {window.CMChart ? <window.CMChart.CMDonut height={184}
                  data={d.spending.items.map(s=>({value:s.value,label:s.label,color:s.color}))}
                  centerValue={fmt(d.spending.total)} centerLabel={(CM.S.bankTx||[]).length?'spent this month':'no spending yet'}/>
                : <DonutChart size={172} thickness={15} gap={0.02} rounded={true}
                  center={{value:fmt(d.spending.total),label:(CM.S.bankTx||[]).length?'spent this month':'no spending yet'}}
                  segments={d.spending.items.length?d.spending.items.map(s=>({value:s.value,color:s.color})):[{value:1,color:'var(--line,#ECE6D8)'}]}/>}
              </div>
              </div>
              <div className="cm-stack" style={{gap:11}}>
                {(d.spending.items.length?d.spending.items.slice(0,5):[]).map((s,i)=>(
                  <div key={i} style={{display:'flex',alignItems:'center',gap:10}}>
                    <span style={{width:10,height:10,borderRadius:3,background:s.color,flex:'0 0 10px'}}/>
                    <span style={{flex:1,fontSize:13,fontWeight:600,color:'var(--ink-700)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{s.label}</span>
                    <span className="num" style={{fontSize:11.5,fontWeight:700,color:'var(--ink-400)',background:'var(--paper,#F4F0E6)',borderRadius:999,padding:'2px 8px'}}>{s.pct}%</span>
                    <span className="num" style={{fontSize:13,fontWeight:700,color:'var(--ink-900)',minWidth:54,textAlign:'right'}}>{fmt2(s.value)}</span>
                  </div>
                ))}
                {!d.spending.items.length && <div style={{textAlign:'center',color:'var(--ink-400)',fontSize:13,padding:'4px 0 8px'}}>Import transactions to see your top categories.</div>}
              </div>
            </ChartCard>

            <ChartCard title="Goals" action={<a href="#/goals" className="t-label text-green" style={{cursor:'pointer'}}>View All</a>}>
              <div className="cm-stack" style={{gap:20}}>
                {d.goals.slice(0,3).map((g,i)=>(
                  <div key={i}>
                    <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}>
                      <span className="cm-act-ic" style={{width:32,height:32,flex:'0 0 32px',background:'#EEF2EC',color:g.color||'var(--forest)'}}><Ip.Goals size={16}/></span>
                      <div style={{flex:1,minWidth:0}}>
                        <div className="t-h3" style={{fontSize:14,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{g.name}</div>
                        <div className="t-caption num">{fmt(g.saved)} <span style={{color:'var(--ink-400)'}}>of {fmt(g.target)}</span></div>
                      </div>
                      <span className="num" style={{fontWeight:700,color:'var(--forest)'}}>{g.pct}%</span>
                    </div>
                    <ProgressBar pct={g.pct} color={g.color||'var(--forest)'} showPct={false}/>
                  </div>
                ))}
              </div>
            </ChartCard>

            <ChartCard title="Net Worth" action={<a href="#/networth" className="t-label text-green" style={{cursor:'pointer'}}>View All</a>}>
              <div style={{display:'flex',alignItems:'baseline',gap:8,marginBottom:10}}>
                <span className="num" style={{fontFamily:'var(--font-serif)',fontSize:26,fontWeight:700,color:'var(--ink-900)'}}>{fmt(d.netWorth)}</span>
                <span className={`cm-delta ${nwPct>=0?'pos':'neg'}`}>{nwPct>=0?<Ip.ArrowUp size={13}/>:<Ip.ArrowDown size={13}/>}{Math.abs(nwPct).toFixed(1)}%</span>
              </div>
              {window.CMChart ? <window.CMChart.CMArea height={172}
                data={(ser.length?ser:[d.netWorth]).map(v=>Math.round(v))} x={(ser.length?ser:[0]).map((_,i)=>'')}
                color="#0F6B4F" color2="#3ECF8E"/>
                : <TrendChart data={ser.length?ser:[d.netWorth]} width={320} height={170}
                color="var(--forest)" yLabels={[fmt(Math.max.apply(null,ser)||d.netWorth),'',fmt(0)]}
                xLabels={['','','','','']}/>}
            </ChartCard>
          </div>
        </div>

        {/* right rail */}
        <div className="cm-stack">
          <div className="cm-card-dark cm-card-pad wscore">
            <div className="lbl">Family Wealth Score <Ip.Info size={14}/></div>
            <div style={{display:'flex',alignItems:'baseline',gap:10,margin:'10px 0 2px'}}>
              <span className="big num">{score}</span><span className="badge">{scoreLabel}</span>
            </div>
            <div className="wscore-bar"><i style={{width:score+'%'}}/></div>
            <p>Based on your savings rate, debt load and emergency cushion.</p>
            <a href="#/analytics" className="cm-btn cm-btn-gold" style={{padding:'9px 16px',textDecoration:'none'}}>View Details</a>
            <svg className="wscore-shield" viewBox="0 0 48 56" fill="none">
              <path d="M24 2 4 9v18c0 14 11 23 20 27 9-4 20-13 20-27V9z" stroke="var(--gold)" strokeWidth="1.5"/>
              <path d="M24 20c-5 0-8 5-8 5s3 5 8 5 8-5 8-5-3-5-8-5z" fill="var(--gold)" opacity=".5"/>
            </svg>
          </div>

          <ChartCard title="Upcoming Bills" action={<a href="#/bills" className="t-label text-green" style={{cursor:'pointer'}}>View All</a>}>
            {d.upcomingBills && d.upcomingBills.length ? (
              <ActivityList items={d.upcomingBills.slice(0,5).map(function(b){
                const BL = window.BrandLogo;
                return { icon: BL?<BL name={b.desc||b.name} size={36}/>:catIcon(b.cat), tint:'transparent', color:CM.catColor(b.cat),
                  title:b.desc||b.name||'Bill', sub:(b.dueDate?('Due '+CM.FDshort(new Date(b.dueDate))):'Upcoming')+(b.cat?(' · '+b.cat):''),
                  amt:'-'+fmt2(b.amount||0), dir:'neg', amtColor:'var(--ink-900)' };
              })}/>
            ) : <div className="t-caption" style={{textAlign:'center',padding:'20px 0'}}>No upcoming bills.</div>}
          </ChartCard>

          <ChartCard title="Recent Activity" action={<a href="#/transactions" className="t-label text-green" style={{cursor:'pointer'}}>View All</a>}>
            <ActivityList items={d.recent.map(function(r){
              const isD = r.dr>0;
              const BL = window.BrandLogo;
              return { icon: BL?<BL name={r.desc||r.cat} size={36}/>:catIcon(r.cat), tint:'transparent', color:CM.catColor(r.cat),
                title:r.desc||'—', sub:CM.FDshort(r.date)+(r.cat?' · '+r.cat:''),
                amt:(isD?'-':'+')+fmt2(r.dr||r.cr||0), dir:isD?'neg':'pos',
                amtColor:isD?'var(--ink-900)':undefined };
            })}/>
          </ChartCard>

          <div className="quote-card">
            <div className="mark">&ldquo;</div>
            <div className="qt">A budget is telling your money where to go instead of wondering where it went.</div>
            <div className="by">— Dave Ramsey</div>
          </div>
        </div>
      </div>

      {/* bottom banner */}
      <div className="dash-banner">
        <div>
          <h3>Built for your family's future</h3>
          <div className="lead">Clear Mint helps you make smart decisions today for a stronger tomorrow.</div>
        </div>
        {[{i:<Ip.User size={20}/>,t:'Family Focused',s:'Manage together'},
          {i:<Ip.Spark size={20}/>,t:'AI Powered',s:'Smarter decisions'},
          {i:<Ip.Shield size={20}/>,t:'Secure & Private',s:'Bank-level security'},
          {i:<Ip.Grow size={20}/>,t:'Long Term Wealth',s:'Build your legacy'}].map((f,i)=>(
          <div className="dash-feat" key={i}>
            <div className="fi">{f.i}</div><div className="ft">{f.t}</div><div className="fs">{f.s}</div>
          </div>
        ))}
      </div>
    </div>
  );
}
window.PageDashboard = PageDashboard;

/* ============================ ACCOUNTS ============================ */
function addAccountPrompt(){
  if(window.CMAccess && !CMAccess.requireWithinLimit('bankAccounts',(window.S.accounts||[]).length,'More bank accounts')) return;
  const name = window.prompt('Account name (e.g. Everyday Chequing)'); if(!name) return;
  const bank = window.prompt('Bank (e.g. TD Bank, RBC, EQ Bank)','TD Bank')||'';
  const type = window.prompt('Type (Chequing / Savings)','Chequing')||'Chequing';
  const bal = parseFloat(window.prompt('Current balance','0'))||0;
  const last4 = (window.prompt('Last 4 digits (optional)','')||'').replace(/\D/g,'').slice(-4);
  window.CM.mutate(function(){ window.S.accounts.push({ name:name, bank:bank, type:type, last4:last4, currency:'CAD', opening:bal, balance:bal }); });
  try { window.CM.logActivity && window.CM.logActivity('linked an account', name+(bank?' · '+bank:''), {cat:'account'}); } catch(e){}
}
// A transaction belongs to account #i if it's linked by index, OR it's an unindexed orphan whose
// account name matches (catches OFX rows imported with acctIdx=null) — so deleting the account removes
// every transaction the file brought in.
function txBelongsToAcct(r, i, name, acctLen){
  if(r.acctIdx===i) return true;
  return (r.acctIdx==null || r.acctIdx<0 || r.acctIdx>=acctLen) && !!name && r.account===name;
}
function delAccountIdx(i){
  const a = window.S.accounts[i]; if(!a) return;
  const accts = window.S.accounts||[];
  const belongs = function(r){ return txBelongsToAcct(r, i, a.name, accts.length); };
  const n = (window.S.bankTx||[]).filter(belongs).length;
  if(!window.confirm('Delete "'+a.name+'"?'+(n?'\nThis also removes '+n+' linked transaction'+(n===1?'':'s')+' from this account.':''))) return;
  window.CM.mutate(function(){
    window.S.bankTx = (window.S.bankTx||[]).filter(function(r){ return !belongs(r); });
    window.S.bankTx.forEach(function(r){ if(r.acctIdx>i) r.acctIdx--; });
    window.S._importBatches = (window.S._importBatches||[]).filter(function(b){ return b.acctIdx!==i; });
    (window.S._importBatches||[]).forEach(function(b){ if(b.acctIdx!=null && b.acctIdx>i) b.acctIdx--; });
    window.S.accounts.splice(i,1);
  });
}
/* ---- shared field wrapper for the account/import modals (hoisted: never define a
   component inside another component's render) ---- */
const ACCT_INP = {width:'100%',fontFamily:'inherit',fontSize:13.5,color:'var(--ink-900)',border:'1px solid var(--line)',borderRadius:10,padding:'9px 11px',background:'#fff',outline:'none',boxSizing:'border-box'};
function AcctField({ label, children, full, hint }){
  return <div style={full?{gridColumn:'1 / -1'}:undefined}>
    <div style={{fontSize:11,fontWeight:700,letterSpacing:'.06em',textTransform:'uppercase',color:'var(--ink-400)',margin:'0 0 5px'}}>{label}</div>
    {children}
    {hint?<div style={{fontSize:11,color:'var(--ink-400)',marginTop:4}}>{hint}</div>:null}
  </div>;
}

/* ---- Edit / Add Bank Account modal (pattern copied from CardModal). On save we mutate
   the account then call CMimport.recalcAccounts() so running balances re-derive. ---- */
function AccountModal({ CM, idx, onClose }){
  const adding = idx==null || idx<0;
  const a = adding ? {} : ((CM.S.accounts||[])[idx]||{});
  const num = v => (v!=null&&v!=='')?String(v):'';
  // net of this account's linked transactions (used to tie opening ↔ current)
  const net = React.useMemo(()=>{
    if(adding) return 0;
    return (CM.S.bankTx||[]).filter(r=>r.acctIdx===idx)
      .reduce((s,r)=>s+(+r.cr||0)-(+r.dr||0),0);
  },[adding,idx]);
  const txCount = adding?0:(CM.S.bankTx||[]).filter(r=>r.acctIdx===idx).length;
  const [f,setF]=React.useState(()=>({
    name:a.name||'', bank:a.bank||'', type:a.type||'Chequing', last4:a.last4||'',
    opening:num(a.opening), balance:num(a.balance), draftLimit:num(a.draftLimit!=null?a.draftLimit:a.overdraftLimit),
    currency:a.currency||'CAD', notes:a.notes||''
  }));
  const set=k=>e=>{ const v=e.target.value; setF(s=>({...s,[k]:v})); };
  // keep current balance in step with opening while the user edits opening (only when there are tx)
  const onOpening=e=>{ const v=e.target.value; setF(s=>{ const ns={...s,opening:v}; if(v!==''&&!isNaN(parseFloat(v))) ns.balance=String(Math.round((parseFloat(v)+net)*100)/100); return ns; }); };
  // editing current balance back-derives the opening so the running balance still ties out
  const onCurrent=e=>{ const v=e.target.value; setF(s=>{ const ns={...s,balance:v}; if(v!==''&&!isNaN(parseFloat(v))) ns.opening=String(Math.round((parseFloat(v)-net)*100)/100); return ns; }); };
  const save=()=>{
    const last4=(f.last4||'').replace(/\D/g,'').slice(-4);
    const opening = f.opening===''?0:(parseFloat(f.opening)||0);
    const rec={ name:f.name||((f.bank||'Account')+(last4?(' ····'+last4):'')), bank:f.bank||'', type:f.type||'Chequing',
      last4:last4, opening:opening, _openingAuto:false, currency:f.currency||'CAD',
      draftLimit:(f.draftLimit===''?null:(parseFloat(f.draftLimit)||0)), notes:f.notes||'' };
    if(f.balance!=='' ) rec.balance=parseFloat(f.balance)||0;
    CM.mutate(function(){ if(adding){ (CM.S.accounts=CM.S.accounts||[]).push(rec); } else { Object.assign(CM.S.accounts[idx], rec); } });
    try{ window.CMimport&&window.CMimport.recalcAccounts&&window.CMimport.recalcAccounts(); }catch(e){}
    try{ CM.save&&CM.save(); CM.emit&&CM.emit(); }catch(e){}
    try{ CM.logActivity&&CM.logActivity(adding?'added a bank account':'updated a bank account', rec.name, {cat:'account'}); }catch(e){}
    onClose();
  };
  const del=()=>{ if(adding){ onClose(); return; }
    const accts=CM.S.accounts||[];
    const belongs=r=>txBelongsToAcct(r, idx, a.name, accts.length);
    const n=(CM.S.bankTx||[]).filter(belongs).length;
    if(!window.confirm('Delete "'+(a.name||'this account')+'"?'+(n?('\nThis also removes '+n+' linked transaction'+(n===1?'':'s')+' from this account.'):''))) return;
    CM.mutate(function(){
      CM.S.bankTx=(CM.S.bankTx||[]).filter(r=>!belongs(r));
      CM.S.bankTx.forEach(r=>{ if(r.acctIdx>idx) r.acctIdx--; });
      CM.S._importBatches=(CM.S._importBatches||[]).filter(b=>b.acctIdx!==idx);
      (CM.S._importBatches||[]).forEach(b=>{ if(b.acctIdx!=null && b.acctIdx>idx) b.acctIdx--; });
      CM.S.accounts.splice(idx,1);
    });
    try{ window.CMimport&&window.CMimport.recalcAccounts&&window.CMimport.recalcAccounts(); CM.save&&CM.save(); CM.emit&&CM.emit(); }catch(e){}
    onClose();
  };
  const TYPES=['Chequing','Checking','Savings','Money Market','Joint','Business','Other'];
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,zIndex:1000,background:'rgba(13,33,26,.45)',display:'flex',alignItems:'center',justifyContent:'center',padding:20}}>
      <div onClick={e=>e.stopPropagation()} className="cm-card" style={{width:600,maxWidth:'100%',maxHeight:'92vh',overflowY:'auto',borderRadius:18,padding:'22px 24px',boxShadow:'0 24px 64px rgba(13,42,33,.28)'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:4}}>
          <div className="t-h2" style={{fontSize:18}}>{adding?'Add Bank Account':'Edit Bank Account'}</div>
          <button onClick={onClose} aria-label="Close" style={{border:0,background:'none',cursor:'pointer',color:'var(--ink-400)',fontSize:20,lineHeight:1,padding:4}}>×</button>
        </div>
        <div className="t-caption" style={{marginBottom:16}}>{adding?'Add an account manually — you can import statements into it later.':('Update '+(a.name||'this account')+'. The opening balance anchors the running balance derived from '+txCount+' linked transaction'+(txCount!==1?'s':'')+'.')}</div>
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14}}>
          <AcctField label="Account Name" full={true}><input style={ACCT_INP} value={f.name} onChange={set('name')} placeholder="e.g. Everyday Chequing"/></AcctField>
          <AcctField label="Bank"><input style={ACCT_INP} value={f.bank} onChange={set('bank')} placeholder="e.g. TD Canada Trust"/></AcctField>
          <AcctField label="Account Type"><select style={ACCT_INP} value={f.type} onChange={set('type')}>{TYPES.map(t=><option key={t} value={t}>{t}</option>)}</select></AcctField>
          <AcctField label="Last 4 Digits"><input style={ACCT_INP} value={f.last4} onChange={set('last4')} maxLength={4} placeholder="1234"/></AcctField>
          <AcctField label="Currency"><input style={ACCT_INP} value={f.currency} onChange={set('currency')} placeholder="CAD"/></AcctField>
          <AcctField label="Opening / Initial Balance ($)" hint={txCount?('auto-derived · '+CM.M(net)+' net from transactions'):null}><input style={ACCT_INP} type="number" value={f.opening} onChange={onOpening}/></AcctField>
          <AcctField label="Current Balance ($) — set this to your bank" hint={txCount?'Type your bank’s balance here; the opening adjusts automatically':null}><input style={ACCT_INP} type="number" value={f.balance} onChange={onCurrent}/></AcctField>
          <AcctField label="Overdraft / Draft Limit ($)"><input style={ACCT_INP} type="number" value={f.draftLimit} onChange={set('draftLimit')} placeholder="0"/></AcctField>
          <AcctField label="Notes" full={true}><textarea style={{...ACCT_INP,minHeight:56,resize:'vertical'}} value={f.notes} onChange={set('notes')} placeholder="Optional notes"/></AcctField>
        </div>
        <div style={{display:'flex',gap:10,marginTop:20,alignItems:'center'}}>
          {!adding && <button className="cm-btn cm-btn-ghost" style={{color:'var(--red-500)'}} onClick={del}>Delete Account</button>}
          <div style={{flex:1}}></div>
          <button className="cm-btn cm-btn-soft" onClick={onClose}>Cancel</button>
          <button className="cm-btn cm-btn-primary" onClick={save}>{adding?'Add Account':'Save Changes'}</button>
        </div>
      </div>
    </div>
  );
}

function PageAccounts() {
  const CM = window.useStore ? window.useStore() : window.CM;
  const [acctModal, setAcctModal] = React.useState(null);
  const BrandIcon = window.BrandIcon;
  const fmt = CM.M, fmt0 = CM.M0;
  const accts = CM.S.accounts||[], cards = CM.S.cards||[], invs = CM.S.investments||[], assets = CM.S.assets||[];
  const cash = CM.totalCash(), ccDebt = CM.totalCardDebt(), invVal = CM.calcInvestValue(), assetVal = CM.totalAssets();
  const netTotal = CM.netWorth();
  const realEstate = assets.filter(function(a){return /real estate|home|property/i.test(a.type||'');}).reduce(function(s,a){return s+(+a.value||0);},0);
  const vehicles = assetVal - realEstate;
  const donut = [
    {value:invVal,color:PU,label:'Investments'},
    {value:realEstate,color:BL,label:'Real Estate'},
    {value:cash,color:G,label:'Cash & Bank'},
    {value:vehicles,color:AM,label:'Vehicles'},
  ].filter(function(s){return s.value>0;});
  const donutTotal = donut.reduce(function(s,x){return s+x.value;},0)||1;
  return (
    <div>
      <KpiRow>
        <MetricCard label="Total Net Worth" labelColor="var(--green-600)" value={fmt0(netTotal)} sub={(accts.length+cards.length+invs.length)+' accounts'}/>
        <MetricCard label="Cash & Bank" value={fmt0(cash)} sub={accts.length+' account'+(accts.length!==1?'s':'')}/>
        <MetricCard label="Credit Card Debt" labelColor="var(--red-500)" value={fmt0(ccDebt)} sub={cards.length+' card'+(cards.length!==1?'s':'')}/>
        <MetricCard label="Investments" labelColor="var(--green-600)" value={fmt0(invVal)} sub={invs.length+' holding'+(invs.length!==1?'s':'')}/>
      </KpiRow>
      <div className="cm-grid-2a cm-mb-24">
        <ChartCard title="Net Worth Trend">
          {window.CMChart ? <window.CMChart.CMArea height={280}
            data={CM.netWorthSeries().map(v=>Math.round(v))} x={CM.netWorthSeries().map((_,i)=>'')}
            color="#0F6B4F" color2="#3ECF8E"/>
            : <TrendChart data={CM.netWorthSeries()} width={760} height={280}
            yLabels={[fmt0(Math.max.apply(null,CM.netWorthSeries())),'', fmt0(0)]} xLabels={['','','','','']}/>}
        </ChartCard>
        <ChartCard title="Balance by Type">
          <div className="cm-donut-card" style={{justifyContent:'center'}}>
            <DonutChart size={170} thickness={22} center={{value:fmt0(donutTotal),label:'Assets'}} segments={donut}/>
          </div>
          <div style={{marginTop:16}}><Legend items={donut.map(function(s){return {color:s.color,label:s.label,pct:Math.round(s.value/donutTotal*100)+'%',value:fmt0(s.value)};})}/></div>
        </ChartCard>
      </div>
      <div className="cm-grid-3">
        <ChartCard title="Cash & Bank">
          <div>
            {accts.map(function(a,i){return (
              <div className="cm-act" key={i}>
                <BrandIcon name={a.bank||a.name} size={36} radius={9}/>
                <div className="cm-act-body"><div className="cm-act-title">{a.name}</div><div className="cm-act-sub num">{(a.type||'Account')+(a.last4?' · ····'+a.last4:'')}</div></div>
                <div className="cm-act-right">
                  <div className="cm-act-amt num" style={{color:a.balance>=0?'var(--ink-900)':RD}}>{fmt(a.balance)}</div>
                  <div style={{display:'flex',gap:10,justifyContent:'flex-end'}}>
                    <div style={{fontSize:11,color:'var(--green-600)',fontWeight:700,cursor:'pointer'}} onClick={function(){setAcctModal({idx:i});}}>Edit</div>
                    <div style={{fontSize:11,color:'var(--ink-400)',cursor:'pointer'}} onClick={function(){delAccountIdx(i);}}>Remove</div>
                  </div>
                </div>
              </div>
            );})}
            {!accts.length && <div className="t-caption" style={{padding:'18px 0',textAlign:'center'}}>No accounts yet</div>}
          </div>
          <button className="cm-add-row green" style={{marginTop:14}} onClick={function(){setAcctModal({idx:-1});}}><Ip.Plus size={16}/>Add account</button>
        </ChartCard>
        <ChartCard title="Credit Cards" action={<a href="#/cards" className="t-label text-green" style={{cursor:'pointer'}}>Manage</a>}>
          <div>
            {cards.map(function(c,i){return (
              <div className="cm-act" key={i}>
                <BrandIcon name={c.issuer||c.name} size={36} radius={9}/>
                <div className="cm-act-body"><div className="cm-act-title">{c.name}</div><div className="cm-act-sub num">{(c.network||'')+(c.last4?' · ····'+c.last4:'')}</div></div>
                <div className="cm-act-right"><div className="cm-act-amt num" style={{color:RD}}>{fmt(c.balance)}</div>
                  <div style={{fontSize:11,color:'var(--ink-400)'}}>limit {fmt0(c.limit)}</div></div>
              </div>
            );})}
            {!cards.length && <div className="t-caption" style={{padding:'18px 0',textAlign:'center'}}>No cards yet</div>}
          </div>
        </ChartCard>
        <ChartCard title="Investments" action={<a href="#/investments" className="t-label text-green" style={{cursor:'pointer'}}>View</a>}>
          <div>
            {invs.map(function(v,i){var mv=v.marketValue!=null?+v.marketValue:(+v.shares||0)*(+v.currentPrice||0);return (
              <div className="cm-act" key={i}>
                <div className="cm-act-ic" style={{background:window.tintFor(PU),color:(CM.INV_COLORS&&CM.INV_COLORS[v.type])||PU}}><Ip.Investments size={16}/></div>
                <div className="cm-act-body"><div className="cm-act-title">{v.symbol||v.name}</div><div className="cm-act-sub">{(v.account||v.broker||'')}</div></div>
                <div className="cm-act-amt num">{fmt0(mv)}</div>
              </div>
            );})}
            {!invs.length && <div className="t-caption" style={{padding:'18px 0',textAlign:'center'}}>No holdings yet</div>}
          </div>
        </ChartCard>
      </div>
      {acctModal && <AccountModal CM={CM} idx={acctModal.idx} onClose={function(){setAcctModal(null);}}/>}
    </div>
  );
}
window.PageAccounts = PageAccounts;

/* ============================ TRANSACTIONS / BANK STATEMENTS ============================ */
/* small stat tile used inside the import-confirm modal */
function ImpStat({ label, value, color }){
  return <div style={{background:'var(--mint-50)',border:'1px solid var(--line)',borderRadius:12,padding:'10px 12px'}}>
    <div style={{fontSize:10.5,fontWeight:700,letterSpacing:'.05em',textTransform:'uppercase',color:'var(--ink-400)'}}>{label}</div>
    <div className="num" style={{fontSize:16,fontWeight:800,color:color||'var(--ink-900)',marginTop:2}}>{value}</div>
  </div>;
}
/* ---- F1: confirm-before-import modal. Shows exactly what WILL be created from a parsed
   (but NOT yet committed) OFX file; only on Confirm do we call CMimport.commit(...). ---- */
function ImportConfirmModal({ CM, rows, meta, fname, defaultType, onCancel, onDone }){
  meta = meta||{};
  const ttype = defaultType || (meta.isCC ? 'cc' : 'bank');
  const isCC = ttype==='cc';
  const fmt = CM.M;
  const list = isCC ? (CM.S.cards||[]) : (CM.S.accounts||[]);
  // find an account/card that already matches this file (by OFX id or last-4)
  const matchIdx = React.useMemo(()=>{
    for(let i=0;i<list.length;i++){ const x=list[i];
      if((x.ofxAcctId&&meta.acctId&&x.ofxAcctId===meta.acctId)||(x.last4&&meta.last4&&x.last4===meta.last4)) return i; }
    return -1;
  },[]);
  const defName = (matchIdx>=0 ? (list[matchIdx].name||'') : ((meta.org||'Account')+(meta.last4?(' ····'+meta.last4):'')));
  const [target,setTarget] = React.useState(matchIdx>=0?String(matchIdx):'new');
  const [name,setName] = React.useState(defName);
  const [opening,setOpening] = React.useState(()=>{
    if(matchIdx>=0){ const x=list[matchIdx]; if(x._openingAuto===false && x.opening!=null) return String(x.opening); }
    return '';
  });
  const [curBal,setCurBal] = React.useState(''); // optional: enter today's balance → back-derive opening
  // optional "start the statement from" date — drops earlier (e.g. prior-year) transactions so the
  // opening reflects your balance ON that date (e.g. Jan 1).
  const [startFrom,setStartFrom] = React.useState('');
  const startTs = startFrom ? new Date(startFrom+'T00:00:00').getTime() : null;
  const effRows = startTs ? rows.filter(r=>{ var t=CM.toTs(r.date); return t==null || t>=startTs; }) : rows;
  const excluded = rows.length - effRows.length;
  // transaction summary
  const dated = effRows.filter(r=>r.date).map(r=>CM.toTs(r.date)).sort((a,b)=>a-b);
  const dr = effRows.filter(r=>(+r.dr||0)>0).length, cr = effRows.filter(r=>(+r.cr||0)>0).length;
  const dateRange = dated.length ? (CM.FD(new Date(dated[0]))+' → '+CM.FD(new Date(dated[dated.length-1]))) : '—';
  const rec = window.CMimport.reconcile(effRows, opening, meta);
  // auto-detected recurring items (income / bills / subscriptions) from the parsed rows
  const det = React.useMemo(()=>{ try{
    if(!(window.CMrecurring&&window.CMrecurring.analyze)) return null;
    return window.CMrecurring.analyze(isCC?{bankTx:[],ccTx:effRows}:{bankTx:effRows,ccTx:[]});
  }catch(e){ return null; } },[startFrom]);
  const detList = det ? [].concat(
    (det.earnings||[]).map(g=>({k:'Income',n:g.name})),
    (det.subscriptions||[]).map(g=>({k:'Subscription',n:g.name})),
    (det.bills||[]).map(g=>({k:'Bill',n:g.name})),
    (det.expenses||[]).map(g=>({k:'Expense',n:g.name}))
  ) : [];
  // ── overdraft-aware reconciliation ──
  // The file's AVAILBAL is the SPENDABLE balance — it includes your overdraft cushion. When the
  // bank's LEDGERBAL is stale (a different DTASOF), that cushion can't be derived from the file.
  // So when you type your real balance and it sits below AVAILBAL, the gap IS the cushion: we
  // capture it as the overdraft limit, store your true balance, and treat it as reconciled.
  const curNum = (curBal!==''&&!isNaN(parseFloat(curBal))) ? parseFloat(curBal) : null;
  const availNum = rec.availBal;
  const impliedDraft = (curNum!=null && availNum!=null && (availNum-curNum)>0.02) ? Math.round((availNum-curNum)*100)/100 : null;
  const effDraft = impliedDraft!=null ? impliedDraft : rec.draftLimit;
  const trueAvail = (availNum!=null && effDraft!=null) ? Math.round((availNum-effDraft)*100)/100 : null;
  const okTrue = trueAvail!=null && Math.abs(rec.computed-trueAvail)<=0.02;
  const recOk = curNum!=null ? true : (rec.reconciled || okTrue);
  const recMsg = curNum!=null
    ? (impliedDraft!=null
        ? ('set to your balance · $'+impliedDraft.toFixed(2)+' overdraft cushion captured (file AVAILBAL '+fmt(availNum)+' is your spendable balance)')
        : 'set to the balance you entered')
    : (recOk ? 'computed current matches the file (±$0.02)' : 'computed current differs from the file — type your current balance above and we’ll do the rest');
  const doConfirm = ()=>{
    let targetIdx;
    if(target==='new'){ targetIdx = window.CMimport.ensureTarget(isCC?'cc':'bank', meta, true); }
    else { targetIdx = parseInt(target); }
    const res = window.CMimport.commit(effRows, ttype, fname, targetIdx, {
      accountName: name,
      opening: isCC ? null : (opening===''?null:opening),
      ledgerBal: meta.ledgerBal, availBal: meta.availBal, draftLimit: (effDraft!=null?effDraft:null), currency: meta.currency
    });
    onDone(res);
  };
  const okColor = recOk ? 'var(--green-600)' : '#B5740F';
  const inp = ACCT_INP;
  return (
    <div onClick={onCancel} style={{position:'fixed',inset:0,zIndex:1000,background:'rgba(13,33,26,.45)',display:'flex',alignItems:'center',justifyContent:'center',padding:20}}>
      <div onClick={e=>e.stopPropagation()} className="cm-card" style={{width:660,maxWidth:'100%',maxHeight:'92vh',overflowY:'auto',borderRadius:18,padding:'22px 24px',boxShadow:'0 24px 64px rgba(13,42,33,.28)'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:4}}>
          <div className="t-h2" style={{fontSize:18}}>Confirm import</div>
          <button onClick={onCancel} aria-label="Close" style={{border:0,background:'none',cursor:'pointer',color:'var(--ink-400)',fontSize:20,lineHeight:1,padding:4}}>×</button>
        </div>
        <div className="t-caption" style={{marginBottom:16}}>Review what will be created from <b>{fname}</b>. Nothing is saved until you press Confirm.</div>

        {/* detected account */}
        <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:14,padding:'10px 12px',background:'var(--mint-50)',border:'1px solid var(--line)',borderRadius:12}}>
          <div style={{flex:1,minWidth:0}}>
            <div style={{fontWeight:700,fontSize:14,color:'var(--ink-900)'}}>{meta.org||(isCC?'Credit Card':'Bank Account')}{meta.last4?(' ····'+meta.last4):''}</div>
            <div className="t-caption">{[meta.acctType||(isCC?'CREDIT':'CHECKING'), meta.currency||'CAD', meta.bankId?('BANKID '+meta.bankId):'', meta.acctId?('ACCTID '+meta.acctId):''].filter(Boolean).join(' · ')}</div>
          </div>
          <span style={{background:(matchIdx>=0?'var(--mint-100)':'#FBE7CF'),color:(matchIdx>=0?'var(--green-600)':'#B5740F'),fontWeight:700,fontSize:11,padding:'3px 9px',borderRadius:20}}>{matchIdx>=0?'Matches existing':'New account'}</span>
        </div>

        {/* transaction stats */}
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:10,marginBottom:16}}>
          <ImpStat label="Transactions" value={rows.length}/>
          <ImpStat label="Date range" value={<span style={{fontSize:11.5,fontWeight:700}}>{dateRange}</span>}/>
          <ImpStat label="Credits" value={cr} color="var(--green-600)"/>
          <ImpStat label="Debits" value={dr} color={RD}/>
        </div>

        {/* target account + name + opening */}
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14,marginBottom:16}}>
          <AcctField label={isCC?'Target card':'Target account'}>
            <select style={inp} value={target} onChange={e=>{ const v=e.target.value; setTarget(v); if(v!=='new'&&list[+v]){ setName(list[+v].name||name); if(!isCC&&list[+v]._openingAuto===false&&list[+v].opening!=null) setOpening(String(list[+v].opening)); } else { setName(defName); } }}>
              <option value="new">➕ Create new {isCC?'card':'account'}</option>
              {list.map((a,i)=><option key={i} value={i}>Merge into: {a.name}{a.last4?(' ····'+a.last4):''}</option>)}
            </select>
          </AcctField>
          <AcctField label={isCC?'Card name':'Account name'}><input style={inp} value={name} onChange={e=>setName(e.target.value)}/></AcctField>
          {!isCC && <AcctField label="Your current balance today ($) — recommended" full={true} hint="Type the balance your bank shows right now. This is the source of truth: we set the opening and capture any overdraft cushion automatically.">
            <input style={inp} type="number" value={curBal} onChange={e=>{ const v=e.target.value; setCurBal(v); if(v!==''&&!isNaN(parseFloat(v))){ const net=(rec.sumIn||0)-(rec.sumOut||0); setOpening(String(Math.round((parseFloat(v)-net)*100)/100)); } }} placeholder="your balance today"/>
          </AcctField>}
          {!isCC && <AcctField label="…or opening balance ($)" full={true} hint="Balance BEFORE the first transaction — not the closing balance of the first day.">
            <input style={inp} type="number" value={opening} onChange={e=>{ setOpening(e.target.value); setCurBal(''); }} placeholder="opening balance"/>
          </AcctField>}
          {!isCC && <AcctField label="Import from (optional)" full={true} hint={startFrom ? ('Excluding '+excluded+' earlier transaction'+(excluded===1?'':'s')+' — opening is your balance on this date.') : 'Leave blank to import everything. Pick a date to start the statement there and drop earlier transactions.'}>
            <input style={inp} type="date" value={startFrom} onChange={e=>{ setStartFrom(e.target.value); setCurBal(''); }}/>
          </AcctField>}
        </div>

        {/* F3: balance validation row */}
        {!isCC && <div style={{border:'1px solid var(--line)',borderRadius:12,overflow:'hidden',marginBottom:16}}>
          <div style={{display:'flex',alignItems:'center',gap:8,padding:'9px 12px',background:(recOk?'var(--mint-100)':'#FCEFD9'),borderBottom:'1px solid var(--line)'}}>
            <span style={{fontWeight:800,color:okColor}}>{recOk?'✓ Reconciles':'⚠ Does not reconcile'}</span>
            <span className="t-caption" style={{color:okColor}}>{recMsg}</span>
          </div>
          <div style={{display:'grid',gridTemplateColumns:'repeat(3,1fr)',gap:0}}>
            {[
              ['Opening', fmt(rec.opening), null],
              ['Σ Credits in', fmt(rec.sumIn), 'var(--green-600)'],
              ['Σ Debits out', fmt(rec.sumOut), RD],
              ['Computed current', fmt(rec.computed), 'var(--ink-900)'],
              ['File AVAILBAL'+(meta.availDtasof?(' ('+meta.availDtasof+')'):''), rec.availBal!=null?fmt(rec.availBal):'—', null],
              ['File LEDGERBAL'+(meta.ledgerDtasof?(' ('+meta.ledgerDtasof+')'):''), rec.ledgerBal!=null?fmt(rec.ledgerBal):'—', null],
            ].map((c,i)=>(
              <div key={i} style={{padding:'9px 12px',borderTop:i>=3?'1px solid var(--line)':'none',borderLeft:(i%3)?'1px solid var(--line)':'none'}}>
                <div style={{fontSize:10.5,fontWeight:700,letterSpacing:'.04em',textTransform:'uppercase',color:'var(--ink-400)'}}>{c[0]}</div>
                <div className="num" style={{fontSize:14,fontWeight:800,color:c[2]||'var(--ink-900)',marginTop:2}}>{c[1]}</div>
              </div>
            ))}
          </div>
          <div style={{display:'flex',gap:18,flexWrap:'wrap',padding:'9px 12px',borderTop:'1px solid var(--line)',background:'var(--mint-50)'}}>
            {effDraft!=null && <span className="t-caption">Overdraft cushion{impliedDraft!=null?' (from your balance)':' (AVAIL − LEDGER)'}: <b className="num">{fmt(effDraft)}</b></span>}
            {curNum==null && rec.deltaAvail!=null && <span className="t-caption">Δ vs AVAILBAL: <b className="num" style={{color:Math.abs(rec.deltaAvail)<=0.02?'var(--green-600)':'#B5740F'}}>{fmt(rec.deltaAvail)}</b></span>}
            {curNum==null && rec.deltaLedger!=null && <span className="t-caption">Δ vs LEDGERBAL: <b className="num" style={{color:Math.abs(rec.deltaLedger)<=0.02?'var(--green-600)':'#B5740F'}}>{fmt(rec.deltaLedger)}</b></span>}
          </div>
        </div>}
        {isCC && (rec.ledgerBal!=null||rec.availBal!=null) && <div style={{padding:'10px 12px',border:'1px solid var(--line)',borderRadius:12,marginBottom:16}}>
          <span className="t-caption">Statement balance: <b className="num">{rec.ledgerBal!=null?fmt(rec.ledgerBal):(rec.availBal!=null?fmt(rec.availBal):'—')}</b>{meta.ledgerDtasof?(' as of '+meta.ledgerDtasof):''}</span>
        </div>}

        {/* auto-detected recurring */}
        <div style={{marginBottom:8}}>
          <div style={{fontSize:11,fontWeight:700,letterSpacing:'.06em',textTransform:'uppercase',color:'var(--ink-400)',marginBottom:6}}>Auto-detected recurring items {det?('· '+(det.total||0)):''}</div>
          {detList.length ? <div style={{display:'flex',flexWrap:'wrap',gap:6}}>
            {detList.slice(0,18).map((d,i)=>(
              <span key={i} style={{display:'inline-flex',alignItems:'center',gap:5,fontSize:12,padding:'4px 9px',borderRadius:20,background:'var(--mint-50)',border:'1px solid var(--line)'}}>
                <span style={{fontWeight:700,color:'var(--green-600)'}}>{d.k}</span><span style={{color:'var(--ink-700)'}}>{d.n}</span>
              </span>
            ))}
            {detList.length>18 && <span className="t-caption">+{detList.length-18} more</span>}
          </div> : <div className="t-caption">No recurring subscriptions, bills, or income detected in this file.</div>}
        </div>

        <div style={{display:'flex',gap:10,marginTop:20,alignItems:'center'}}>
          <div style={{flex:1}}></div>
          <button className="cm-btn cm-btn-soft" onClick={onCancel}>Cancel</button>
          <button className="cm-btn cm-btn-primary" onClick={doConfirm}>Confirm &amp; import {rows.length} txn{rows.length!==1?'s':''}</button>
        </div>
      </div>
    </div>
  );
}
function ImportControls({ type, idx }) {
  const CM = window.CM;
  const [acct, setAcct] = React.useState(0);
  const [status, setStatus] = React.useState('');
  const [confirm, setConfirm] = React.useState(null); // {rows, meta, fname}
  const fileRef = React.useRef(null);
  const fixed = idx!=null;
  const effIdx = fixed ? idx : acct;
  const list = type==='cc' ? (CM.S.cards||[]) : (CM.S.accounts||[]);
  const finishMsg = (res)=>{
    const at = res.auto && res.auto.total;
    setStatus('✓ Added '+res.added+' transaction'+(res.added!==1?'s':'')+(res.dup?(' · '+res.dup+' duplicate'+(res.dup!==1?'s':'')+' skipped'):'')+(at?(' · '+at+' recurring item'+(at!==1?'s':'')+' added'):''));
  };
  const onFile = (e)=>{
    const f = e.target.files && e.target.files[0]; if(!f) return;
    const nm = (f.name||'').toLowerCase();
    // OFX / QFX → parse first, then show the confirm modal (commit happens on Confirm)
    if(nm.endsWith('.ofx')||nm.endsWith('.qfx')){
      setStatus('Reading '+f.name+'…');
      const fr = new FileReader();
      fr.onload = (ev)=>{ try{
        const parsed = window.CMimport.parseOFX(ev.target.result);
        if(!parsed.rows.length){ setStatus('⚠ No transactions found in this file.'); return; }
        setConfirm({ rows:parsed.rows, meta:parsed.meta, fname:f.name });
        setStatus('');
      }catch(err){ setStatus('⚠ '+((err&&err.message)||'Could not read OFX')); } };
      fr.onerror = ()=>setStatus('⚠ Could not read file.');
      fr.readAsText(f);
      e.target.value=''; return;
    }
    // CSV / PDF / TXT — unchanged one-shot path
    setStatus('Importing '+f.name+'…');
    window.CMimport.importFile(f, type, parseInt(effIdx)).then(function(res){
      finishMsg(res);
      try { CM.logActivity && CM.logActivity('imported '+res.added+' transaction'+(res.added!==1?'s':''), f.name, {cat:'import'}); } catch(e){}
    }).catch(function(err){ setStatus('⚠ '+(err&&err.message||'Import failed')); });
    e.target.value='';
  };
  return (
    <div style={{display:'flex',alignItems:'center',gap:10,flexWrap:'wrap'}}>
      {!fixed && type!=='cc' && <select className="cm-select" value={acct} onChange={e=>setAcct(e.target.value)}>
        {list.map((a,i)=><option key={i} value={i}>{a.name}{a.last4?' ····'+a.last4:''}</option>)}
      </select>}
      <button className="cm-btn cm-btn-ghost" onClick={()=>fileRef.current&&fileRef.current.click()}><Ip.Plus size={16}/>{type==='cc'?'Upload card statement':'Import statement'}</button>
      <input ref={fileRef} type="file" accept=".ofx,.qfx,.csv,.txt,.pdf" style={{display:'none'}} onChange={onFile}/>
      {status
        ? <span className="t-caption" style={{color:status[0]==='⚠'?'var(--red-500)':'var(--green-600)',fontWeight:700,maxWidth:360,lineHeight:1.4}}>{status}</span>
        : <span className="t-caption" style={{color:'var(--ink-400)'}}>OFX · QFX · CSV</span>}
      {confirm && <ImportConfirmModal CM={CM} rows={confirm.rows} meta={confirm.meta} fname={confirm.fname} defaultType={type}
        onCancel={()=>{ setConfirm(null); setStatus('Import cancelled.'); }}
        onDone={(res)=>{ setConfirm(null); finishMsg(res); try{ CM.logActivity && CM.logActivity('imported '+res.added+' transaction'+(res.added!==1?'s':''), confirm.fname, {cat:'import'}); }catch(e){} }}/>}
    </div>
  );
}
window.ImportControls = ImportControls;

function PageTransactions() {
  const CM = window.useStore ? window.useStore() : window.CM;
  const BrandIcon = window.BrandIcon;
  const [tab, setTab] = React.useState('All');
  const [q, setQ] = React.useState('');
  const [period, setPeriod] = React.useState('month');
  const fmt = CM.M;
  const TRANSFER = {'Transfer':1,'Credit Card Payment':1,'Savings':1};
  const all = [].concat(CM.S.bankTx||[]).sort((a,b)=>(CM.toTs(b.date)||0)-(CM.toTs(a.date)||0));   // most recent first — today at the top
  const inPeriod = (d)=> !window.cmInPeriod || window.cmInPeriod(d, period);
  const periodLabel = window.cmPeriodLabel?window.cmPeriodLabel(period):'';
  const periodTx = all.filter(r=>inPeriod(r.date));
  const incomeM = periodTx.reduce((s,r)=>s+(r.cr||0),0);
  const spendM = periodTx.filter(r=>!TRANSFER[r.cat]).reduce((s,r)=>s+(r.dr||0),0);
  const rows = all.filter(r=>{
    if(tab==='Income' && !(r.cr>0)) return false;
    if(tab==='Expenses' && !(r.dr>0 && !TRANSFER[r.cat])) return false;
    if(tab==='Transfers' && !TRANSFER[r.cat]) return false;
    if(q && (r.desc||'').toLowerCase().indexOf(q.toLowerCase())<0 && (r.cat||'').toLowerCase().indexOf(q.toLowerCase())<0) return false;
    if(!inPeriod(r.date)) return false;
    return true;
  });
  const cols = [
    {header:'Transaction', cell:r=>(
      <div className="cm-cell-lead"><BrandIcon name={r.desc} size={38} radius={9}/>
        <div><div className="cm-cell-primary" style={{whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',maxWidth:280}}>{r.desc}</div>
        <div className="cm-cell-sub">{r.account||'—'}</div></div></div>)},
    {header:'Date', cell:r=><span className="cm-cell-meta num" style={{fontSize:13,color:'var(--ink-500)'}}>{CM.FD(r.date)}</span>},
    {header:'Category', cell:r=><StatusPill tone={r.cr>0?'green':(TRANSFER[r.cat]?'slate':'slate')}>{r.cat||'Other'}</StatusPill>},
    {header:'Amount', align:'right', cell:r=>{const isD=r.dr>0;return <span className="num" style={{fontWeight:700,fontSize:14,color:isD?'var(--ink-900)':G}}>{(isD?'-':'+')+fmt(r.dr||r.cr||0).slice(1)}</span>;}},
  ];
  return (
    <div>
      <div style={{display:'flex',justifyContent:'flex-end',marginBottom:14}}>{window.PeriodBar ? <window.PeriodBar value={period} onChange={setPeriod}/> : null}</div>
      <KpiRow>
        <MetricCard label="Income" labelColor="var(--green-600)" value={CM.M0(incomeM)} sub={periodLabel}/>
        <MetricCard label="Spending" labelColor="var(--red-500)" value={CM.M0(spendM)} sub={'excl. transfers · '+periodLabel}/>
        <MetricCard label="Net" labelColor={incomeM-spendM>=0?'var(--green-600)':'var(--red-500)'} value={(incomeM-spendM>=0?'+':'-')+CM.M0(Math.abs(incomeM-spendM)).slice(1)} sub={periodLabel}/>
        <MetricCard label="Transactions" value={periodTx.length.toLocaleString()} sub={periodLabel}/>
      </KpiRow>
      <ChartCard>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-start',gap:16,flexWrap:'wrap',marginBottom:6}}>
          <Tabs tabs={['All','Income','Expenses','Transfers']} value={tab} onChange={setTab}/>
          <ImportControls type="bank"/>
        </div>
        <div style={{display:'flex',gap:10,alignItems:'center',marginBottom:14,flexWrap:'wrap'}}>
          <div className="st-searchbox" style={{maxWidth:260,flex:'1 1 180px'}}>
            <Ip.Search size={16}/><input placeholder="Search transactions…" value={q} onChange={e=>setQ(e.target.value)}/>
          </div>
        </div>
        {rows.length ? <DataTable columns={cols} rows={rows.slice(0,60)} minWidth={720}/>
          : <div className="t-caption" style={{textAlign:'center',padding:'40px 0'}}>No transactions. Import a CSV, OFX/QFX or PDF statement to get started.</div>}
        {rows.length>60 && <div className="t-caption" style={{textAlign:'center',marginTop:12}}>Showing the latest 60 of {rows.length.toLocaleString()} — use search or the period filter to narrow.</div>}
      </ChartCard>
    </div>
  );
}
window.PageTransactions = PageTransactions;

/* ============================ BUDGET (fallback) ============================ */
/* The full Budget & Planner lives in cm-pages-budget.jsx, which overrides this.
   This lightweight, real-data version is only a safety net — never fake numbers. */
function PageBudget() {
  const CM = window.useStore ? window.useStore() : window.CM;
  const fmt = CM.M0;
  const budgets = CM.S.budgets || [];
  const spend = (CM.spendingByCategory ? CM.spendingByCategory(1).items : []) || [];
  const spentByCat = {}; spend.forEach(s=>{ spentByCat[s.label]=s.value; });
  const totalBudget = budgets.reduce((a,b)=>a+(+b.limit||0),0);
  const totalSpent = budgets.reduce((a,b)=>a+(spentByCat[b.cat]||0),0);
  const remaining = totalBudget - totalSpent;
  const pal = [G,PU,AM,BL,PINK,RD,TEAL];
  return (
    <div>
      <KpiRow>
        <MetricCard label="Total Budget" value={totalBudget?fmt(totalBudget):'—'} sub="this month"/>
        <MetricCard label="Spent" labelColor="var(--red-500)" value={fmt(totalSpent)} sub={totalBudget?Math.round(totalSpent/totalBudget*100)+'% used':'no budget set'}/>
        <MetricCard label="Remaining" labelColor="var(--green-600)" value={fmt(Math.max(0,remaining))} sub={totalBudget?(remaining>=0?'left to spend':'over budget'):'—'}/>
        <MetricCard label="Categories" value={String(budgets.length)} sub="with a limit"/>
      </KpiRow>
      <ChartCard title="Category Budgets">
        {budgets.length ? <div>
          {budgets.map((b,i)=>{ const spent=spentByCat[b.cat]||0, lim=+b.limit||0, over=spent>lim, c=pal[i%pal.length];
            return (
              <div key={i} style={{padding:'12px 0', borderBottom:i<budgets.length-1?'1px solid var(--line)':'0'}}>
                <div style={{display:'flex',alignItems:'center',marginBottom:8}}>
                  <span className="t-h3" style={{flex:1}}>{b.cat}</span>
                  <span className="num" style={{fontWeight:700,fontSize:13,color:over?RD:'var(--ink-900)'}}>{fmt(spent)}</span>
                  <span className="t-caption num" style={{marginLeft:4}}>/ {fmt(lim)}</span>
                </div>
                <ProgressBar pct={lim?Math.round(spent/lim*100):0} color={over?RD:c} showPct={false}
                  caption={over?`${fmt(spent-lim)} over`:`${fmt(lim-spent)} left`}/>
              </div>
            ); })}
        </div> : <div className="t-caption" style={{textAlign:'center',padding:'36px 0'}}>No budgets yet — set a monthly limit per category to start tracking.</div>}
      </ChartCard>
    </div>
  );
}
window.PageBudget = PageBudget;
