Spaces:
Running on Zero
Running on Zero
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" /> | |
| <title>BudgetBuddy</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" /> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script> | |
| <style> | |
| :root{ | |
| --bg:#0a0c11; --surface:#15181f; --surface2:#1c2029; --surface3:#242936; --line:#2a2f3b; | |
| --ink:#f0f3f8; --muted:#8b94a6; --faint:#5e6678; | |
| --lime:#d7f25a; --lime-ink:#161f08; --lav:#bca8f9; --lav-ink:#1c1430; | |
| --good:#74dca6; --bad:#f08aa6; --warn:#f5cd76; --r:20px; | |
| --sh:0 1px 0 rgba(255,255,255,.03), 0 6px 22px rgba(0,0,0,.30); | |
| } | |
| *{box-sizing:border-box} html,body{margin:0;height:100%} | |
| body{background:var(--bg);color:var(--ink);font-family:Inter,system-ui,sans-serif;-webkit-font-smoothing:antialiased;font-size:14px;line-height:1.5} | |
| button{font-family:inherit;cursor:pointer;border:0;color:inherit} input,select,textarea{font-family:inherit} | |
| .hidden{display:none !important} | |
| .vh{position:absolute;left:-9999px;width:1px;height:1px;opacity:0} | |
| .muted{color:var(--muted)} .good{color:var(--good)} .bad{color:var(--bad)} .warn{color:var(--warn)} | |
| .up{color:var(--good);font-weight:700} .down{color:var(--bad);font-weight:700} | |
| ::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-thumb{background:#2d3340;border-radius:6px} | |
| svg{stroke-width:1.9;fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round} | |
| input,select,textarea{width:100%;background:var(--surface2);border:1px solid var(--line);color:var(--ink);border-radius:12px;padding:11px 13px;font-size:.9rem;outline:none;transition:border-color .15s} | |
| input:focus,select:focus,textarea:focus{border-color:var(--lav)} | |
| select{-webkit-appearance:none;appearance:none;cursor:pointer;padding-right:34px;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a7a3c4' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");background-repeat:no-repeat;background-position:right 11px center;background-size:16px} | |
| select:hover{border-color:var(--lav)} | |
| select option{background:var(--surface);color:var(--ink)} | |
| select option:disabled{color:var(--muted)} | |
| input[type=date]{cursor:pointer} | |
| input[type=date]::-webkit-calendar-picker-indicator{filter:invert(.65);cursor:pointer;opacity:.8} | |
| input[type=date]::-webkit-calendar-picker-indicator:hover{opacity:1} | |
| input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0} | |
| input[type=number]{-moz-appearance:textfield;appearance:textfield} | |
| .empty-chart{height:230px;display:flex;align-items:center;justify-content:center;color:var(--faint);font-size:.86rem;border:1px dashed var(--line);border-radius:14px} | |
| label{display:block;font-size:.76rem;color:var(--muted);margin-bottom:6px;font-weight:600} | |
| .field{margin:13px 0} .row{display:flex;gap:11px}.row>*{flex:1} .msg{margin-top:11px;font-size:.84rem;min-height:1em} | |
| .btn{background:var(--lime);color:var(--lime-ink);font-weight:800;border-radius:13px;padding:11px 17px;font-size:.88rem;transition:filter .12s,transform .05s} | |
| .btn:hover{filter:brightness(1.05)} .btn:active{transform:translateY(1px)} .btn:disabled{opacity:.4;cursor:not-allowed} | |
| .btn.alt{background:var(--surface3);color:var(--ink);border:1px solid var(--line)} | |
| .btn.lav{background:var(--lav);color:var(--lav-ink)} .btn.sm{padding:8px 13px;font-size:.8rem;border-radius:11px} | |
| .logo{display:flex;align-items:center;gap:11px;font-weight:850;font-size:1.3rem;letter-spacing:-.02em} | |
| .logo .dot{width:32px;height:32px;border-radius:10px;background:var(--lime);display:grid;place-items:center;color:var(--lime-ink)} | |
| /* login */ | |
| #login{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;background:radial-gradient(1000px 560px at 72% -12%,#1e2a12 0%,var(--bg) 58%)} | |
| .login-card{width:100%;max-width:380px;background:var(--surface);border:1px solid var(--line);border-radius:26px;padding:32px;box-shadow:var(--sh)} | |
| /* shell */ | |
| #app{display:flex;min-height:100vh} | |
| .sidebar{width:236px;flex:0 0 236px;background:var(--surface);border-right:1px solid var(--line);padding:22px 16px;display:flex;flex-direction:column;gap:5px;position:sticky;top:0;height:100vh} | |
| .side-logo{margin:2px 6px 20px} | |
| .nav-item{display:flex;align-items:center;gap:13px;padding:12px 14px;border-radius:14px;color:var(--muted);font-weight:650;background:transparent;width:100%;text-align:left;font-size:.9rem;transition:.12s} | |
| .nav-item svg{width:19px;height:19px;flex:0 0 19px} | |
| .nav-item:hover{background:var(--surface2);color:var(--ink)} | |
| .nav-item.active{background:var(--lime);color:var(--lime-ink);box-shadow:0 4px 16px rgba(215,242,90,.18)} | |
| .side-foot{margin-top:auto;border-top:1px solid var(--line);padding-top:14px} | |
| .side-user{display:flex;align-items:center;gap:11px;padding:6px} | |
| .avatar{width:34px;height:34px;border-radius:50%;background:var(--lav);color:var(--lav-ink);display:grid;place-items:center;font-weight:800} | |
| .main{flex:1;min-width:0;padding:26px 32px 70px} | |
| .topbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:22px;gap:16px;flex-wrap:wrap} | |
| .title{font-size:1.55rem;font-weight:850;letter-spacing:-.025em} .sub{color:var(--muted);font-size:.85rem;margin-top:2px} | |
| .controls{display:flex;gap:10px;align-items:center;flex-wrap:wrap} | |
| .controls select{width:auto;min-width:150px;padding:9px 34px 9px 14px;border-radius:12px;font-weight:600;background-color:var(--surface);font-size:.84rem} | |
| .seg{display:flex;gap:4px;background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:4px} | |
| .seg button{background:transparent;color:var(--muted);padding:7px 13px;border-radius:9px;font-weight:650;font-size:.8rem} | |
| .seg button.active{background:var(--surface3);color:var(--ink)} | |
| /* cards */ | |
| .cards{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:18px} | |
| .card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:18px 20px;box-shadow:var(--sh)} | |
| .card .lbl{color:var(--muted);font-size:.8rem;font-weight:600;display:flex;align-items:center;gap:8px} | |
| .card .ic{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;background:var(--surface3)} | |
| .card .ic svg{width:16px;height:16px} .card .val{font-size:1.7rem;font-weight:850;margin-top:10px;letter-spacing:-.02em} | |
| .card .delta{font-size:.8rem;margin-top:4px} | |
| .grid2{display:grid;grid-template-columns:.95fr 1.4fr;gap:16px;margin-bottom:18px} | |
| .grid2b{display:grid;grid-template-columns:1.4fr 1fr;gap:16px;margin-bottom:18px} | |
| .panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);padding:20px;box-shadow:var(--sh)} | |
| .panel h3{margin:0;font-size:1rem;font-weight:750} .panel .ph-sub{color:var(--muted);font-size:.8rem;margin:2px 0 14px} | |
| .chart-box{position:relative;height:230px} | |
| .ring-wrap{display:flex;align-items:center;gap:20px} | |
| .ring{position:relative;width:160px;height:160px;flex:0 0 160px} | |
| .ring .rc{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:0 14px} | |
| .rc .big{font-size:1.28rem;font-weight:850;letter-spacing:-.02em;line-height:1.1;max-width:100%} .rc .sm{color:var(--muted);font-size:.71rem;margin-top:2px} | |
| /* pills + comparison */ | |
| .pill{display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;font-size:.73rem;font-weight:700} | |
| .dotc{width:8px;height:8px;border-radius:50%;display:inline-block;flex:0 0 8px} | |
| .cmp{display:flex;flex-direction:column;gap:13px} | |
| .cmp-row{display:grid;grid-template-columns:108px 1fr 86px;gap:12px;align-items:center;font-size:.83rem} | |
| .bars{display:flex;flex-direction:column;gap:5px} | |
| .bar{height:9px;border-radius:6px;background:var(--surface3);overflow:hidden}.bar>i{display:block;height:100%;border-radius:6px} | |
| .bar.t>i{background:var(--lime)} .bar.l>i{background:var(--lav)} | |
| .legend{display:flex;gap:14px;font-size:.74rem;color:var(--muted);margin-top:13px} | |
| .legend span{display:inline-flex;align-items:center;gap:6px}.legend i{width:9px;height:9px;border-radius:3px;display:inline-block} | |
| /* tables */ | |
| table{width:100%;border-collapse:collapse;font-size:.86rem} | |
| th{text-align:left;color:var(--muted);font-weight:600;padding:9px 10px;border-bottom:1px solid var(--line);font-size:.76rem} | |
| td{padding:9px 10px;border-bottom:1px solid #1a1e27;vertical-align:middle} tr:last-child td{border-bottom:0} | |
| td input,td select{padding:8px 9px;border-radius:9px} | |
| .col-qty{width:64px} .col-amt{width:104px} .col-cat{width:140px} .col-x{width:38px} | |
| .iconbtn{background:var(--surface3);color:var(--muted);width:30px;height:30px;border-radius:9px;display:grid;place-items:center} | |
| .iconbtn:hover{color:var(--ink)} .iconbtn svg{width:15px;height:15px} | |
| /* add */ | |
| .modes{display:flex;gap:9px;margin-bottom:18px;flex-wrap:wrap} | |
| .mode{background:var(--surface);border:1px solid var(--line);color:var(--muted);padding:11px 17px;border-radius:14px;font-weight:650;font-size:.86rem;display:flex;gap:9px;align-items:center} | |
| .mode svg{width:17px;height:17px} .mode.active{background:var(--lime);color:var(--lime-ink);border-color:var(--lime)} | |
| .drop{border:1.5px dashed var(--line);border-radius:15px;padding:18px;text-align:center;color:var(--muted);font-size:.85rem;display:block;cursor:pointer} | |
| .drop:hover{border-color:var(--lav);color:var(--ink)} .drop img{max-height:280px;max-width:100%;border-radius:12px;margin-top:10px} | |
| .cap-row{display:flex;gap:9px;margin-top:10px} | |
| .upload-row{display:flex;gap:14px;align-items:stretch} | |
| .upload-actions{display:flex;flex-direction:column;gap:9px;justify-content:center;min-width:160px} | |
| @media(max-width:720px){.upload-row{flex-direction:column}.upload-actions{min-width:0}} | |
| .banner{border-radius:12px;padding:11px 14px;font-size:.84rem;margin:12px 0;border:1px solid transparent} | |
| .banner.ok{background:#10271d;color:var(--good);border-color:#1d3a2c} .banner.warn{background:#2c2611;color:var(--warn);border-color:#3c331a} | |
| .banner.lav{background:#211a37;color:var(--lav);border-color:#352c57} | |
| /* chat */ | |
| .chat-wrap{display:flex;flex-direction:column;height:66vh} | |
| .msgs{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding:4px 2px} | |
| .bubble{max-width:82%;padding:12px 15px;border-radius:18px;font-size:.9rem;line-height:1.5;white-space:pre-wrap} | |
| .bubble.me{align-self:flex-end;background:var(--lime);color:var(--lime-ink);border-bottom-right-radius:6px;font-weight:600} | |
| .bubble.bot{align-self:flex-start;background:var(--surface2);border:1px solid var(--line);border-bottom-left-radius:6px} | |
| .bubble .tr{font-size:.69rem;color:var(--muted);margin-top:9px;border-top:1px solid #ffffff14;padding-top:7px;display:flex;flex-wrap:wrap;align-items:center;gap:5px} | |
| .bubble .tr-h{color:var(--lime);font-weight:700;letter-spacing:.02em;margin-right:2px} | |
| .bubble .tr-step{background:#ffffff10;border:1px solid var(--line);border-radius:7px;padding:2px 7px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:.66rem;color:var(--ink)} | |
| .bubble .tr-arrow{color:var(--muted);opacity:.7} | |
| .chips{display:flex;gap:8px;flex-wrap:wrap;margin:12px 0} | |
| .chip{background:var(--surface2);border:1px solid var(--line);color:var(--muted);border-radius:999px;padding:8px 13px;font-size:.8rem}.chip:hover{color:var(--ink);border-color:var(--lav)} | |
| /* calendar */ | |
| .cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:8px} | |
| .cal-grid .head{color:var(--muted);font-size:.72rem;text-align:center;padding:4px;font-weight:600} | |
| .cell{aspect-ratio:1;border-radius:13px;background:var(--surface2);border:1px solid var(--line);padding:8px;display:flex;flex-direction:column;justify-content:space-between;cursor:pointer;transition:.1s} | |
| .cell.empty{background:transparent;border:0;cursor:default} .cell:not(.empty):hover{border-color:var(--lav)} | |
| .cell.sel{outline:2px solid var(--lime)} .cell .d{font-size:.74rem;color:var(--muted)} .cell .a{font-size:.74rem;font-weight:750} | |
| /* transactions */ | |
| .txn-filters{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px;align-items:end} | |
| .txn-filters .field{margin:0;min-width:140px} .txn-filters .field.search{flex:1;min-width:200px} | |
| .day-group{margin-bottom:18px} .day-head{color:var(--muted);font-size:.78rem;font-weight:700;margin:0 2px 8px;text-transform:uppercase;letter-spacing:.03em} | |
| .txn{background:var(--surface2);border:1px solid var(--line);border-radius:14px;padding:13px 15px;margin-bottom:9px} | |
| .txn-top{display:flex;align-items:center;gap:12px;cursor:pointer} | |
| .txn-cat{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;font-weight:800;flex:0 0 38px} | |
| .txn-main{flex:1;min-width:0} .txn-v{font-weight:700} .txn-u{color:var(--muted);font-size:.78rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} | |
| .txn-amt{font-weight:800;text-align:right} .txn-items{margin:11px 0 2px;border-top:1px solid var(--line);padding-top:10px;display:flex;flex-direction:column;gap:6px} | |
| .txn-item{display:flex;justify-content:space-between;font-size:.82rem;gap:10px} | |
| .txn-subh{color:var(--muted);font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;margin:10px 0 4px} | |
| .txn-charge{color:var(--muted)} | |
| .txn-tot{font-weight:800;border-top:1px solid var(--line);padding-top:8px;margin-top:7px} | |
| .spin{display:inline-block;width:14px;height:14px;border:2px solid #00000033;border-top-color:currentColor;border-radius:50%;animation:sp .8s linear infinite;vertical-align:-2px} | |
| @keyframes sp{to{transform:rotate(360deg)}} | |
| /* responsive */ | |
| @media(max-width:1040px){.grid2,.grid2b{grid-template-columns:1fr}.cards{grid-template-columns:repeat(2,1fr)}} | |
| @media(max-width:720px){ | |
| #app{flex-direction:column} | |
| .sidebar{width:100%;flex-basis:auto;height:auto;position:sticky;top:0;z-index:9;flex-direction:row;flex-wrap:wrap;gap:4px;padding:10px;overflow-x:auto} | |
| .side-logo{display:none}.side-foot{margin:0 0 0 auto;border:0;padding:0;display:flex;align-items:center} | |
| .side-user .u{display:none}.nav-item span{display:none}.nav-item{padding:11px} | |
| .main{padding:16px}.title{font-size:1.3rem}.cards{grid-template-columns:1fr 1fr;gap:11px} | |
| .ring-wrap{flex-direction:column;align-items:stretch}.cmp-row{grid-template-columns:90px 1fr 72px} | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- LOGIN --> | |
| <div id="login"> | |
| <div class="login-card"> | |
| <div class="logo"><span class="dot"><svg viewBox="0 0 24 24" width="18" height="18"><path d="M3 7h18v12H3z"/><path d="M3 10.5h18"/></svg></span> BudgetBuddy</div> | |
| <p class="muted" style="margin:.6rem 0 1.1rem">Snap a bill, ask where your money went — private, on-device AI.</p> | |
| <div class="field"><label>Username</label><input id="lg-user" placeholder="3–30 chars" autocomplete="username"></div> | |
| <div class="field"><label>PIN</label><input id="lg-pin" type="password" placeholder="4–8 digits" autocomplete="current-password"></div> | |
| <div class="row"><button class="btn" id="lg-login">Log in</button><button class="btn alt" id="lg-signup">Sign up</button></div> | |
| <div class="msg" id="lg-msg"></div> | |
| </div> | |
| </div> | |
| <!-- APP --> | |
| <div id="app" class="hidden"> | |
| <aside class="sidebar"> | |
| <div class="side-logo logo"><span class="dot"><svg viewBox="0 0 24 24" width="18" height="18"><path d="M3 7h18v12H3z"/><path d="M3 10.5h18"/></svg></span> BudgetBuddy</div> | |
| <button class="nav-item active" data-view="dashboard"><svg viewBox="0 0 24 24"><rect x="3" y="3" width="8" height="8" rx="2"/><rect x="13" y="3" width="8" height="5" rx="2"/><rect x="13" y="10" width="8" height="11" rx="2"/><rect x="3" y="13" width="8" height="8" rx="2"/></svg><span>Dashboard</span></button> | |
| <button class="nav-item" data-view="add"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8v8M8 12h8"/></svg><span>Add spend</span></button> | |
| <button class="nav-item" data-view="calendar"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="17" rx="3"/><path d="M3 9h18M8 2v4M16 2v4"/></svg><span>Calendar</span></button> | |
| <button class="nav-item" data-view="chat"><svg viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg><span>Assistant</span></button> | |
| <button class="nav-item" data-view="txns"><svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h10"/></svg><span>Transactions</span></button> | |
| <div class="side-foot"> | |
| <div class="side-user"><span class="avatar" id="av">?</span><span class="u" id="who"></span></div> | |
| <button class="nav-item" id="logout" style="margin-top:4px"><svg viewBox="0 0 24 24"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/></svg><span>Log out</span></button> | |
| </div> | |
| </aside> | |
| <main class="main"> | |
| <!-- Dashboard --> | |
| <section id="view-dashboard"> | |
| <div class="topbar"> | |
| <div><div class="title" id="greet">Welcome</div><div class="sub" id="todaystr"></div></div> | |
| <div class="controls"> | |
| <select id="period"></select> | |
| <div class="seg" id="gran"><button data-g="Daily">Daily</button><button data-g="Weekly">Weekly</button><button class="active" data-g="Monthly">Monthly</button></div> | |
| </div> | |
| </div> | |
| <div class="cards" id="cards"></div> | |
| <div class="grid2"> | |
| <div class="panel"><h3>Budget</h3><div class="ph-sub" id="budget-sub">Monthly budget</div> | |
| <div class="ring-wrap"><div class="ring"><canvas id="ring-budget"></canvas><div class="rc" id="ring-budget-txt"></div></div> | |
| <div style="flex:1"><div class="field" style="margin:0"><label>Set monthly budget</label><div class="row"><input id="budget-in" type="number" inputmode="decimal" placeholder="e.g. 20000"><button class="btn sm lav" id="budget-save" style="flex:0 0 auto">Set</button></div></div><div class="muted" id="budget-msg" style="font-size:.8rem;margin-top:9px"></div></div></div> | |
| </div> | |
| <div class="panel"><h3>Spend by category</h3><div class="ph-sub">Where your money goes</div><div class="chart-box"><canvas id="chart-cat"></canvas></div></div> | |
| </div> | |
| <div class="grid2b"> | |
| <div class="panel"><h3>Spend over time</h3><div class="ph-sub" id="ot-sub">Monthly</div><div class="chart-box"><canvas id="chart-time"></canvas></div></div> | |
| <div class="panel"><h3>This month vs last</h3><div class="ph-sub">By category</div><div class="cmp" id="comparison"></div></div> | |
| </div> | |
| <div class="panel"><div style="display:flex;justify-content:space-between;align-items:center"><h3>Recent transactions</h3><button class="btn sm alt" id="see-all">See all</button></div><div class="ph-sub">Your latest spends</div><div id="recent"></div></div> | |
| </section> | |
| <!-- Add --> | |
| <section id="view-add" class="hidden"> | |
| <div class="topbar"><div class="title">Add a spend</div></div> | |
| <div class="modes"> | |
| <button class="mode active" data-mode="receipt"><svg viewBox="0 0 24 24"><path d="M5 2v20l2.5-1.6L10 22l2-1.6L14 22l2.5-1.6L19 22V2l-2.5 1.6L14 2l-2 1.6L10 2 7.5 3.6z"/><path d="M9 8h6M9 12h6"/></svg>Receipt</button> | |
| <button class="mode" data-mode="payment"><svg viewBox="0 0 24 24"><rect x="2" y="5" width="20" height="14" rx="3"/><path d="M2 10h20"/></svg>Payment screenshot</button> | |
| <button class="mode" data-mode="manual"><svg viewBox="0 0 24 24"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>Manual</button> | |
| </div> | |
| <div id="mode-receipt"> | |
| <div class="panel" style="margin-bottom:16px"> | |
| <h3>Bill photo</h3><div class="ph-sub">Snap or upload — items, taxes, round-off and all</div> | |
| <div class="upload-row"> | |
| <label class="drop" for="rc-file" style="flex:1;margin:0">Tap to choose a photo<div id="rc-prev"></div></label> | |
| <div class="upload-actions"> | |
| <label class="btn alt sm" for="rc-file" style="text-align:center">Choose file</label> | |
| <label class="btn alt sm" for="rc-cam" style="text-align:center">Take photo</label> | |
| <button class="btn" id="rc-extract">Extract</button> | |
| </div> | |
| </div> | |
| <input id="rc-file" type="file" accept="image/*" class="vh"><input id="rc-cam" type="file" accept="image/*" capture="environment" class="vh"> | |
| <div class="msg" id="rc-status"></div> | |
| </div> | |
| <div class="panel"> | |
| <h3>Review & understand</h3><div class="ph-sub">Fix anything wrong, then let the AI categorize it</div> | |
| <div class="row"><div class="field"><label>Vendor</label><input id="rc-vendor"></div><div class="field"><label>Date</label><input id="rc-date" type="date"></div><div class="field"><label>Currency</label><input id="rc-currency"></div></div> | |
| <label>Line items</label> | |
| <table id="rc-items"><thead><tr><th>Item</th><th class="col-qty">Qty</th><th class="col-amt">Amount</th><th class="col-cat">Category</th><th class="col-x"></th></tr></thead><tbody></tbody></table> | |
| <button class="btn sm alt" id="rc-additem" style="margin-top:8px">+ Add item</button> | |
| <div class="row" style="margin-top:16px;align-items:flex-start"> | |
| <div style="flex:1"><label>Charges & taxes</label><table id="rc-charges"><thead><tr><th>Label</th><th class="col-amt">Amount</th><th class="col-x"></th></tr></thead><tbody></tbody></table><button class="btn sm alt" id="rc-addcharge" style="margin-top:8px">+ Add charge</button></div> | |
| <div style="flex:1"><div class="field" style="margin:0 0 13px"><label>Total</label><input id="rc-total" type="number" inputmode="decimal"></div><div class="field" style="margin:0"><label>Overall category</label><select id="rc-cat"></select></div></div> | |
| </div> | |
| <div id="rc-recon" class="banner ok">Totals reconcile.</div> | |
| <div id="rc-understand" class="banner lav hidden"></div> | |
| <div class="row" style="margin-top:4px"><button class="btn lav" id="rc-confirm">Understand & categorize</button><button class="btn" id="rc-save" disabled>Save</button></div> | |
| <div class="msg" id="rc-savemsg"></div> | |
| </div> | |
| </div> | |
| <div id="mode-payment" class="grid2b hidden"> | |
| <div class="panel"><h3>Payment screenshot</h3><div class="ph-sub">UPI / GPay / PhonePe / card</div> | |
| <label class="drop" for="pm-file">Tap to choose a screenshot<div id="pm-prev"></div></label> | |
| <input id="pm-file" type="file" accept="image/*" class="vh"><input id="pm-cam" type="file" accept="image/*" capture="environment" class="vh"> | |
| <div class="cap-row"><label class="btn alt sm" for="pm-file" style="flex:1;display:inline-flex;align-items:center;justify-content:center">Choose file</label><label class="btn alt sm" for="pm-cam" style="flex:1;display:inline-flex;align-items:center;justify-content:center">Take photo</label></div> | |
| <button class="btn" id="pm-extract" style="margin-top:11px;width:100%">Read</button><div class="msg" id="pm-status"></div></div> | |
| <div class="panel"><h3>Review & save</h3> | |
| <div class="field"><label>Paid to</label><input id="pm-vendor"></div> | |
| <div class="row"><div class="field"><label>Amount</label><input id="pm-amount" type="number" inputmode="decimal"></div><div class="field"><label>Currency</label><input id="pm-currency"></div></div> | |
| <div class="row"><div class="field"><label>Date</label><input id="pm-date" type="date"></div><div class="field"><label>Category</label><select id="pm-cat"></select></div></div> | |
| <div class="field"><label>Note</label><input id="pm-note"></div> | |
| <button class="btn" id="pm-save" style="width:100%" disabled>Save</button><div class="msg" id="pm-savemsg"></div></div> | |
| </div> | |
| <div id="mode-manual" class="panel hidden" style="max-width:680px"> | |
| <h3>Add a spend</h3><div class="ph-sub">A single amount, or add items one by one</div> | |
| <div class="row"><div class="field"><label>What / where</label><input id="mn-vendor"></div><div class="field"><label>Date</label><input id="mn-date" type="date"></div></div> | |
| <label>Items (optional)</label> | |
| <table id="mn-items"><thead><tr><th>Item</th><th class="col-amt">Amount</th><th class="col-cat">Category</th><th class="col-x"></th></tr></thead><tbody></tbody></table> | |
| <button class="btn sm alt" id="mn-additem" style="margin-top:8px">+ Add item</button> | |
| <div class="row" style="margin-top:15px"><div class="field"><label>Total amount</label><input id="mn-amount" type="number" inputmode="decimal"></div><div class="field"><label>Category</label><select id="mn-cat"></select></div></div> | |
| <div class="field"><label>Note</label><input id="mn-note"></div> | |
| <button class="btn" id="mn-save" style="width:100%">Add spend</button><div class="msg" id="mn-savemsg"></div> | |
| </div> | |
| </section> | |
| <!-- Calendar --> | |
| <section id="view-calendar" class="hidden"> | |
| <div class="topbar"><div class="title">Calendar</div> | |
| <div class="controls"><button class="btn sm alt" id="cal-prev">‹ Prev</button><div class="sub" id="cal-month" style="min-width:120px;text-align:center"></div><button class="btn sm alt" id="cal-next">Next ›</button></div> | |
| </div> | |
| <div class="grid2b"> | |
| <div class="panel"><div class="cal-grid" id="cal"></div></div> | |
| <div class="panel"><h3 id="cal-day-title">Pick a day</h3><div class="ph-sub">Spending on the selected day</div><div id="cal-day"></div></div> | |
| </div> | |
| </section> | |
| <!-- Chat --> | |
| <section id="view-chat" class="hidden"> | |
| <div class="topbar"><div class="title">Assistant</div></div> | |
| <div class="panel chat-wrap"> | |
| <div class="msgs" id="chat-msgs"><div class="bubble bot">Ask me about your spending — I only use your saved data. The first question after a while may take a moment to warm up.</div></div> | |
| <div class="chips" id="chat-chips"></div> | |
| <div class="row"><input id="chat-in" placeholder="e.g. How much did I spend on Dining this month?" style="flex:5"><button class="btn" id="chat-send" style="flex:0 0 auto;min-width:84px">Send</button></div> | |
| </div> | |
| </section> | |
| <!-- Transactions --> | |
| <section id="view-txns" class="hidden"> | |
| <div class="topbar"><div class="title">Transactions</div></div> | |
| <div class="panel"> | |
| <div class="txn-filters"> | |
| <div class="field search"><label>Search</label><input id="tx-q" placeholder="vendor or item…"></div> | |
| <div class="field"><label>Category</label><select id="tx-cat"></select></div> | |
| <div class="field"><label>Period</label><select id="tx-period"></select></div> | |
| </div> | |
| <div id="txn-list"></div> | |
| </div> | |
| </section> | |
| </main> | |
| </div> | |
| <script type="module" src="/assets/app.js"></script> | |
| </body> | |
| </html> | |