/* Clear Mint — Credit Card Statements "Command Center"
   WIRED to live state: every figure is derived from CM.S.cards and
   CM.S.ccStatements (the same localStorage the rest of the app reads),
   plus CM.S.bankTx for real card-payment coverage. The credit-score model
   matches the Analytics page (modeled from real utilization) so the two
   pages always agree. window.PageStatements overrides the legacy fallback
   (loaded AFTER cm-pages-banking.jsx).
   Depends on cm-charts + cm-components + cm-app-kit + cm-data. */
(function () {
const { ChartCard, DonutChart, Legend } = window;

/* palette for this page */
const stG    = '#10915F';            // forest green
const stGold = 'var(--gold)';        // #D4A95D
const stTeal = '#0E9F8E';
const stBlue = '#3E7CC4';
const stPlum = '#8A6E9E';
const stPink = '#D8688F';
const stSlate= '#9AA7B2';
const stRed  = 'var(--red-500)';

const usd  = n => '$' + Number(n||0).toLocaleString('en-CA', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const usd0 = n => '$' + Math.round(Number(n||0)).toLocaleString('en-CA');
const compact = n => { n=Number(n||0); return n>=1e6?('$'+(n/1e6).toFixed(2)+'M'):n>=1e3?('$'+(n/1e3).toFixed(1)+'k'):'$'+Math.round(n); };
const clamp = (v,lo,hi) => Math.max(lo, Math.min(hi, v));
function ramp(a, b, n=6){ const out=[]; for(let i=0;i<n;i++){ const t=i/(n-1), e=t*t*(3-2*t); out.push(a+(b-a)*e); } return out; }

/* ---- local icons (stroke, 24 grid) ---- */
const si = (paths, fill=false) => ({ size=18, ...p }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill?'currentColor':'none'}
       stroke={fill?'none':'currentColor'} strokeWidth="1.8" strokeLinecap="round"
       strokeLinejoin="round" {...p}>{paths}</svg>
);
const Si = {
  upload : si(<><path d="M12 16V4M8 8l4-4 4 4"/><path d="M5 14a4 4 0 0 0 0 8h14a4 4 0 0 0 .5-7.97 6 6 0 0 0-11.7-1.86"/></>),
  layers : si(<><path d="M12 3 3 8l9 5 9-5z"/><path d="M3 13l9 5 9-5M3 17l9 5 9-5"/></>),
  download: si(<><path d="M12 4v12M8 12l4 4 4-4"/><path d="M5 20h14"/></>),
  filter : si(<path d="M3 5h18l-7 8v6l-4-2v-4z"/>),
  cal    : si(<><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 9h18M8 3v4M16 3v4"/></>),
  edit   : si(<><path d="M4 20h4l10.5-10.5a2 2 0 0 0-3-3L5 17z"/><path d="M13.5 6.5l3 3"/></>),
  trash  : si(<><path d="M4 7h16M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2M6 7l1 13h10l1-13"/></>),
  kebab  : si(<><circle cx="12" cy="5" r="1.3" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.3" fill="currentColor" stroke="none"/><circle cx="12" cy="19" r="1.3" fill="currentColor" stroke="none"/></>),
  search : si(<><circle cx="11" cy="11" r="7"/><path d="m20 20-3-3"/></>),
  chev   : si(<path d="m6 9 6 6 6-6"/>),
  chevR  : si(<path d="m9 6 6 6-6 6"/>),
  arrUp  : si(<path d="M12 19V5M6 11l6-6 6 6"/>),
  arrDn  : si(<path d="M12 5v14M6 13l6 6 6-6"/>),
  grid   : si(<><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></>),
  x      : si(<path d="M6 6l12 12M18 6 6 18"/>),
  spark  : si(<><path d="M12 3v3M12 18v3M3 12h3M18 12h3M6 6l2 2M16 16l2 2M18 6l-2 2M8 16l-2 2"/><circle cx="12" cy="12" r="2.4"/></>),
  fire   : si(<path d="M12 3c2 3 .5 4.5 2 6.5 1-1 1.2-2 1-3.5 2 1.5 3 4 3 6.5a6 6 0 1 1-12 0c0-2 1-3.5 2.5-4.5C12 8.5 10.5 6 12 3z"/>),
  gauge  : si(<><path d="M12 13l4-3"/><path d="M5 18a8 8 0 1 1 14 0"/><circle cx="12" cy="13" r="1" fill="currentColor" stroke="none"/></>),
  trend  : si(<><path d="M4 17 10 11l3 3 7-7"/><path d="M16 7h4v4"/></>),
  shield : si(<><path d="M12 3 5 6v5c0 4 3 7.5 7 9 4-1.5 7-5 7-9V6z"/><path d="m9 12 2 2 4-4"/></>),
  clock  : si(<><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>),
  pdf    : si(<><path d="M6 2h8l4 4v16H6z"/><path d="M14 2v4h4"/></>),
  info   : si(<><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></>),
  wallet : si(<><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18M17 14h.01"/></>),
  doc    : si(<><path d="M6 2h8l4 4v16H6z"/><path d="M14 2v4h4M9 13h6M9 17h6"/></>),
  card   : si(<><rect x="3" y="6" width="18" height="12" rx="2"/><path d="M3 10h18"/></>),
  check  : si(<path d="M20 6 9 17l-5-5"/>),
  star   : ({ size=14, ...p }) => (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" {...p}>
      <path d="M12 2.5 14.85 8.3l6.4.93-4.63 4.5 1.1 6.37L12 17.1 6.28 20.1l1.1-6.37L2.75 9.23l6.4-.93z"/>
    </svg>
  ),
};

/* ============================ NETWORK META ============================ */
const NETBG = { 'Visa':'#1A1F71', 'American Express':'#2E77BC', 'Amex':'#2E77BC', 'Mastercard':'#C8102E', 'Discover':'#E8772E' };
const NETLABEL = { 'American Express':'AMEX', 'Visa':'VISA', 'Mastercard':'MC', 'Discover':'DISC' };
const palette = [stG, stGold, stTeal, stBlue, stPlum, stPink, stSlate];

/* ============================ DERIVE — everything live ============================ */
function deriveStatements(CM) {
  const FD = CM.FD || (d=>String(d));
  const today = CM.today ? CM.today() : new Date();
  const cards = CM.S.cards || [];
  const allStmts = CM.S.ccStatements || [];

  // group statements by card name + last4
  const group = {};
  allStmts.forEach(s => { const k=(s.card||'')+'|'+(s.last4||''); (group[k]=group[k]||[]).push(s); });

  const cardObjs = cards.map((c, idx) => {
    const key = (c.name||'')+'|'+(c.last4||'');
    const apr = (c.apr!=null && +c.apr) ? +c.apr : 0;
    let stmts = (group[key]||[]).slice().sort((a,b)=> new Date(b.date)-new Date(a.date));
    let rows = stmts.map((s, ri) => {
      const bal = +s.balance || 0;
      const int = Math.round(bal * apr/1200 * 100)/100;     // ~1 month interest estimate from real APR
      const purch = Math.round((bal - int)*100)/100;
      const dt = new Date(s.date);
      const due = new Date(dt); due.setDate(due.getDate()+21);
      return { date:FD(dt), dateObj:dt, up:FD(new Date(dt.getTime()+86400000)),
        bal, min:+s.minPayment || Math.round(bal*0.03), due:FD(due), dueObj:due,
        purch, int, status: ((s.paid===true) ? true : (s.paid===false ? false : (ri>0))) ? 'paid' : 'open', _s:s };
    });
    // card with a balance but no statement on file → synthesize the current open statement
    if (!rows.length && (+c.balance||0) > 0) {
      const bal=+c.balance||0, int=Math.round(bal*apr/1200*100)/100;
      const due = c.dueDate ? new Date(c.dueDate) : new Date(today.getTime()+18*86400000);
      rows = [{ date:FD(today), dateObj:today, up:FD(today), bal, min:Math.max(25,Math.round(bal*0.03)),
        due:FD(due), dueObj:due, purch:Math.round((bal-int)*100)/100, int, status:'open' }];
    }
    const util = c.limit ? (+c.balance||0)/c.limit*100 : 0;
    const prevBal = rows.length>1 ? rows[1].bal : null;
    const trendUp = prevBal!=null ? (+c.balance||0) < prevBal : true;   // balance falling = good
    return {
      id: c.last4 || ('c'+idx), name:c.name || 'Credit Card', last4:c.last4 || '',
      net: NETLABEL[c.network] || (c.network||'CARD').slice(0,4).toUpperCase(),
      netBg: NETBG[c.network] || '#334155', color: palette[idx % palette.length],
      issuer: c.issuer || c.network || '', apr, limit:+c.limit||0, balance:+c.balance||0, util,
      dueDate: c.dueDate ? new Date(c.dueDate) : (rows[0] && rows[0].dueObj) || null,
      rows, trendUp, latest: idx===0,
    };
  });

  const totalLimit   = cardObjs.reduce((s,c)=>s+c.limit,0);
  const totalBalance = cardObjs.reduce((s,c)=>s+c.balance,0);
  const util = totalLimit ? totalBalance/totalLimit*100 : 0;

  const openStmts = allStmts.filter(s=>!s.paid);
  const paidStmts = allStmts.filter(s=>s.paid);
  const totalMin  = cardObjs.reduce((s,c)=> s + (c.rows.find(r=>r.status==='open')?.min || 0), 0);

  // purchases vs interest across the current (open) statements
  let totPurch=0, totInt=0;
  cardObjs.forEach(c => { const open=c.rows.find(r=>r.status==='open'); if(open){ totPurch+=open.purch; totInt+=open.int; } });

  // payment coverage: real CC payments in the ledger + scheduled CC-payment bills
  const tm = CM.getMonthKey ? CM.getMonthKey(today) : '';
  const ccPaidLedger = (CM.S.bankTx||[])
    .filter(t => /credit card payment/i.test(t.cat||'') && (+t.dr||0)>0 && (!CM.getMonthKey || CM.getMonthKey(t.date)===tm))
    .reduce((s,t)=>s+(+t.dr||0),0);
  const ccPaidBills = (CM.S.bills||[])
    .filter(b => /credit card/i.test(b.cat||''))
    .reduce((s,b)=>s+(+b.amount||0),0);
  const ccPaid = ccPaidLedger + ccPaidBills;
  const coverage = totalMin ? Math.min(100, Math.round(ccPaid/totalMin*100)) : (ccPaid>0?100:0);

  // upcoming dues (next 14 days)
  const upcoming = cardObjs.filter(c => c.dueDate && (c.dueDate - today)/86400000 <= 14 && (c.dueDate - today) >= -1);
  const upcomingTotal = upcoming.reduce((s,c)=>s+c.balance,0);

  // utilization alerts
  const overCards = cardObjs.filter(c=>c.util>=30);
  const highest = [...cardObjs].sort((a,b)=>b.util-a.util)[0];

  // interest saved by paying full balance vs minimum (one cycle estimate)
  const interestSaved = cardObjs.reduce((s,c)=>{ const open=c.rows.find(r=>r.status==='open'); if(!open) return s;
    const carried = Math.max(0, open.bal - open.min); return s + Math.round(carried*c.apr/1200*100)/100; }, 0);

  /* ---- credit-score model (matches Analytics page) ---- */
  const creditScore = clamp(Math.round(760 - util*1.6 + (util<10?15:0)), 560, 850);
  const band = creditScore>=800?'Exceptional':creditScore>=740?'Very Good':creditScore>=670?'Good':creditScore>=580?'Fair':'Poor';
  const stars = clamp(Math.round((creditScore-300)/110), 0, 5);

  const intBurden = totalBalance ? totInt/totalBalance : 0;
  const onTime = (paidStmts.length || openStmts.length) ? 1 : 1;     // no overdue statements in data
  const improving = cardObjs.some(c=>c.trendUp);

  const FACTORS = [
    { ic:Si.gauge,  name:'Utilization',              wt:'30%', val:util.toFixed(0)+'%',           fill:clamp(1-util/50,0,1),        color: util<30?stG:stGold },
    { ic:Si.check,  name:'Payment Timeliness',       wt:'25%', val:Math.round(onTime*100)+'%',    fill:onTime,                       color:stG },
    { ic:Si.shield, name:'Min Payment Coverage',     wt:'15%', val:coverage+'%',                  fill:clamp(coverage/100,0,1),      color: coverage>=100?stG:stGold },
    { ic:Si.fire,   name:'Interest Burden',          wt:'10%', val:(intBurden*100).toFixed(0)+'%',fill:clamp(1-intBurden*4,0,1),     color: intBurden<0.05?stG:stGold },
    { ic:Si.trend,  name:'Balance Trend',            wt:'10%', val:improving?'Improving':'Rising', fill:improving?0.82:0.5,           color: improving?stG:stGold, good:improving },
    { ic:Si.doc,    name:'Statement Health',         wt:'10%', val: allStmts.length?'Good':'—',   fill: allStmts.length?0.8:0.2,     color:stG, good:!!allStmts.length },
  ];

  return {
    cardObjs, allStmts, openStmts, paidStmts,
    totalLimit, totalBalance, totalMin, util, totPurch, totInt, coverage, ccPaid,
    upcoming, upcomingTotal, overCards, highest, interestSaved,
    creditScore, band, stars, FACTORS, today,
  };
}

/* ============================ GAUGES ============================ */
function HealthGauge({ score, band, stars }) {
  const W=260, H=152, cx=130, cy=130, r=112, stroke=20;
  const arc = `M ${cx-r} ${cy} A ${r} ${r} 0 0 1 ${cx+r} ${cy}`;
  const f = clamp((score-300)/550,0,1), a = Math.PI*(1-f);
  const mx = cx + r*Math.cos(a), my = cy - r*Math.sin(a);
  return (
    <div className="st-gauge-wrap" style={{height:H}}>
      <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{display:'block'}}>
        <defs>
          <linearGradient id="stHealthGrad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%"   stopColor="#C9554F"/>
            <stop offset="34%"  stopColor="#DB8A4A"/>
            <stop offset="55%"  stopColor="#E0A94E"/>
            <stop offset="78%"  stopColor="#9DB94E"/>
            <stop offset="100%" stopColor="#2E8B57"/>
          </linearGradient>
        </defs>
        <path d={arc} fill="none" stroke="var(--line)" strokeWidth={stroke} strokeLinecap="round"/>
        <path d={arc} fill="none" stroke="url(#stHealthGrad)" strokeWidth={stroke} strokeLinecap="round"/>
        <circle cx={mx} cy={my} r="8" fill="#fff" stroke="#2E8B57" strokeWidth="3"/>
      </svg>
      <span className="st-gauge-tick" style={{left:6, top:H-20}}>300</span>
      <span className="st-gauge-tick" style={{right:6, top:H-20}}>850</span>
      <span className="st-gauge-tick" style={{left:36, top:30}}>550</span>
      <span className="st-gauge-tick" style={{right:36, top:30}}>700</span>
      <div className="st-gauge-center">
        <div className="st-gauge-score num">{score}</div>
        <div className="st-gauge-rate">{band}</div>
        <div className="st-stars">{[0,1,2,3,4].map(i=><Si.star key={i} size={15} style={{opacity:i<stars?1:.22}}/>)}</div>
      </div>
    </div>
  );
}

function CoverageGauge({ pct=100 }) {
  const W=200, H=118, cx=100, cy=104, r=86, stroke=18;
  const arc = `M ${cx-r} ${cy} A ${r} ${r} 0 0 1 ${cx+r} ${cy}`;
  const len = Math.PI*r;
  const frac = Math.min(pct,100)/100;
  return (
    <div className="st-cov-wrap" style={{height:H}}>
      <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{display:'block'}}>
        <defs>
          <linearGradient id="stCovGrad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%" stopColor="#2E8B57"/><stop offset="100%" stopColor="#19A77A"/>
          </linearGradient>
        </defs>
        <path d={arc} fill="none" stroke="var(--mint-100)" strokeWidth={stroke} strokeLinecap="round"/>
        <path d={arc} fill="none" stroke="url(#stCovGrad)" strokeWidth={stroke} strokeLinecap="round"
              strokeDasharray={`${frac*len} ${len}`}/>
      </svg>
      <div className="st-cov-center">
        <div className="st-cov-val num">{pct}%</div>
        <div className="st-cov-lab">{pct>=100?'Great':pct>=80?'Good':'Low'}</div>
      </div>
    </div>
  );
}

/* ============================ SCORE TREND ============================ */
function ScoreTrend({ score }) {
  const W=392, H=200, P={l:6,r:14,t:34,b:26};
  const data = ramp(clamp(score-58,520,840), score, 6).map(v=>Math.round(v));
  const months=['Jan','Feb','Mar','Apr','May','Jun'];
  const lo=Math.min(...data)-30, hi=Math.max(...data)+30;
  const X = i => P.l + (i/(data.length-1))*(W-P.l-P.r);
  const Y = v => P.t + (1-(v-lo)/(hi-lo))*(H-P.t-P.b);
  const line = data.map((v,i)=>`${i?'L':'M'}${X(i)},${Y(v)}`).join(' ');
  const area = `${line} L${X(data.length-1)},${H-P.b} L${X(0)},${H-P.b} Z`;
  return (
    <svg width="100%" viewBox={`0 0 ${W} ${H}`} style={{display:'block'}}>
      <defs>
        <linearGradient id="stTrendFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={stG} stopOpacity=".18"/><stop offset="100%" stopColor={stG} stopOpacity="0"/>
        </linearGradient>
      </defs>
      {[0,1,2,3].map(i=>{ const y=P.t+i*((H-P.t-P.b)/3);
        return <line key={i} x1={P.l} y1={y} x2={W-P.r} y2={y} stroke="var(--line)" strokeWidth="1"/>; })}
      <path d={area} fill="url(#stTrendFill)"/>
      <path d={line} fill="none" stroke={stG} strokeWidth="2.6" strokeLinejoin="round"/>
      {data.map((v,i)=>{
        const last=i===data.length-1;
        return (
          <g key={i}>
            {!last && <circle cx={X(i)} cy={Y(v)} r="3.4" fill="#fff" stroke={stG} strokeWidth="1.8"/>}
            <text x={X(i)} y={H-6} textAnchor={i===0?'start':last?'end':'middle'} fontSize="11"
                  fontWeight="700" fill="var(--ink-400)">{months[i]}</text>
          </g>
        );
      })}
      {(()=>{ const i=data.length-1; const x=X(i), y=Y(data[i]);
        return <g>
          <rect x={x-30} y={y-26} width="36" height="20" rx="6" fill={stG}/>
          <text x={x-12} y={y-12} textAnchor="middle" fontSize="12" fontWeight="800" fill="#fff">{data[i]}</text>
          <circle cx={x} cy={y} r="4.5" fill={stG} stroke="#fff" strokeWidth="2"/>
        </g>;
      })()}
    </svg>
  );
}

/* ============================ SUMMARY TILES ============================ */
function Tile({ icon, tint, color, label, value, foot, footColor }) {
  return (
    <div className="st-tile">
      <div className="st-tile-head">
        <span className="st-tile-ic" style={{background:tint, color}}>{icon}</span>
        <span className="st-tile-label">{label}</span>
      </div>
      <div className="st-tile-val num">{value}</div>
      <div className="st-tile-foot" style={footColor?{color:footColor}:null}>{foot}</div>
    </div>
  );
}

function PortfolioSummary({ D }) {
  const utilColor = D.util>=30 ? stRed : 'var(--ink-400)';
  return (
    <ChartCard pad={false}>
      <div style={{padding:'18px 22px 4px'}} className="cm-row">
        <span className="st-mini-title">Portfolio Summary</span>
        <span className="st-mini-sub" style={{marginTop:0,marginLeft:8}}>· {D.cardObjs.length} card{D.cardObjs.length===1?'':'s'}</span>
      </div>
      <div className="st-summary">
        <Tile icon={<Si.card size={16}/>}   tint="var(--mint-100)" color={stG}    label="Total Credit Limit" value={usd0(D.totalLimit)} foot={<span style={{color:'var(--ink-400)'}}>Across {D.cardObjs.length} cards</span>}/>
        <Tile icon={<Si.doc size={16}/>}    tint="#FEF1DD"         color="#B5740F" label="Total Statement Balance" value={usd(D.totalBalance)} foot={<span style={{color:utilColor}}>{D.util.toFixed(1)}% utilization</span>} footColor={utilColor}/>
        <Tile icon={<Si.wallet size={16}/>} tint="#EDE7F9"         color={stPlum}  label="Total Minimum Payment" value={usd(D.totalMin)} foot="Due this cycle" footColor="var(--ink-400)"/>
        <Tile icon={<Si.layers size={16}/>} tint="#E7EEFE"         color={stBlue}  label="Statements Tracked" value={String(D.allStmts.length)} foot={`Across ${D.cardObjs.length} cards`} footColor="var(--ink-400)"/>
        <Tile icon={<Si.clock size={16}/>}  tint="var(--red-100)"  color={stRed}   label="Open · Not Paid" value={String(D.openStmts.length)} foot="Awaiting payment" footColor={D.openStmts.length?stRed:'var(--ink-400)'}/>
        <Tile icon={<Si.check size={16}/>}  tint="var(--mint-100)" color={stG}     label="Paid Statements" value={String(D.paidStmts.length)} foot="Resolved" footColor="var(--ink-400)"/>
      </div>
    </ChartCard>
  );
}

/* ============================ HEALTH SCORE ============================ */
function HealthScore({ D }) {
  const [open, setOpen] = React.useState(false);
  const proj = clamp(D.creditScore + (D.util>=30?12:5), 300, 850);
  return (
    <ChartCard pad={false} className="cm-mt-20">
      <div style={{padding:'22px 26px'}}>
        <div className="st-health">
          {/* gauge */}
          <div style={{textAlign:'center',display:'flex',flexDirection:'column'}}>
            <div className="cm-row" style={{justifyContent:'center',gap:7,marginBottom:4}}>
              <span style={{color:stG,display:'inline-flex'}}><Si.shield size={17}/></span>
              <span className="st-mini-title">Credit Card Health Score</span>
            </div>
            <HealthGauge score={D.creditScore} band={D.band} stars={D.stars}/>
            <div className="t-caption num" style={{marginTop:14}}>Modeled from utilization & payment history · 300 – 850</div>
          </div>
          {/* breakdown */}
          <div className="col-sep">
            <div className="cm-row" style={{justifyContent:'space-between',marginBottom:8}}>
              <span className="st-eyebrow">Score Breakdown</span>
              <span className="t-caption">weighted</span>
            </div>
            <div className="st-break">
              {D.FACTORS.map((f,i)=>(
                <div className="st-break-row" key={i}>
                  <span className="st-break-ic" style={{background:'var(--mint-50)',color:f.color}}><f.ic size={15}/></span>
                  <div>
                    <div><span className="st-break-name">{f.name}</span><span className="st-break-wt">({f.wt})</span></div>
                    <div className="st-break-track"><div className="st-break-fill" style={{width:`${f.fill*100}%`,background:f.color}}/></div>
                  </div>
                  <div className="st-break-val" style={f.good?{color:stG}:null}>
                    {f.good && <span style={{display:'inline-flex',marginRight:3,verticalAlign:'-2px'}}><Si.arrUp size={12}/></span>}
                    {f.val}
                  </div>
                </div>
              ))}
            </div>
            <div onClick={()=>setOpen(o=>!o)} style={{marginTop:14,display:'flex',alignItems:'center',gap:8,cursor:'pointer',
              padding:'11px 14px',borderRadius:'var(--r-ctrl)',background:'var(--mint-50)'}}>
              <span style={{color:stG,display:'inline-flex'}}><Si.info size={16}/></span>
              <span className="t-label" style={{color:'var(--ink-900)',flex:1}}>What is affecting my score?</span>
              <span style={{color:'var(--ink-400)',display:'inline-flex',transition:'.2s',transform:open?'rotate(180deg)':'none'}}><Si.chev size={16}/></span>
            </div>
            {open && (
              <div style={{marginTop:10,padding:'14px 16px',borderRadius:'var(--r-ctrl)',border:'1px solid var(--line)',
                fontSize:13,lineHeight:1.55,color:'var(--ink-600)',fontWeight:600}}>
                {D.overCards.length
                  ? <>{D.overCards.length} card{D.overCards.length>1?'s sit':' sits'} above 30% utilization — paying {D.overCards.length>1?'them':'it'} down lifts your score fastest.</>
                  : <>Your overall utilization is a healthy <b style={{color:'var(--ink-900)'}}>{D.util.toFixed(0)}%</b>.</>}
                {' '}Your strongest factor is on-time payment history. Keeping balances low could lift you toward <b style={{color:stG}}>{proj}</b>.
              </div>
            )}
          </div>
          {/* trend */}
          <div className="col-sep">
            <div className="cm-row" style={{justifyContent:'space-between',marginBottom:6}}>
              <span className="st-eyebrow">Score Trend</span>
              <span className="t-caption">last 6 months</span>
            </div>
            <ScoreTrend score={D.creditScore}/>
            <div style={{display:'flex',gap:18,marginTop:10}}>
              <div><div className="t-caption">Current</div><div className="num" style={{fontWeight:800,color:stG,fontSize:18}}>{D.creditScore}</div></div>
              <div><div className="t-caption">Projected next</div><div className="num" style={{fontWeight:800,color:'var(--ink-900)',fontSize:18}}>{proj}</div></div>
            </div>
          </div>
        </div>
      </div>
    </ChartCard>
  );
}

/* ============================ ANALYTICS ROW ============================ */
function MiniHead({ title, sub }) {
  return <div style={{marginBottom:16}}><div className="st-mini-title">{title}</div><div className="st-mini-sub">{sub}</div></div>;
}

function AnalyticsRow({ D }) {
  const purchPct = (D.totPurch+D.totInt) ? D.totPurch/(D.totPurch+D.totInt)*100 : 100;
  return (
    <div className="st-analytics cm-mt-20">
      {/* balance by card donut */}
      <ChartCard>
        <MiniHead title="Statement Balance by Card" sub="Current period"/>
        <div style={{display:'flex',alignItems:'center',gap:18,flexWrap:'wrap'}}>
          <DonutChart size={130} thickness={20}
            segments={D.cardObjs.map(c=>({value:Math.max(c.balance,0.01),color:c.color}))}
            center={{value:compact(D.totalBalance), label:'Total'}}/>
          <div style={{flex:1,minWidth:130,display:'flex',flexDirection:'column',gap:7}}>
            {D.cardObjs.map((c,i)=>(
              <div key={i} className="cm-row" style={{gap:8,fontSize:12}}>
                <span style={{width:8,height:8,borderRadius:'50%',background:c.color,flex:'0 0 8px'}}/>
                <span style={{flex:1,fontWeight:600,color:'var(--ink-700)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{c.name}</span>
                <span className="num" style={{fontWeight:700,color:'var(--ink-900)'}}>{usd(c.balance)}</span>
              </div>
            ))}
          </div>
        </div>
      </ChartCard>

      {/* utilization by card */}
      <ChartCard>
        <MiniHead title="Utilization % by Card" sub="Balance vs limit"/>
        <div>
          {D.cardObjs.map((c,i)=>{
            const col = c.util>=30?stRed : c.util>=22?stGold : stG;
            return (
              <div className="st-util-row" key={i}>
                <span className="st-util-name">{c.name}</span>
                <span className="st-util-track"><span className="st-util-fill" style={{width:`${Math.min(c.util*2.2,100)}%`,background:col}}/></span>
                <span className="st-util-pct num" style={{color:col}}>{c.util.toFixed(0)}%</span>
              </div>
            );
          })}
        </div>
        <div className="cm-row" style={{justifyContent:'space-between',marginTop:10,fontSize:10.5,color:'var(--ink-400)',fontWeight:700}}>
          <span>0%</span><span>30%</span><span>50%+</span>
        </div>
      </ChartCard>

      {/* purchases vs interest */}
      <ChartCard>
        <MiniHead title="Purchases vs Interest" sub="This period · est. from APR"/>
        <div style={{display:'flex',justifyContent:'center',marginBottom:6}}>
          <DonutChart size={140} thickness={20}
            segments={[{value:Math.max(D.totPurch,0.01),color:stG},{value:Math.max(D.totInt,0.01),color:stPlum}]}
            center={{value:purchPct.toFixed(1)+'%', label:'purchases'}}/>
        </div>
        <Legend items={[
          { color:stG,    label:'Purchases', value:usd(D.totPurch) },
          { color:stPlum, label:'Interest',  value:usd(D.totInt) },
        ]}/>
      </ChartCard>

      {/* min payment coverage */}
      <ChartCard>
        <MiniHead title="Min Payment Coverage" sub="From your bank ledger"/>
        <CoverageGauge pct={clamp(D.coverage,0,100)}/>
        <p className="t-caption" style={{textAlign:'center',marginTop:10,lineHeight:1.5}}>
          {D.ccPaid>0
            ? <>Your payments cover <b style={{color:stG}}>{D.coverage}%</b> of the {usd0(D.totalMin)} minimum due this cycle.</>
            : <>No card payments recorded yet · {usd0(D.totalMin)} minimum due.</>}
        </p>
      </ChartCard>
    </div>
  );
}

/* ============================ INFO BANNER ============================ */
function Banner() {
  const [show, setShow] = React.useState(true);
  if (!show) return null;
  return (
    <div className="st-banner">
      <span className="ic"><Si.spark size={18}/></span>
      <span className="tx">Only the <b>most recent statement</b> for each card carries the open balance. Older statements are shown as <b>Paid</b> once resolved. {' '}<a>Learn more</a></span>
      <span className="x" onClick={()=>setShow(false)}><Si.x size={16}/></span>
    </div>
  );
}

/* ============================ STATEMENTS TABLE ============================ */
function StatusCell({ status }) {
  if (status==='open')
    return <div className="st-statuswrap"><span className="cm-pill cm-pill-red"><Si.clock size={11}/>Not Paid</span><span className="cap">Most recent</span></div>;
  return <div className="st-statuswrap"><span className="cm-pill cm-pill-green"><Si.check size={11}/>Paid</span><span className="cap">Resolved</span></div>;
}
function ActionsCell({ row, card }) {
  const CM = window.CM;
  const del = () => { if(!row||!row._s){ window.alert('This is the current open balance — no saved statement to delete.'); return; } if(!window.confirm('Delete this statement?')) return; CM.mutate(function(){ CM.S.ccStatements=(CM.S.ccStatements||[]).filter(function(x){return x!==row._s;}); }); };
  const edit = () => { if(!row||!row._s) return; var s=row._s; var b=window.prompt('Statement balance ($)', s.balance!=null?s.balance:''); if(b===null) return; var min=window.prompt('Minimum payment ($)', s.minPayment!=null?s.minPayment:''); CM.mutate(function(){ var nb=parseFloat(b); if(!isNaN(nb)) s.balance=nb; var nm=parseFloat(min); if(!isNaN(nm)) s.minPayment=nm; }); };
  return (
    <div className="st-act">
      <span className="st-actbtn" onClick={edit} style={{cursor:'pointer'}}><Si.edit size={14}/>Edit</span>
      <span className="st-actbtn" onClick={del} style={{cursor:'pointer'}}><Si.trash size={14}/>Delete</span>
      <span className="st-kebab"><Si.kebab size={16}/></span>
    </div>
  );
}

const COLS = ['Statement Card','Statement Date','Limit','Statement Balance','Min Payment','Due Date','Purchases','Interest','Bill','Status','Actions'];

function StatementsTable({ D, period }) {
  const [openCards, setOpenCards] = React.useState(()=>{ const o={}; D.cardObjs.forEach(c=>o[c.id]=true); return o; });
  const [query, setQuery] = React.useState('');
  const [statusF, setStatusF] = React.useState('All Status');
  const toggle = id => setOpenCards(o=>({ ...o, [id]:!o[id] }));
  const inPer = r => !period || period==='all' || !window.cmInPeriod || window.cmInPeriod(r.dateObj, period);
  const visibleCards = D.cardObjs
    .filter(c => (c.name+c.last4).toLowerCase().includes(query.toLowerCase()))
    .map(c => ({ ...c, rows: c.rows.filter(inPer) }));
  const shownCount = visibleCards.reduce((s,c)=> s + (statusF==='Paid Only' ? c.rows.filter(r=>r.status==='paid').length
    : statusF==='Open Only' ? c.rows.filter(r=>r.status==='open').length : c.rows.length), 0);

  return (
    <ChartCard className="cm-mt-20">
      <div className="st-tablehead">
        <div>
          <div className="cm-row" style={{gap:8}}>
            <span className="st-mini-title">Statements</span>
            <span className="cm-pill cm-pill-slate" style={{fontWeight:700}}>Most recent first</span>
          </div>
          <div className="st-mini-sub">Grouped by card · newest statement carries the open balance</div>
        </div>
        <div className="filters">
          <div className="st-searchbox">
            <Si.search size={15}/>
            <input placeholder="Search statements…" value={query} onChange={e=>setQuery(e.target.value)}/>
          </div>
          <span className="st-control" onClick={()=>setStatusF(s=>s==='All Status'?'Open Only':s==='Open Only'?'Paid Only':'All Status')}>
            {statusF} <Si.chev size={14}/>
          </span>
          <span className="st-control"><Si.grid size={15}/></span>
        </div>
      </div>

      <div style={{overflowX:'auto'}}>
        <table className="st-table" style={{minWidth:1080}}>
          <thead><tr>
            {COLS.map((c,i)=><th key={i} className={c==='Actions'?'r':''}>{c}</th>)}
          </tr></thead>
          <tbody>
            {visibleCards.map(card=>{
              const isOpen = openCards[card.id];
              const head = card.rows[0];
              if (!head) return null;
              const showHead = statusF!=='Paid Only' || head.status==='paid';
              const subRows = card.rows.slice(1).filter(r => statusF==='All Status' || (statusF==='Open Only'?r.status==='open':r.status==='paid'));
              return (
                <React.Fragment key={card.id}>
                  {showHead && (
                  <tr className="grouprow">
                    <td>
                      <div className="cm-row" style={{gap:8}}>
                        <span className={`st-expand ${isOpen?'open':''}`} onClick={()=>toggle(card.id)}><Si.chev size={16}/></span>
                        <span className="st-net" style={{background:card.netBg}}>{card.net}</span>
                        <span className="st-cardname">{card.name}</span>
                        <span className="text-muted num" style={{fontSize:12,fontWeight:700}}>•••• {card.last4}</span>
                        {card.latest && <span className="cm-pill cm-pill-green" style={{fontSize:10}}>Latest</span>}
                      </div>
                    </td>
                    <td><div className="st-num">{head.date}</div><div className="t-caption num">Uploaded {head.up}</div></td>
                    <td><span className="st-num">{usd0(card.limit)}</span></td>
                    <td><span className="st-num">{usd(head.bal)}</span></td>
                    <td><span className="st-num mut">{usd(head.min)}</span></td>
                    <td><span className="st-num mut">{head.due}</span></td>
                    <td><span className="st-num">{usd(head.purch)}</span></td>
                    <td><span className="st-num" style={{color:stRed}}>{usd(head.int)}</span></td>
                    <td><span className="st-pdf"><Si.pdf size={15}/></span></td>
                    <td><StatusCell status={head.status}/></td>
                    <td style={{textAlign:'right'}}><ActionsCell row={head} card={card}/></td>
                  </tr>)}
                  {isOpen && subRows.map((r,ri)=>(
                    <tr className="subrow" key={ri}>
                      <td>
                        <span className="st-substem"><Si.chevR size={14}/></span>
                        <span className="st-num mut" style={{fontWeight:700}}>{r.date} statement</span>
                      </td>
                      <td><div className="st-num mut">{r.date}</div><div className="t-caption num">Uploaded {r.up}</div></td>
                      <td><span className="st-num mut">{usd0(card.limit)}</span></td>
                      <td><span className="st-num">{usd(r.bal)}</span></td>
                      <td><span className="st-num mut">{usd(r.min)}</span></td>
                      <td><span className="st-num mut">{r.due}</span></td>
                      <td><span className="st-num mut">{usd(r.purch)}</span></td>
                      <td><span className="st-num mut">{usd(r.int)}</span></td>
                      <td><span className="st-pdf"><Si.pdf size={15}/></span></td>
                      <td><StatusCell status={r.status}/></td>
                      <td style={{textAlign:'right'}}><ActionsCell row={r} card={card}/></td>
                    </tr>
                  ))}
                </React.Fragment>
              );
            })}
          </tbody>
        </table>
      </div>

      <div className="st-pager">
        <span className="t-caption">Showing <b style={{color:'var(--ink-700)'}}>{shownCount}</b> of <b style={{color:'var(--ink-700)'}}>{D.allStmts.length}</b> statements</span>
        <span className="st-control">All on one page</span>
      </div>
    </ChartCard>
  );
}

/* ============================ INSIGHTS ============================ */
function Insight({ icon, tint, color, title, sub, big, bigColor, text, link }) {
  return (
    <div className="st-insight">
      <div className="ihead">
        <span className="iic" style={{background:tint,color}}>{icon}</span>
        <div><div className="ititle">{title}</div><div className="isub">{sub}</div></div>
      </div>
      <div className="ibig num" style={bigColor?{color:bigColor}:null}>{big}</div>
      <div className="itext">{text}</div>
      <span className="ilink">{link} <Si.chevR size={14}/></span>
    </div>
  );
}

function Insights({ D }) {
  const cards = [];
  if (D.overCards.length) cards.push({ icon:<Si.gauge size={18}/>, tint:'var(--red-100)', color:stRed,
    title:'Utilization Alert', sub:'Action recommended', big:`${D.overCards.length} card${D.overCards.length>1?'s':''}`, bigColor:stRed,
    text:`Above 30% utilization — paying ${D.overCards.length>1?'these':'this'} down lifts your score fastest.`, link:'View details' });
  else cards.push({ icon:<Si.gauge size={18}/>, tint:'var(--mint-100)', color:stG,
    title:'Utilization Healthy', sub:'Well managed', big:`${D.util.toFixed(0)}%`, bigColor:stG,
    text:`Overall utilization is below the 30% threshold. Highest is ${D.highest?D.highest.name:'—'} at ${D.highest?D.highest.util.toFixed(0):0}%.`, link:'View details' });

  cards.push({ icon:<Si.cal size={18}/>, tint:'#FEF1DD', color:'#B5740F',
    title:'Upcoming Due', sub:'Next 14 days', big: D.upcoming.length?usd(D.upcomingTotal):'None',
    text: D.upcoming.length ? `${D.upcoming.length} card${D.upcoming.length>1?'s':''} due soon — schedule payments to avoid interest.` : 'No statement balances fall due in the next two weeks.', link:'View calendar' });

  cards.push({ icon:<Si.spark size={18}/>, tint:'var(--mint-100)', color:stG,
    title:'Interest Saved', sub:'vs minimum only', big:usd(D.interestSaved), bigColor:stG,
    text:'Estimated one-cycle interest avoided if you pay full statement balances instead of the minimum.', link:'See breakdown' });

  const lift = D.util>=30?'+18 to 28':'+8 to 14';
  cards.push({ icon:<Si.trend size={18}/>, tint:'#E7EEFE', color:stBlue,
    title:'Score Impact', sub:'If you pay in full', big:lift+' pts', bigColor:stBlue,
    text:'Estimated improvement over the next two statement cycles from low utilization and on-time payments.', link:'See scenarios' });

  return (
    <>
      <div style={{margin:'24px 0 8px'}} className="cm-row">
        <span className="st-eyebrow">Insights &amp; Alerts</span>
      </div>
      <div className="st-insights">{cards.map((c,i)=><Insight key={i} {...c}/>)}</div>
    </>
  );
}

/* ============================ EMPTY STATE ============================ */
function EmptyState({ onUpload }) {
  return (
    <ChartCard>
      <div style={{textAlign:'center',padding:'60px 0'}}>
        <div style={{color:stG,marginBottom:14,display:'flex',justifyContent:'center'}}><Si.card size={40}/></div>
        <div className="title" style={{fontSize:18}}>No statements yet</div>
        <div className="t-caption" style={{marginTop:6}}>Upload a credit-card statement (OFX, QFX or CSV) to track balances, due dates and utilization.</div>
        <button className="cm-btn cm-btn-primary" style={{marginTop:18}} onClick={onUpload}><Si.upload size={16}/>Upload Statement</button>
      </div>
    </ChartCard>
  );
}

/* ============================ PAGE ============================ */
function PageStatements() {
  if(window.CMAccess && !CMAccess.can('statements')){
    return (<div className="cm-card" style={{maxWidth:520,margin:'40px auto',padding:'32px',textAlign:'center'}}>
      <div style={{fontFamily:'var(--font-serif)',fontSize:20,fontWeight:700,color:'var(--ink-900)',marginBottom:8}}>Statements are a Premium feature</div>
      <p style={{fontSize:14,color:'var(--ink-500)',margin:'0 0 16px'}}>Upgrade to upload, read and analyze your credit-card statements. You can still add transactions manually on any plan.</p>
      <button onClick={()=>window.CMAccess&&CMAccess.openUpgradeModal('Credit Card Statements')} style={{fontFamily:'inherit',fontSize:14,fontWeight:700,border:0,borderRadius:12,padding:'11px 20px',background:'#D4A95D',color:'#16321F',cursor:'pointer'}}>Upgrade to Premium</button>
    </div>);
  }
  const CM = window.useStore ? window.useStore() : window.CM;
  const D = deriveStatements(CM);
  const asOf = CM.FD ? CM.FD(CM.today ? CM.today() : new Date()) : '';
  const fileRef = React.useRef(null);
  const [status, setStatus] = React.useState('');
  const [period, setPeriod] = React.useState('all');
  const openPicker = () => { setStatus(''); if (fileRef.current) fileRef.current.click(); };
  const onFile = (e) => {
    const files = e.target.files ? Array.prototype.slice.call(e.target.files) : []; if (!files.length) { return; }
    if (!(window.CMimport && window.CMimport.importFile)) { setStatus('⚠ Import engine unavailable.'); return; }
    setStatus('Importing '+files.length+' statement'+(files.length>1?'s':'')+'…');
    let added=0, done=0, errs=0;
    files.reduce(function(p,f){ return p.then(function(){ return window.CMimport.importFile(f,'cc',null).then(function(res){ added+=(res&&res.added)||0; done++; }).catch(function(){ errs++; }); }); }, Promise.resolve()).then(function(){
      setStatus('✓ Imported '+done+' statement'+(done!==1?'s':'')+(added?(' · '+added+' transaction'+(added!==1?'s':'')):'')+(errs?(' · '+errs+' couldn’t be read'):''));
      try { CM.logActivity && CM.logActivity('uploaded '+done+' card statement'+(done!==1?'s':''), files.map(function(f){return f.name;}).join(', '), {cat:'import'}); } catch(_){}
    });
    e.target.value='';
  };

  const Toolbar = (
    <div className="st-toolbar">
      <div className="left">
        <span className="st-control" style={{cursor:'default'}}><Si.cal size={15}/>As of {asOf}</span>
        {status && <span className="t-caption" style={{marginLeft:10,fontWeight:700,maxWidth:420,lineHeight:1.4,color:status[0]==='⚠'?stRed:stG}}>{status}</span>}
      </div>
      <div className="right">
        <span className="st-control" onClick={openPicker} style={{cursor:'pointer'}}><Si.layers size={15}/>Bulk Upload</span>
        <span className="st-control"><Si.download size={15}/>Export Data</span>
        {window.PeriodBar ? <window.PeriodBar value={period} onChange={setPeriod}/> : null}
        <button className="cm-btn cm-btn-primary" onClick={openPicker}><Si.upload size={16}/>Upload Statement</button>
        <input ref={fileRef} type="file" multiple accept=".ofx,.qfx,.csv,.txt,.pdf" style={{display:'none'}} onChange={onFile}/>
      </div>
    </div>
  );

  if (!D.cardObjs.length) return <div>{Toolbar}<EmptyState onUpload={openPicker}/></div>;

  return (
    <div>
      {Toolbar}
      {window.Reorderable
        ? <window.Reorderable pageKey="cc-statements" items={[
            {key:'summary',    label:'Portfolio Summary', node:<PortfolioSummary D={D}/>},
            {key:'statements', label:'Statements',        node:<StatementsTable D={D} period={period}/>},
            {key:'health',     label:'Health Score',      node:<HealthScore D={D}/>},
            {key:'analytics',  label:'Analytics',         node:<AnalyticsRow D={D}/>},
            {key:'insights',   label:'Insights',          node:<Insights D={D}/>},
          ]}/>
        : <React.Fragment><PortfolioSummary D={D}/><StatementsTable D={D} period={period}/><HealthScore D={D}/><AnalyticsRow D={D}/><Insights D={D}/></React.Fragment>}
      <Banner/>
    </div>
  );
}

window.PageStatements = PageStatements;
})();
