/* global React, Wordmark, Money, Sparkline, Donut, BarChart, AreaChart, Progress, Avatar, Icon */ const { useState: useStateApp, useMemo: useMemoApp } = React; /* ============================================================ PRODUCT APP — shared shell + screens ============================================================ */ function AppShell({ goTo, view, theme, wordmark, children }) { const nav = [ ["dashboard", "Overview", "home"], ["budget", "Budget", "pie"], ["goals", "Goals", "target"], ["invest", "Invest", "seed"], ["transactions", "Activity", "list"], ["settings", "Settings", "settings"], ]; return (
{new Date().toLocaleDateString("en-US",{ weekday: "long", month: "long", day: "numeric" })}
Good morning, Alex.
{children}
); } /* ===== DASHBOARD ===== */ function Dashboard() { return (
Net worth
↑ $1,284.42 today · +12.3% YTD
{["1W","1M","3M","1Y","All"].map((t, i) => ( ))}
May spending
on track
$3,006 / $3,500
14 DAYS LEFT · PACE −$120/DAY
Auto-invest
Weekly
$1,200/wk
NEXT · TUE, MAY 13 · INDEX 80/20
Cash savings
4.50% APY
$64,321.55
+$241.18 INTEREST IN APRIL
Recent activity
{[ ["Trader Joe's", "Groceries", -42.18, "Today", "cart"], ["Auto-invest deposit", "Invest", -300, "Today", "seed"], ["Stripe: Salary", "Income", 5840, "Yesterday", "arrowDown"], ["Verve Coffee", "Dining", -7.25, "Yesterday", "coffee"], ["Vacation goal", "Tokyo", -120, "Mon", "plane"], ].map((t, i) => (
{t[0]}
{t[1]}
{t[3]} 0 ? "var(--positive)" : "var(--ink)", fontSize: 13 }}> {t[2] > 0 ? "+" : "−"}${Math.abs(t[2]).toFixed(2)}
))}
Kaizen insight
"You'd hit your Tokyo goal six weeks earlier by raising auto-invest from $240 to $300/wk."
Goals · 4 active
{[ ["Tokyo trip", 4280, 6000, "var(--accent)", "plane"], ["Down payment", 18420, 60000, "var(--accent-2)", "house"], ["New bike", 1240, 2200, "#c47a08", "car"], ].map((g, i) => (
{g[0]} ${g[1].toLocaleString()} / ${g[2].toLocaleString()}
))}
); function Legend({ swatch, label, v }) { return (
{label} {v}
); } } /* ===== BUDGET ===== */ function BudgetView() { const cats = [ { name: "Rent", v: 1850, max: 1850, color: "var(--ink)", icon: "house" }, { name: "Groceries", v: 412, max: 600, color: "var(--accent)", icon: "cart" }, { name: "Transit", v: 88, max: 150, color: "var(--accent-2)", icon: "car" }, { name: "Dining", v: 246, max: 300, color: "#c47a08", icon: "coffee" }, { name: "Subscriptions", v: 92, max: 100, color: "var(--ink-2)", icon: "card" }, { name: "Discretionary", v: 318, max: 500, color: "var(--negative)", icon: "sparkle" }, ]; return (
May · week 2 of 4
of $3,500
14 DAYS LEFT · PACE −$120/DAY · YOU'LL FINISH AT $3,180
({ value: c.v, color: c.color }))} label="86%" sublabel="of plan"/>
Categories
{cats.map((c, i) => { const pct = (c.v / c.max) * 100; return (
{c.name}
{Math.round(pct)}% used
${c.v} / ${c.max}
); })}
Trend · last 6 months
Pattern detected

Dining out spikes on Fridays.

