Skip to content

Instantly share code, notes, and snippets.

@Fizder
Created February 12, 2026 22:26
Show Gist options
  • Select an option

  • Save Fizder/a1a7cbdf89e68753ff0ac434fd7496f0 to your computer and use it in GitHub Desktop.

Select an option

Save Fizder/a1a7cbdf89e68753ff0ac434fd7496f0 to your computer and use it in GitHub Desktop.
A Subscription Tracker in Obsidian that uses Dataview.
payment_methods currencies subscriptions
Debit Card
PayPal
Credit Card
Crypto
Apple Pay
$
£
USDT
await (async () => {

  const DEF_CUR="$",GRACE=3;
  const MO=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  const td=()=>new Date().toISOString().split('T')[0];
  const td0=()=>{const d=new Date();d.setHours(0,0,0,0);return d;};
  const pd=s=>s?new Date(s+'T00:00:00'):null;
  const fD=s=>{const d=pd(s);if(!d)return'—';return`${d.getDate()} ${MO[d.getMonth()]} ${String(d.getFullYear()).slice(2)}`;};
  const dL=s=>{const d=pd(s);if(!d)return 0;return Math.ceil((d-td0())/864e5);};
  const gid=()=>'s'+Date.now().toString(36)+'_'+Math.random().toString(36).slice(2,6);
  function fP(p,c){const n=Number(p)||0;const f=n%1===0?n.toLocaleString():n.toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});if(c&&c.length===1)return c+f;return f+' '+(c||'$');}
  function adv(s,cy){const d=pd(s);if(!d)return s;switch(cy){case'monthly':d.setMonth(d.getMonth()+1);break;case'yearly':d.setFullYear(d.getFullYear()+1);break;case'weekly':d.setDate(d.getDate()+7);break;case'quarterly':d.setMonth(d.getMonth()+3);break;case'biannual':d.setMonth(d.getMonth()+6);break;default:d.setMonth(d.getMonth()+1);}return d.toISOString().split('T')[0];}
  const fCy=c=>({monthly:'Monthly',yearly:'Yearly',weekly:'Weekly',quarterly:'Quarterly',biannual:'Bi-annual'}[c]||c||'');
  const MC={NassPay:{dot:'#ef4444',text:'#fca5a5',bg:'rgba(239,68,68,.07)'},PayPal:{dot:'#3b82f6',text:'#93c5fd',bg:'rgba(59,130,246,.07)'},TIB:{dot:'#22c55e',text:'#86efac',bg:'rgba(34,197,94,.07)'},Crypto:{dot:'#7f1d1d',text:'#fca5a5',bg:'rgba(127,29,29,.07)'}};
  const gmc=m=>MC[m]||{dot:'#6b7280',text:'#d1d5db',bg:'rgba(107,114,128,.07)'};

  const cf=app.workspace.getActiveFile();
  if(!cf){dv.paragraph('No active file.');return;}
  function rd(){const c=app.metadataCache.getFileCache(cf);const f=c?.frontmatter||{};return{subs:JSON.parse(JSON.stringify(f.subscriptions||[])),methods:JSON.parse(JSON.stringify(f.payment_methods||['NassPay','FIB','TIB','Crypto'])),currencies:JSON.parse(JSON.stringify(f.currencies||['$','€','£','IQD']))};}
  async function sv(subs,methods,currencies){await app.fileManager.processFrontMatter(cf,fm=>{fm.subscriptions=subs;if(methods)fm.payment_methods=methods;if(currencies)fm.currencies=currencies;});}

  let exRate=null;
  try{const ef=app.vault.getAbstractFileByPath('Habits/Expenses Calculator.md');if(ef){const ec=app.metadataCache.getFileCache(ef);if(ec?.frontmatter?.exchange_rate_100usd)exRate=Number(ec.frontmatter.exchange_rate_100usd)||null;}}catch(e){}
  function toUSD(price,cur){if(!price)return 0;const n=Number(price)||0;if(cur==='IQD'&&exRate)return(n/exRate)*100;return n;}

  function openDaily(dateStr){if(!dateStr)return;const path='Habits/Habit Days/'+dateStr+'.md';const file=app.vault.getAbstractFileByPath(path);if(file)app.workspace.openLinkText(path,'',true);}
  function dailyExists(dateStr){if(!dateStr)return false;return !!app.vault.getAbstractFileByPath('Habits/Habit Days/'+dateStr+'.md');}

  let D=rd(),ns=false;const nw=td();
  D.subs.forEach(s=>{
    if(!s.id){s.id=gid();ns=true;}
    if(s.type==='recurring'&&s.status==='active'&&s.nextDate){let a=false;while(s.nextDate<nw){s.nextDate=adv(s.nextDate,s.cycle||'monthly');a=true;ns=true;}if(a)s.lastAdv=nw;}
    if(s.type==='non-recurring'&&s.status==='active'&&s.nextDate&&s.nextDate<nw){s.status='dead';s.deathDate=s.nextDate;s.deathReason='expired';ns=true;}
  });
  if(ns)await sv(D.subs,D.methods,D.currencies);D=rd();

  const SK='__stA';if(!window[SK])window[SK]={deadOpen:false,filters:{},sorts:{},showUSD:false};const ST=window[SK];

  const SID='stAcss';document.getElementById(SID)?.remove();
  const sty=document.createElement('style');sty.id=SID;
  sty.textContent=`
#STA,#STA *{box-sizing:border-box !important;margin:0 !important;padding:0 !important;font-family:'Consolas','Monaco','Courier New',monospace !important;border-radius:0 !important}
#STA table,#STA table th,#STA table td{border-color:#0e1828 !important}
#STA{background:#060a10 !important;color:#b0bcc8 !important;border:2px solid #0f1a2a !important;border-radius:8px !important;line-height:1.4 !important;-webkit-font-smoothing:antialiased !important;position:relative !important;overflow:hidden !important}
/* ── Shell decoration ── */
#STA::before{content:'' !important;position:absolute !important;top:8px !important;left:8px !important;right:8px !important;bottom:8px !important;border:1px solid rgba(34,211,238,.05) !important;border-radius:4px !important;pointer-events:none !important;z-index:0 !important}
#STA::after{content:'' !important;position:absolute !important;top:0 !important;left:0 !important;right:0 !important;height:2px !important;background:linear-gradient(90deg,#060a10,#164e63,#22d3ee,#164e63,#060a10) !important;border-radius:8px 8px 0 0 !important;pointer-events:none !important;z-index:3 !important}
.VA-corner{position:absolute !important;width:14px !important;height:14px !important;pointer-events:none !important;z-index:1 !important}
.VA-corner::before,.VA-corner::after{content:'' !important;position:absolute !important;background:#0c3a4a !important}
.VA-corner::before{width:14px !important;height:1px !important}
.VA-corner::after{width:1px !important;height:14px !important}
.VA-tl{top:12px !important;left:12px !important}.VA-tl::before{top:0;left:0}.VA-tl::after{top:0;left:0}
.VA-tr{top:12px !important;right:12px !important}.VA-tr::before{top:0;right:0}.VA-tr::after{top:0;right:0}
.VA-bl{bottom:12px !important;left:12px !important}.VA-bl::before{bottom:0;left:0}.VA-bl::after{bottom:0;left:0}
.VA-br{bottom:12px !important;right:12px !important}.VA-br::before{bottom:0;right:0}.VA-br::after{bottom:0;right:0}
/* ── Background pattern: faint grid ── */
.VA-bg{position:absolute !important;inset:0 !important;pointer-events:none !important;z-index:0 !important;background-image:
  repeating-linear-gradient(0deg,transparent,transparent 39px,rgba(34,211,238,.018) 39px,rgba(34,211,238,.018) 40px),
  repeating-linear-gradient(90deg,transparent,transparent 59px,rgba(34,211,238,.012) 59px,rgba(34,211,238,.012) 60px) !important;background-size:40px 40px,60px 60px !important}
/* ── Decorative scan line accent ── */
.VA-scan{position:absolute !important;pointer-events:none !important;z-index:0 !important;height:1px !important;left:20px !important;right:20px !important;background:linear-gradient(90deg,transparent,rgba(34,211,238,.04),transparent) !important}

/* Header */
.VA-hdr{display:flex !important;align-items:center !important;justify-content:space-between !important;flex-wrap:wrap !important;gap:8px !important;border-bottom:1px solid #0f1a2a !important;position:relative !important;z-index:1 !important}
.VA-ttl{font-size:15px !important;font-weight:800 !important;letter-spacing:4px !important;text-transform:uppercase !important;color:#22d3ee !important;text-shadow:0 0 14px rgba(34,211,238,.15) !important;border-left:3px solid #22d3ee !important;line-height:1 !important}
.VA-btns{display:flex !important;gap:4px !important;flex-wrap:wrap !important}
/* Buttons */
.VA-b{font-size:9px !important;font-weight:700 !important;letter-spacing:1.5px !important;text-transform:uppercase !important;cursor:pointer !important;transition:all .15s ease !important;user-select:none !important;display:inline-flex !important;align-items:center !important;gap:5px !important;line-height:1 !important;white-space:nowrap !important;position:relative !important}
.VA-b-add{background:linear-gradient(180deg,#0c2030,#06111a) !important;color:#22d3ee !important;border:1px solid #164e63 !important;box-shadow:inset 0 1px 0 rgba(34,211,238,.08) !important}
.VA-b-add:hover{border-color:#22d3ee !important;box-shadow:0 0 12px rgba(34,211,238,.1) !important;color:#67e8f9 !important}
.VA-b-add .VA-bi{font-size:12px !important;color:#0891b2 !important}
.VA-b-g{background:#080c14 !important;color:#3a5a70 !important;border:1px solid #121e2e !important}
.VA-b-g:hover{border-color:#1a3a50 !important;color:#22d3ee !important}
.VA-b-d{background:#120808 !important;color:#ef4444 !important;border:1px solid #2a1010 !important}
.VA-b-d:hover{border-color:#501515 !important;box-shadow:0 0 10px rgba(239,68,68,.06) !important}

/* Sections */
.VA-sec{border:1px solid #0f1a2a !important;background:#080e18 !important;position:relative !important;z-index:1 !important;border-radius:3px !important;overflow:visible !important}
.VA-sec-rec{border-top:3px solid #0891b2 !important}
.VA-sec-non{border-top:3px solid #7c3aed !important}
.VA-sec-ded{border-top:3px solid #374151 !important}
.VA-sh{background:linear-gradient(180deg,#0c1624,#0a1220) !important;display:flex !important;align-items:center !important;justify-content:space-between !important;flex-wrap:wrap !important;gap:6px !important}
.VA-shl{display:flex !important;align-items:center !important;gap:8px !important}
.VA-sn{font-size:12px !important;font-weight:800 !important;letter-spacing:2.5px !important;text-transform:uppercase !important;line-height:1 !important}
.VA-sc{font-size:10px !important;font-weight:600 !important;color:#1e3a50 !important;line-height:1 !important}
.VA-stog{font-size:9px !important;color:#1e3a50 !important;cursor:pointer !important;transition:color .15s !important}
.VA-stog:hover{color:#22d3ee !important}
.VA-usd{font-size:7px !important;color:#122030 !important;cursor:pointer !important;transition:color .15s !important;letter-spacing:.5px !important;margin-left:auto !important}
.VA-usd:hover{color:#1a4a60 !important}
.VA-usd.act{color:#0e7490 !important}
/* Toolbar */
.VA-tb{background:#091018 !important;border-top:1px solid #0c1420 !important;border-bottom:1px solid #0f1a2a !important;display:flex !important;align-items:center !important;gap:6px !important;flex-wrap:wrap !important}
.VA-srch{width:110px !important;font-size:10px !important;border:1px solid #121e2e !important;background:#060a10 !important;color:#b0bcc8 !important;outline:none !important;transition:border-color .15s !important;line-height:1.2 !important;height:auto !important;-webkit-appearance:none !important;appearance:none !important;box-shadow:inset 0 1px 2px rgba(0,0,0,.3) !important;border-radius:2px !important}
.VA-srch:focus{border-color:#164e63 !important}
.VA-srch::placeholder{color:#14202e !important;font-size:9px !important}
.VA-sep{width:1px !important;height:14px !important;background:#121e2e !important;flex-shrink:0 !important}
.VA-fwrap{position:relative !important;display:inline-block !important}
.VA-fb{font-size:8px !important;font-weight:700 !important;letter-spacing:.8px !important;text-transform:uppercase !important;cursor:pointer !important;transition:all .12s !important;border:1px solid #121e2e !important;background:#060a10 !important;color:#2a4a60 !important;display:inline-flex !important;align-items:center !important;gap:4px !important;border-radius:2px !important;white-space:nowrap !important}
.VA-fb:hover{border-color:#1a3a50 !important;color:#22d3ee !important}
.VA-fb.act{border-color:#164e63 !important;color:#22d3ee !important;background:rgba(34,211,238,.03) !important}
.VA-farr{font-size:6px !important;transition:transform .15s !important}
.VA-fwrap.open .VA-farr{transform:rotate(180deg) !important}
.VA-fpop{position:absolute !important;top:calc(100% + 3px) !important;left:0 !important;min-width:140px !important;background:#0c1424 !important;border:1px solid #1a2a40 !important;border-radius:3px !important;z-index:90 !important;display:none !important;box-shadow:0 6px 20px rgba(0,0,0,.6) !important}
.VA-fwrap.open .VA-fpop{display:flex !important;flex-wrap:wrap !important;gap:3px !important}
.VA-fp{font-size:8px !important;font-weight:700 !important;letter-spacing:.4px !important;cursor:pointer !important;transition:all .12s !important;border:1px solid #121e2e !important;background:#080e18 !important;color:#2a4a60 !important;display:inline-flex !important;align-items:center !important;gap:4px !important;border-radius:2px !important;white-space:nowrap !important}
.VA-fp:hover{border-color:#1a3a50 !important;color:#6aa0c0 !important}
.VA-fp.act{border-color:#164e63 !important;background:rgba(34,211,238,.05) !important;color:#22d3ee !important}
.VA-fpdot{width:5px !important;height:5px !important;border-radius:50% !important;flex-shrink:0 !important;display:inline-block !important}
.VA-slbl{font-size:8px !important;color:#121e2e !important;letter-spacing:.8px !important;font-weight:600 !important;text-transform:uppercase !important}
.VA-sp{font-size:8px !important;font-weight:600 !important;cursor:pointer !important;transition:all .12s !important;border:1px solid #0e1624 !important;background:transparent !important;color:#1a3048 !important;border-radius:2px !important;white-space:nowrap !important;letter-spacing:.4px !important}
.VA-sp:hover{color:#3a6a80 !important;border-color:#152030 !important}
.VA-sp.act{color:#0891b2 !important;border-color:#164e63 !important;background:rgba(8,145,178,.03) !important}

/* Table */
.VA-tw{overflow-x:auto !important;background:#080e18 !important;scrollbar-width:none !important;-ms-overflow-style:none !important}
.VA-tw::-webkit-scrollbar{display:none !important}
.VA-t{width:100% !important;border-collapse:collapse !important;min-width:700px !important;border:none !important}
.VA-t th{text-align:left !important;font-size:9px !important;font-weight:800 !important;text-transform:uppercase !important;letter-spacing:2px !important;color:#162840 !important;background:#091220 !important;border:none !important;border-bottom:1px solid #0f1a2a !important;border-right:1px solid #0c1420 !important;white-space:nowrap !important;line-height:1 !important}
.VA-t th:last-child{text-align:right !important;border-right:none !important}
.VA-t td{border:none !important;border-bottom:1px solid #0c1420 !important;border-right:1px solid #0a1018 !important;vertical-align:middle !important;line-height:1.2 !important;background:transparent !important}
.VA-t td:last-child{border-right:none !important}
.VA-t tbody tr{transition:background .1s !important;background:#080e18 !important}
.VA-t tbody tr:nth-child(even){background:#091220 !important}
.VA-t tbody tr:hover{background:#0c1828 !important}
.VA-t .VA-emp td{text-align:center !important;color:#121e2e !important;font-size:10px !important;letter-spacing:2px !important;background:#080e18 !important;border-right:none !important}

/* Cells */
.VA-nm{font-size:12px !important;font-weight:700 !important;color:#bcc8d4 !important;white-space:nowrap !important;overflow:hidden !important;text-overflow:ellipsis !important;max-width:180px !important;display:inline-block !important;vertical-align:middle !important;border-left:2px solid #162840 !important}
.VA-pr{font-size:12px !important;font-weight:800 !important;color:#3d9b7a !important;white-space:nowrap !important;letter-spacing:.2px !important;text-shadow:0 0 6px rgba(61,155,122,.08) !important}
.VA-ded .VA-pr{color:#162840 !important}
.VA-pu{font-size:8px !important;color:#0c3a4a !important;font-weight:600 !important}
.VA-cy{font-size:10px !important;color:#1a4a6a !important;font-style:italic !important;white-space:nowrap !important}
.VA-mt{display:inline-flex !important;align-items:center !important;gap:5px !important;white-space:nowrap !important;border-radius:2px !important;border:1px solid transparent !important}
.VA-mtd{width:6px !important;height:6px !important;border-radius:50% !important;flex-shrink:0 !important;display:inline-block !important}
.VA-mtn{font-size:10px !important;font-weight:600 !important}
.VA-dt{font-size:10px !important;color:#1a4060 !important;white-space:nowrap !important}
.VA-dtlink{cursor:pointer !important;transition:color .12s !important;text-decoration:none !important}
.VA-dtlink:hover{color:#22d3ee !important;text-decoration:underline !important}
.VA-dl{font-size:12px !important;font-weight:800 !important;white-space:nowrap !important}
.VA-dl-ok{color:#0c6a80 !important}
.VA-dl-w{color:#a04a10 !important}
.VA-dl-c{color:#a01818 !important}
.VA-rsn{font-size:9px !important;font-weight:700 !important;letter-spacing:.4px !important}
.VA-rsn-e{color:#162840 !important}.VA-rsn-c{color:#a01818 !important}
.VA-acts{text-align:right !important;white-space:nowrap !important}
.VA-ab{font-size:8px !important;font-weight:700 !important;letter-spacing:.8px !important;text-transform:uppercase !important;cursor:pointer !important;transition:all .12s !important;background:transparent !important;display:inline-block !important;border-radius:2px !important}
.VA-ab-e{border:1px solid #121e2e !important;color:#1e5a7a !important}.VA-ab-e:hover{border-color:#1a4060 !important;color:#22d3ee !important}
.VA-ab-can{border:1px solid #1a1420 !important;color:#6a3050 !important}.VA-ab-can:hover{color:#c04070 !important;border-color:#301228 !important}
.VA-ab-arc{border:1px solid #161010 !important;color:#5a2a2a !important}.VA-ab-arc:hover{color:#ef4444 !important;border-color:#301010 !important}
.VA-ab-rev{border:1px solid #121e2e !important;color:#1e5a7a !important}.VA-ab-rev:hover{border-color:#1a4060 !important;color:#22d3ee !important}
.VA-grace{font-size:8px !important;font-weight:700 !important;border:1px solid rgba(180,83,9,.18) !important;color:#a04a10 !important;cursor:pointer !important;vertical-align:middle !important;background:rgba(180,83,9,.03) !important;border-radius:2px !important}
.VA-cbadge{font-size:7px !important;font-weight:700 !important;border:1px solid rgba(196,74,128,.18) !important;color:#c04070 !important;vertical-align:middle !important;background:rgba(196,74,128,.03) !important;border-radius:2px !important;letter-spacing:.4px !important;text-transform:uppercase !important}

/* Modal */
.VA-mbg{position:fixed !important;inset:0 !important;z-index:9998 !important;background:rgba(4,6,12,.88) !important;backdrop-filter:blur(8px) !important;-webkit-backdrop-filter:blur(8px) !important;display:flex !important;align-items:center !important;justify-content:center !important}
.VA-mod{width:100% !important;max-width:400px !important;max-height:calc(100vh - 40px) !important;overflow-y:auto !important;background:linear-gradient(180deg,#0c1624,#081018) !important;border:1px solid #152030 !important;border-top:2px solid #164e63 !important;position:relative !important;border-radius:5px !important;box-shadow:0 8px 40px rgba(0,0,0,.6),0 0 20px rgba(34,211,238,.03) !important}
.VA-mtl{font-size:12px !important;font-weight:800 !important;letter-spacing:2.5px !important;text-transform:uppercase !important;color:#22d3ee !important;border-left:3px solid #22d3ee !important;line-height:1 !important}
.VA-fld{display:block !important}
.VA-fr{display:flex !important;gap:10px !important}
.VA-fr .VA-fld{flex:1 !important;min-width:0 !important}
.VA-lbl{font-size:8px !important;font-weight:700 !important;letter-spacing:1.5px !important;text-transform:uppercase !important;color:#1e4a60 !important;display:block !important;line-height:1 !important}
.VA-inp{width:100% !important;border:1px solid #121e2e !important;background:#060a10 !important;color:#b0bcc8 !important;font-size:12px !important;outline:none !important;transition:border-color .15s !important;line-height:1.3 !important;height:auto !important;min-height:34px !important;-webkit-appearance:none !important;appearance:none !important;box-shadow:inset 0 1px 3px rgba(0,0,0,.3) !important;border-radius:3px !important}
.VA-inp:focus{border-color:#164e63 !important}
.VA-inp::placeholder{color:rgba(20,32,46,.6) !important;font-size:11px !important}
select.VA-inp{cursor:pointer !important;background-image:url("data:image/svg+xml,%3Csvg width='8' height='5' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%231e4a60' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") !important;background-repeat:no-repeat !important;background-position:right 8px center !important;background-color:#060a10 !important}
select.VA-inp option{background:#081018 !important;color:#b0bcc8 !important}
input[type="date"].VA-inp{color-scheme:dark !important}
.VA-hint{font-size:9px !important;color:#0e5a70 !important;cursor:pointer !important;transition:color .12s !important;display:block !important}
.VA-hint:hover{color:#22d3ee !important}
.VA-mft{display:flex !important;justify-content:flex-end !important;gap:6px !important;border-top:1px solid #0f1a2a !important}
.VA-ctxt{color:#2a5a70 !important;font-size:11px !important;line-height:1.5 !important}
.VA-ctxt strong{color:#22d3ee !important;font-weight:700 !important}
.VA-cinfo{font-size:9px !important;color:#1a4060 !important;line-height:1.4 !important}

/* Stats */
.VA-stats{width:100% !important;max-width:380px !important;max-height:calc(100vh - 40px) !important;overflow-y:auto !important;background:#081020 !important;border:1px solid #152030 !important;position:relative !important;border-radius:5px !important;box-shadow:0 8px 40px rgba(0,0,0,.7),0 0 30px rgba(34,211,238,.05) !important;overflow:hidden !important}
.VA-stats::before{content:'' !important;position:absolute !important;top:0;left:0;right:0;height:2px !important;background:linear-gradient(90deg,transparent,#22d3ee,transparent) !important;z-index:2 !important}
.VA-sthdr{display:flex !important;align-items:center !important;justify-content:space-between !important;background:linear-gradient(180deg,#0c1828,#0a1220) !important}
.VA-sttl{font-size:11px !important;font-weight:800 !important;letter-spacing:2.5px !important;text-transform:uppercase !important;color:#22d3ee !important;text-shadow:0 0 10px rgba(34,211,238,.2) !important}
.VA-stclose{font-size:14px !important;color:#1a3048 !important;cursor:pointer !important;transition:color .15s !important;line-height:1 !important}
.VA-stclose:hover{color:#22d3ee !important}
.VA-stgrid{display:grid !important;grid-template-columns:1fr 1fr !important;gap:1px !important;background:#060a10 !important}
.VA-stcard{background:linear-gradient(180deg,#0a1220,#081018) !important;position:relative !important;overflow:hidden !important}
.VA-stcard::after{content:'' !important;position:absolute !important;top:0;left:0;width:2px !important;height:100% !important}
.VA-stcard-t::after{background:#0891b2 !important}
.VA-stcard-g::after{background:#3d9b7a !important}
.VA-stcard-p::after{background:#7c3aed !important}
.VA-stcard-r::after{background:#ef4444 !important}
.VA-stcard-y::after{background:#a08020 !important}
.VA-stcard-w{grid-column:1/-1 !important}
.VA-stlbl{font-size:7px !important;font-weight:700 !important;letter-spacing:1.5px !important;text-transform:uppercase !important;color:#162840 !important;display:block !important}
.VA-stval{font-size:16px !important;font-weight:800 !important;color:#d0dce6 !important;line-height:1 !important}
.VA-stval-t{color:#22d3ee !important}
.VA-stval-g{color:#3d9b7a !important}
.VA-stval-p{color:#a78bfa !important}
.VA-stval-y{color:#c0a030 !important}
.VA-stsub{font-size:8px !important;color:#162840 !important;display:block !important}
.VA-stbar-wrap{display:block !important}
.VA-stbar-row{display:flex !important;align-items:center !important;gap:6px !important}
.VA-stbar-nm{font-size:8px !important;font-weight:600 !important;color:#3a6a80 !important;width:60px !important;text-align:right !important;flex-shrink:0 !important}
.VA-stbar-bg{flex:1 !important;height:10px !important;background:#060a10 !important;border:1px solid #0c1420 !important;border-radius:1px !important;overflow:hidden !important;position:relative !important}
.VA-stbar-fill{height:100% !important;transition:width .5s ease-out !important;position:relative !important;overflow:hidden !important}
.VA-stbar-fill::after{content:'' !important;position:absolute !important;inset:0 !important;background:linear-gradient(90deg,transparent,rgba(255,255,255,.06),transparent) !important;animation:VAshim 2s infinite !important}
@keyframes VAshim{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}
.VA-stbar-ct{font-size:8px !important;font-weight:700 !important;color:#1e3a50 !important;width:16px !important;flex-shrink:0 !important}
.VA-stft{background:#060a10 !important;border-top:1px solid #0c1420 !important;text-align:center !important}
.VA-stft-txt{font-size:7px !important;color:#0c1e30 !important;letter-spacing:.8px !important}

/* List editor */
.VA-li{display:flex !important;align-items:center !important;gap:8px !important;background:#060a10 !important;border:1px solid #0e1624 !important;border-radius:3px !important}
.VA-li:hover{border-color:#121e2e !important}
.VA-lin{flex:1 !important;font-size:11px !important;color:#b0bcc8 !important;font-weight:600 !important}
.VA-lix{font-size:16px !important;color:#301818 !important;cursor:pointer !important;transition:color .12s !important;line-height:1 !important}
.VA-lix:hover{color:#ef4444 !important}
.VA-lar{display:flex !important;gap:6px !important}
`;
  document.head.appendChild(sty);

  let root=document.getElementById('STA');
  if(!root){root=document.createElement('div');root.id='STA';dv.container.appendChild(root);}else root.innerHTML='';

  let mo=false;
  function openM(fn){if(mo)return;mo=true;const bg=document.createElement('div');bg.className='VA-mbg';bg.style.cssText='padding:12px !important';const cd=document.createElement('div');cd.className='VA-mod';cd.style.cssText='padding:20px !important';function cl(){bg.remove();mo=false;}bg.addEventListener('click',e=>{if(e.target===bg)cl();});cd.addEventListener('click',e=>e.stopPropagation());fn(cd,cl);bg.appendChild(cd);document.body.appendChild(bg);}
  function selO(arr,sel){return arr.map(v=>`<option value="${v}" ${v===sel?'selected':''}>${v}</option>`).join('');}

  function openAE(es){
    const ie=!!es;const sub=ie?{...es}:{name:'',price:'',currency:DEF_CUR,method:'',type:'recurring',cycle:'monthly',started:td(),nextDate:'',status:'active'};
    openM((cd,cl)=>{
      function bld(){
        const ir=sub.type==='recurring';const sugDate=(ir&&sub.started)?adv(sub.started,sub.cycle||'monthly'):'';
        cd.innerHTML=`<div class="VA-mtl" style="padding-left:12px !important;margin-bottom:16px !important">${ie?'Edit':'New'} Subscription</div>
          <div class="VA-fld" style="margin-bottom:10px !important"><label class="VA-lbl" style="margin-bottom:4px !important">Name</label><input class="VA-inp" id="sf-n" type="text" value="${sub.name}" autocomplete="off" style="padding:8px 10px !important" /></div>
          <div class="VA-fr" style="margin-bottom:10px !important"><div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">Price</label><input class="VA-inp" id="sf-p" type="number" step="0.01" value="${sub.price}" autocomplete="off" style="padding:8px 10px !important" /></div><div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">Currency</label><select class="VA-inp" id="sf-cur" style="padding:8px 24px 8px 10px !important">${selO(D.currencies,sub.currency)}</select></div></div>
          <div class="VA-fld" style="margin-bottom:10px !important"><label class="VA-lbl" style="margin-bottom:4px !important">Payment Method</label><select class="VA-inp" id="sf-m" style="padding:8px 24px 8px 10px !important"><option value=""></option>${selO(D.methods,sub.method)}</select></div>
          <div class="VA-fr" style="margin-bottom:10px !important"><div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">Type</label><select class="VA-inp" id="sf-ty" style="padding:8px 24px 8px 10px !important"><option value="recurring" ${sub.type==='recurring'?'selected':''}>Recurring</option><option value="non-recurring" ${sub.type==='non-recurring'?'selected':''}>Non-recurring</option></select></div>${ir?`<div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">Cycle</label><select class="VA-inp" id="sf-cy" style="padding:8px 24px 8px 10px !important"><option value="monthly" ${sub.cycle==='monthly'?'selected':''}>Monthly</option><option value="yearly" ${sub.cycle==='yearly'?'selected':''}>Yearly</option><option value="weekly" ${sub.cycle==='weekly'?'selected':''}>Weekly</option><option value="quarterly" ${sub.cycle==='quarterly'?'selected':''}>Quarterly</option><option value="biannual" ${sub.cycle==='biannual'?'selected':''}>Bi-annual</option></select></div>`:''}</div>
          <div class="VA-fr" style="margin-bottom:10px !important"><div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">Started</label><input class="VA-inp" id="sf-ds" type="date" value="${sub.started}" style="padding:8px 10px 8px 28px !important" /></div><div class="VA-fld"><label class="VA-lbl" style="margin-bottom:4px !important">${ir?'Renews':'Ends'}</label><input class="VA-inp" id="sf-nd" type="date" value="${sub.nextDate}" style="padding:8px 10px 8px 28px !important" />${ir&&sugDate&&!sub.nextDate?`<div class="VA-hint" style="margin-top:3px !important" id="sf-hint">↳ ${fD(sugDate)} — click to apply</div>`:''}</div></div>
          <div class="VA-mft" style="margin-top:14px !important;padding-top:12px !important"><div class="VA-b VA-b-g" id="sf-x" style="padding:7px 14px !important">Cancel</div><div class="VA-b VA-b-add" id="sf-s" style="padding:7px 14px !important"><span class="VA-bi">⊕</span> Save</div></div>`;
        cd.querySelector('#sf-hint')?.addEventListener('click',()=>{cd.querySelector('#sf-nd').value=sugDate;cd.querySelector('#sf-hint')?.remove();});
        cd.querySelector('#sf-ty').addEventListener('change',e=>{sub.type=e.target.value;sub.name=cd.querySelector('#sf-n').value;sub.price=cd.querySelector('#sf-p').value;sub.currency=cd.querySelector('#sf-cur').value;sub.method=cd.querySelector('#sf-m').value;sub.started=cd.querySelector('#sf-ds').value;sub.nextDate=cd.querySelector('#sf-nd').value;if(cd.querySelector('#sf-cy'))sub.cycle=cd.querySelector('#sf-cy').value;bld();});
        cd.querySelector('#sf-cy')?.addEventListener('change',e=>{sub.cycle=e.target.value;sub.name=cd.querySelector('#sf-n').value;sub.price=cd.querySelector('#sf-p').value;sub.currency=cd.querySelector('#sf-cur').value;sub.method=cd.querySelector('#sf-m').value;sub.started=cd.querySelector('#sf-ds').value;sub.nextDate='';sub.type=cd.querySelector('#sf-ty').value;bld();});
        cd.querySelector('#sf-ds')?.addEventListener('change',e=>{sub.started=e.target.value;sub.name=cd.querySelector('#sf-n').value;sub.price=cd.querySelector('#sf-p').value;sub.currency=cd.querySelector('#sf-cur').value;sub.method=cd.querySelector('#sf-m').value;sub.nextDate='';sub.type=cd.querySelector('#sf-ty').value;if(cd.querySelector('#sf-cy'))sub.cycle=cd.querySelector('#sf-cy').value;bld();});
        cd.querySelector('#sf-x').addEventListener('click',cl);
        cd.querySelector('#sf-s').addEventListener('click',async()=>{
          const nm=cd.querySelector('#sf-n').value.trim();if(!nm){cd.querySelector('#sf-n').style.borderColor='#ef4444';return;}
          const mt=cd.querySelector('#sf-m').value;if(!mt){cd.querySelector('#sf-m').style.borderColor='#ef4444';return;}
          const ty=cd.querySelector('#sf-ty').value;let nD=cd.querySelector('#sf-nd').value;
          if(ty==='recurring'&&!nD&&cd.querySelector('#sf-ds').value)nD=adv(cd.querySelector('#sf-ds').value,cd.querySelector('#sf-cy')?.value||'monthly');
          const o={id:ie?sub.id:gid(),name:nm,price:parseFloat(cd.querySelector('#sf-p').value)||0,currency:cd.querySelector('#sf-cur').value||DEF_CUR,method:mt,type:ty,cycle:ty==='recurring'?(cd.querySelector('#sf-cy')?.value||'monthly'):'',started:cd.querySelector('#sf-ds').value||td(),nextDate:nD||'',status:'active'};
          if(ie&&es.cancelled)o.cancelled=es.cancelled;
          const subs=rd().subs;if(ie){const i=subs.findIndex(s=>s.id===sub.id);if(i>=0)subs[i]={...subs[i],...o};}else subs.push(o);
          await sv(subs);D=rd();cl();setTimeout(renderAll,150);
        });
      }bld();
    });
  }

  function openCancel(sub){openM((cd,cl)=>{
    const hasTime=sub.nextDate&&sub.nextDate>=nw;
    cd.innerHTML=`<div class="VA-mtl" style="padding-left:12px !important;margin-bottom:12px !important">Cancel Renewal</div><div class="VA-ctxt" style="margin-bottom:12px !important">Stop recurring payments for <strong>${sub.name}</strong>?${hasTime?`<div class="VA-cinfo" style="margin-top:4px !important">Access until <strong>${fD(sub.nextDate)}</strong>. Moves to Non-Recurring.</div>`:''}</div><div class="VA-mft" style="margin-top:12px !important;padding-top:10px !important"><div class="VA-b VA-b-g" id="cc-n" style="padding:7px 14px !important">Keep</div><div class="VA-b VA-b-d" id="cc-y" style="padding:7px 14px !important">Cancel</div></div>`;
    cd.querySelector('#cc-n').addEventListener('click',cl);
    cd.querySelector('#cc-y').addEventListener('click',async()=>{const subs=rd().subs;const s=subs.find(x=>x.id===sub.id);if(s){if(hasTime){s.type='non-recurring';s.cancelled=true;s.cycle='';}else{s.status='dead';s.deathDate=td();s.deathReason='cancelled';}}await sv(subs);D=rd();cl();setTimeout(renderAll,150);});
  });}

  function openArchive(sub){openM((cd,cl)=>{
    cd.innerHTML=`<div class="VA-mtl" style="padding-left:12px !important;margin-bottom:12px !important">Archive</div><div class="VA-ctxt" style="margin-bottom:12px !important">Archive <strong>${sub.name}</strong>?</div><div class="VA-mft" style="margin-top:12px !important;padding-top:10px !important"><div class="VA-b VA-b-g" id="aa-n" style="padding:7px 14px !important">Keep</div><div class="VA-b VA-b-d" id="aa-y" style="padding:7px 14px !important">Archive</div></div>`;
    cd.querySelector('#aa-n').addEventListener('click',cl);
    cd.querySelector('#aa-y').addEventListener('click',async()=>{const subs=rd().subs;const s=subs.find(x=>x.id===sub.id);if(s){s.status='dead';s.deathDate=td();s.deathReason='cancelled';}await sv(subs);D=rd();cl();setTimeout(renderAll,150);});
  });}

  function openStats(){
    if(mo)return;mo=true;
    const bg=document.createElement('div');bg.className='VA-mbg';bg.style.cssText='padding:12px !important';
    const cd=document.createElement('div');cd.className='VA-stats';
    function cl(){bg.remove();mo=false;}
    bg.addEventListener('click',e=>{if(e.target===bg)cl();});cd.addEventListener('click',e=>e.stopPropagation());
    const subs=D.subs;const active=subs.filter(s=>s.status==='active');const rec=active.filter(s=>s.type==='recurring');const non=active.filter(s=>s.type==='non-recurring');const dead=subs.filter(s=>s.status==='dead');
    // Monthly cost: only monthly-cycle recurring subs
    let monthlyCost=0;rec.forEach(s=>{if(s.cycle==='monthly')monthlyCost+=toUSD(s.price,s.currency);});
    // Yearly cost: all recurring normalized to yearly
    let yearlyCost=0;rec.forEach(s=>{let u=toUSD(s.price,s.currency);switch(s.cycle){case'monthly':u*=12;break;case'weekly':u*=52;break;case'quarterly':u*=4;break;case'biannual':u*=2;break;case'yearly':break;default:u*=12;}yearlyCost+=u;});
    // Costliest: highest raw USD price among all active, regardless of cycle
    let topSub=null,topVal=0;active.forEach(s=>{const v=toUSD(s.price,s.currency);if(v>topVal){topVal=v;topSub=s;}});
    const methodCounts={};active.forEach(s=>{methodCounts[s.method]=(methodCounts[s.method]||0)+1;});
    const maxMC=Math.max(...Object.values(methodCounts),1);
    const methodBars=Object.entries(methodCounts).sort((a,b)=>b[1]-a[1]).map(([m,c])=>{const mc=gmc(m);const pct=Math.round((c/maxMC)*100);return`<div class="VA-stbar-row" style="margin-bottom:4px !important"><span class="VA-stbar-nm">${m}</span><div class="VA-stbar-bg"><div class="VA-stbar-fill" style="width:${pct}% !important;background:${mc.dot} !important"></div></div><span class="VA-stbar-ct">${c}</span></div>`;}).join('');
    const curCounts={};active.forEach(s=>{curCounts[s.currency]=(curCounts[s.currency]||0)+1;});
    const curText=Object.entries(curCounts).map(([c,n])=>`${c}: ${n}`).join(' · ');
    cd.innerHTML=`
      <div class="VA-sthdr" style="padding:10px 14px !important"><div class="VA-sttl">Dashboard</div><span class="VA-stclose" id="stx">×</span></div>
      <div class="VA-stgrid">
        <div class="VA-stcard VA-stcard-g" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:4px !important">Monthly Recurring</span><span class="VA-stval VA-stval-g" style="font-size:15px !important">$${monthlyCost.toFixed(2)}</span></div>
        <div class="VA-stcard VA-stcard-y" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:4px !important">Yearly Total</span><span class="VA-stval VA-stval-y" style="font-size:15px !important">$${yearlyCost.toFixed(0)}</span></div>
        <div class="VA-stcard VA-stcard-t" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:4px !important">Active</span><span class="VA-stval VA-stval-t" style="font-size:15px !important">${active.length}</span><span class="VA-stsub" style="margin-top:2px !important">${rec.length} rec · ${non.length} one-time</span></div>
        <div class="VA-stcard VA-stcard-p" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:4px !important">Archived</span><span class="VA-stval VA-stval-p" style="font-size:15px !important">${dead.length}</span><span class="VA-stsub" style="margin-top:2px !important">${dead.filter(s=>s.deathReason==='cancelled').length}c · ${dead.filter(s=>s.deathReason==='expired').length}e</span></div>
        <div class="VA-stcard VA-stcard-r" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:4px !important">Costliest</span><span class="VA-stval" style="font-size:13px !important;color:#d0dce6 !important">${topSub?topSub.name:'—'}</span><span class="VA-stsub" style="margin-top:2px !important">${topSub?fP(topSub.price,topSub.currency)+' · '+fCy(topSub.cycle):''}</span></div>
        <div class="VA-stcard VA-stcard-t VA-stcard-w" style="padding:10px 12px 10px 10px !important"><span class="VA-stlbl" style="margin-bottom:6px !important">Methods</span><div class="VA-stbar-wrap">${methodBars||'<span class="VA-stsub">—</span>'}</div></div>
      </div>
      <div class="VA-stft" style="padding:6px 12px !important"><span class="VA-stft-txt">${curText}</span></div>`;
    cd.querySelector('#stx').addEventListener('click',cl);
    bg.appendChild(cd);document.body.appendChild(bg);
    setTimeout(()=>{cd.querySelectorAll('.VA-stbar-fill').forEach(b=>{const w=b.style.width;b.style.width='0%';setTimeout(()=>{b.style.width=w;},20);});},20);
  }

  function openLE(title,key){openM((cd,cl)=>{
    let items=[...D[key]];
    function ren(){
      cd.innerHTML=`<div class="VA-mtl" style="padding-left:12px !important;margin-bottom:12px !important">${title}</div><div>${items.map((m,i)=>`<div class="VA-li" style="padding:8px 12px !important;margin-bottom:3px !important"><span class="VA-lin">${m}</span><span class="VA-lix" data-i="${i}">×</span></div>`).join('')}</div><div class="VA-lar" style="margin-top:8px !important;margin-bottom:12px !important"><input class="VA-inp" id="le-n" type="text" style="flex:1 !important;padding:7px 10px !important" autocomplete="off" /><div class="VA-b VA-b-add" id="le-a" style="padding:7px 12px !important"><span class="VA-bi">⊕</span> Add</div></div><div class="VA-mft" style="margin-top:10px !important;padding-top:10px !important"><div class="VA-b VA-b-add" id="le-d" style="padding:7px 14px !important">Done</div></div>`;
      cd.querySelectorAll('[data-i]').forEach(el=>{el.addEventListener('click',()=>{items.splice(+el.dataset.i,1);ren();});});
      cd.querySelector('#le-a').addEventListener('click',()=>{const v=cd.querySelector('#le-n').value.trim();if(v&&!items.includes(v)){items.push(v);ren();}});
      cd.querySelector('#le-n').addEventListener('keydown',e=>{if(e.key==='Enter')cd.querySelector('#le-a').click();});
      cd.querySelector('#le-d').addEventListener('click',async()=>{const subs=rd().subs;await sv(subs,key==='methods'?items:D.methods,key==='currencies'?items:D.currencies);D=rd();cl();setTimeout(renderAll,150);});
    }ren();
  });}

  function showGrace(sub){if(sub.type!=='recurring'||sub.status!=='active'||!sub.lastAdv)return false;return Math.ceil((td0()-pd(sub.lastAdv))/864e5)<=GRACE;}

  // p = padding shorthand
  function bRow(sub,sec){
    const isDead=sec==='dead';const m=gmc(sub.method);const dl=dL(sub.nextDate);
    const dlC=dl<=3?'VA-dl-c':dl<=7?'VA-dl-w':'VA-dl-ok';
    let gr='';if(sec==='recurring'&&showGrace(sub))gr=`<span class="VA-grace" style="padding:2px 5px !important;margin-left:5px !important" data-act="didnt-renew" data-id="${sub.id}">didn't renew?</span>`;
    const cBadge=sub.cancelled?'<span class="VA-cbadge" style="padding:1px 4px !important;margin-left:4px !important">cancelled</span>':'';
    const usdEq=ST.showUSD&&sub.currency==='IQD'&&exRate?`<span class="VA-pu" style="margin-left:3px !important">≈$${((Number(sub.price)/exRate)*100).toFixed(2)}</span>`:'';
    const usdNorm=toUSD(sub.price,sub.currency);
    const sL=sub.started&&dailyExists(sub.started)?`<span class="VA-dt VA-dtlink" data-open="${sub.started}">${fD(sub.started)}</span>`:`<span class="VA-dt">${fD(sub.started)}</span>`;
    const P='style="padding:10px 12px !important"';
    if(isDead){const rc=sub.deathReason==='expired';return`<tr data-id="${sub.id}" data-nm="${(sub.name||'').toLowerCase()}" data-mt="${sub.method}" data-pr="${usdNorm}" data-dt="${sub.deathDate||sub.nextDate||''}"><td ${P}><span class="VA-nm" style="padding-left:7px !important">${sub.name}</span></td><td ${P}><span class="VA-pr">${fP(sub.price,sub.currency)}</span>${usdEq}</td><td ${P}><span class="VA-mt" style="background:${m.bg} !important;border-color:${m.dot} !important;padding:2px 6px !important"><span class="VA-mtd" style="background:${m.dot} !important"></span><span class="VA-mtn" style="color:${m.text} !important">${sub.method||'—'}</span></span></td><td ${P}>${sL}</td><td ${P}><span class="VA-dt">${fD(sub.deathDate||sub.nextDate)}</span></td><td ${P}><span class="VA-rsn ${rc?'VA-rsn-e':'VA-rsn-c'}">${rc?'Expired':'Cancelled'}</span></td><td style="padding:10px 6px !important" class="VA-acts"><span class="VA-ab VA-ab-rev" style="padding:4px 8px !important" data-act="reactivate" data-id="${sub.id}">Revive</span></td></tr>`;}
    const isRec=sec==='recurring';
    return`<tr data-id="${sub.id}" data-nm="${(sub.name||'').toLowerCase()}" data-mt="${sub.method}" data-pr="${usdNorm}" data-dt="${sub.nextDate||''}" data-dl="${dl}"><td ${P}><span class="VA-nm" style="padding-left:7px !important">${sub.name}</span>${gr}${cBadge}</td><td ${P}><span class="VA-pr">${fP(sub.price,sub.currency)}</span>${usdEq}</td><td ${P}><span class="VA-cy">${fCy(sub.cycle)||'One-time'}</span></td><td ${P}><span class="VA-mt" style="background:${m.bg} !important;border-color:${m.dot} !important;padding:2px 6px !important"><span class="VA-mtd" style="background:${m.dot} !important"></span><span class="VA-mtn" style="color:${m.text} !important">${sub.method||'—'}</span></span></td><td ${P}>${sL}</td><td ${P}><span class="VA-dt">${fD(sub.nextDate)}</span></td><td style="padding:10px 6px !important"><span class="VA-dl ${dlC}">${dl}d</span></td><td style="padding:10px 6px !important" class="VA-acts"><span class="VA-ab VA-ab-e" style="padding:4px 8px !important" data-act="edit" data-id="${sub.id}">Edit</span>${isRec?` <span class="VA-ab VA-ab-can" style="padding:4px 8px !important;margin-left:2px !important" data-act="cancel" data-id="${sub.id}">Cancel</span>`:''} <span class="VA-ab VA-ab-arc" style="padding:4px 8px !important;margin-left:2px !important" data-act="archive" data-id="${sub.id}">Archive</span></td></tr>`;
  }

  function bSec(title,subs,sec,color){
    const isDead=sec==='dead';const open=isDead?ST.deadOpen:true;
    const secC=sec==='recurring'?'VA-sec-rec':sec==='dead'?'VA-sec-ded':'VA-sec-non';
    const THP='style="padding:9px 12px !important"';
    const aH=`<th ${THP}>Name</th><th ${THP}>Price</th><th ${THP}>Cycle</th><th ${THP}>Method</th><th ${THP}>Started</th><th ${THP}>${sec==='recurring'?'Renews':'Ends'}</th><th ${THP}>Days</th><th ${THP}></th>`;
    const dH=`<th ${THP}>Name</th><th ${THP}>Price</th><th ${THP}>Method</th><th ${THP}>Started</th><th ${THP}>Ended</th><th ${THP}>Status</th><th ${THP}></th>`;
    const rows=subs.map(s=>bRow(s,sec)).join('');
    const empty=subs.length===0?`<tr class="VA-emp"><td colspan="8" style="padding:20px 12px !important">—</td></tr>`:'';
    const cf=ST.filters[sec]||'all';const fLabel=cf==='all'?'Filter':cf;
    const pills=`<span class="VA-fp${cf==='all'?' act':''}" style="padding:4px 7px !important" data-fv="all" data-fs="${sec}">All</span>`+D.methods.map(m=>{const c=gmc(m);return`<span class="VA-fp${cf===m?' act':''}" style="padding:4px 7px !important" data-fv="${m}" data-fs="${sec}"><span class="VA-fpdot" style="background:${c.dot} !important"></span>${m}</span>`;}).join('');
    const cs=ST.sorts[sec]||'date-asc';
    const sorts=[{v:'date-asc',l:'Soonest'},{v:'date-desc',l:'Latest'},{v:'price-desc',l:'Priciest'},{v:'price-asc',l:'Cheapest'},{v:'name-asc',l:'A→Z'}].map(s=>`<span class="VA-sp${cs===s.v?' act':''}" style="padding:3px 7px !important" data-sv="${s.v}" data-ss="${sec}">${s.l}</span>`).join('');
    const usdToggle=exRate?`<span class="VA-usd${ST.showUSD?' act':''}" data-usdtog>≈USD</span>`:'';
    return`<div class="VA-sec ${secC} ${isDead?'VA-ded':''}" data-sec="${sec}" style="margin-bottom:12px !important">
      <div class="VA-sh" style="padding:10px 14px 8px !important"><div class="VA-shl"><span class="VA-sn" style="color:${color} !important">${title}</span><span class="VA-sc">${subs.length}</span>${isDead?`<span class="VA-stog" data-tog="dead">${open?'▾ hide':'▸ show'}</span>`:''}</div>${usdToggle}</div>
      ${!open?'':`<div class="VA-tb" style="padding:7px 14px !important"><input class="VA-srch" type="text" placeholder="Search…" style="padding:5px 8px !important" data-sr="${sec}" autocomplete="off" /><span class="VA-sep"></span><div class="VA-fwrap" data-fw="${sec}"><span class="VA-fb" style="padding:5px 8px !important"><span>${fLabel}</span><span class="VA-farr">▾</span></span><div class="VA-fpop" style="padding:5px !important">${pills}</div></div><span class="VA-sep"></span><span class="VA-slbl">Sort:</span>${sorts}</div>
      <div class="VA-tw"><table class="VA-t"><thead><tr>${isDead?dH:aH}</tr></thead><tbody data-tb="${sec}">${rows||empty}</tbody></table></div>`}
    </div>`;
  }

  function renderAll(){
    D=rd();const subs=D.subs;
    const corners='<div class="VA-corner VA-tl"></div><div class="VA-corner VA-tr"></div><div class="VA-corner VA-bl"></div><div class="VA-corner VA-br"></div>';
    // Decorative scan lines positioned between sections
    const scans='<div class="VA-scan" style="top:50%"></div><div class="VA-scan" style="top:30%"></div><div class="VA-scan" style="top:70%"></div>';
    root.innerHTML=`${corners}<div class="VA-bg"></div>${scans}
      <div class="VA-hdr" style="padding:14px 18px 10px !important;margin-bottom:14px !important"><div class="VA-ttl" style="padding-left:14px !important;margin-left:2px !important">Subscriptions</div><div class="VA-btns"><div class="VA-b VA-b-add" id="st-add" style="padding:6px 12px !important"><span class="VA-bi">⊕</span> Add</div><div class="VA-b VA-b-g" id="st-stats" style="padding:6px 12px !important">Stats</div><div class="VA-b VA-b-g" id="st-met" style="padding:6px 12px !important">Methods</div><div class="VA-b VA-b-g" id="st-cur" style="padding:6px 12px !important">Currencies</div></div></div>
      ${bSec('Recurring',subs.filter(s=>s.status==='active'&&s.type==='recurring'),'recurring','#22d3ee')}
      ${bSec('Non-Recurring',subs.filter(s=>s.status==='active'&&s.type==='non-recurring'),'non-recurring','#a78bfa')}
      ${bSec('Archived',subs.filter(s=>s.status==='dead'),'dead','#4a5568')}`;
    bindEv();
  }

  function applyF(sec){const tb=root.querySelector(`[data-tb="${sec}"]`);if(!tb)return;const si=root.querySelector(`.VA-srch[data-sr="${sec}"]`);const q=si?si.value.toLowerCase():'';const fv=ST.filters[sec]||'all';tb.querySelectorAll('tr[data-id]').forEach(r=>{r.style.display=(!q||(r.dataset.nm||'').includes(q))&&(fv==='all'||r.dataset.mt===fv)?'':'none';});}
  function applyS(sec){const tb=root.querySelector(`[data-tb="${sec}"]`);if(!tb)return;const s=ST.sorts[sec]||'date-asc';const[k,dir]=s.split('-');const asc=dir==='asc';const rows=Array.from(tb.querySelectorAll('tr[data-id]'));rows.sort((a,b)=>{if(k==='price')return asc?(parseFloat(a.dataset.pr)||0)-(parseFloat(b.dataset.pr)||0):(parseFloat(b.dataset.pr)||0)-(parseFloat(a.dataset.pr)||0);if(k==='name')return asc?(a.dataset.nm||'').localeCompare(b.dataset.nm||''):(b.dataset.nm||'').localeCompare(a.dataset.nm||'');return asc?(a.dataset.dt||'').localeCompare(b.dataset.dt||''):(b.dataset.dt||'').localeCompare(a.dataset.dt||'');});rows.forEach(r=>tb.appendChild(r));}

  function bindEv(){
    root.querySelector('#st-add')?.addEventListener('click',()=>openAE(null));
    root.querySelector('#st-stats')?.addEventListener('click',()=>openStats());
    root.querySelector('#st-met')?.addEventListener('click',()=>openLE('Payment Methods','methods'));
    root.querySelector('#st-cur')?.addEventListener('click',()=>openLE('Currencies','currencies'));
    root.querySelectorAll('[data-tog="dead"]').forEach(e=>{e.addEventListener('click',()=>{ST.deadOpen=!ST.deadOpen;renderAll();});});
    root.querySelectorAll('[data-usdtog]').forEach(e=>{e.addEventListener('click',()=>{ST.showUSD=!ST.showUSD;renderAll();});});
    root.querySelectorAll('[data-act]').forEach(el=>{
      el.addEventListener('click',e=>{
        e.stopPropagation();const act=el.dataset.act,id=el.dataset.id;const sub=D.subs.find(s=>s.id===id);if(!sub)return;
        if(act==='edit')openAE(sub);else if(act==='cancel'||act==='didnt-renew')openCancel(sub);else if(act==='archive')openArchive(sub);
        else if(act==='reactivate'){(async()=>{const subs=rd().subs;const s=subs.find(x=>x.id===id);if(s){s.status='active';s.type='recurring';delete s.deathDate;delete s.deathReason;delete s.cancelled;if(!s.cycle)s.cycle='monthly';if(s.nextDate&&s.nextDate<td()){while(s.nextDate<td())s.nextDate=adv(s.nextDate,s.cycle);}}await sv(subs);D=rd();renderAll();})();}
      });
    });
    root.querySelectorAll('[data-open]').forEach(el=>{el.addEventListener('click',e=>{e.stopPropagation();openDaily(el.dataset.open);});});
    root.querySelectorAll('.VA-srch').forEach(inp=>{inp.addEventListener('input',()=>applyF(inp.dataset.sr));});
    root.querySelectorAll('.VA-fwrap').forEach(fw=>{
      fw.querySelector('.VA-fb').addEventListener('click',e=>{e.stopPropagation();root.querySelectorAll('.VA-fwrap.open').forEach(f=>{if(f!==fw)f.classList.remove('open');});fw.classList.toggle('open');});
      fw.querySelectorAll('.VA-fp').forEach(fp=>{fp.addEventListener('click',e=>{e.stopPropagation();const sec=fp.dataset.fs;const val=fp.dataset.fv;ST.filters[sec]=val;fw.querySelectorAll('.VA-fp').forEach(x=>x.classList.remove('act'));fp.classList.add('act');const btn=fw.querySelector('.VA-fb');btn.querySelector('span:first-child').textContent=val==='all'?'Filter':val;if(val!=='all')btn.classList.add('act');else btn.classList.remove('act');fw.classList.remove('open');applyF(sec);});});
    });
    document.addEventListener('click',()=>{root.querySelectorAll('.VA-fwrap.open').forEach(f=>f.classList.remove('open'));});
    root.querySelectorAll('.VA-sp').forEach(s=>{s.addEventListener('click',()=>{const sec=s.dataset.ss;ST.sorts[sec]=s.dataset.sv;root.querySelectorAll(`.VA-sp[data-ss="${sec}"]`).forEach(x=>x.classList.remove('act'));s.classList.add('act');applyS(sec);});});
  }

  renderAll();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment