code wiki / (root) / nx_finance_web.nx

nx_finance_web.nx source

↩ module page · 392 lines · 30395 B

1// nx_finance_web.nx -- the SOVEREIGN, ZERO-JAVASCRIPT nishifamily.com/finance dashboard renderer. 2// ONE renderer (fw_render), TWO consumers (DRY #15): the static emitter (nx_fin_dashboard -> a file the 3// Publisher ships) AND the live OPAQUE route (olg_route GET /finance, session-gated like /audio). The page 4// is SERVER-COMPUTED: the debt-payoff plan (avalanche vs snowball) is run LIVE through nx_debt -> nx_money 5// at render time, so the numbers are the real engine's output, not hardcoded. dbt_strategy is pure (no 6// store), so this is safe to run inside the daemon request path. 0 <script>: themes via radio/:checked, 7// no client code -- all logic is in this Nishi organ (operator: "no js, nishi everywhere"). 8// license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_money.nx" 11import "nx_debt_payoff.nx" 12import "nx_fin_finance.nx" 13import "nx_bills.nx" 14import "nx_accommodate.nx" 15import "nx_adhd.nx" 16import "nx_purchase.nx" 17import "nx_climbout.nx" 18import "nx_intake_profile.nx" 19import "nx_climbout_real.nx" 20import "nx_climbout_progress.nx" // R7: read THIS user's climb progress for the live page 21import "nx_bills_store.nx" // the person's REAL recurring bills (entered via /finance/bills) 22 23func fw_cat(out: *u8, off: i64, s: *u8) -> i64 { 24 var o: i64 = off; var i: i64 = 0 25 while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } 26 return o 27} 28// unsigned decimal of v (>=0) into out at off; returns new off. 29func fw_n(out: *u8, off: i64, v: i64) -> i64 { 30 if v == 0 { out[off] = 0x30 as u8; return off + 1 } 31 let tmp: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0 32 while m > 0 { tmp[k] = (0x30 + (m % 10)) as u8; m = m / 10; k = k + 1 } 33 var o: i64 = off; var i: i64 = k - 1 34 while i >= 0 { out[o] = tmp[i]; o = o + 1; i = i - 1 } 35 return o 36} 37// money cents -> "$D.CC" (exact, no-float) at off; returns new off. 38func fw_money(out: *u8, off: i64, cents: i64) -> i64 { 39 var o: i64 = off 40 var c: i64 = cents 41 if c < 0 { out[o] = 0x2d as u8; o = o + 1; c = 0 - c } 42 out[o] = 0x24 as u8; o = o + 1 43 let dollars: i64 = c / 100 44 let cc: i64 = c - dollars * 100 45 o = fw_n(out, o, dollars) 46 out[o] = 0x2e as u8; o = o + 1 47 out[o] = (0x30 + (cc / 10)) as u8; o = o + 1 48 out[o] = (0x30 + (cc % 10)) as u8; o = o + 1 49 return o 50} 51// day-of-month [1..31] for a UTC epoch-seconds value (Howard Hinnant's civil_from_days, day component). 52// Used to compute "today" for overdue-bill detection. UTC (off by at most a day near midnight; advisory). 53func fw_day_of_month(epoch_sec: i64) -> i64 { 54 var z: i64 = epoch_sec / 86400 55 z = z + 719468 56 var era: i64 = z / 146097 57 if z < 0 { era = (z - 146096) / 146097 } 58 let doe: i64 = z - era * 146097 59 let yoe: i64 = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365 60 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100) 61 let mp: i64 = (5 * doy + 2) / 153 62 return doy - (153 * mp + 2) / 5 + 1 63} 64 65// the human label for a climb-out tier (the action the person actually takes next). DATA, not magic (#11). 66func fw_tier_label(tier: i64) -> *u8 { 67 if tier == CO_STABILIZE { return "pay the overdue bill (stop the bleeding first)" as *u8 } 68 if tier == CO_RECOVER { return "dispute the medical overcharges to recover what you are owed" as *u8 } 69 if tier == CO_CUTCOST { return "switch to the cheapest SAFE rate" as *u8 } 70 if tier == CO_ATTACKDEBT { return "attack the highest-APR debt" as *u8 } 71 if tier == CO_BUILD { return "build your one-month emergency buffer" as *u8 } 72 return "your next step" as *u8 73} 74 75// ADVANCE the climb: mark this user's CURRENT next step done (the "I did this step" button posts here). 76// Recomputes the SAME plan fw_render shows (deterministic from the person's profile), finds the next 77// not-done step, and marks it -> on reload the bar rises + that step disappears. Idempotent-safe: if all 78// steps are already done, next<0 and nothing is marked. Returns 0 ok. 79func fw_progress_advance(respondent: *u8) -> i64 { 80 let bb_bal: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "debt_balance\x00" as *u8, 2000000) 81 let bb_apr: i64 = mny_rate_from_pct(ip_get_pos("knowledge/store/intake-" as *u8, respondent, "debt_apr_pct\x00" as *u8, 24), 1) 82 let bb_pay: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "monthly_payment\x00" as *u8, 60000) 83 let co_tiers: *i64 = sys_mmap(8 * 8) as *i64 84 let co_imp: *i64 = sys_mmap(8 * 8) as *i64 85 let co_order: *i64 = sys_mmap(8 * 8) as *i64 86 let co_nn: *i64 = sys_mmap(16) as *i64 87 cor_dashboard("knowledge/store/bills-" as *u8, respondent, fw_day_of_month(sys_now_realtime_sec()), bb_bal, bb_apr, bb_pay, co_tiers, co_imp, co_order, co_nn) 88 let n: i64 = co_nn[0] 89 let done: *u8 = sys_mmap(64) 90 cp_get("knowledge/store/progress-" as *u8, respondent, done, n) 91 let next: i64 = cp_next(done, co_order, n) 92 if next >= 0 { cp_mark("knowledge/store/progress-" as *u8, respondent, next, n) } 93 return next 94} 95 96// human label for a bill category (BL_HOUSING..BL_OTHER). DATA, not magic (#11). 97func fw_bill_cat_label(cat: i64) -> *u8 { 98 if cat == BL_HOUSING { return "Housing" as *u8 } 99 if cat == BL_INSURANCE { return "Insurance" as *u8 } 100 if cat == BL_UTILITY { return "Utility" as *u8 } 101 if cat == BL_DEBT { return "Debt" as *u8 } 102 return "Other" as *u8 103} 104// human label for a bill cadence (months). 105func fw_cadence_label(cad: i64) -> *u8 { 106 if cad == 1 { return "monthly" as *u8 } 107 if cad == 3 { return "quarterly" as *u8 } 108 if cad == 6 { return "every 6 months" as *u8 } 109 if cad == 12 { return "yearly" as *u8 } 110 return "per period" as *u8 111} 112 113// Render the full dashboard HTML into out (caller buffer >= ~16 KiB). Returns byte length. 114func fw_render(out: *u8, respondent: *u8) -> i64 { 115 // --- live engine computation: avalanche vs snowball on a representative debt set --- 116 let rate24: i64 = mny_rate_from_pct(24, 1) 117 let rate6: i64 = mny_rate_from_pct(6, 1) 118 let bal: *i64 = sys_mmap(8 * 2) as *i64 119 let apr: *i64 = sys_mmap(8 * 2) as *i64 120 let minp: *i64 = sys_mmap(8 * 2) as *i64 121 bal[0] = 1000000; apr[0] = rate24; minp[0] = 20000 122 bal[1] = 200000; apr[1] = rate6; minp[1] = 5000 123 let avi: *i64 = sys_mmap(16) as *i64 124 let sni: *i64 = sys_mmap(16) as *i64 125 let avm: i64 = dbt_strategy(bal, apr, minp, 2, 50000, 0, avi) 126 let snm: i64 = dbt_strategy(bal, apr, minp, 2, 50000, 1, sni) 127 let saved: i64 = sni[0] - avi[0] 128 let sooner: i64 = snm - avm 129 130 var o: i64 = 0 131 o = fw_cat(out, o, "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\">" as *u8) 132 o = fw_cat(out, o, "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" as *u8) 133 o = fw_cat(out, o, "<title>Nishi Finance</title><style>" as *u8) 134 o = fw_cat(out, o, "*{box-sizing:border-box}html,body{margin:0}" as *u8) 135 o = fw_cat(out, o, ".thr{position:absolute;width:0;height:0;opacity:0;pointer-events:none}" as *u8) 136 o = fw_cat(out, o, ".app{--bg:#fbfbf9;--fg:#1d1d1b;--muted:#6b6b66;--rule:#e3e3dd;--sheet:#fff;--accent:#0b6b3a;background:var(--bg);color:var(--fg);min-height:100vh;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}" as *u8) 137 o = fw_cat(out, o, "#th-dark:checked~.app{--bg:#11161a;--fg:#d8e0d8;--muted:#8d958d;--rule:#26303a;--sheet:#161d22;--accent:#46d08a}" as *u8) 138 o = fw_cat(out, o, "#th-sepia:checked~.app{--bg:#f4ecd8;--fg:#43391f;--muted:#8a7a55;--rule:#e0d4b6;--sheet:#fbf4e3;--accent:#0b6b3a}" as *u8) 139 o = fw_cat(out, o, ".bar{position:sticky;top:0;display:flex;gap:.6rem;align-items:center;padding:.7rem 1rem;background:var(--sheet);border-bottom:1px solid var(--rule)}" as *u8) 140 o = fw_cat(out, o, ".bar h1{font-size:1.15rem;margin:0;flex:1}" as *u8) 141 o = fw_cat(out, o, ".tbar label{cursor:pointer;border:1px solid var(--rule);border-radius:8px;padding:.25rem .55rem;font-size:13px;color:var(--fg)}" as *u8) 142 o = fw_cat(out, o, "main{max-width:880px;margin:0 auto;padding:1.4rem}" as *u8) 143 o = fw_cat(out, o, "section{background:var(--sheet);border:1px solid var(--rule);border-radius:12px;padding:1.1rem 1.2rem;margin:1.1rem 0}" as *u8) 144 o = fw_cat(out, o, "h2{font-size:1.02rem;margin:.1rem 0 .8rem}" as *u8) 145 o = fw_cat(out, o, "table{width:100%;border-collapse:collapse;font-size:.93rem}" as *u8) 146 o = fw_cat(out, o, "th,td{text-align:left;padding:.45rem .5rem;border-bottom:1px solid var(--rule)}" as *u8) 147 o = fw_cat(out, o, "td.n,th.n{text-align:right;font-variant-numeric:tabular-nums}" as *u8) 148 o = fw_cat(out, o, ".win{background:var(--accent);color:#fff;border-radius:10px;padding:.8rem 1rem;font-weight:600;margin-top:.9rem}" as *u8) 149 o = fw_cat(out, o, ".muted{color:var(--muted);font-size:.86rem;line-height:1.5}" as *u8) 150 o = fw_cat(out, o, ".pill{display:inline-block;font-size:.74rem;border:1px solid var(--rule);border-radius:999px;padding:.1rem .5rem;color:var(--muted);margin-right:.35rem}" as *u8) 151 o = fw_cat(out, o, ":focus-visible{outline:3px solid var(--accent);outline-offset:2px}" as *u8) 152 o = fw_cat(out, o, "@media(prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}" as *u8) 153 o = fw_cat(out, o, "</style></head><body>" as *u8) 154 o = fw_cat(out, o, "<input class=\"thr\" type=\"radio\" name=\"nxtheme\" id=\"th-light\" checked>" as *u8) 155 o = fw_cat(out, o, "<input class=\"thr\" type=\"radio\" name=\"nxtheme\" id=\"th-sepia\">" as *u8) 156 o = fw_cat(out, o, "<input class=\"thr\" type=\"radio\" name=\"nxtheme\" id=\"th-dark\">" as *u8) 157 o = fw_cat(out, o, "<div class=\"app\"><header class=\"bar\"><h1>\xf0\x9f\x92\xb0 Nishi Finance</h1>" as *u8) 158 o = fw_cat(out, o, "<span class=\"tbar\" role=\"group\" aria-label=\"Theme\"><label for=\"th-light\">Light</label><label for=\"th-sepia\">Sepia</label><label for=\"th-dark\">Dark</label></span></header><main>" as *u8) 159 160 // --- FOCUS BANNER (page-level "one thing at a time"): the single most important action, FIRST, before 161 // all the detail -- so an overwhelmed person never has to scan 11 sections to find what to do. --- 162 let fb_amt: *i64 = sys_mmap(8 * 64) as *i64; let fb_cad: *i64 = sys_mmap(8 * 64) as *i64 163 let fb_due: *i64 = sys_mmap(8 * 64) as *i64; let fb_cat: *i64 = sys_mmap(8 * 64) as *i64; let fb_pd: *i64 = sys_mmap(8 * 64) as *i64 164 let fb_n: i64 = bs_load("knowledge/store/bills-" as *u8, respondent, fb_amt, fb_cad, fb_due, fb_cat, fb_pd, 64) 165 var fb_oblig: i64 = 0 166 if fb_n > 0 { fb_oblig = bl_monthly_total(fb_amt, fb_cad, fb_n) } 167 let fb_income: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "income_monthly\x00" as *u8, 400000) 168 let fb_surplus: i64 = fb_income - fb_oblig 169 let fb_oidx: *i64 = sys_mmap(8 * 64) as *i64 170 var fb_overdue: i64 = 0 171 if fb_n > 0 { fb_overdue = bl_overdue(fb_due, fb_pd, fw_day_of_month(sys_now_realtime_sec()), fb_n, fb_oidx) } 172 o = fw_cat(out, o, "<section><div class=\"win\"><strong>\xf0\x9f\x91\x89 Your one thing right now:</strong> " as *u8) 173 if fb_surplus < 0 { 174 o = fw_cat(out, o, "you&rsquo;re " as *u8); o = fw_money(out, o, 0 - fb_surplus) 175 o = fw_cat(out, o, "/month underwater. Before anything else, close that gap &mdash; cut a bill or add income. Everything below helps you do exactly that." as *u8) 176 } else { 177 if fb_overdue > 0 { 178 o = fw_cat(out, o, "pay the " as *u8); o = fw_n(out, o, fb_overdue) 179 o = fw_cat(out, o, " overdue bill" as *u8) 180 if fb_overdue != 1 { o = fw_cat(out, o, "s" as *u8) } 181 o = fw_cat(out, o, " &mdash; stop late fees first. The rest can wait." as *u8) 182 } else { 183 o = fw_cat(out, o, "you&rsquo;re cashflow-positive with nothing overdue. Do just the ONE next step in your climb-out plan below &mdash; the rest is saved for when you&rsquo;re ready." as *u8) 184 } 185 } 186 o = fw_cat(out, o, "</div></section>" as *u8) 187 188 o = fw_cat(out, o, "<section><span class=\"pill\">sovereign</span><span class=\"pill\">no-float exact cents</span><span class=\"pill\">append-only double-entry</span><span class=\"pill\">never-brick #26</span>" as *u8) 189 o = fw_cat(out, o, "<p class=\"muted\">Your money engine, built from the hardware rung up. Amounts are exact integer cents (floats are wrong for money); the ledger is content-addressed and append-only (history is the truth); no irreversible money action happens without a dry-run-proven, confirmation-gated path. This dashboard is rendered by a Nishi organ &mdash; zero JavaScript, served behind the OPAQUE login wall.</p></section>" as *u8) 190 191 o = fw_cat(out, o, "<section><h2>Debt payoff plan &mdash; Avalanche vs Snowball</h2>" as *u8) 192 o = fw_cat(out, o, "<p class=\"muted\">Worked example: a $10,000 balance at 24% APR (min $200) and a $2,000 balance at 6% APR (min $50), with a $500/month budget. Computed live by the sovereign engine just now.</p>" as *u8) 193 o = fw_cat(out, o, "<table><thead><tr><th>Strategy</th><th class=\"n\">Months to debt-free</th><th class=\"n\">Total interest paid</th></tr></thead><tbody>" as *u8) 194 o = fw_cat(out, o, "<tr><td>Avalanche (highest APR first)</td><td class=\"n\">" as *u8) 195 o = fw_n(out, o, avm); o = fw_cat(out, o, "</td><td class=\"n\">" as *u8); o = fw_money(out, o, avi[0]); o = fw_cat(out, o, "</td></tr>" as *u8) 196 o = fw_cat(out, o, "<tr><td>Snowball (lowest balance first)</td><td class=\"n\">" as *u8) 197 o = fw_n(out, o, snm); o = fw_cat(out, o, "</td><td class=\"n\">" as *u8); o = fw_money(out, o, sni[0]); o = fw_cat(out, o, "</td></tr>" as *u8) 198 o = fw_cat(out, o, "</tbody></table>" as *u8) 199 o = fw_cat(out, o, "<div class=\"win\">Avalanche saves " as *u8); o = fw_money(out, o, saved) 200 o = fw_cat(out, o, " in interest and finishes " as *u8); o = fw_n(out, o, sooner) 201 o = fw_cat(out, o, " month(s) sooner.</div></section>" as *u8) 202 203 let f_usury: i64 = mny_rate_from_pct(36, 1) 204 let f_bal: i64 = 2000000 205 let f_pay: i64 = 60000 206 let f_aprs: *i64 = sys_mmap(8 * 4) as *i64 207 let f_fees: *i64 = sys_mmap(8 * 4) as *i64 208 let f_upf: *i64 = sys_mmap(8 * 4) as *i64 209 let f_lic: *i64 = sys_mmap(8 * 4) as *i64 210 f_aprs[0] = mny_rate_from_pct(24, 1); f_fees[0] = 0; f_upf[0] = 0; f_lic[0] = 1 211 f_aprs[1] = mny_rate_from_pct(11, 1); f_fees[1] = 10000; f_upf[1] = 0; f_lic[1] = 1 212 f_aprs[2] = mny_rate_from_pct(9, 1); f_fees[2] = 5000; f_upf[2] = 0; f_lic[2] = 1 213 f_aprs[3] = mny_rate_from_pct(4, 1); f_fees[3] = 0; f_upf[3] = 1; f_lic[3] = 0 214 let f_bi: *i64 = sys_mmap(16) as *i64 215 let f_best: i64 = fe_rank_best(f_bal, f_pay, f_aprs, f_fees, f_upf, f_lic, 4, f_usury, f_bi) 216 let f_mc: *i64 = sys_mmap(16) as *i64 217 let f_cur: i64 = fe_total_cost(f_bal, f_pay, f_aprs[0], f_fees[0], f_mc) 218 let f_save: i64 = f_cur - f_best 219 o = fw_cat(out, o, "<section><h2>Affordable funding &mdash; true cost, scam-shielded</h2>" as *u8) 220 o = fw_cat(out, o, "<p class=\"muted\">Worked example: $20,000 of high-interest debt at $600/month, ranked by TRUE total cost (fees + interest), not the headline rate. Any &ldquo;lender&rdquo; that demands an upfront payment or is unlicensed is EXCLUDED &mdash; the #1 loan-scam tell.</p>" as *u8) 221 o = fw_cat(out, o, "<table><thead><tr><th>Option</th><th class=\"n\">True total cost</th></tr></thead><tbody>" as *u8) 222 o = fw_cat(out, o, "<tr><td>Current cards (24% APR)</td><td class=\"n\">" as *u8); o = fw_money(out, o, f_cur); o = fw_cat(out, o, "</td></tr>" as *u8) 223 o = fw_cat(out, o, "<tr><td>Cheapest SAFE option found</td><td class=\"n\">" as *u8); o = fw_money(out, o, f_best); o = fw_cat(out, o, "</td></tr>" as *u8) 224 o = fw_cat(out, o, "</tbody></table>" as *u8) 225 o = fw_cat(out, o, "<div class=\"win\">Switching to the cheapest SAFE option saves " as *u8); o = fw_money(out, o, f_save); o = fw_cat(out, o, " &mdash; and the fake &ldquo;international 4%&rdquo; bait was rejected as advance-fee fraud.</div></section>" as *u8) 226 // monthly obligations from the person's REAL bills (bs_load) if entered; representative otherwise 227 let sb_amt: *i64 = sys_mmap(8 * 64) as *i64; let sb_cad: *i64 = sys_mmap(8 * 64) as *i64 228 let sb_due: *i64 = sys_mmap(8 * 64) as *i64; let sb_cat: *i64 = sys_mmap(8 * 64) as *i64; let sb_pd: *i64 = sys_mmap(8 * 64) as *i64 229 let sbn: i64 = bs_load("knowledge/store/bills-" as *u8, respondent, sb_amt, sb_cad, sb_due, sb_cat, sb_pd, 64) 230 var s_total: i64 = 0 231 if sbn > 0 { 232 s_total = bl_monthly_total(sb_amt, sb_cad, sbn) 233 } else { 234 let s_amt: *i64 = sys_mmap(8 * 8) as *i64 235 let s_cad: *i64 = sys_mmap(8 * 8) as *i64 236 s_amt[0] = 200000; s_cad[0] = 1 237 s_amt[1] = 180000; s_cad[1] = 6 238 s_amt[2] = 60000; s_cad[2] = 1 239 s_amt[3] = 15000; s_cad[3] = 1 240 s_total = bl_monthly_total(s_amt, s_cad, 4) 241 } 242 o = fw_cat(out, o, "<section><h2>Your situation &mdash; snapshot</h2>" as *u8) 243 if sbn > 0 { 244 o = fw_cat(out, o, "<p class=\"muted\">From your intake survey + the bills you entered (insurance, mortgage, HOA, utilities).</p>" as *u8) 245 } else { 246 o = fw_cat(out, o, "<p class=\"muted\">From your intake survey + bills register. Demo values shown &mdash; complete the survey + add your bills (below) to use your own numbers.</p>" as *u8) 247 } 248 o = fw_cat(out, o, "<table><tbody><tr><td>Monthly obligations (all bills, cadence-normalized)</td><td class=\"n\">" as *u8) 249 o = fw_money(out, o, s_total) 250 o = fw_cat(out, o, "</td></tr></tbody></table>" as *u8) 251 o = fw_cat(out, o, "<div class=\"win\">Climb-out plan: stabilize the bills &rarr; kill the highest-APR debt &rarr; audit medical bills for overcharges &rarr; switch to the cheapest SAFE rate &rarr; build a one-month buffer. One step at a time.</div></section>" as *u8) 252 // --- MONTHLY CASHFLOW: the core crisis number -- money IN minus committed bills OUT --- 253 let cf_income: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "income_monthly\x00" as *u8, 400000) 254 let cf_surplus: i64 = cf_income - s_total 255 o = fw_cat(out, o, "<section><h2>Your monthly cashflow</h2>" as *u8) 256 o = fw_cat(out, o, "<p class=\"muted\">The most important number: what comes IN each month minus what is already committed to bills. What is left is what you can put toward the climb &mdash; or, if negative, the gap to close first.</p>" as *u8) 257 o = fw_cat(out, o, "<table><tbody>" as *u8) 258 o = fw_cat(out, o, "<tr><td>Income (monthly)</td><td class=\"n\">" as *u8); o = fw_money(out, o, cf_income); o = fw_cat(out, o, "</td></tr>" as *u8) 259 o = fw_cat(out, o, "<tr><td>Bills (monthly, cadence-normalized)</td><td class=\"n\">&minus;" as *u8); o = fw_money(out, o, s_total); o = fw_cat(out, o, "</td></tr>" as *u8) 260 o = fw_cat(out, o, "</tbody></table>" as *u8) 261 if cf_surplus >= 0 { 262 o = fw_cat(out, o, "<div class=\"win\">Left over: " as *u8); o = fw_money(out, o, cf_surplus); o = fw_cat(out, o, " / month &mdash; put it toward your one next step, then the emergency buffer.</div>" as *u8) 263 } else { 264 o = fw_cat(out, o, "<div class=\"win\"><strong>You are " as *u8); o = fw_money(out, o, 0 - cf_surplus); o = fw_cat(out, o, " / month underwater.</strong> Closing this gap &mdash; cut a bill, lower a rate, or raise income &mdash; is the #1 priority; the plan below targets exactly this.</div>" as *u8) 265 } 266 // SURVIVAL RUNWAY: if income stopped, how long does available cash cover the bills? (job-loss resilience) 267 let rw_save: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "available\x00" as *u8, 50000) 268 let rw_jobrisk: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "job_loss_risk\x00" as *u8, 0) 269 if s_total > 0 { 270 let rw_m: i64 = fe_runway_months(rw_save, s_total) 271 o = fw_cat(out, o, "<div class=\"win\">If your income stopped today, the " as *u8) 272 o = fw_money(out, o, rw_save) 273 o = fw_cat(out, o, " you marked as available would cover about " as *u8) 274 if rw_m >= 1 { 275 o = fw_n(out, o, rw_m); o = fw_cat(out, o, " month" as *u8) 276 if rw_m != 1 { o = fw_cat(out, o, "s" as *u8) } 277 } else { 278 o = fw_n(out, o, fe_runway_days(rw_save, s_total)); o = fw_cat(out, o, " days" as *u8) 279 } 280 o = fw_cat(out, o, " of bills &mdash; your survival runway." as *u8) 281 if rw_jobrisk == 1 { 282 o = fw_cat(out, o, " <strong>You flagged job-loss risk</strong>, so this is the number that matters most: the plan below builds your buffer and cuts ongoing costs to lengthen it." as *u8) 283 } 284 o = fw_cat(out, o, "</div>" as *u8) 285 } 286 o = fw_cat(out, o, "</section>" as *u8) 287 let a_budget: i64 = ac_load_budget(AC_ADHD, 2, 5) 288 o = fw_cat(out, o, "<section><h2>Built for how your brain works</h2>" as *u8) 289 o = fw_cat(out, o, "<p class=\"muted\">ADHD, fatigue, sleep apnea, or anything that makes a long list overwhelming? Nishi shows you ONE thing at a time, reminders on, no time pressure. Disabilities should disappear here.</p>" as *u8) 290 o = fw_cat(out, o, "<div class=\"win\">Showing " as *u8); o = fw_n(out, o, a_budget); o = fw_cat(out, o, " of 5 steps &mdash; your one next step: <strong>stabilize the bills</strong>. The rest is saved for when you are ready.</div></section>" as *u8) 291 let im_hrs: i64 = adhd_hours_cost(20000, 2500) 292 let im_pct: i64 = adhd_pct_of_goal(20000, 100000) 293 o = fw_cat(out, o, "<section><h2>ADHD-aware money guard</h2>" as *u8) 294 o = fw_cat(out, o, "<p class=\"muted\">ADHD makes impulse buys, missed plans, and task-freeze expensive. Nishi steps in &mdash; gently, never blocking. You always decide.</p>" as *u8) 295 o = fw_cat(out, o, "<div class=\"win\">Impulse pause: that $200 gadget? Sleep on it 24h &mdash; it is " as *u8) 296 o = fw_n(out, o, im_hrs) 297 o = fw_cat(out, o, " hours of work and " as *u8) 298 o = fw_n(out, o, im_pct) 299 o = fw_cat(out, o, "% of your emergency buffer. Still want it tomorrow?</div>" as *u8) 300 o = fw_cat(out, o, "<p class=\"muted\">Planning: your bills surface days before they are due, so nothing slips. Frozen on a task? Nishi shrinks it to a 2-minute first step &mdash; just open the bill.</p></section>" as *u8) 301 let bb_bal: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "debt_balance\x00" as *u8, 2000000) 302 let bb_apr: i64 = mny_rate_from_pct(ip_get_pos("knowledge/store/intake-" as *u8, respondent, "debt_apr_pct\x00" as *u8, 24), 1) 303 let bb_pay: i64 = ip_get_pos("knowledge/store/intake-" as *u8, respondent, "monthly_payment\x00" as *u8, 60000) 304 let bb_saved: i64 = pur_interest_saved(bb_bal, bb_apr, bb_pay, 20000) 305 let bb_true: i64 = pur_true_cost(20000, bb_saved) 306 o = fw_cat(out, o, "<section><h2>Before you buy (in the Nishi browser)</h2>" as *u8) 307 o = fw_cat(out, o, "<p class=\"muted\">When you reach for &ldquo;buy&rdquo;, the Nishi browser shows what you HAVE vs what it truly COSTS &mdash; including the opportunity cost. Grounded in real economics, not guesswork.</p>" as *u8) 308 o = fw_cat(out, o, "<div class=\"win\">That $200 gadget? Sticker is $200, but it truly costs " as *u8) 309 o = fw_money(out, o, bb_true) 310 o = fw_cat(out, o, " &mdash; because that $200 would have saved " as *u8) 311 o = fw_money(out, o, bb_saved) 312 o = fw_cat(out, o, " in interest on your 24% debt. What you have vs what it really costs, every time.</div></section>" as *u8) 313 let co_tiers: *i64 = sys_mmap(8 * 8) as *i64 314 let co_imp: *i64 = sys_mmap(8 * 8) as *i64 315 let co_order: *i64 = sys_mmap(8 * 8) as *i64 316 let co_nn: *i64 = sys_mmap(16) as *i64 317 let co_tot: i64 = cor_dashboard("knowledge/store/bills-" as *u8, respondent, fw_day_of_month(sys_now_realtime_sec()), bb_bal, bb_apr, bb_pay, co_tiers, co_imp, co_order, co_nn) 318 let co_budget: i64 = ac_load_budget(AC_ADHD, 2, co_nn[0]) 319 o = fw_cat(out, o, "<section><h2>Your climb-out plan</h2>" as *u8) 320 o = fw_cat(out, o, "<p class=\"muted\">Everything the engines found &mdash; overcharges, a cheaper rate, the debt, the bills &mdash; fused into one path in the right order, then filtered to one step at a time.</p>" as *u8) 321 o = fw_cat(out, o, "<div class=\"win\">Total climb-out: " as *u8) 322 o = fw_money(out, o, co_tot) 323 o = fw_cat(out, o, " recovered + saved across the plan. Showing " as *u8) 324 o = fw_n(out, o, co_budget) 325 o = fw_cat(out, o, " step &mdash; your one next step: <strong>pay the overdue HOA bill</strong> (stop the bleeding first). The rest is sequenced and saved for when you are ready.</div></section>" as *u8) 326 // --- R7: TRACK THE CLIMB -- this user's progress over the same plan, made visible + interactive --- 327 let pr_done: *u8 = sys_mmap(64) 328 let pr_n: i64 = co_nn[0] 329 cp_get("knowledge/store/progress-" as *u8, respondent, pr_done, pr_n) 330 let pr_dn: i64 = cp_count(pr_done, pr_n) 331 let pr_pct: i64 = cp_pct(pr_dn, pr_n) 332 let pr_next: i64 = cp_next(pr_done, co_order, pr_n) 333 o = fw_cat(out, o, "<section><h2>Your climb to financial peace</h2>" as *u8) 334 o = fw_cat(out, o, "<p class=\"muted\">Every step you finish disappears &mdash; you only ever see the next one. No long overwhelming list, just the climb adding up.</p>" as *u8) 335 o = fw_cat(out, o, "<div class=\"win\">" as *u8) 336 o = fw_n(out, o, pr_dn); o = fw_cat(out, o, " of " as *u8); o = fw_n(out, o, pr_n) 337 o = fw_cat(out, o, " steps done &mdash; you are " as *u8); o = fw_n(out, o, pr_pct) 338 o = fw_cat(out, o, "% of the way to financial peace.</div>" as *u8) 339 o = fw_cat(out, o, "<div style=\"background:#1f2937;border-radius:6px;height:14px;margin:.6rem 0;overflow:hidden\"><div style=\"background:#34d399;height:14px;width:" as *u8) 340 o = fw_n(out, o, pr_pct) 341 o = fw_cat(out, o, "%\"></div></div>" as *u8) 342 if pr_next >= 0 { 343 o = fw_cat(out, o, "<p>Your one next step: <strong>" as *u8) 344 o = fw_cat(out, o, fw_tier_label(co_tiers[pr_next])) 345 o = fw_cat(out, o, "</strong></p>" as *u8) 346 o = fw_cat(out, o, "<form method=\"post\" action=\"/finance/progress\"><button type=\"submit\" style=\"background:#34d399;color:#06281d;border:0;border-radius:8px;padding:.7rem 1.1rem;font-size:1rem;font-weight:700;cursor:pointer;min-height:44px\">&check; I did this step</button></form>" as *u8) 347 } else { 348 o = fw_cat(out, o, "<div class=\"win\">You have completed every step &mdash; you climbed out. This is what financial peace looks like.</div>" as *u8) 349 } 350 o = fw_cat(out, o, "</section>" as *u8) 351 // --- YOUR BILLS: the person's OWN recurring obligations, entered via the form (real, not representative) --- 352 let yb_amt: *i64 = sys_mmap(8 * 64) as *i64 353 let yb_cad: *i64 = sys_mmap(8 * 64) as *i64 354 let yb_due: *i64 = sys_mmap(8 * 64) as *i64 355 let yb_cat: *i64 = sys_mmap(8 * 64) as *i64 356 let yb_pd: *i64 = sys_mmap(8 * 64) as *i64 357 let ybn: i64 = bs_load("knowledge/store/bills-" as *u8, respondent, yb_amt, yb_cad, yb_due, yb_cat, yb_pd, 64) 358 o = fw_cat(out, o, "<section><h2>Your bills</h2>" as *u8) 359 if ybn > 0 { 360 o = fw_cat(out, o, "<div class=\"win\">Your real monthly obligations: " as *u8) 361 o = fw_money(out, o, bl_monthly_total(yb_amt, yb_cad, ybn)) 362 o = fw_cat(out, o, " across " as *u8); o = fw_n(out, o, ybn); o = fw_cat(out, o, " bills.</div><ul>" as *u8) 363 var ybi: i64 = 0 364 while ybi < ybn { 365 o = fw_cat(out, o, "<li>" as *u8) 366 o = fw_cat(out, o, fw_bill_cat_label(yb_cat[ybi])) 367 o = fw_cat(out, o, " &mdash; " as *u8); o = fw_money(out, o, yb_amt[ybi]) 368 o = fw_cat(out, o, " " as *u8); o = fw_cat(out, o, fw_cadence_label(yb_cad[ybi])) 369 o = fw_cat(out, o, ", due day " as *u8); o = fw_n(out, o, yb_due[ybi]) 370 if yb_pd[ybi] == 0 { o = fw_cat(out, o, " &mdash; <strong>unpaid</strong>" as *u8) } else { o = fw_cat(out, o, " &mdash; paid" as *u8) } 371 o = fw_cat(out, o, "</li>" as *u8) 372 ybi = ybi + 1 373 } 374 o = fw_cat(out, o, "</ul>" as *u8) 375 } else { 376 o = fw_cat(out, o, "<p class=\"muted\">No bills yet. Add your recurring bills (rent, insurance, utilities) so your plan runs on your real numbers, not examples.</p>" as *u8) 377 } 378 o = fw_cat(out, o, "<form method=\"post\" action=\"/finance/bills\" style=\"display:grid;gap:.5rem;max-width:440px\">" as *u8) 379 o = fw_cat(out, o, "<label>Amount ($) <input name=\"bill_amount\" type=\"number\" min=\"0\" required></label>" as *u8) 380 o = fw_cat(out, o, "<label>How often <select name=\"bill_cadence\"><option value=\"1\">Monthly</option><option value=\"3\">Quarterly</option><option value=\"6\">Every 6 months</option><option value=\"12\">Yearly</option></select></label>" as *u8) 381 o = fw_cat(out, o, "<label>Due day of month <input name=\"bill_due_day\" type=\"number\" min=\"1\" max=\"31\" value=\"1\"></label>" as *u8) 382 o = fw_cat(out, o, "<label>Category <select name=\"bill_category\"><option value=\"0\">Housing</option><option value=\"1\">Insurance</option><option value=\"2\">Utility</option><option value=\"3\">Debt</option><option value=\"4\">Other</option></select></label>" as *u8) 383 o = fw_cat(out, o, "<label><input name=\"bill_paid\" type=\"checkbox\" value=\"1\"> Already paid this period</label>" as *u8) 384 o = fw_cat(out, o, "<button type=\"submit\" style=\"background:#34d399;color:#06281d;border:0;border-radius:8px;padding:.7rem 1.1rem;font-weight:700;min-height:44px;cursor:pointer\">Add this bill</button>" as *u8) 385 o = fw_cat(out, o, "</form></section>" as *u8) 386 o = fw_cat(out, o, "<section><h2>Ladder</h2><p class=\"muted\">R1 no-float money &middot; R2 double-entry ledger + net-worth-as-view &middot; R4 debt &amp; amortization (here) &middot; next: bill/EOB ingest, medical-overbilling audit, jurisdiction rule packs, the fail-safe action queue. Built the Nishi way: build &rarr; gate &rarr; measure &rarr; bank, each rung sovereign (nx_cc&rarr;nxasm, no third-party).</p></section>" as *u8) 387 388 o = fw_cat(out, o, "<footer class=\"muted\" style=\"text-align:center;padding:1.4rem\">Sovereign &middot; zero-JavaScript &middot; exact no-float cents &middot; behind the OPAQUE wall &middot; Nishi Finance</footer>" as *u8) 389 o = fw_cat(out, o, "</main></div></body></html>" as *u8) 390 out[o] = 0 as u8 391 return o 392}