Six of your last seven Fridays exceeded $40. Want to set a Friday cap of $25?

); } /* ===== GOALS ===== */ function GoalsView() { const goals = [ { name: "Tokyo trip", icon: "plane", saved: 4280, target: 6000, eta: "Oct 2026", weekly: 240, color: "var(--accent)", note: "October peak season. Book flights early." }, { name: "Down payment", icon: "house", saved: 18420, target: 60000, eta: "Mar 2028", weekly: 380, color: "var(--accent-2)", note: "Auto-invested in conservative portfolio" }, { name: "New bike", icon: "car", saved: 1240, target: 2200, eta: "Aug 2026", weekly: 60, color: "#c47a08", note: "Almost there, 56% complete." }, { name: "Emergency fund", icon: "shield", saved: 9600, target: 12000, eta: "On schedule", weekly: 80, color: "var(--ink)", note: "6 months of expenses" }, { name: "Wedding", icon: "sparkle", saved: 3100, target: 25000, eta: "Sep 2027", weekly: 280, color: "var(--negative)", note: "Just started saving" }, { name: "Tuition", icon: "grad", saved: 8400, target: 18000, eta: "Aug 2026", weekly: 200, color: "var(--warn)", note: "On track for fall semester" }, ]; return (
Saving for
across 6 goals
$1,240/WK AUTO-ALLOCATED · ALL ON TRACK
{goals.map((g, i) => { const pct = (g.saved / g.target) * 100; return (
{g.eta}

{g.name}

of ${g.target.toLocaleString()}
{Math.round(pct)}% saved ${g.weekly}/wk auto

{g.note}

); })}
); } /* ===== INVEST ===== */ function InvestView() { const holdings = [ { sym: "VTI", name: "Total US Stock", v: 110023.7, alloc: 56, day: 1.2, color: "var(--accent)" }, { sym: "VXUS", name: "Total Intl Stock", v: 47140.9, alloc: 24, day: 0.4, color: "var(--accent-2)" }, { sym: "BND", name: "Total Bond", v: 31427.3, alloc: 16, day: -0.1, color: "#c47a08" }, { sym: "CASH", name: "Cash buffer", v: 7829.0, alloc: 4, day: 0.0, color: "var(--ink-3)" }, ]; return (
Index portfolio
↑ +$24,103 (+14.0%) all-time · +$1,284 today
{[["1D","+0.66%"],["1W","+1.4%"],["1M","+2.1%"],["1Y","+12.4%"],["3Y","+38.2%"],["All","+14.0%"]].map((p, i) => (
{p[0]}
{p[1]}
))}
Allocation · 80/20
({ value: h.alloc, color: h.color }))} label="80/20" sublabel="stocks/bonds"/>
{holdings.map((h, i) => (
{h.sym} {h.alloc}%
))}
Tax-loss harvesting
$1,284

Saved this year. Automatic, daily, in the background.

Holdings
{holdings.map((h, i) => ( ))}
SymbolNameAllocationValueDayTrend
{h.sym} {h.name} {h.alloc}% ${h.v.toLocaleString(undefined, {minimumFractionDigits: 2})} 0 ? "var(--positive)" : h.day < 0 ? "var(--negative)" : "var(--ink-3)" }}> {h.day > 0 ? "+" : ""}{h.day.toFixed(1)}% v + (h.day < 0 ? -2 : 0))} stroke={h.color} w={80} h={24}/>
); } /* ===== TRANSACTIONS ===== */ function TransactionsView() { const tx = [ { d: "Today", items: [ ["Trader Joe's", "Groceries", -42.18, "Chase ••4421", "cart"], ["Auto-invest deposit", "Invest · weekly", -300, "Kaizen Brokerage", "seed"], ["Spotify", "Subscriptions", -10.99, "Chase ••4421", "card"], ]}, { d: "Yesterday", items: [ ["Stripe: Payroll", "Income", 5840.00, "Chase ••4421", "arrowDown"], ["Verve Coffee", "Dining", -7.25, "Chase ••4421", "coffee"], ["Lyft", "Transit", -14.20, "Chase ••4421", "car"], ["Vacation goal: Tokyo", "Goal transfer", -120.00, "Goals reserve", "plane"], ]}, { d: "Mon, May 5", items: [ ["Whole Foods", "Groceries", -88.40, "Chase ••4421", "cart"], ["Dividend: VTI", "Income", 142.31, "Kaizen Brokerage", "arrowDown"], ["Comcast", "Subscriptions", -65.00, "Chase ••4421", "card"], ]}, { d: "Sat, May 3", items: [ ["Olive Garden", "Dining", -64.22, "Chase ••4421", "coffee"], ["Uniqlo", "Shopping", -118.50, "Chase ••4421", "cart"], ]}, ]; return (
Activity
Last 7 days
{tx.map((day, i) => (
{day.d} {day.items.length} items · ${day.items.reduce((s, it) => s + it[2], 0).toFixed(2)}
{day.items.map((t, j) => (
{t[0]}
{t[1]}
{t[3]}
0 ? "var(--positive)" : "var(--ink)" }}> {t[2] > 0 ? "+" : "−"}${Math.abs(t[2]).toFixed(2)}
))}
))}
); function SumCell({ label, value, positive, large, prefix = "$", decimals = 2 }) { return (
{label}
{value < 0 ? "−" : ""}{prefix}{Math.abs(value).toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals })}
); } } /* ===== ONBOARDING ===== */ function OnboardingView({ goTo, wordmark = "slash" }) { const [step, setStep] = useStateApp(0); const steps = ["Welcome","Connect bank","Pick goals","Set auto-invest","Review"]; return (
{steps.map((s, i) => (
{String(i + 1).padStart(2, "0")} {s}
))}
{step === 0 && setStep(1)} />} {step === 1 && setStep(2)} />} {step === 2 && setStep(3)} />} {step === 3 && setStep(4)} />} {step === 4 && goTo("dashboard")} />}
); } function OnbWelcome({ onNext }) { return (
Welcome

Money is a practice.

Setup takes 4 minutes. We'll connect your bank, set up two or three goals, and turn on auto-invest. You can change anything, any time.

); } function OnbBank({ onNext }) { const banks = [ { name: "Chase", logo: "C", color: "#117ACA" }, { name: "Bank of America", logo: "B", color: "#E31837" }, { name: "Wells Fargo", logo: "W", color: "#D71E28" }, { name: "Citi", logo: "Ci", color: "#003D7A" }, { name: "Capital One", logo: "C1", color: "#D03027" }, { name: "Ally", logo: "A", color: "#7F2487" }, { name: "SoFi", logo: "S", color: "#00A6E2" }, { name: "Schwab", logo: "Sc", color: "#0C84B6" }, ]; return (
Step 02 of 05

Connect your bank.

Read-only access via Plaid. We never see your password.

{banks.map((b, i) => ( ))}
Read-only by default
We can see balances and transactions, never move money without permission.
); } function OnbGoals({ onNext }) { const presets = [ { name: "Vacation", icon: "plane", color: "var(--accent)" }, { name: "Down payment", icon: "house", color: "var(--accent-2)" }, { name: "New car", icon: "car", color: "#c47a08" }, { name: "Emergency fund", icon: "shield", color: "var(--ink)" }, { name: "Wedding", icon: "sparkle", color: "var(--negative)" }, { name: "Tuition", icon: "grad", color: "var(--warn)" }, ]; const [picked, setPicked] = useStateApp(["Vacation", "Emergency fund"]); const toggle = (n) => setPicked(p => p.includes(n) ? p.filter(x => x !== n) : [...p, n]); return (
Step 03 of 05

What are you saving for?

Pick one or many. We'll figure out the math.

{presets.map((p, i) => { const on = picked.includes(p.name); return ( ); })}
); } function OnbInvest({ onNext }) { const [risk, setRisk] = useStateApp(2); const profiles = [ { name: "Conservative", split: "30/70", desc: "Bond-heavy. Lower swings, lower upside.", color: "var(--ink-3)" }, { name: "Balanced", split: "60/40", desc: "Classic mix. Smooth ride.", color: "#c47a08" }, { name: "Growth", split: "80/20", desc: "Stock-heavy. Recommended for your horizon.", color: "var(--accent)" }, { name: "Aggressive", split: "100/0", desc: "All stocks. For long timelines and steady stomachs.", color: "var(--negative)" }, ]; return (
Step 04 of 05

Pick your portfolio.

Index funds. We'll rebalance automatically.

{profiles.map((p, i) => ( ))}
Auto-invest / week
); } function OnbReview({ onDone }) { return (
Step 05 of 05

You're ready.

Here's what we'll do. Change anything any time.

); function ReviewRow({ label, v, tone }) { return (
{label} {v}
); } } /* ===== SETTINGS ===== */ function SettingsView() { return (
Account
Settings
Profile
Alex Morgan
alex@morgan.io · Member since Mar 2024
Security
Linked accounts
Documents
Danger zone

Pause auto-invest, withdraw funds, or close your account. No exit fees.

Preferences
); } function SetRow({ label, v, pos, download, toggle }) { return (
{label} {v} {download && } {toggle && }
); } /* Export */ Object.assign(window, { AppShell, Dashboard, BudgetView, GoalsView, InvestView, TransactionsView, OnboardingView, SettingsView, });