AndyKandy26 commited on
Commit
00e4c29
·
verified ·
1 Parent(s): d90af1b

Upload 9 files

Browse files
Files changed (9) hide show
  1. README.md +74 -0
  2. calculators.html +905 -0
  3. index.html +409 -0
  4. insights.html +860 -0
  5. portfolio.html +673 -0
  6. risk.html +618 -0
  7. shared.css +772 -0
  8. shared.js +172 -0
  9. tracker.html +551 -0
README.md ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FinWise — AI Personal Finance Platform
3
+ emoji: 📈
4
+ colorFrom: indigo
5
+ colorTo: green
6
+ sdk: static
7
+ pinned: false
8
+ license: mit
9
+ short_description: Beginner-friendly stock & portfolio management web app
10
+ ---
11
+
12
+ # 📈 FinWise — AI-Powered Personal Finance Platform
13
+
14
+ A **comprehensive, beginner-friendly** stock and mutual fund portfolio management platform built for clarity, interactivity, and smart financial guidance.
15
+
16
+ ## 🚀 Features
17
+
18
+ | Module | Description |
19
+ |--------|-------------|
20
+ | 🏠 **Dashboard** | Portfolio overview, performance chart, market snapshot |
21
+ | 🏗️ **Portfolio Builder** | 3-step guided portfolio creation with live preview |
22
+ | 🎯 **Risk Analyzer** | Risk quiz, gauge, scenario simulation, rebalancing suggestions |
23
+ | 📈 **Investment Tracker** | Holdings table, editable cost basis, P&L, allocation chart |
24
+ | 🧮 **Calculators** | Growth, Retirement, SIP/Recurring, Inflation Impact |
25
+ | 💡 **AI Insights** | Claude-powered chat advisor, tips, concepts, glossary |
26
+
27
+ ## 🛠 Tech Stack
28
+
29
+ - **Pure HTML/CSS/JavaScript** — no build step required
30
+ - **Chart.js** — all charts and visualizations
31
+ - **Claude API** — AI advisor in the Insights tab
32
+ - **localStorage** — portfolio persistence across sessions
33
+ - **Google Fonts** — Syne + DM Sans typography
34
+
35
+ ## 📁 File Structure
36
+
37
+ ```
38
+ finwise/
39
+ ├── index.html # Dashboard
40
+ ├── portfolio.html # Portfolio Builder
41
+ ├── risk.html # Risk Analyzer
42
+ ├── tracker.html # Investment Tracker
43
+ ├── calculators.html # Financial Calculators
44
+ ├── insights.html # AI Insights
45
+ ├── shared.css # Design system & styles
46
+ ├── shared.js # Utilities, data, helpers
47
+ └── README.md
48
+ ```
49
+
50
+ ## 🎨 Design
51
+
52
+ - **Dark luxury fintech** aesthetic
53
+ - **Syne** (headings) + **DM Sans** (body) typography
54
+ - Cyan & Emerald accent palette on deep navy
55
+ - Mobile-first responsive design with bottom navigation
56
+ - Animated stats, hover effects, smooth transitions
57
+
58
+ ## 🤖 AI Features
59
+
60
+ The **AI Advisor** in the Insights tab uses Claude claude-sonnet-4-20250514 to provide:
61
+ - Personalized portfolio analysis based on your actual holdings
62
+ - Plain-language explanations of investing concepts
63
+ - Context-aware rebalancing suggestions
64
+ - Smart financial Q&A
65
+
66
+ ## 📱 Responsive
67
+
68
+ - Full sidebar navigation on desktop
69
+ - Bottom tab bar on mobile
70
+ - All charts resize gracefully
71
+
72
+ ---
73
+
74
+ *FinWise is an educational tool. It does not constitute licensed financial advice.*
calculators.html ADDED
@@ -0,0 +1,905 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — Calculators</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
+ <style>
10
+ .calc-tabs {
11
+ display: flex;
12
+ gap: 8px;
13
+ flex-wrap: wrap;
14
+ margin-bottom: 28px;
15
+ }
16
+ .calc-tab {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 8px;
20
+ padding: 12px 20px;
21
+ border-radius: var(--r);
22
+ border: 1px solid var(--border);
23
+ background: var(--card);
24
+ color: var(--text2);
25
+ font-family: var(--font-body);
26
+ font-size: 14px;
27
+ font-weight: 600;
28
+ cursor: pointer;
29
+ transition: all var(--transition);
30
+ white-space: nowrap;
31
+ }
32
+ .calc-tab:hover { border-color: var(--border2); color: var(--text); transform: translateY(-1px); }
33
+ .calc-tab.active {
34
+ border-color: var(--cyan);
35
+ background: rgba(34,211,238,0.08);
36
+ color: var(--cyan);
37
+ box-shadow: var(--glow-c);
38
+ }
39
+ .calc-tab-icon { font-size: 20px; }
40
+
41
+ .calc-panel { display: none; }
42
+ .calc-panel.active { display: block; }
43
+
44
+ .result-hero {
45
+ text-align: center;
46
+ padding: 32px 20px;
47
+ background: linear-gradient(135deg, rgba(34,211,238,0.06), rgba(16,185,129,0.04));
48
+ border: 1px solid var(--border2);
49
+ border-radius: var(--r-lg);
50
+ margin-bottom: 24px;
51
+ position: relative;
52
+ overflow: hidden;
53
+ }
54
+ .result-hero::before {
55
+ content: '';
56
+ position: absolute;
57
+ width: 300px; height: 300px;
58
+ border-radius: 50%;
59
+ background: radial-gradient(circle, rgba(34,211,238,0.06) 0%, transparent 70%);
60
+ top: -100px; left: 50%;
61
+ transform: translateX(-50%);
62
+ }
63
+ .result-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text2); font-weight: 700; }
64
+ .result-value { font-family: var(--font-head); font-size: 52px; font-weight: 800; line-height: 1; margin: 12px 0; }
65
+ .result-value.positive { color: var(--emerald); }
66
+ .result-sub { font-size: 14px; color: var(--text2); }
67
+
68
+ .result-breakdown {
69
+ display: grid;
70
+ grid-template-columns: repeat(3,1fr);
71
+ gap: 12px;
72
+ margin-bottom: 24px;
73
+ }
74
+ .rb-box {
75
+ background: var(--bg3);
76
+ border-radius: var(--r-sm);
77
+ padding: 14px;
78
+ text-align: center;
79
+ }
80
+ .rb-box-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text2); margin-bottom: 6px; font-weight: 700; }
81
+ .rb-box-val { font-family: var(--font-head); font-size: 20px; font-weight: 800; }
82
+
83
+ .chart-panel { height: 260px; position: relative; }
84
+
85
+ .input-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
86
+ @media (max-width: 600px) { .input-row { grid-template-columns: 1fr; } .result-breakdown { grid-template-columns: 1fr 1fr; } }
87
+
88
+ .preset-pills {
89
+ display: flex;
90
+ gap: 6px;
91
+ flex-wrap: wrap;
92
+ margin-bottom: 8px;
93
+ }
94
+ .preset-pill {
95
+ padding: 4px 10px;
96
+ border-radius: 100px;
97
+ border: 1px solid var(--border2);
98
+ background: transparent;
99
+ color: var(--text2);
100
+ font-size: 12px;
101
+ font-weight: 600;
102
+ cursor: pointer;
103
+ font-family: var(--font-body);
104
+ transition: all var(--transition);
105
+ }
106
+ .preset-pill:hover { border-color: var(--cyan); color: var(--cyan); }
107
+ .preset-pill.active { background: rgba(34,211,238,0.1); border-color: var(--cyan); color: var(--cyan); }
108
+
109
+ .milestone-grid {
110
+ display: grid;
111
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
112
+ gap: 10px;
113
+ margin-top: 16px;
114
+ }
115
+ .milestone-card {
116
+ background: var(--bg3);
117
+ border: 1px solid var(--border);
118
+ border-radius: var(--r-sm);
119
+ padding: 14px;
120
+ text-align: center;
121
+ transition: all var(--transition);
122
+ }
123
+ .milestone-card.reached {
124
+ border-color: var(--emerald);
125
+ background: rgba(16,185,129,0.07);
126
+ }
127
+ .milestone-card.reached .ms-icon { filter: none; }
128
+ .milestone-icon { font-size: 28px; margin-bottom: 6px; filter: grayscale(0.7); }
129
+ .milestone-val { font-family: var(--font-head); font-size: 18px; font-weight: 800; margin-bottom: 4px; }
130
+ .milestone-lbl { font-size: 11px; color: var(--text2); }
131
+ .milestone-yr { font-size: 10px; color: var(--emerald); margin-top: 4px; font-weight: 700; }
132
+
133
+ .compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top:16px; }
134
+ .compare-box {
135
+ background: var(--bg3);
136
+ border-radius: var(--r-sm);
137
+ padding: 16px;
138
+ text-align: center;
139
+ border: 1px solid var(--border);
140
+ }
141
+ .cb-label { font-size: 12px; color: var(--text2); margin-bottom: 8px; font-weight: 600; }
142
+ .cb-val { font-family: var(--font-head); font-size: 24px; font-weight: 800; }
143
+
144
+ .sip-frequency {
145
+ display: flex;
146
+ gap: 8px;
147
+ margin-bottom: 16px;
148
+ }
149
+ .freq-btn {
150
+ flex: 1;
151
+ padding: 10px;
152
+ border-radius: var(--r-sm);
153
+ border: 1px solid var(--border);
154
+ background: transparent;
155
+ color: var(--text2);
156
+ font-family: var(--font-body);
157
+ font-size: 13px;
158
+ font-weight: 600;
159
+ cursor: pointer;
160
+ transition: all var(--transition);
161
+ }
162
+ .freq-btn.active { border-color: var(--cyan); background: rgba(34,211,238,0.1); color: var(--cyan); }
163
+
164
+ .inflation-impact {
165
+ display: flex;
166
+ align-items: center;
167
+ gap: 16px;
168
+ padding: 16px;
169
+ background: var(--bg3);
170
+ border-radius: var(--r-sm);
171
+ border-left: 3px solid var(--rose);
172
+ margin-bottom: 16px;
173
+ }
174
+ .inf-icon { font-size: 32px; }
175
+ .inf-text { flex: 1; }
176
+ .inf-title { font-weight: 700; margin-bottom: 4px; }
177
+ .inf-desc { font-size: 13px; color: var(--text2); }
178
+ </style>
179
+ </head>
180
+ <body>
181
+ <div class="app-shell">
182
+ <nav class="sidebar">
183
+ <div class="sidebar-logo">
184
+ <div class="logo-mark">
185
+ <div class="logo-icon">📈</div>
186
+ <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
187
+ </div>
188
+ </div>
189
+ <div class="nav-section">
190
+ <div class="nav-label">Main</div>
191
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
192
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
193
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
194
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
195
+ <div class="nav-label">Tools</div>
196
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
197
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights</a>
198
+ </div>
199
+ <div class="sidebar-footer">
200
+ <div class="market-ticker">Live Market</div>
201
+ <div id="sidebar-tickers"></div>
202
+ </div>
203
+ </nav>
204
+
205
+ <main class="main-content">
206
+ <div class="page-header fade-in">
207
+ <div class="page-title">Financial <span>Calculators</span></div>
208
+ <div class="page-subtitle">Interactive tools to plan, project, and optimize your wealth</div>
209
+ </div>
210
+
211
+ <!-- Calculator Tabs -->
212
+ <div class="calc-tabs fade-in">
213
+ <button class="calc-tab active" data-calc="growth">
214
+ <span class="calc-tab-icon">📈</span> Investment Growth
215
+ </button>
216
+ <button class="calc-tab" data-calc="retirement">
217
+ <span class="calc-tab-icon">🏖️</span> Retirement
218
+ </button>
219
+ <button class="calc-tab" data-calc="sip">
220
+ <span class="calc-tab-icon">🔄</span> SIP / Recurring
221
+ </button>
222
+ <button class="calc-tab" data-calc="inflation">
223
+ <span class="calc-tab-icon">🔥</span> Inflation Impact
224
+ </button>
225
+ </div>
226
+
227
+ <!-- ════════════════════════════════════════════════════════════
228
+ CALCULATOR 1: Investment Growth
229
+ ════════════════════════════════════════════════════════════ -->
230
+ <div class="calc-panel active fade-in" id="calc-growth">
231
+ <div class="grid-60-40">
232
+ <div>
233
+ <div class="card">
234
+ <div class="section-title">📈 Investment Growth Calculator</div>
235
+ <div class="text-muted text-sm" style="margin-bottom:20px">See how your one-time investment grows over time with compound returns</div>
236
+
237
+ <div class="slider-wrap">
238
+ <div class="slider-header"><span class="slider-label">💵 Initial Investment</span><span class="slider-val" id="g-init-val">$10,000</span></div>
239
+ <div class="preset-pills">
240
+ <button class="preset-pill" onclick="setGrowthPreset('g-init',1000)">$1K</button>
241
+ <button class="preset-pill active" onclick="setGrowthPreset('g-init',10000)">$10K</button>
242
+ <button class="preset-pill" onclick="setGrowthPreset('g-init',25000)">$25K</button>
243
+ <button class="preset-pill" onclick="setGrowthPreset('g-init',50000)">$50K</button>
244
+ <button class="preset-pill" onclick="setGrowthPreset('g-init',100000)">$100K</button>
245
+ </div>
246
+ <input type="range" id="g-init" min="500" max="200000" value="10000" step="500" oninput="calcGrowth()">
247
+ </div>
248
+
249
+ <div class="slider-wrap">
250
+ <div class="slider-header"><span class="slider-label">📅 Time Horizon</span><span class="slider-val" id="g-years-val">20 years</span></div>
251
+ <div class="preset-pills">
252
+ <button class="preset-pill" onclick="setGrowthPreset('g-years',5)">5yr</button>
253
+ <button class="preset-pill" onclick="setGrowthPreset('g-years',10)">10yr</button>
254
+ <button class="preset-pill active" onclick="setGrowthPreset('g-years',20)">20yr</button>
255
+ <button class="preset-pill" onclick="setGrowthPreset('g-years',30)">30yr</button>
256
+ </div>
257
+ <input type="range" id="g-years" min="1" max="50" value="20" step="1" oninput="calcGrowth()">
258
+ </div>
259
+
260
+ <div class="slider-wrap">
261
+ <div class="slider-header"><span class="slider-label">📊 Annual Return Rate</span><span class="slider-val" id="g-rate-val">8%</span></div>
262
+ <div class="preset-pills">
263
+ <button class="preset-pill" onclick="setGrowthPreset('g-rate',4,'%')">Bonds 4%</button>
264
+ <button class="preset-pill" onclick="setGrowthPreset('g-rate',7,'%')">S&P Avg 7%</button>
265
+ <button class="preset-pill active" onclick="setGrowthPreset('g-rate',8,'%')">Moderate 8%</button>
266
+ <button class="preset-pill" onclick="setGrowthPreset('g-rate',12,'%')">Aggressive 12%</button>
267
+ </div>
268
+ <input type="range" id="g-rate" min="1" max="25" value="8" step="0.5" oninput="calcGrowth()">
269
+ </div>
270
+
271
+ <div class="slider-wrap">
272
+ <div class="slider-header"><span class="slider-label">📆 Compound Frequency</span><span class="slider-val" id="g-compound-val">Annually</span></div>
273
+ <input type="range" id="g-compound" min="1" max="12" value="1" step="1" oninput="calcGrowth()">
274
+ <div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text3);margin-top:4px">
275
+ <span>Annually</span><span>Quarterly</span><span>Monthly</span>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+
281
+ <div>
282
+ <div class="result-hero">
283
+ <div class="result-label">Future Value</div>
284
+ <div class="result-value positive" id="g-future">$0</div>
285
+ <div class="result-sub" id="g-sub">You'd earn <strong id="g-profit">$0</strong> in profit</div>
286
+ </div>
287
+
288
+ <div class="result-breakdown">
289
+ <div class="rb-box">
290
+ <div class="rb-box-label">Invested</div>
291
+ <div class="rb-box-val" id="g-rb-invested">$0</div>
292
+ </div>
293
+ <div class="rb-box">
294
+ <div class="rb-box-label">Earnings</div>
295
+ <div class="rb-box-val" style="color:var(--emerald)" id="g-rb-earn">$0</div>
296
+ </div>
297
+ <div class="rb-box">
298
+ <div class="rb-box-label">Total Return</div>
299
+ <div class="rb-box-val" style="color:var(--cyan)" id="g-rb-pct">0%</div>
300
+ </div>
301
+ </div>
302
+
303
+ <div class="card" style="padding:16px">
304
+ <div class="card-title">📊 Growth Over Time</div>
305
+ <div class="chart-panel">
306
+ <canvas id="growthChart"></canvas>
307
+ </div>
308
+ </div>
309
+
310
+ <!-- Milestones -->
311
+ <div class="card" style="margin-top:16px;padding:16px">
312
+ <div class="card-title">🏆 Milestones</div>
313
+ <div class="milestone-grid" id="milestones-grid"></div>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ </div>
318
+
319
+ <!-- ════════════════════════════════════════════════════════════
320
+ CALCULATOR 2: Retirement
321
+ ════════════════════════════════════════════════════════════ -->
322
+ <div class="calc-panel" id="calc-retirement">
323
+ <div class="grid-60-40">
324
+ <div>
325
+ <div class="card">
326
+ <div class="section-title">🏖️ Retirement Calculator</div>
327
+ <div class="text-muted text-sm" style="margin-bottom:20px">Plan your retirement savings and see if you're on track</div>
328
+
329
+ <div class="input-row" style="margin-bottom:16px">
330
+ <div class="field-group">
331
+ <label class="field-label">Current Age</label>
332
+ <input type="number" id="ret-age" value="30" min="18" max="80" oninput="calcRetirement()">
333
+ </div>
334
+ <div class="field-group">
335
+ <label class="field-label">Retirement Age</label>
336
+ <input type="number" id="ret-age2" value="65" min="40" max="85" oninput="calcRetirement()">
337
+ </div>
338
+ </div>
339
+
340
+ <div class="slider-wrap">
341
+ <div class="slider-header"><span class="slider-label">💰 Current Savings</span><span class="slider-val" id="ret-saved-val">$15,000</span></div>
342
+ <input type="range" id="ret-saved" min="0" max="500000" value="15000" step="1000" oninput="calcRetirement()">
343
+ </div>
344
+
345
+ <div class="slider-wrap">
346
+ <div class="slider-header"><span class="slider-label">📅 Monthly Contribution</span><span class="slider-val" id="ret-monthly-val">$500/mo</span></div>
347
+ <input type="range" id="ret-monthly" min="50" max="5000" value="500" step="50" oninput="calcRetirement()">
348
+ </div>
349
+
350
+ <div class="slider-wrap">
351
+ <div class="slider-header"><span class="slider-label">📈 Annual Return</span><span class="slider-val" id="ret-rate-val">7%</span></div>
352
+ <input type="range" id="ret-rate" min="2" max="15" value="7" step="0.5" oninput="calcRetirement()">
353
+ </div>
354
+
355
+ <div class="slider-wrap">
356
+ <div class="slider-header"><span class="slider-label">🏦 Desired Monthly Income in Retirement</span><span class="slider-val" id="ret-need-val">$4,000/mo</span></div>
357
+ <input type="range" id="ret-need" min="1000" max="20000" value="4000" step="500" oninput="calcRetirement()">
358
+ </div>
359
+ </div>
360
+ </div>
361
+
362
+ <div>
363
+ <div class="result-hero">
364
+ <div class="result-label">Retirement Nest Egg</div>
365
+ <div class="result-value positive" id="ret-future">$0</div>
366
+ <div class="result-sub" id="ret-status">—</div>
367
+ </div>
368
+
369
+ <div class="result-breakdown">
370
+ <div class="rb-box">
371
+ <div class="rb-box-label">Years to Retire</div>
372
+ <div class="rb-box-val" style="color:var(--amber)" id="ret-rb-years">0</div>
373
+ </div>
374
+ <div class="rb-box">
375
+ <div class="rb-box-label">Monthly Draw</div>
376
+ <div class="rb-box-val" style="color:var(--cyan)" id="ret-rb-draw">$0/mo</div>
377
+ </div>
378
+ <div class="rb-box">
379
+ <div class="rb-box-label">Fund Duration</div>
380
+ <div class="rb-box-val" style="color:var(--emerald)" id="ret-rb-dur">0 yrs</div>
381
+ </div>
382
+ </div>
383
+
384
+ <div class="card" style="padding:16px">
385
+ <div class="card-title">📊 Savings Trajectory</div>
386
+ <div class="chart-panel">
387
+ <canvas id="retChart"></canvas>
388
+ </div>
389
+ </div>
390
+
391
+ <div id="ret-on-track-box" style="margin-top:16px;padding:16px;border-radius:var(--r-sm);border:1px solid"></div>
392
+ </div>
393
+ </div>
394
+ </div>
395
+
396
+ <!-- ════════════════════════════════════════════════════════════
397
+ CALCULATOR 3: SIP / Recurring
398
+ ════════════════════════════════════════════════════════════ -->
399
+ <div class="calc-panel" id="calc-sip">
400
+ <div class="grid-60-40">
401
+ <div>
402
+ <div class="card">
403
+ <div class="section-title">🔄 SIP / Recurring Investment</div>
404
+ <div class="text-muted text-sm" style="margin-bottom:16px">How regular investments compound over time</div>
405
+
406
+ <div style="margin-bottom:16px">
407
+ <div class="field-label" style="margin-bottom:8px">Contribution Frequency</div>
408
+ <div class="sip-frequency">
409
+ <button class="freq-btn" onclick="setSipFreq('weekly',this)">Weekly</button>
410
+ <button class="freq-btn active" onclick="setSipFreq('monthly',this)">Monthly</button>
411
+ <button class="freq-btn" onclick="setSipFreq('quarterly',this)">Quarterly</button>
412
+ <button class="freq-btn" onclick="setSipFreq('annually',this)">Annually</button>
413
+ </div>
414
+ </div>
415
+
416
+ <div class="slider-wrap">
417
+ <div class="slider-header"><span class="slider-label">💵 Contribution Amount</span><span class="slider-val" id="sip-amount-val">$300/mo</span></div>
418
+ <div class="preset-pills">
419
+ <button class="preset-pill" onclick="setSipPreset('sip-amount',100)">$100</button>
420
+ <button class="preset-pill active" onclick="setSipPreset('sip-amount',300)">$300</button>
421
+ <button class="preset-pill" onclick="setSipPreset('sip-amount',500)">$500</button>
422
+ <button class="preset-pill" onclick="setSipPreset('sip-amount',1000)">$1K</button>
423
+ </div>
424
+ <input type="range" id="sip-amount" min="10" max="5000" value="300" step="10" oninput="calcSIP()">
425
+ </div>
426
+
427
+ <div class="slider-wrap">
428
+ <div class="slider-header"><span class="slider-label">📅 Duration</span><span class="slider-val" id="sip-years-val">15 years</span></div>
429
+ <input type="range" id="sip-years" min="1" max="40" value="15" step="1" oninput="calcSIP()">
430
+ </div>
431
+
432
+ <div class="slider-wrap">
433
+ <div class="slider-header"><span class="slider-label">📊 Expected Return</span><span class="slider-val" id="sip-rate-val">9%</span></div>
434
+ <input type="range" id="sip-rate" min="2" max="20" value="9" step="0.5" oninput="calcSIP()">
435
+ </div>
436
+
437
+ <div class="slider-wrap">
438
+ <div class="slider-header"><span class="slider-label">📈 Annual Step-Up %</span><span class="slider-val" id="sip-stepup-val">0%</span></div>
439
+ <input type="range" id="sip-stepup" min="0" max="20" value="0" step="1" oninput="calcSIP()">
440
+ <div class="text-muted text-sm" style="margin-top:4px">Increase contribution amount each year by this %</div>
441
+ </div>
442
+ </div>
443
+ </div>
444
+
445
+ <div>
446
+ <div class="result-hero">
447
+ <div class="result-label">Total Wealth Created</div>
448
+ <div class="result-value positive" id="sip-future">$0</div>
449
+ <div class="result-sub">Total invested: <strong id="sip-invested">$0</strong></div>
450
+ </div>
451
+
452
+ <div class="result-breakdown">
453
+ <div class="rb-box">
454
+ <div class="rb-box-label">Invested</div>
455
+ <div class="rb-box-val" id="sip-rb-inv">$0</div>
456
+ </div>
457
+ <div class="rb-box">
458
+ <div class="rb-box-label">Gains</div>
459
+ <div class="rb-box-val" style="color:var(--emerald)" id="sip-rb-gain">$0</div>
460
+ </div>
461
+ <div class="rb-box">
462
+ <div class="rb-box-label">Gain %</div>
463
+ <div class="rb-box-val" style="color:var(--cyan)" id="sip-rb-pct">0%</div>
464
+ </div>
465
+ </div>
466
+
467
+ <div class="card" style="padding:16px">
468
+ <div class="card-title">📊 Contribution vs Growth</div>
469
+ <div class="chart-panel">
470
+ <canvas id="sipChart"></canvas>
471
+ </div>
472
+ </div>
473
+
474
+ <div class="compare-grid">
475
+ <div class="compare-box">
476
+ <div class="cb-label">💤 Without Investing<br>(under mattress)</div>
477
+ <div class="cb-val" id="sip-no-invest" style="color:var(--rose)">$0</div>
478
+ </div>
479
+ <div class="compare-box">
480
+ <div class="cb-label">📈 With Investing<br>(your strategy)</div>
481
+ <div class="cb-val" id="sip-with-invest" style="color:var(--emerald)">$0</div>
482
+ </div>
483
+ </div>
484
+ </div>
485
+ </div>
486
+ </div>
487
+
488
+ <!-- ════════════════════════════════════════════════════════════
489
+ CALCULATOR 4: Inflation Impact
490
+ ════════════════════════════════════════════════════════════ -->
491
+ <div class="calc-panel" id="calc-inflation">
492
+ <div class="grid-60-40">
493
+ <div>
494
+ <div class="card">
495
+ <div class="section-title">🔥 Inflation Impact Calculator</div>
496
+ <div class="text-muted text-sm" style="margin-bottom:20px">Understand how inflation erodes your money's purchasing power</div>
497
+
498
+ <div class="slider-wrap">
499
+ <div class="slider-header"><span class="slider-label">💵 Amount Today</span><span class="slider-val" id="inf-amount-val">$10,000</span></div>
500
+ <div class="preset-pills">
501
+ <button class="preset-pill" onclick="setInfPreset('inf-amount',5000)">$5K</button>
502
+ <button class="preset-pill active" onclick="setInfPreset('inf-amount',10000)">$10K</button>
503
+ <button class="preset-pill" onclick="setInfPreset('inf-amount',50000)">$50K</button>
504
+ <button class="preset-pill" onclick="setInfPreset('inf-amount',100000)">$100K</button>
505
+ </div>
506
+ <input type="range" id="inf-amount" min="1000" max="500000" value="10000" step="1000" oninput="calcInflation()">
507
+ </div>
508
+
509
+ <div class="slider-wrap">
510
+ <div class="slider-header"><span class="slider-label">📅 Years Ahead</span><span class="slider-val" id="inf-years-val">20 years</span></div>
511
+ <input type="range" id="inf-years" min="1" max="50" value="20" step="1" oninput="calcInflation()">
512
+ </div>
513
+
514
+ <div class="slider-wrap">
515
+ <div class="slider-header"><span class="slider-label">🔥 Annual Inflation Rate</span><span class="slider-val" id="inf-rate-val">3.5%</span></div>
516
+ <div class="preset-pills">
517
+ <button class="preset-pill" onclick="setInfPreset('inf-rate',2)">Low 2%</button>
518
+ <button class="preset-pill active" onclick="setInfPreset('inf-rate',3.5)">Average 3.5%</button>
519
+ <button class="preset-pill" onclick="setInfPreset('inf-rate',6)">High 6%</button>
520
+ <button class="preset-pill" onclick="setInfPreset('inf-rate',10)">Crisis 10%</button>
521
+ </div>
522
+ <input type="range" id="inf-rate" min="0.5" max="15" value="3.5" step="0.5" oninput="calcInflation()">
523
+ </div>
524
+
525
+ <div class="slider-wrap">
526
+ <div class="slider-header"><span class="slider-label">📈 Investment Return Rate</span><span class="slider-val" id="inf-invest-val">7%</span></div>
527
+ <input type="range" id="inf-invest" min="0" max="20" value="7" step="0.5" oninput="calcInflation()">
528
+ <div class="text-muted text-sm" style="margin-top:4px">If you invest vs keep as cash</div>
529
+ </div>
530
+ </div>
531
+ </div>
532
+
533
+ <div>
534
+ <div class="inflation-impact">
535
+ <div class="inf-icon">💸</div>
536
+ <div class="inf-text">
537
+ <div class="inf-title">Purchasing Power Loss</div>
538
+ <div class="inf-desc">Your money <strong id="inf-today">$10,000</strong> today will only be worth <strong id="inf-future-worth" style="color:var(--rose)">—</strong> in real terms after <span id="inf-years-txt">20</span> years.</div>
539
+ </div>
540
+ </div>
541
+
542
+ <div class="result-hero">
543
+ <div class="result-label">Inflation-Adjusted Value</div>
544
+ <div class="result-value" id="inf-result" style="color:var(--rose)">$0</div>
545
+ <div class="result-sub">Real purchasing power loss: <strong id="inf-loss" style="color:var(--rose)">$0</strong></div>
546
+ </div>
547
+
548
+ <div class="result-breakdown">
549
+ <div class="rb-box">
550
+ <div class="rb-box-label">Nominal Value</div>
551
+ <div class="rb-box-val" id="inf-rb-nom">$0</div>
552
+ </div>
553
+ <div class="rb-box">
554
+ <div class="rb-box-label">Real Value</div>
555
+ <div class="rb-box-val" style="color:var(--rose)" id="inf-rb-real">$0</div>
556
+ </div>
557
+ <div class="rb-box">
558
+ <div class="rb-box-label">If Invested</div>
559
+ <div class="rb-box-val" style="color:var(--emerald)" id="inf-rb-inv">$0</div>
560
+ </div>
561
+ </div>
562
+
563
+ <div class="card" style="padding:16px">
564
+ <div class="card-title">📊 Cash vs Invested vs Inflation</div>
565
+ <div class="chart-panel">
566
+ <canvas id="infChart"></canvas>
567
+ </div>
568
+ </div>
569
+ </div>
570
+ </div>
571
+ </div>
572
+
573
+ </main>
574
+ </div>
575
+
576
+ <nav class="bottom-nav">
577
+ <div class="bottom-nav-inner">
578
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
579
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
580
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
581
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
582
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
583
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
584
+ </div>
585
+ </nav>
586
+
587
+ <script src="shared.js"></script>
588
+ <script>
589
+ // ══ Tab Switching ═════════════════════════════════════════════════
590
+ document.querySelectorAll('.calc-tab').forEach(btn => {
591
+ btn.addEventListener('click', () => {
592
+ document.querySelectorAll('.calc-tab,.calc-panel').forEach(el => el.classList.remove('active'));
593
+ btn.classList.add('active');
594
+ document.getElementById('calc-' + btn.dataset.calc).classList.add('active');
595
+ setTimeout(() => { calcGrowth(); calcRetirement(); calcSIP(); calcInflation(); }, 50);
596
+ });
597
+ });
598
+
599
+ // ══ Shared helpers ════════════════════════════════════════════════
600
+ let growthChart=null, retChart=null, sipChart=null, infChart=null;
601
+ let sipFreq = 'monthly'; // periods per year
602
+ const FREQ_MAP = { weekly:52, monthly:12, quarterly:4, annually:1 };
603
+ const FREQ_LABEL = { weekly:'wk', monthly:'mo', quarterly:'qtr', annually:'yr' };
604
+
605
+ function sliderSync(id, labelId, prefix='$', suffix='', scale=1, decimals=0) {
606
+ const el = document.getElementById(id);
607
+ const lbl = document.getElementById(labelId);
608
+ const update = () => {
609
+ const v = parseFloat(el.value) * scale;
610
+ lbl.textContent = prefix + (decimals ? v.toFixed(decimals) : Math.round(v).toLocaleString()) + suffix;
611
+ };
612
+ el.addEventListener('input', update);
613
+ }
614
+
615
+ // ══ CALCULATOR 1: Investment Growth ═══════════════════════════════
616
+ function setGrowthPreset(id, val, unit) {
617
+ document.getElementById(id).value = val;
618
+ calcGrowth();
619
+ }
620
+ function setSipPreset(id, val) { document.getElementById(id).value = val; calcSIP(); }
621
+ function setInfPreset(id, val) { document.getElementById(id).value = val; calcInflation(); }
622
+
623
+ function calcGrowth() {
624
+ const P = parseFloat(document.getElementById('g-init').value);
625
+ const n = parseInt(document.getElementById('g-years').value);
626
+ const r = parseFloat(document.getElementById('g-rate').value) / 100;
627
+ const c = parseInt(document.getElementById('g-compound').value);
628
+
629
+ document.getElementById('g-init-val').textContent = '$' + P.toLocaleString();
630
+ document.getElementById('g-years-val').textContent = n + ' year' + (n===1?'':'s');
631
+ const compoundLabels = {1:'Annually',2:'Semi-annually',3:'Tri-annually',4:'Quarterly',6:'Bi-monthly',12:'Monthly'};
632
+ document.getElementById('g-compound-val').textContent = compoundLabels[c] || c + 'x/yr';
633
+ document.getElementById('g-rate-val').textContent = r*100 + '%';
634
+
635
+ const FV = P * Math.pow(1 + r/c, c*n);
636
+ const profit = FV - P;
637
+ const pct = (profit/P)*100;
638
+
639
+ document.getElementById('g-future').textContent = fmt$(FV);
640
+ document.getElementById('g-profit').textContent = fmt$(profit);
641
+ document.getElementById('g-rb-invested').textContent = fmt$(P);
642
+ document.getElementById('g-rb-earn').textContent = fmt$(profit);
643
+ document.getElementById('g-rb-pct').textContent = '+' + pct.toFixed(1) + '%';
644
+
645
+ // Chart data
646
+ const years = Array.from({length:n+1},(_,i)=>i);
647
+ const vals = years.map(y => P * Math.pow(1+r/c, c*y));
648
+ const invested = years.map(() => P);
649
+
650
+ if (growthChart) growthChart.destroy();
651
+ const ctx = document.getElementById('growthChart').getContext('2d');
652
+ const grad = ctx.createLinearGradient(0,0,0,260);
653
+ grad.addColorStop(0,'rgba(16,185,129,0.35)'); grad.addColorStop(1,'rgba(16,185,129,0)');
654
+
655
+ growthChart = new Chart(ctx, {
656
+ type:'line',
657
+ data:{
658
+ labels: years.map(y => 'Yr '+y),
659
+ datasets:[
660
+ { label:'Portfolio Value', data:vals, borderColor:'#10b981', backgroundColor:grad, borderWidth:2.5, fill:true, tension:0.4, pointRadius:0 },
661
+ { label:'Amount Invested', data:invested, borderColor:'rgba(34,211,238,0.5)', borderDash:[5,5], borderWidth:1.5, fill:false, pointRadius:0, tension:0 }
662
+ ]
663
+ },
664
+ options:{ responsive:true, maintainAspectRatio:false,
665
+ plugins:{ legend:{ labels:{font:{size:11}} },
666
+ tooltip:{ callbacks:{ label: ctx => ' '+fmt$(ctx.raw) } } },
667
+ scales:{
668
+ x:{ grid:{display:false}, ticks:{maxTicksLimit:8,font:{size:11}} },
669
+ y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback:v=>'$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
670
+ }
671
+ }
672
+ });
673
+
674
+ // Milestones
675
+ const milestones = [
676
+ { icon:'💯', label:'2× Money', target:P*2 },
677
+ { icon:'🚀', label:'5× Money', target:P*5 },
678
+ { icon:'💎', label:'10× Money', target:P*10 },
679
+ { icon:'🌟', label:'$100K', target:100000 },
680
+ { icon:'🏆', label:'$250K', target:250000 },
681
+ { icon:'👑', label:'$1M', target:1000000 },
682
+ ];
683
+ document.getElementById('milestones-grid').innerHTML = milestones.map(m => {
684
+ const reached = FV >= m.target;
685
+ const yearsTo = reached ? Math.log(m.target/P) / (c * Math.log(1+r/c)) : null;
686
+ return `
687
+ <div class="milestone-card ${reached?'reached':''}">
688
+ <div class="milestone-icon">${m.icon}</div>
689
+ <div class="milestone-val">${m.label}</div>
690
+ <div class="milestone-lbl">${fmt$(m.target)}</div>
691
+ ${reached ? `<div class="milestone-yr">✅ ~Yr ${Math.round(yearsTo)}</div>` : `<div class="milestone-yr" style="color:var(--text3)">Not reached</div>`}
692
+ </div>`;
693
+ }).join('');
694
+ }
695
+
696
+ // ══ CALCULATOR 2: Retirement ══════════════════════════════════════
697
+ function calcRetirement() {
698
+ const age = parseInt(document.getElementById('ret-age').value) || 30;
699
+ const retAge = parseInt(document.getElementById('ret-age2').value) || 65;
700
+ const saved = parseFloat(document.getElementById('ret-saved').value);
701
+ const mo = parseFloat(document.getElementById('ret-monthly').value);
702
+ const r = parseFloat(document.getElementById('ret-rate').value)/100;
703
+ const need = parseFloat(document.getElementById('ret-need').value);
704
+
705
+ document.getElementById('ret-saved-val').textContent = '$'+saved.toLocaleString();
706
+ document.getElementById('ret-monthly-val').textContent = '$'+mo.toLocaleString()+'/mo';
707
+ document.getElementById('ret-rate-val').textContent = r*100 + '%';
708
+ document.getElementById('ret-need-val').textContent = '$'+need.toLocaleString()+'/mo';
709
+
710
+ const years = Math.max(retAge - age, 1);
711
+ const rMo = r/12;
712
+ const FV_existing = saved * Math.pow(1+r, years);
713
+ const FV_contrib = mo * ((Math.pow(1+rMo, years*12)-1)/rMo);
714
+ const total = FV_existing + FV_contrib;
715
+ const annualy = need * 12;
716
+ const fundDur = total > 0 ? Math.log(1 - (annualy * (1-(1+r/4))) / (total * (r/4))) / Math.log(1+r/4) * (-1) : 0;
717
+ const safeDrawdown = total * 0.04 / 12;
718
+ const onTrack = total >= annualy * 25;
719
+
720
+ document.getElementById('ret-future').textContent = fmt$(total);
721
+ document.getElementById('ret-rb-years').textContent = years + ' yrs';
722
+ document.getElementById('ret-rb-draw').textContent = '$' + Math.round(safeDrawdown).toLocaleString() + '/mo';
723
+ document.getElementById('ret-rb-dur').textContent = Math.round(Math.max(fundDur,0)) + ' yrs';
724
+
725
+ const box = document.getElementById('ret-on-track-box');
726
+ if (onTrack) {
727
+ box.style.cssText = 'padding:16px;border-radius:var(--r-sm);border:1px solid rgba(16,185,129,0.4);background:rgba(16,185,129,0.08)';
728
+ box.innerHTML = `<div style="font-weight:700;color:var(--emerald);margin-bottom:6px">✅ You're on track!</div>
729
+ <div style="font-size:13px;color:var(--text2)">Your projected nest egg supports $${Math.round(safeDrawdown).toLocaleString()}/mo using the 4% rule. That's more than your $${need.toLocaleString()}/mo target.</div>`;
730
+ } else {
731
+ const gap = annualy*25 - total;
732
+ box.style.cssText = 'padding:16px;border-radius:var(--r-sm);border:1px solid rgba(245,158,11,0.4);background:rgba(245,158,11,0.08)';
733
+ box.innerHTML = `<div style="font-weight:700;color:var(--amber);margin-bottom:6px">⚠️ Savings Gap Detected</div>
734
+ <div style="font-size:13px;color:var(--text2)">You're projected to be <strong style="color:var(--amber)">${fmt$(gap)}</strong> short of your retirement goal. Consider increasing contributions by $${Math.round(gap/(years*12)).toLocaleString()}/mo.</div>`;
735
+ }
736
+
737
+ document.getElementById('ret-status').textContent = onTrack ? '🎉 On track for retirement!' : `Need ${fmt$(annualy*25)} total`;
738
+
739
+ // Chart
740
+ const yrs = Array.from({length:years+1},(_,i)=>i);
741
+ const trajectory = yrs.map(y => {
742
+ const fe = saved * Math.pow(1+r,y);
743
+ const fc = mo * ((Math.pow(1+r/12, y*12)-1)/(r/12));
744
+ return fe+fc;
745
+ });
746
+
747
+ if (retChart) retChart.destroy();
748
+ const ctx = document.getElementById('retChart').getContext('2d');
749
+ const grad = ctx.createLinearGradient(0,0,0,260);
750
+ grad.addColorStop(0,'rgba(34,211,238,0.3)'); grad.addColorStop(1,'rgba(34,211,238,0)');
751
+
752
+ retChart = new Chart(ctx, {
753
+ type:'line',
754
+ data:{
755
+ labels: yrs.map(y => age+y),
756
+ datasets:[
757
+ { label:'Projected Savings', data:trajectory, borderColor:'#22d3ee', backgroundColor:grad, borderWidth:2.5, fill:true, tension:0.4, pointRadius:0 },
758
+ { label:'Target', data:yrs.map(()=>annualy*25), borderColor:'rgba(245,158,11,0.6)', borderDash:[6,4], borderWidth:1.5, fill:false, pointRadius:0 }
759
+ ]
760
+ },
761
+ options:{ responsive:true, maintainAspectRatio:false,
762
+ plugins:{ legend:{labels:{font:{size:11}}}, tooltip:{ callbacks:{ label:ctx=>' '+fmt$(ctx.raw) } } },
763
+ scales:{
764
+ x:{ grid:{display:false}, ticks:{maxTicksLimit:8,font:{size:11}} },
765
+ y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback:v=>'$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
766
+ }
767
+ }
768
+ });
769
+ }
770
+
771
+ // ══ CALCULATOR 3: SIP ═════════════════════════════════════════════
772
+ function setSipFreq(freq, btn) {
773
+ sipFreq = freq;
774
+ document.querySelectorAll('.freq-btn').forEach(b=>b.classList.remove('active'));
775
+ btn.classList.add('active');
776
+ calcSIP();
777
+ }
778
+
779
+ function calcSIP() {
780
+ const amount = parseFloat(document.getElementById('sip-amount').value);
781
+ const years = parseInt(document.getElementById('sip-years').value);
782
+ const rate = parseFloat(document.getElementById('sip-rate').value)/100;
783
+ const stepup = parseFloat(document.getElementById('sip-stepup').value)/100;
784
+ const n = FREQ_MAP[sipFreq];
785
+ const lbl = FREQ_LABEL[sipFreq];
786
+
787
+ document.getElementById('sip-amount-val').textContent = '$'+amount.toLocaleString()+'/'+lbl;
788
+ document.getElementById('sip-years-val').textContent = years + ' year' + (years===1?'':'s');
789
+ document.getElementById('sip-rate-val').textContent = rate*100 + '%';
790
+ document.getElementById('sip-stepup-val').textContent = (stepup*100).toFixed(0) + '%';
791
+
792
+ // Step-up SIP calculation
793
+ let FV = 0, totalInvested = 0;
794
+ let currentAmount = amount;
795
+ const yearlyVals = [], yearlyInv = [];
796
+
797
+ for (let y=1; y<=years; y++) {
798
+ if (y>1 && stepup>0) currentAmount *= (1+stepup);
799
+ for (let p=0; p<n; p++) {
800
+ FV = (FV + currentAmount) * (1 + rate/n);
801
+ totalInvested += currentAmount;
802
+ }
803
+ yearlyVals.push(FV);
804
+ yearlyInv.push(totalInvested);
805
+ }
806
+
807
+ const gain = FV - totalInvested;
808
+ const gainPct = (gain/totalInvested)*100;
809
+
810
+ document.getElementById('sip-future').textContent = fmt$(FV);
811
+ document.getElementById('sip-invested').textContent = fmt$(totalInvested);
812
+ document.getElementById('sip-rb-inv').textContent = fmt$(totalInvested);
813
+ document.getElementById('sip-rb-gain').textContent = fmt$(gain);
814
+ document.getElementById('sip-rb-pct').textContent = '+'+gainPct.toFixed(1)+'%';
815
+ document.getElementById('sip-no-invest').textContent = fmt$(totalInvested);
816
+ document.getElementById('sip-with-invest').textContent = fmt$(FV);
817
+
818
+ if (sipChart) sipChart.destroy();
819
+ const ctx = document.getElementById('sipChart').getContext('2d');
820
+ const labels = Array.from({length:years},(_,i)=>'Yr '+(i+1));
821
+
822
+ sipChart = new Chart(ctx, {
823
+ type:'bar',
824
+ data:{
825
+ labels,
826
+ datasets:[
827
+ { label:'Total Invested', data:yearlyInv, backgroundColor:'rgba(34,211,238,0.4)', borderRadius:4, order:2 },
828
+ { label:'Portfolio Value', data:yearlyVals, type:'line', borderColor:'#10b981', backgroundColor:'rgba(16,185,129,0.15)', borderWidth:2.5, fill:true, tension:0.4, pointRadius:0, order:1 }
829
+ ]
830
+ },
831
+ options:{ responsive:true, maintainAspectRatio:false,
832
+ plugins:{ legend:{labels:{font:{size:11}}}, tooltip:{ callbacks:{ label:ctx=>' '+fmt$(ctx.raw) } } },
833
+ scales:{
834
+ x:{ grid:{display:false}, stacked:false, ticks:{maxTicksLimit:8,font:{size:11}} },
835
+ y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback:v=>'$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
836
+ }
837
+ }
838
+ });
839
+ }
840
+
841
+ // ══ CALCULATOR 4: Inflation ════���══════════════════════════════════
842
+ function calcInflation() {
843
+ const amount = parseFloat(document.getElementById('inf-amount').value);
844
+ const years = parseInt(document.getElementById('inf-years').value);
845
+ const infRate = parseFloat(document.getElementById('inf-rate').value)/100;
846
+ const invRate = parseFloat(document.getElementById('inf-invest').value)/100;
847
+
848
+ document.getElementById('inf-amount-val').textContent = '$'+amount.toLocaleString();
849
+ document.getElementById('inf-years-val').textContent = years+' year'+(years===1?'':'s');
850
+ document.getElementById('inf-rate-val').textContent = (infRate*100).toFixed(1)+'%';
851
+ document.getElementById('inf-invest-val').textContent = (invRate*100).toFixed(1)+'%';
852
+
853
+ const realValue = amount / Math.pow(1+infRate, years);
854
+ const nomValue = amount; // cash under mattress, nominal stays same
855
+ const invValue = amount * Math.pow(1+invRate, years);
856
+ const loss = amount - realValue;
857
+
858
+ document.getElementById('inf-today').textContent = fmt$(amount);
859
+ document.getElementById('inf-future-worth').textContent = fmt$(realValue);
860
+ document.getElementById('inf-years-txt').textContent = years;
861
+ document.getElementById('inf-result').textContent = fmt$(realValue);
862
+ document.getElementById('inf-loss').textContent = fmt$(loss);
863
+ document.getElementById('inf-rb-nom').textContent = fmt$(nomValue);
864
+ document.getElementById('inf-rb-real').textContent = fmt$(realValue);
865
+ document.getElementById('inf-rb-inv').textContent = fmt$(invValue);
866
+
867
+ // Chart
868
+ const yrs = Array.from({length:years+1},(_,i)=>i);
869
+ const cash = yrs.map(()=>amount);
870
+ const real = yrs.map(y => amount/Math.pow(1+infRate,y));
871
+ const inv = yrs.map(y => amount*Math.pow(1+invRate,y));
872
+
873
+ if (infChart) infChart.destroy();
874
+ const ctx = document.getElementById('infChart').getContext('2d');
875
+
876
+ infChart = new Chart(ctx, {
877
+ type:'line',
878
+ data:{
879
+ labels: yrs.map(y=>'Yr '+y),
880
+ datasets:[
881
+ { label:'Invested', data:inv, borderColor:'#10b981', backgroundColor:'rgba(16,185,129,0.1)', borderWidth:2.5, fill:true, tension:0.4, pointRadius:0 },
882
+ { label:'Cash (nominal)', data:cash, borderColor:'rgba(34,211,238,0.5)', borderDash:[5,5], borderWidth:1.5, fill:false, pointRadius:0 },
883
+ { label:'Real Value (after inflation)', data:real, borderColor:'#f43f5e', backgroundColor:'rgba(244,63,94,0.1)', borderWidth:2, fill:true, tension:0.4, pointRadius:0 }
884
+ ]
885
+ },
886
+ options:{ responsive:true, maintainAspectRatio:false,
887
+ plugins:{ legend:{labels:{font:{size:11}}}, tooltip:{ callbacks:{ label:ctx=>' '+fmt$(ctx.raw) } } },
888
+ scales:{
889
+ x:{ grid:{display:false}, ticks:{maxTicksLimit:8,font:{size:11}} },
890
+ y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback:v=>'$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
891
+ }
892
+ }
893
+ });
894
+ }
895
+
896
+ document.addEventListener('DOMContentLoaded', () => {
897
+ applyChartDefaults();
898
+ calcGrowth();
899
+ calcRetirement();
900
+ calcSIP();
901
+ calcInflation();
902
+ });
903
+ </script>
904
+ </body>
905
+ </html>
index.html ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — Dashboard</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
+ <style>
10
+ .hero-banner {
11
+ background: linear-gradient(135deg, rgba(34,211,238,0.08) 0%, rgba(16,185,129,0.05) 100%);
12
+ border: 1px solid var(--border2);
13
+ border-radius: var(--r-lg);
14
+ padding: 28px 32px;
15
+ margin-bottom: 24px;
16
+ display: flex;
17
+ align-items: center;
18
+ justify-content: space-between;
19
+ gap: 20px;
20
+ position: relative;
21
+ overflow: hidden;
22
+ }
23
+ .hero-banner::before {
24
+ content: '';
25
+ position: absolute;
26
+ top: -40px; right: -40px;
27
+ width: 200px; height: 200px;
28
+ border-radius: 50%;
29
+ background: radial-gradient(circle, rgba(34,211,238,0.12) 0%, transparent 70%);
30
+ pointer-events: none;
31
+ }
32
+ .hero-greeting { font-family: var(--font-head); font-size: 13px; color: var(--text2); text-transform: uppercase; letter-spacing: 0.1em; }
33
+ .hero-amount { font-family: var(--font-head); font-size: 42px; font-weight: 800; line-height: 1; margin: 8px 0; }
34
+ .hero-sub { font-size: 14px; color: var(--text2); }
35
+ .hero-actions { display: flex; gap: 12px; flex-wrap: wrap; }
36
+ .portfolio-value-change { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
37
+
38
+ .quick-actions {
39
+ display: grid;
40
+ grid-template-columns: repeat(4, 1fr);
41
+ gap: 12px;
42
+ margin-bottom: 24px;
43
+ }
44
+ .quick-action-card {
45
+ background: var(--card);
46
+ border: 1px solid var(--border);
47
+ border-radius: var(--r);
48
+ padding: 18px;
49
+ text-align: center;
50
+ cursor: pointer;
51
+ text-decoration: none;
52
+ transition: all var(--transition);
53
+ display: block;
54
+ }
55
+ .quick-action-card:hover {
56
+ border-color: var(--border2);
57
+ transform: translateY(-3px);
58
+ box-shadow: var(--glow-c);
59
+ }
60
+ .qa-icon { font-size: 28px; margin-bottom: 8px; }
61
+ .qa-label { font-size: 12px; font-weight: 700; color: var(--text2); text-transform: uppercase; letter-spacing: 0.06em; }
62
+
63
+ .chart-card { height: 100%; }
64
+ .chart-container { position: relative; height: 240px; }
65
+
66
+ .goal-item {
67
+ margin-bottom: 16px;
68
+ }
69
+ .goal-header {
70
+ display: flex;
71
+ justify-content: space-between;
72
+ margin-bottom: 6px;
73
+ font-size: 13px;
74
+ }
75
+ .goal-name { font-weight: 600; }
76
+ .goal-pct { color: var(--cyan); font-family: var(--font-mono); }
77
+
78
+ .activity-item {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 14px;
82
+ padding: 12px 0;
83
+ border-bottom: 1px solid rgba(34,211,238,0.05);
84
+ }
85
+ .activity-item:last-child { border-bottom: none; }
86
+ .activity-icon {
87
+ width: 38px; height: 38px;
88
+ border-radius: 10px;
89
+ display: flex; align-items: center; justify-content: center;
90
+ font-size: 16px;
91
+ flex-shrink: 0;
92
+ }
93
+ .activity-info { flex: 1; }
94
+ .activity-name { font-weight: 600; font-size: 14px; }
95
+ .activity-date { font-size: 12px; color: var(--text2); margin-top: 2px; }
96
+ .activity-amount { font-family: var(--font-mono); font-size: 14px; font-weight: 600; }
97
+
98
+ .market-overview {
99
+ display: grid;
100
+ grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
101
+ gap: 10px;
102
+ }
103
+ .market-item {
104
+ background: var(--bg3);
105
+ border-radius: var(--r-sm);
106
+ padding: 12px;
107
+ text-align: center;
108
+ }
109
+ .market-sym { font-size: 11px; font-weight: 700; color: var(--text2); margin-bottom: 4px; }
110
+ .market-val { font-family: var(--font-mono); font-size: 14px; font-weight: 600; }
111
+ .market-chg { font-size: 11px; margin-top: 2px; font-weight: 600; }
112
+
113
+ .allocation-legend { margin-top: 12px; }
114
+ .legend-item {
115
+ display: flex; align-items: center; gap: 10px;
116
+ padding: 6px 0;
117
+ font-size: 13px;
118
+ }
119
+ .legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
120
+ .legend-name { flex: 1; color: var(--text2); }
121
+ .legend-pct { font-family: var(--font-mono); font-size: 12px; color: var(--text); font-weight: 600; }
122
+ </style>
123
+ </head>
124
+ <body>
125
+ <div class="app-shell">
126
+
127
+ <!-- Sidebar -->
128
+ <nav class="sidebar">
129
+ <div class="sidebar-logo">
130
+ <div class="logo-mark">
131
+ <div class="logo-icon">📈</div>
132
+ <div>
133
+ <div class="logo-text">FinWise</div>
134
+ <div class="logo-sub">Smart Investing</div>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ <div class="nav-section">
139
+ <div class="nav-label">Main</div>
140
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
141
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
142
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
143
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
144
+ <div class="nav-label">Tools</div>
145
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
146
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights <span class="nav-badge">New</span></a>
147
+ </div>
148
+ <div class="sidebar-footer">
149
+ <div class="market-ticker">Live Market</div>
150
+ <div id="sidebar-tickers"></div>
151
+ </div>
152
+ </nav>
153
+
154
+ <!-- Main -->
155
+ <main class="main-content">
156
+
157
+ <!-- Hero Banner -->
158
+ <div class="hero-banner fade-in">
159
+ <div>
160
+ <div class="hero-greeting">👋 Good morning, Investor</div>
161
+ <div class="hero-amount" id="hero-amount">$0.00</div>
162
+ <div class="portfolio-value-change">
163
+ <span class="badge badge-emerald" id="hero-badge">▲ +$0.00 today</span>
164
+ <span class="text-muted text-sm">Total portfolio value</span>
165
+ </div>
166
+ <div class="hero-sub" style="margin-top:10px;">Your portfolio is performing <strong style="color:var(--cyan)">above average</strong> this month 🚀</div>
167
+ </div>
168
+ <div class="hero-actions">
169
+ <a href="portfolio.html" class="btn btn-primary">🔧 Build Portfolio</a>
170
+ <a href="tracker.html" class="btn btn-secondary">📊 View Tracker</a>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Stat Grid -->
175
+ <div class="stat-grid fade-in fade-in-1">
176
+ <div class="stat-card" style="--accent-color: var(--cyan)">
177
+ <div class="stat-label">Total Invested</div>
178
+ <div class="stat-value" id="stat-invested">$0</div>
179
+ <div class="stat-change up">📅 Since inception</div>
180
+ </div>
181
+ <div class="stat-card" style="--accent-color: var(--emerald)">
182
+ <div class="stat-label">Total Gain/Loss</div>
183
+ <div class="stat-value up" id="stat-gain">+$0</div>
184
+ <div class="stat-change up" id="stat-gain-pct">▲ 0.00%</div>
185
+ </div>
186
+ <div class="stat-card" style="--accent-color: var(--violet)">
187
+ <div class="stat-label">Risk Score</div>
188
+ <div class="stat-value" id="stat-risk">—</div>
189
+ <div class="stat-change" id="stat-risk-label">Moderate</div>
190
+ </div>
191
+ <div class="stat-card" style="--accent-color: var(--amber)">
192
+ <div class="stat-label">Diversification</div>
193
+ <div class="stat-value" id="stat-div">—</div>
194
+ <div class="stat-change up">▲ Well balanced</div>
195
+ </div>
196
+ </div>
197
+
198
+ <!-- Quick Actions -->
199
+ <div class="quick-actions fade-in fade-in-2">
200
+ <a href="portfolio.html" class="quick-action-card"><div class="qa-icon">🏗️</div><div class="qa-label">Build Portfolio</div></a>
201
+ <a href="risk.html" class="quick-action-card"><div class="qa-icon">🎯</div><div class="qa-label">Risk Check</div></a>
202
+ <a href="calculators.html" class="quick-action-card"><div class="qa-icon">🧮</div><div class="qa-label">Calculators</div></a>
203
+ <a href="insights.html" class="quick-action-card"><div class="qa-icon">💡</div><div class="qa-label">AI Insights</div></a>
204
+ </div>
205
+
206
+ <!-- Charts Row -->
207
+ <div class="grid-60-40 fade-in fade-in-3" style="margin-bottom:20px">
208
+ <div class="card chart-card">
209
+ <div class="card-title">📈 Portfolio Performance <span class="badge badge-emerald" style="margin-left:auto">+12.4% YTD</span></div>
210
+ <div class="chart-container">
211
+ <canvas id="performanceChart"></canvas>
212
+ </div>
213
+ </div>
214
+ <div class="card">
215
+ <div class="card-title">🥧 Allocation</div>
216
+ <div style="position:relative;height:180px">
217
+ <canvas id="allocChart"></canvas>
218
+ </div>
219
+ <div class="allocation-legend" id="alloc-legend"></div>
220
+ </div>
221
+ </div>
222
+
223
+ <!-- Bottom Row -->
224
+ <div class="grid-2 fade-in fade-in-4">
225
+ <!-- Goals -->
226
+ <div class="card">
227
+ <div class="card-title">🎯 Goal Progress</div>
228
+ <div id="goals-list">
229
+ <div class="goal-item">
230
+ <div class="goal-header"><span class="goal-name">🏖️ Retirement Fund</span><span class="goal-pct">34%</span></div>
231
+ <div class="progress-bar"><div class="progress-fill" style="width:34%;background:linear-gradient(90deg,var(--cyan),var(--emerald))"></div></div>
232
+ <div class="text-sm text-muted" style="margin-top:4px">$34,000 / $100,000 target by 2045</div>
233
+ </div>
234
+ <div class="goal-item">
235
+ <div class="goal-header"><span class="goal-name">🏠 Down Payment</span><span class="goal-pct">61%</span></div>
236
+ <div class="progress-bar"><div class="progress-fill" style="width:61%;background:linear-gradient(90deg,var(--violet),var(--cyan))"></div></div>
237
+ <div class="text-sm text-muted" style="margin-top:4px">$30,500 / $50,000 target by 2027</div>
238
+ </div>
239
+ <div class="goal-item">
240
+ <div class="goal-header"><span class="goal-name">📚 Education Fund</span><span class="goal-pct">18%</span></div>
241
+ <div class="progress-bar"><div class="progress-fill" style="width:18%;background:linear-gradient(90deg,var(--amber),var(--rose))"></div></div>
242
+ <div class="text-sm text-muted" style="margin-top:4px">$3,600 / $20,000 target by 2030</div>
243
+ </div>
244
+ </div>
245
+ <a href="calculators.html" class="btn btn-secondary btn-sm btn-full" style="margin-top:16px">Plan a new goal →</a>
246
+ </div>
247
+
248
+ <!-- Recent Activity -->
249
+ <div class="card">
250
+ <div class="card-title">⚡ Recent Activity</div>
251
+ <div id="activity-list">
252
+ <div class="activity-item">
253
+ <div class="activity-icon" style="background:rgba(16,185,129,0.15)">💰</div>
254
+ <div class="activity-info"><div class="activity-name">Bought VOO</div><div class="activity-date">May 1, 2026</div></div>
255
+ <div class="activity-amount up">+3 shares</div>
256
+ </div>
257
+ <div class="activity-item">
258
+ <div class="activity-icon" style="background:rgba(34,211,238,0.15)">📥</div>
259
+ <div class="activity-info"><div class="activity-name">Dividend — AAPL</div><div class="activity-date">Apr 28, 2026</div></div>
260
+ <div class="activity-amount up">+$12.40</div>
261
+ </div>
262
+ <div class="activity-item">
263
+ <div class="activity-icon" style="background:rgba(139,92,246,0.15)">🔄</div>
264
+ <div class="activity-info"><div class="activity-name">Rebalanced Portfolio</div><div class="activity-date">Apr 20, 2026</div></div>
265
+ <div class="activity-amount" style="color:var(--text2)">Optimized</div>
266
+ </div>
267
+ <div class="activity-item">
268
+ <div class="activity-icon" style="background:rgba(245,158,11,0.15)">💹</div>
269
+ <div class="activity-info"><div class="activity-name">Added NVDA</div><div class="activity-date">Apr 15, 2026</div></div>
270
+ <div class="activity-amount up">+1 share</div>
271
+ </div>
272
+ </div>
273
+ <a href="tracker.html" class="btn btn-secondary btn-sm btn-full" style="margin-top:16px">Full history →</a>
274
+ </div>
275
+ </div>
276
+
277
+ <!-- Market Overview -->
278
+ <div class="card fade-in" style="margin-top:20px">
279
+ <div class="card-title">🌐 Market Overview</div>
280
+ <div class="market-overview">
281
+ <div class="market-item"><div class="market-sym">S&P 500</div><div class="market-val">5,308</div><div class="market-chg up">▲ +0.26%</div></div>
282
+ <div class="market-item"><div class="market-sym">NASDAQ</div><div class="market-val">16,742</div><div class="market-chg down">▼ -0.46%</div></div>
283
+ <div class="market-item"><div class="market-sym">DOW</div><div class="market-val">39,512</div><div class="market-chg up">▲ +0.18%</div></div>
284
+ <div class="market-item"><div class="market-sym">BTC</div><div class="market-val">$68,420</div><div class="market-chg up">▲ +2.14%</div></div>
285
+ <div class="market-item"><div class="market-sym">GOLD</div><div class="market-val">$2,318</div><div class="market-chg up">▲ +1.49%</div></div>
286
+ <div class="market-item"><div class="market-sym">10Y BOND</div><div class="market-val">4.48%</div><div class="market-chg down">▼ -0.03%</div></div>
287
+ <div class="market-item"><div class="market-sym">USD/EUR</div><div class="market-val">0.924</div><div class="market-chg down">▼ -0.12%</div></div>
288
+ <div class="market-item"><div class="market-sym">VIX</div><div class="market-val">14.82</div><div class="market-chg up">▲ +3.2%</div></div>
289
+ </div>
290
+ </div>
291
+
292
+ </main>
293
+ </div>
294
+
295
+ <!-- Mobile Bottom Nav -->
296
+ <nav class="bottom-nav">
297
+ <div class="bottom-nav-inner">
298
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
299
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
300
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
301
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
302
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
303
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
304
+ </div>
305
+ </nav>
306
+
307
+ <script src="shared.js"></script>
308
+ <script>
309
+ document.addEventListener('DOMContentLoaded', () => {
310
+ const portfolio = getPortfolio();
311
+ const currentValue = portfolio.assets.reduce((s,a) => s + a.shares * a.price, 0);
312
+ const invested = portfolio.totalInvested;
313
+ const gain = currentValue - invested;
314
+ const gainPct = (gain / invested) * 100;
315
+ const todayChange = currentValue * 0.0026;
316
+
317
+ // Hero
318
+ animateCounter(document.getElementById('hero-amount'), currentValue, '$');
319
+ document.getElementById('hero-badge').textContent = `▲ +$${todayChange.toFixed(2)} today`;
320
+
321
+ // Stats
322
+ document.getElementById('stat-invested').textContent = fmtK(invested);
323
+ const gainEl = document.getElementById('stat-gain');
324
+ gainEl.textContent = (gain >= 0 ? '+' : '') + fmt$(gain);
325
+ gainEl.className = 'stat-value ' + (gain >= 0 ? 'up' : 'down');
326
+ document.getElementById('stat-gain-pct').textContent = (gainPct >= 0 ? '▲ +' : '▼ ') + gainPct.toFixed(2) + '%';
327
+ document.getElementById('stat-gain-pct').className = 'stat-change ' + (gain >= 0 ? 'up' : 'down');
328
+
329
+ const riskScore = calcRiskScore(portfolio);
330
+ const divScore = calcDiversification(portfolio);
331
+ document.getElementById('stat-risk').textContent = riskScore + '/100';
332
+ const riskLabels = ['Very Low','Low','Moderate','Moderate-High','High'];
333
+ const riskLabel = riskLabels[Math.floor(riskScore / 25)];
334
+ document.getElementById('stat-risk-label').textContent = '→ ' + riskLabel;
335
+ document.getElementById('stat-div').textContent = divScore + '/100';
336
+
337
+ // Performance Chart
338
+ const history = generateHistory(90, invested * 0.88);
339
+ const perfCtx = document.getElementById('performanceChart').getContext('2d');
340
+ const grad = perfCtx.createLinearGradient(0, 0, 0, 240);
341
+ grad.addColorStop(0, 'rgba(34,211,238,0.25)');
342
+ grad.addColorStop(1, 'rgba(34,211,238,0)');
343
+
344
+ new Chart(perfCtx, {
345
+ type: 'line',
346
+ data: {
347
+ labels: history.filter((_,i) => i % 6 === 0).map(d => d.date),
348
+ datasets: [{
349
+ label: 'Portfolio Value',
350
+ data: history.filter((_,i) => i % 6 === 0).map(d => d.value),
351
+ borderColor: '#22d3ee',
352
+ backgroundColor: grad,
353
+ borderWidth: 2.5,
354
+ fill: true,
355
+ tension: 0.45,
356
+ pointRadius: 0,
357
+ pointHoverRadius: 5,
358
+ pointHoverBackgroundColor: '#22d3ee',
359
+ }]
360
+ },
361
+ options: {
362
+ responsive: true, maintainAspectRatio: false,
363
+ plugins: { legend: { display: false }, tooltip: {
364
+ callbacks: { label: ctx => ' $' + ctx.raw.toLocaleString('en-US', {maximumFractionDigits:0}) }
365
+ }},
366
+ scales: {
367
+ x: { grid: { display: false }, ticks: { maxTicksLimit: 6, font: { size: 11 } } },
368
+ y: { grid: { color: 'rgba(34,211,238,0.06)' }, ticks: {
369
+ callback: v => '$' + (v/1000).toFixed(0) + 'K', font: { size: 11 }
370
+ }}
371
+ },
372
+ interaction: { intersect: false, mode: 'index' }
373
+ }
374
+ });
375
+
376
+ // Allocation Donut
377
+ const allocCtx = document.getElementById('allocChart').getContext('2d');
378
+ new Chart(allocCtx, {
379
+ type: 'doughnut',
380
+ data: {
381
+ labels: portfolio.assets.map(a => a.ticker),
382
+ datasets: [{
383
+ data: portfolio.assets.map(a => a.pct),
384
+ backgroundColor: portfolio.assets.map(a => a.color),
385
+ borderColor: 'rgba(5,13,26,0.8)',
386
+ borderWidth: 3,
387
+ hoverOffset: 8
388
+ }]
389
+ },
390
+ options: {
391
+ responsive: true, maintainAspectRatio: false,
392
+ cutout: '70%',
393
+ plugins: { legend: { display: false } }
394
+ }
395
+ });
396
+
397
+ // Legend
398
+ const legendEl = document.getElementById('alloc-legend');
399
+ legendEl.innerHTML = portfolio.assets.slice(0,5).map(a => `
400
+ <div class="legend-item">
401
+ <div class="legend-dot" style="background:${a.color}"></div>
402
+ <span class="legend-name">${a.ticker}</span>
403
+ <span class="legend-pct">${a.pct}%</span>
404
+ </div>
405
+ `).join('') + (portfolio.assets.length > 5 ? `<div class="legend-item"><span class="legend-name text-muted">+ ${portfolio.assets.length-5} more</span></div>` : '');
406
+ });
407
+ </script>
408
+ </body>
409
+ </html>
insights.html ADDED
@@ -0,0 +1,860 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — AI Insights</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <style>
9
+ /* ── AI Chat Interface ──────────────────────────────────────────── */
10
+ .ai-chat-wrap {
11
+ display: flex;
12
+ flex-direction: column;
13
+ height: 420px;
14
+ background: var(--bg2);
15
+ border: 1px solid var(--border);
16
+ border-radius: var(--r);
17
+ overflow: hidden;
18
+ }
19
+ .chat-messages {
20
+ flex: 1;
21
+ overflow-y: auto;
22
+ padding: 20px;
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: 14px;
26
+ }
27
+ .chat-message {
28
+ display: flex;
29
+ gap: 10px;
30
+ max-width: 90%;
31
+ animation: fadeInUp 0.3s ease;
32
+ }
33
+ .chat-message.user {
34
+ margin-left: auto;
35
+ flex-direction: row-reverse;
36
+ }
37
+ .chat-avatar {
38
+ width: 32px; height: 32px;
39
+ border-radius: 50%;
40
+ display: flex; align-items: center; justify-content: center;
41
+ font-size: 16px;
42
+ flex-shrink: 0;
43
+ align-self: flex-end;
44
+ }
45
+ .chat-avatar.ai-av { background: linear-gradient(135deg, var(--cyan), var(--emerald)); }
46
+ .chat-avatar.user-av { background: linear-gradient(135deg, var(--violet), var(--indigo)); }
47
+ .chat-bubble {
48
+ padding: 12px 16px;
49
+ border-radius: 14px;
50
+ font-size: 14px;
51
+ line-height: 1.6;
52
+ max-width: 100%;
53
+ }
54
+ .chat-bubble.ai { background: var(--card); border: 1px solid var(--border); border-bottom-left-radius: 4px; }
55
+ .chat-bubble.user { background: linear-gradient(135deg,var(--cyan-d),var(--cyan)); color: var(--bg); border-bottom-right-radius: 4px; }
56
+ .chat-bubble.ai strong { color: var(--cyan); }
57
+ .chat-bubble.ai em { color: var(--emerald); font-style: normal; font-weight: 600; }
58
+ .chat-bubble ul { padding-left: 18px; margin-top: 6px; }
59
+ .chat-bubble li { margin-bottom: 4px; }
60
+
61
+ .chat-typing {
62
+ display: flex;
63
+ gap: 4px;
64
+ padding: 14px 16px;
65
+ background: var(--card);
66
+ border: 1px solid var(--border);
67
+ border-radius: 14px;
68
+ border-bottom-left-radius: 4px;
69
+ width: fit-content;
70
+ }
71
+ .chat-typing span {
72
+ width: 7px; height: 7px;
73
+ border-radius: 50%;
74
+ background: var(--cyan);
75
+ animation: typing 1.2s infinite;
76
+ }
77
+ .chat-typing span:nth-child(2) { animation-delay: 0.2s; }
78
+ .chat-typing span:nth-child(3) { animation-delay: 0.4s; }
79
+ @keyframes typing { 0%,100%{opacity:0.3;transform:scale(0.8)} 50%{opacity:1;transform:scale(1)} }
80
+
81
+ .chat-input-bar {
82
+ border-top: 1px solid var(--border);
83
+ padding: 12px 16px;
84
+ display: flex;
85
+ gap: 10px;
86
+ background: var(--bg2);
87
+ }
88
+ .chat-input {
89
+ flex: 1;
90
+ background: var(--bg3);
91
+ border: 1px solid var(--border);
92
+ border-radius: 100px;
93
+ padding: 10px 18px;
94
+ color: var(--text);
95
+ font-family: var(--font-body);
96
+ font-size: 14px;
97
+ outline: none;
98
+ transition: border-color var(--transition);
99
+ width: auto;
100
+ }
101
+ .chat-input:focus { border-color: var(--cyan); }
102
+ .chat-send-btn {
103
+ width: 42px; height: 42px;
104
+ border-radius: 50%;
105
+ background: linear-gradient(135deg, var(--cyan-d), var(--cyan));
106
+ border: none;
107
+ cursor: pointer;
108
+ display: flex; align-items: center; justify-content: center;
109
+ font-size: 18px;
110
+ transition: all var(--transition);
111
+ flex-shrink: 0;
112
+ }
113
+ .chat-send-btn:hover { box-shadow: 0 0 16px rgba(34,211,238,0.5); transform: scale(1.05); }
114
+ .chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
115
+
116
+ .quick-prompts {
117
+ display: flex;
118
+ gap: 8px;
119
+ flex-wrap: wrap;
120
+ padding: 10px 16px;
121
+ border-top: 1px solid var(--border);
122
+ background: var(--bg2);
123
+ }
124
+ .quick-prompt {
125
+ padding: 6px 12px;
126
+ border-radius: 100px;
127
+ border: 1px solid var(--border2);
128
+ background: transparent;
129
+ color: var(--text2);
130
+ font-size: 12px;
131
+ font-weight: 600;
132
+ cursor: pointer;
133
+ font-family: var(--font-body);
134
+ transition: all var(--transition);
135
+ white-space: nowrap;
136
+ }
137
+ .quick-prompt:hover { border-color: var(--cyan); color: var(--cyan); background: rgba(34,211,238,0.06); }
138
+
139
+ /* ── Concept Cards ──────────────────────────────────────────────── */
140
+ .concept-grid {
141
+ display: grid;
142
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
143
+ gap: 16px;
144
+ }
145
+ .concept-card {
146
+ background: var(--card);
147
+ border: 1px solid var(--border);
148
+ border-radius: var(--r);
149
+ padding: 20px;
150
+ transition: all var(--transition);
151
+ cursor: pointer;
152
+ position: relative;
153
+ overflow: hidden;
154
+ }
155
+ .concept-card::before {
156
+ content: '';
157
+ position: absolute;
158
+ top: 0; left: 0; right: 0;
159
+ height: 2px;
160
+ background: var(--concept-color, var(--cyan));
161
+ }
162
+ .concept-card:hover {
163
+ border-color: var(--border2);
164
+ transform: translateY(-3px);
165
+ box-shadow: 0 8px 30px rgba(0,0,0,0.4);
166
+ }
167
+ .concept-icon { font-size: 32px; margin-bottom: 10px; }
168
+ .concept-title { font-family: var(--font-head); font-size: 16px; font-weight: 700; margin-bottom: 6px; }
169
+ .concept-desc { font-size: 13px; color: var(--text2); line-height: 1.5; }
170
+ .concept-tag { margin-top: 12px; }
171
+
172
+ /* ── Do's & Don'ts ──────────────────────────────────────────────── */
173
+ .dos-donts {
174
+ display: grid;
175
+ grid-template-columns: 1fr 1fr;
176
+ gap: 20px;
177
+ }
178
+ .do-card {
179
+ background: rgba(16,185,129,0.05);
180
+ border: 1px solid rgba(16,185,129,0.2);
181
+ border-radius: var(--r);
182
+ padding: 20px;
183
+ }
184
+ .dont-card {
185
+ background: rgba(244,63,94,0.05);
186
+ border: 1px solid rgba(244,63,94,0.2);
187
+ border-radius: var(--r);
188
+ padding: 20px;
189
+ }
190
+ .do-header { font-family: var(--font-head); font-size: 16px; font-weight: 800; color: var(--emerald); margin-bottom: 14px; }
191
+ .dont-header { font-family: var(--font-head); font-size: 16px; font-weight: 800; color: var(--rose); margin-bottom: 14px; }
192
+ .tip-item {
193
+ display: flex;
194
+ gap: 10px;
195
+ margin-bottom: 12px;
196
+ font-size: 13px;
197
+ line-height: 1.5;
198
+ }
199
+ .tip-icon { flex-shrink: 0; font-size: 16px; margin-top: 1px; }
200
+
201
+ /* ── Insight Cards ──────────────────────────────────────────────── */
202
+ .insight-feed { display: flex; flex-direction: column; gap: 14px; }
203
+ .insight-item {
204
+ display: flex;
205
+ gap: 14px;
206
+ padding: 16px;
207
+ background: var(--card);
208
+ border: 1px solid var(--border);
209
+ border-radius: var(--r-sm);
210
+ transition: all var(--transition);
211
+ }
212
+ .insight-item:hover { border-color: var(--border2); }
213
+ .insight-category {
214
+ display: inline-flex;
215
+ align-items: center;
216
+ gap: 5px;
217
+ font-size: 10px;
218
+ font-weight: 800;
219
+ text-transform: uppercase;
220
+ letter-spacing: 0.1em;
221
+ margin-bottom: 6px;
222
+ }
223
+ .insight-title { font-weight: 700; font-size: 14px; margin-bottom: 5px; }
224
+ .insight-body { font-size: 13px; color: var(--text2); line-height: 1.5; }
225
+
226
+ /* ── Glossary ───────────────────────────────────────────────────── */
227
+ .glossary-grid {
228
+ display: grid;
229
+ grid-template-columns: repeat(auto-fill, minmax(240px,1fr));
230
+ gap: 12px;
231
+ }
232
+ .glossary-item {
233
+ background: var(--bg3);
234
+ border-radius: var(--r-sm);
235
+ padding: 14px;
236
+ border: 1px solid var(--border);
237
+ }
238
+ .glos-term { font-weight: 700; color: var(--cyan); margin-bottom: 4px; font-size: 14px; }
239
+ .glos-def { font-size: 12px; color: var(--text2); line-height: 1.5; }
240
+
241
+ /* ── Tabs ───────────────────────────────────────────────────────── */
242
+ .insight-tabs {
243
+ display: flex;
244
+ gap: 6px;
245
+ margin-bottom: 24px;
246
+ flex-wrap: wrap;
247
+ }
248
+ .ins-tab {
249
+ padding: 9px 18px;
250
+ border-radius: 100px;
251
+ border: 1px solid var(--border);
252
+ background: transparent;
253
+ color: var(--text2);
254
+ font-family: var(--font-body);
255
+ font-size: 13px;
256
+ font-weight: 600;
257
+ cursor: pointer;
258
+ transition: all var(--transition);
259
+ }
260
+ .ins-tab:hover { border-color: var(--border2); color: var(--text); }
261
+ .ins-tab.active { border-color: var(--cyan); background: rgba(34,211,238,0.1); color: var(--cyan); }
262
+
263
+ .ins-panel { display: none; }
264
+ .ins-panel.active { display: block; }
265
+
266
+ .gamification-bar {
267
+ background: linear-gradient(135deg, rgba(139,92,246,0.1), rgba(34,211,238,0.05));
268
+ border: 1px solid rgba(139,92,246,0.2);
269
+ border-radius: var(--r);
270
+ padding: 20px 24px;
271
+ margin-bottom: 24px;
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 20px;
275
+ }
276
+ .xp-badge {
277
+ background: linear-gradient(135deg, var(--violet), var(--indigo));
278
+ border-radius: var(--r-sm);
279
+ padding: 14px;
280
+ text-align: center;
281
+ min-width: 80px;
282
+ }
283
+ .xp-val { font-family: var(--font-head); font-size: 22px; font-weight: 800; }
284
+ .xp-lbl { font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; opacity: 0.8; }
285
+ .level-info { flex: 1; }
286
+ .level-name { font-family: var(--font-head); font-size: 18px; font-weight: 700; margin-bottom: 4px; }
287
+ .achievements {
288
+ display: flex;
289
+ gap: 8px;
290
+ flex-wrap: wrap;
291
+ margin-top: 12px;
292
+ }
293
+ .achievement {
294
+ background: var(--bg3);
295
+ border-radius: var(--r-sm);
296
+ padding: 8px 12px;
297
+ font-size: 12px;
298
+ display: flex;
299
+ align-items: center;
300
+ gap: 6px;
301
+ border: 1px solid var(--border);
302
+ }
303
+ .achievement.earned { border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.08); }
304
+ </style>
305
+ </head>
306
+ <body>
307
+ <div class="app-shell">
308
+ <nav class="sidebar">
309
+ <div class="sidebar-logo">
310
+ <div class="logo-mark">
311
+ <div class="logo-icon">📈</div>
312
+ <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
313
+ </div>
314
+ </div>
315
+ <div class="nav-section">
316
+ <div class="nav-label">Main</div>
317
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
318
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
319
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
320
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
321
+ <div class="nav-label">Tools</div>
322
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
323
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights <span class="nav-badge">New</span></a>
324
+ </div>
325
+ <div class="sidebar-footer">
326
+ <div class="market-ticker">Live Market</div>
327
+ <div id="sidebar-tickers"></div>
328
+ </div>
329
+ </nav>
330
+
331
+ <main class="main-content">
332
+ <div class="page-header fade-in">
333
+ <div class="page-title">AI <span>Insights</span></div>
334
+ <div class="page-subtitle">Personalized guidance, education, and smart financial tips powered by AI</div>
335
+ </div>
336
+
337
+ <!-- Gamification Bar -->
338
+ <div class="gamification-bar fade-in">
339
+ <div class="xp-badge">
340
+ <div class="xp-val">340</div>
341
+ <div class="xp-lbl">XP Points</div>
342
+ </div>
343
+ <div class="level-info">
344
+ <div class="level-name">🌱 Growing Investor — Level 4</div>
345
+ <div class="text-muted text-sm">160 XP until Level 5: Savvy Saver</div>
346
+ <div class="progress-bar" style="margin-top:8px">
347
+ <div class="progress-fill" style="width:68%;background:linear-gradient(90deg,var(--violet),var(--cyan))"></div>
348
+ </div>
349
+ <div class="achievements">
350
+ <div class="achievement earned">🏆 First Portfolio</div>
351
+ <div class="achievement earned">📊 Risk Assessed</div>
352
+ <div class="achievement earned">🧮 Calc Explorer</div>
353
+ <div class="achievement" style="opacity:0.5">🎯 Goal Setter</div>
354
+ <div class="achievement" style="opacity:0.5">💰 $10K Milestone</div>
355
+ </div>
356
+ </div>
357
+ </div>
358
+
359
+ <!-- Tab Nav -->
360
+ <div class="insight-tabs fade-in">
361
+ <button class="ins-tab active" data-ins="ai-chat">🤖 AI Advisor</button>
362
+ <button class="ins-tab" data-ins="daily">💡 Daily Insights</button>
363
+ <button class="ins-tab" data-ins="concepts">📚 Concepts</button>
364
+ <button class="ins-tab" data-ins="dos-donts">✅ Do's & Don'ts</button>
365
+ <button class="ins-tab" data-ins="glossary">📖 Glossary</button>
366
+ </div>
367
+
368
+ <!-- ════════════════════════════════════════════════════════════
369
+ AI ADVISOR TAB
370
+ ════════════════════════════════════════════════════════════ -->
371
+ <div class="ins-panel active fade-in" id="ins-ai-chat">
372
+ <div class="grid-60-40">
373
+ <div>
374
+ <div class="card" style="padding:0;overflow:hidden">
375
+ <div style="padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px">
376
+ <div style="width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--cyan),var(--emerald));display:flex;align-items:center;justify-content:center;font-size:18px">🤖</div>
377
+ <div>
378
+ <div style="font-weight:700;font-size:15px">FinWise AI</div>
379
+ <div style="font-size:12px;color:var(--emerald)">● Online · Powered by Claude</div>
380
+ </div>
381
+ <div style="margin-left:auto">
382
+ <span class="badge badge-cyan">Beta</span>
383
+ </div>
384
+ </div>
385
+ <div class="ai-chat-wrap" style="border:none;border-radius:0;height:380px">
386
+ <div class="chat-messages" id="chat-messages">
387
+ <!-- Initial message -->
388
+ <div class="chat-message">
389
+ <div class="chat-avatar ai-av">🤖</div>
390
+ <div class="chat-bubble ai">
391
+ <strong>Hey there, investor! 👋</strong><br><br>
392
+ I'm your AI financial advisor. I can help you understand your portfolio, explain investing concepts, and give personalized guidance based on your goals.<br><br>
393
+ <em>What would you like to explore today?</em>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ <div class="quick-prompts">
398
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">📊 Analyze my portfolio</button>
399
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">🎯 Am I taking too much risk?</button>
400
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">💡 How do ETFs work?</button>
401
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">🏖️ Retirement planning tips</button>
402
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">📈 What is dollar-cost averaging?</button>
403
+ <button class="quick-prompt" onclick="sendQuickPrompt(this)">⚡ Should I rebalance now?</button>
404
+ </div>
405
+ <div class="chat-input-bar">
406
+ <input type="text" class="chat-input" id="chat-input" placeholder="Ask me anything about investing…" onkeydown="if(event.key==='Enter')sendMessage()">
407
+ <button class="chat-send-btn" id="send-btn" onclick="sendMessage()">➤</button>
408
+ </div>
409
+ </div>
410
+ </div>
411
+ </div>
412
+
413
+ <!-- Sidebar topics -->
414
+ <div>
415
+ <div class="card">
416
+ <div class="card-title">📌 Suggested Topics</div>
417
+ <div class="insight-feed">
418
+ <div class="insight-item" style="cursor:pointer" onclick="sendText('What is compound interest and why is it important?')">
419
+ <div>
420
+ <div class="insight-category" style="color:var(--cyan)">🎓 Education</div>
421
+ <div class="insight-title">The Magic of Compound Interest</div>
422
+ <div class="insight-body">Why time in market beats timing the market</div>
423
+ </div>
424
+ </div>
425
+ <div class="insight-item" style="cursor:pointer" onclick="sendText('Explain the 4% rule for retirement withdrawals')">
426
+ <div>
427
+ <div class="insight-category" style="color:var(--emerald)">🏖️ Retirement</div>
428
+ <div class="insight-title">The 4% Withdrawal Rule</div>
429
+ <div class="insight-body">How much you can safely spend in retirement</div>
430
+ </div>
431
+ </div>
432
+ <div class="insight-item" style="cursor:pointer" onclick="sendText('What is portfolio rebalancing and when should I do it?')">
433
+ <div>
434
+ <div class="insight-category" style="color:var(--violet)">⚖️ Strategy</div>
435
+ <div class="insight-title">When to Rebalance</div>
436
+ <div class="insight-body">Keep your risk in check automatically</div>
437
+ </div>
438
+ </div>
439
+ <div class="insight-item" style="cursor:pointer" onclick="sendText('What is the difference between ETFs and mutual funds?')">
440
+ <div>
441
+ <div class="insight-category" style="color:var(--amber)">📊 Basics</div>
442
+ <div class="insight-title">ETFs vs Mutual Funds</div>
443
+ <div class="insight-body">Which is better for beginner investors?</div>
444
+ </div>
445
+ </div>
446
+ </div>
447
+ </div>
448
+
449
+ <!-- AI context card -->
450
+ <div class="card" style="margin-top:16px;background:linear-gradient(135deg,rgba(34,211,238,0.06),rgba(16,185,129,0.03))">
451
+ <div class="card-title">📊 Your Portfolio Context</div>
452
+ <div id="context-summary" style="font-size:13px;color:var(--text2);line-height:1.6"></div>
453
+ </div>
454
+ </div>
455
+ </div>
456
+ </div>
457
+
458
+ <!-- ════════════════════════════════════════════════════════════
459
+ DAILY INSIGHTS TAB
460
+ ════════════════════════════════════════════════════════════ -->
461
+ <div class="ins-panel" id="ins-daily">
462
+ <div class="insight-feed">
463
+ <div class="insight-item">
464
+ <div style="font-size:36px">🌅</div>
465
+ <div>
466
+ <div class="insight-category badge-cyan">Daily Tip</div>
467
+ <div class="insight-title">Automate Your Investments</div>
468
+ <div class="insight-body">Set up automatic monthly contributions — even $100/month invested consistently can grow to over $200,000 in 30 years at a 10% return. Automation removes emotion from the equation and builds the habit effortlessly.</div>
469
+ </div>
470
+ </div>
471
+ <div class="insight-item">
472
+ <div style="font-size:36px">📰</div>
473
+ <div>
474
+ <div class="insight-category badge-amber">Market Insight</div>
475
+ <div class="insight-title">Fed Policy & Your Bonds</div>
476
+ <div class="insight-body">When interest rates rise, bond prices fall. If you're holding BND or similar ETFs in your portfolio, rising rates reduce their short-term value — but also mean higher yields going forward. Long-term investors should stay the course.</div>
477
+ </div>
478
+ </div>
479
+ <div class="insight-item">
480
+ <div style="font-size:36px">⚡</div>
481
+ <div>
482
+ <div class="insight-category badge-emerald">Action Item</div>
483
+ <div class="insight-title">Review Your Emergency Fund First</div>
484
+ <div class="insight-body">Before investing more, ensure you have 3-6 months of expenses in a high-yield savings account (HYSA). With rates above 4.5% APY at some banks, your emergency fund can earn real money while staying liquid.</div>
485
+ </div>
486
+ </div>
487
+ <div class="insight-item">
488
+ <div style="font-size:36px">🧠</div>
489
+ <div>
490
+ <div class="insight-category badge-violet">Behavioral Finance</div>
491
+ <div class="insight-title">Don't Check Your Portfolio Daily</div>
492
+ <div class="insight-body">Studies show that investors who check their portfolios more often make more emotional trades and achieve worse returns. Set a calendar reminder to review quarterly — not daily. Your future self will thank you.</div>
493
+ </div>
494
+ </div>
495
+ <div class="insight-item">
496
+ <div style="font-size:36px">💡</div>
497
+ <div>
498
+ <div class="insight-category badge-rose">Watch Out For</div>
499
+ <div class="insight-title">Expense Ratios Add Up</div>
500
+ <div class="insight-body">A fund with 1% expense ratio vs 0.03% (like VOO) costs you over $250,000 on a $100K investment over 30 years. Always compare expense ratios before buying mutual funds or ETFs.</div>
501
+ </div>
502
+ </div>
503
+ </div>
504
+ </div>
505
+
506
+ <!-- ════════════════════════════════════════════════════════════
507
+ CONCEPTS TAB
508
+ ════════════════════════════════════════════════════════════ -->
509
+ <div class="ins-panel" id="ins-concepts">
510
+ <div class="concept-grid">
511
+ <div class="concept-card" style="--concept-color:var(--cyan)" onclick="sendText('Explain compound interest with a real example')">
512
+ <div class="concept-icon">⚡</div>
513
+ <div class="concept-title">Compound Interest</div>
514
+ <div class="concept-desc">The 8th wonder of the world — earning interest on your interest. Time is your greatest asset.</div>
515
+ <div class="concept-tag"><span class="badge badge-cyan">Beginner</span></div>
516
+ </div>
517
+ <div class="concept-card" style="--concept-color:var(--emerald)" onclick="sendText('What is dollar-cost averaging and how do I use it?')">
518
+ <div class="concept-icon">📅</div>
519
+ <div class="concept-title">Dollar-Cost Averaging</div>
520
+ <div class="concept-desc">Invest a fixed amount regularly regardless of price. Reduces the impact of volatility automatically.</div>
521
+ <div class="concept-tag"><span class="badge badge-emerald">Strategy</span></div>
522
+ </div>
523
+ <div class="concept-card" style="--concept-color:var(--violet)" onclick="sendText('Explain diversification and why it matters')">
524
+ <div class="concept-icon">🌍</div>
525
+ <div class="concept-title">Diversification</div>
526
+ <div class="concept-desc">Don't put all your eggs in one basket. Spreading across assets reduces risk without sacrificing returns.</div>
527
+ <div class="concept-tag"><span class="badge badge-violet">Risk Management</span></div>
528
+ </div>
529
+ <div class="concept-card" style="--concept-color:var(--amber)" onclick="sendText('What is asset allocation and how should I allocate my portfolio?')">
530
+ <div class="concept-icon">🥧</div>
531
+ <div class="concept-title">Asset Allocation</div>
532
+ <div class="concept-desc">How you divide money between stocks, bonds, and other assets. The most important investment decision you'll make.</div>
533
+ <div class="concept-tag"><span class="badge badge-amber">Portfolio</span></div>
534
+ </div>
535
+ <div class="concept-card" style="--concept-color:var(--rose)" onclick="sendText('What is beta in investing and what does it tell me?')">
536
+ <div class="concept-icon">β</div>
537
+ <div class="concept-title">Beta & Volatility</div>
538
+ <div class="concept-desc">Beta measures how much an asset moves relative to the market. A beta of 1.5 means 50% more volatile than S&P 500.</div>
539
+ <div class="concept-tag"><span class="badge badge-rose">Metrics</span></div>
540
+ </div>
541
+ <div class="concept-card" style="--concept-color:var(--indigo)" onclick="sendText('What is an index fund and why do most pros recommend it?')">
542
+ <div class="concept-icon">📊</div>
543
+ <div class="concept-title">Index Funds</div>
544
+ <div class="concept-desc">Passively track a market index like the S&P 500. Low cost, diversified, and statistically outperform most active funds.</div>
545
+ <div class="concept-tag"><span class="badge badge-cyan">Beginner</span></div>
546
+ </div>
547
+ <div class="concept-card" style="--concept-color:var(--emerald)" onclick="sendText('Explain the 4% rule for retirement')">
548
+ <div class="concept-icon">🏖️</div>
549
+ <div class="concept-title">The 4% Rule</div>
550
+ <div class="concept-desc">Withdraw 4% of your nest egg annually in retirement. Based on 95% survival rate across 30-year historical periods.</div>
551
+ <div class="concept-tag"><span class="badge badge-emerald">Retirement</span></div>
552
+ </div>
553
+ <div class="concept-card" style="--concept-color:var(--cyan)" onclick="sendText('What is rebalancing and when should I rebalance my portfolio?')">
554
+ <div class="concept-icon">⚖️</div>
555
+ <div class="concept-title">Rebalancing</div>
556
+ <div class="concept-desc">Selling overweight assets and buying underweight ones to restore your target allocation. Do it annually or at 5% drift.</div>
557
+ <div class="concept-tag"><span class="badge badge-cyan">Strategy</span></div>
558
+ </div>
559
+ <div class="concept-card" style="--concept-color:var(--amber)" onclick="sendText('What is expense ratio and how does it affect my returns?')">
560
+ <div class="concept-icon">💸</div>
561
+ <div class="concept-title">Expense Ratios</div>
562
+ <div class="concept-desc">The annual fee funds charge as % of your investment. Even 0.5% more can cost $100K+ over 30 years.</div>
563
+ <div class="concept-tag"><span class="badge badge-amber">Cost</span></div>
564
+ </div>
565
+ </div>
566
+ </div>
567
+
568
+ <!-- ════════════════════════════════════════════════════════════
569
+ DO'S & DON'TS TAB
570
+ ════════════════════════════════════════════════════════════ -->
571
+ <div class="ins-panel" id="ins-dos-donts">
572
+ <div class="dos-donts">
573
+ <div class="do-card">
574
+ <div class="do-header">✅ Do This</div>
575
+ <div class="tip-item"><div class="tip-icon">🕰️</div><div><strong>Start early</strong> — Even $50/month at 22 beats $500/month starting at 40. Time is your biggest multiplier.</div></div>
576
+ <div class="tip-item"><div class="tip-icon">📅</div><div><strong>Automate contributions</strong> — Set it and forget it. Remove emotion and ensure consistency every month.</div></div>
577
+ <div class="tip-item"><div class="tip-icon">📊</div><div><strong>Keep expense ratios low</strong> — Choose ETFs like VOO (0.03%) over actively managed funds (1%+).</div></div>
578
+ <div class="tip-item"><div class="tip-icon">🌍</div><div><strong>Diversify broadly</strong> — No single stock should exceed 10% of your portfolio when starting out.</div></div>
579
+ <div class="tip-item"><div class="tip-icon">💰</div><div><strong>Max out tax-advantaged accounts</strong> — 401k, Roth IRA, or HSA first before taxable accounts.</div></div>
580
+ <div class="tip-item"><div class="tip-icon">🛡️</div><div><strong>Maintain an emergency fund</strong> — 3–6 months of expenses in cash before investing aggressively.</div></div>
581
+ <div class="tip-item"><div class="tip-icon">📚</div><div><strong>Keep learning</strong> — Read one finance book a year. Try "The Little Book of Common Sense Investing" by Bogle.</div></div>
582
+ <div class="tip-item"><div class="tip-icon">📆</div><div><strong>Review quarterly</strong> — Check your portfolio every 3 months, not every day. Avoid reaction trading.</div></div>
583
+ </div>
584
+ <div class="dont-card">
585
+ <div class="dont-header">❌ Don't Do This</div>
586
+ <div class="tip-item"><div class="tip-icon">📰</div><div><strong>Don't invest based on news</strong> — By the time it's news, the market has already priced it in. Stay the course.</div></div>
587
+ <div class="tip-item"><div class="tip-icon">🎲</div><div><strong>Don't YOLO into meme stocks</strong> — Treat speculative picks as entertainment money — never more than 5% of portfolio.</div></div>
588
+ <div class="tip-item"><div class="tip-icon">😱</div><div><strong>Don't panic sell in crashes</strong> — Market drops of 20–40% are normal and temporary. Selling locks in your loss permanently.</div></div>
589
+ <div class="tip-item"><div class="tip-icon">⏰</div><div><strong>Don't try to time the market</strong> — Missing the 10 best days in the market over 20 years can cut your returns in half.</div></div>
590
+ <div class="tip-item"><div class="tip-icon">💳</div><div><strong>Don't invest money you need soon</strong> — Any money needed within 3 years should be in a HYSA, not the market.</div></div>
591
+ <div class="tip-item"><div class="tip-icon">🤙</div><div><strong>Don't take tips from social media</strong> — Most "finfluencers" are not licensed advisors. Verify everything independently.</div></div>
592
+ <div class="tip-item"><div class="tip-icon">🏦</div><div><strong>Don't ignore fees</strong> — Trading commissions, fund fees, and advisory fees compound over time just like returns do — negatively.</div></div>
593
+ <div class="tip-item"><div class="tip-icon">🔮</div><div><strong>Don't predict the market</strong> — Even professional fund managers fail to consistently beat the index. Humility pays.</div></div>
594
+ </div>
595
+ </div>
596
+ </div>
597
+
598
+ <!-- ════════════════════════════════════════════════════════════
599
+ GLOSSARY TAB
600
+ ════════════════════════════════════════════════════════════ -->
601
+ <div class="ins-panel" id="ins-glossary">
602
+ <div style="margin-bottom:16px">
603
+ <input type="text" id="glossary-search" placeholder="🔍 Search terms…" oninput="filterGlossary()" style="max-width:320px">
604
+ </div>
605
+ <div class="glossary-grid" id="glossary-grid"></div>
606
+ </div>
607
+
608
+ </main>
609
+ </div>
610
+
611
+ <nav class="bottom-nav">
612
+ <div class="bottom-nav-inner">
613
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
614
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
615
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
616
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
617
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
618
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
619
+ </div>
620
+ </nav>
621
+
622
+ <script src="shared.js"></script>
623
+ <script>
624
+ // ── Tab Switching ─────────────────────────────────────────────────
625
+ document.querySelectorAll('.ins-tab').forEach(btn => {
626
+ btn.addEventListener('click', () => {
627
+ document.querySelectorAll('.ins-tab,.ins-panel').forEach(el => el.classList.remove('active'));
628
+ btn.classList.add('active');
629
+ document.getElementById('ins-' + btn.dataset.ins).classList.add('active');
630
+ });
631
+ });
632
+
633
+ // ── Portfolio Context Summary ─────────────────────────────────────
634
+ function buildContextSummary() {
635
+ const p = getPortfolio();
636
+ const totalVal = p.assets.reduce((s,a)=>s+a.shares*a.price,0);
637
+ const riskScore = calcRiskScore(p);
638
+ const divScore = calcDiversification(p);
639
+ const equityPct = p.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0);
640
+ document.getElementById('context-summary').innerHTML = `
641
+ <div style="display:flex;flex-direction:column;gap:6px">
642
+ <div>💼 <strong>${p.assets.length} holdings</strong> · Total: <strong>${fmt$(totalVal)}</strong></div>
643
+ <div>🎯 Risk Score: <strong style="color:var(--amber)">${riskScore}/100</strong> · Profile: <strong>${p.riskProfile||'Moderate'}</strong></div>
644
+ <div>🌍 Diversification: <strong style="color:var(--emerald)">${divScore}/100</strong></div>
645
+ <div>📊 Equity: <strong>${equityPct}%</strong> · Goals: <strong>${(p.goals||['Wealth Building']).join(', ')}</strong></div>
646
+ </div>
647
+ `;
648
+ }
649
+
650
+ // ── AI Chat ───────────────────────────────────────────────────────
651
+ let chatHistory = [];
652
+ let isLoading = false;
653
+
654
+ function buildSystemPrompt() {
655
+ const p = getPortfolio();
656
+ const totalVal = p.assets.reduce((s,a)=>s+a.shares*(MARKET_PRICES[a.ticker]?.price||a.price),0);
657
+ const riskScore = calcRiskScore(p);
658
+ const divScore = calcDiversification(p);
659
+ const equityPct = p.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0);
660
+ const bondPct = p.assets.filter(a=>a.type==='Bond').reduce((s,a)=>s+a.pct,0);
661
+
662
+ return `You are FinWise AI, a friendly, expert financial advisor built into the FinWise personal finance app.
663
+
664
+ USER'S PORTFOLIO CONTEXT:
665
+ - Holdings: ${p.assets.map(a=>`${a.ticker} (${a.pct}%)`).join(', ')}
666
+ - Total Value: $${totalVal.toFixed(2)}
667
+ - Total Invested: $${p.totalInvested}
668
+ - Risk Score: ${riskScore}/100 (${p.riskProfile || 'Moderate'})
669
+ - Diversification Score: ${divScore}/100
670
+ - Equity Allocation: ${equityPct}%
671
+ - Bond Allocation: ${bondPct}%
672
+ - Investment Goals: ${(p.goals||['Wealth Building']).join(', ')}
673
+
674
+ PERSONA:
675
+ - Friendly, warm, encouraging — never condescending
676
+ - Use simple language; avoid jargon (or explain it when necessary)
677
+ - Be direct and actionable — give concrete advice, not just generic platitudes
678
+ - Use bullet points and bold text for clarity
679
+ - Add relevant emojis to make responses engaging
680
+ - Be concise but complete — aim for 100-200 words per response
681
+ - Always note: you're an AI, not a licensed financial advisor
682
+
683
+ FOCUS: Help beginners understand investing. Provide personalized insights based on their actual portfolio above.`;
684
+ }
685
+
686
+ async function sendMessage() {
687
+ const input = document.getElementById('chat-input');
688
+ const msg = input.value.trim();
689
+ if (!msg || isLoading) return;
690
+ input.value = '';
691
+ sendText(msg);
692
+ }
693
+
694
+ function sendQuickPrompt(btn) {
695
+ sendText(btn.textContent.replace(/^[^ ]+ /,'').trim());
696
+ }
697
+
698
+ async function sendText(text) {
699
+ if (isLoading) return;
700
+
701
+ appendMessage('user', text);
702
+ chatHistory.push({ role:'user', content: text });
703
+
704
+ isLoading = true;
705
+ document.getElementById('send-btn').disabled = true;
706
+
707
+ const typingEl = appendTyping();
708
+
709
+ try {
710
+ const response = await fetch('https://api.anthropic.com/v1/messages', {
711
+ method: 'POST',
712
+ headers: { 'Content-Type': 'application/json' },
713
+ body: JSON.stringify({
714
+ model: 'claude-sonnet-4-20250514',
715
+ max_tokens: 1000,
716
+ system: buildSystemPrompt(),
717
+ messages: chatHistory
718
+ })
719
+ });
720
+
721
+ typingEl.remove();
722
+ const data = await response.json();
723
+
724
+ if (data.error) throw new Error(data.error.message);
725
+
726
+ const reply = data.content?.[0]?.text || 'Sorry, I had trouble generating a response. Please try again.';
727
+ appendMessage('ai', formatAIReply(reply));
728
+ chatHistory.push({ role:'assistant', content: reply });
729
+
730
+ } catch (err) {
731
+ typingEl.remove();
732
+ // Fallback: use built-in responses
733
+ const fallback = getFallbackResponse(text);
734
+ appendMessage('ai', formatAIReply(fallback));
735
+ chatHistory.push({ role:'assistant', content: fallback });
736
+ }
737
+
738
+ isLoading = false;
739
+ document.getElementById('send-btn').disabled = false;
740
+ }
741
+
742
+ function formatAIReply(text) {
743
+ return text
744
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
745
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
746
+ .replace(/\n\n/g, '<br><br>')
747
+ .replace(/\n- /g, '<br>• ')
748
+ .replace(/\n• /g, '<br>• ')
749
+ .replace(/\n(\d+)\. /g, '<br>$1. ');
750
+ }
751
+
752
+ function getFallbackResponse(query) {
753
+ const q = query.toLowerCase();
754
+ const p = getPortfolio();
755
+ const equityPct = p.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0);
756
+ const riskScore = calcRiskScore(p);
757
+
758
+ if (q.includes('portfolio') || q.includes('analyze')) {
759
+ return `**Here's a quick analysis of your portfolio:** 📊\n\n` +
760
+ `You're holding **${p.assets.length} assets** with **${equityPct}% in equities**. ` +
761
+ `Your risk score is **${riskScore}/100**, which puts you in the **${p.riskProfile||'Moderate'}** category.\n\n` +
762
+ `**Strengths:**\n- Good asset count for diversification\n- Mix of ETFs and individual stocks\n\n` +
763
+ `**Suggestions:**\n- Consider if bond allocation matches your timeline\n- Review any single position over 30%\n\n` +
764
+ `*Disclaimer: This is AI-generated analysis, not licensed financial advice.*`;
765
+ }
766
+ if (q.includes('etf') || q.includes('index')) {
767
+ return `**ETFs (Exchange-Traded Funds) 101:** 📊\n\nAn ETF is a basket of stocks or bonds that trades on an exchange like a single stock.\n\n**Why they're great for beginners:**\n- **Instant diversification** — VOO holds ~500 companies\n- **Low cost** — VOO charges just 0.03% annually\n- **No stock picking required** — just buy the market\n\n**Your portfolio already has ETFs:** ${p.assets.filter(a=>a.type==='ETF').map(a=>a.ticker).join(', ')||'None yet'} — great choice! 🎉`;
768
+ }
769
+ if (q.includes('compound')) {
770
+ return `**Compound Interest — The 8th Wonder of the World** ⚡\n\nCompound interest means earning returns on your returns.\n\n**Simple example:**\n- Invest $10,000 at 8%/year\n- Year 1: $10,800\n- Year 10: $21,589\n- Year 30: $100,627 — **10× your money!**\n\nThe key insight: **time accelerates everything**. Starting at 25 vs 35 can mean the difference of $300,000+ at retirement — even with the same contributions.`;
771
+ }
772
+ if (q.includes('risk') || q.includes('rebalance')) {
773
+ return `**Your Risk Profile:** 🎯\n\nYour current risk score is **${riskScore}/100**.\n\n**Quick rebalancing check:**\n- If any single asset has drifted >5% from target → trim it\n- If equity % has grown significantly → add bonds\n- Do this **annually** or when allocation drifts >5%\n\n**For your portfolio:** ${equityPct > 80 ? '⚠️ High equity concentration. Consider adding some bonds for stability.' : '✅ Equity balance looks reasonable for a growth-oriented investor.'}\n\n*Review every quarter, rebalance annually.*`;
774
+ }
775
+ if (q.includes('retirement') || q.includes('4%')) {
776
+ return `**The 4% Rule Explained** 🏖️\n\nThe 4% rule says: in retirement, you can safely withdraw 4% of your portfolio annually and it should last 30+ years.\n\n**Example:**\n- $1,000,000 nest egg → $40,000/year ($3,333/mo)\n- $500,000 → $20,000/year ($1,667/mo)\n\n**Your goal:** Save **25× your annual expenses**\n\n**Use our Retirement Calculator** to see exactly when you'll hit your number! 🧮`;
777
+ }
778
+ if (q.includes('dollar') || q.includes('dca') || q.includes('averaging')) {
779
+ return `**Dollar-Cost Averaging (DCA)** 📅\n\nInstead of investing a lump sum, you invest a fixed amount at regular intervals — regardless of the market price.\n\n**Why it works:**\n- Removes emotion from investing\n- You buy more shares when prices are low\n- You buy fewer shares when prices are high\n- Averages out your cost over time\n\n**Example:** $300/month in VOO no matter what the market does. You don't need to predict anything — just stay consistent. That's it! 🎯`;
780
+ }
781
+
782
+ return `**Great question!** 💡\n\nThat's an important topic in personal finance. Based on your portfolio (${p.assets.map(a=>a.ticker).join(', ')}), here's what I'd suggest:\n\n- Keep learning and asking questions like this — financial literacy compounds just like money does!\n- Use the Calculators to model different scenarios\n- Review the Concepts section for deeper dives\n\n*Connect to the internet to get fully personalized AI responses powered by Claude.* 🤖`;
783
+ }
784
+
785
+ function appendMessage(role, html) {
786
+ const msgs = document.getElementById('chat-messages');
787
+ const div = document.createElement('div');
788
+ div.className = 'chat-message ' + (role==='user'?'user':'');
789
+ div.innerHTML = `
790
+ <div class="chat-avatar ${role==='user'?'user-av':'ai-av'}">${role==='user'?'👤':'🤖'}</div>
791
+ <div class="chat-bubble ${role}">${html}</div>
792
+ `;
793
+ msgs.appendChild(div);
794
+ msgs.scrollTop = msgs.scrollHeight;
795
+ return div;
796
+ }
797
+
798
+ function appendTyping() {
799
+ const msgs = document.getElementById('chat-messages');
800
+ const div = document.createElement('div');
801
+ div.className = 'chat-message';
802
+ div.innerHTML = `
803
+ <div class="chat-avatar ai-av">🤖</div>
804
+ <div class="chat-typing"><span></span><span></span><span></span></div>
805
+ `;
806
+ msgs.appendChild(div);
807
+ msgs.scrollTop = msgs.scrollHeight;
808
+ return div;
809
+ }
810
+
811
+ // ── Glossary ──────────────────────────────────────────────────────
812
+ const GLOSSARY = [
813
+ { term:'APY', def:'Annual Percentage Yield — the real return earned in a year, including compound interest.' },
814
+ { term:'Asset Allocation', def:'How you divide your portfolio among different asset classes like stocks, bonds, and cash.' },
815
+ { term:'Bear Market', def:'A market decline of 20% or more from recent highs, lasting at least 2 months.' },
816
+ { term:'Beta', def:'Measures an asset\'s volatility vs the market. Beta>1 = more volatile than the S&P 500.' },
817
+ { term:'Blue-Chip Stock', def:'Shares of large, reputable companies with long track records of stable performance.' },
818
+ { term:'Bond', def:'A loan you give to a company or government. They pay you interest and return your principal at maturity.' },
819
+ { term:'Bull Market', def:'A period of rising prices, generally defined as a 20%+ gain from recent lows.' },
820
+ { term:'Compound Interest', def:'Earning returns on your returns. The longer you invest, the more powerful this becomes.' },
821
+ { term:'Diversification', def:'Spreading investments across many assets to reduce risk without necessarily reducing returns.' },
822
+ { term:'Dividend', def:'A portion of a company\'s profits paid to shareholders, usually quarterly.' },
823
+ { term:'DCA', def:'Dollar-Cost Averaging — investing a fixed amount regularly, regardless of market price.' },
824
+ { term:'ETF', def:'Exchange-Traded Fund — a basket of securities that trades on an exchange like a single stock.' },
825
+ { term:'Expense Ratio', def:'Annual fee charged by mutual funds/ETFs, expressed as % of your investment.' },
826
+ { term:'FIRE', def:'Financial Independence, Retire Early — movement focused on saving aggressively to retire young.' },
827
+ { term:'Index Fund', def:'A fund that tracks a market index (like S&P 500) passively. Usually low-cost and diversified.' },
828
+ { term:'Liquidity', def:'How easily an asset can be converted to cash. Stocks are liquid; real estate is not.' },
829
+ { term:'Market Cap', def:'Total market value of a company\'s outstanding shares (Price × Shares Outstanding).' },
830
+ { term:'P/E Ratio', def:'Price-to-Earnings ratio — how much investors pay per dollar of earnings. Measures valuation.' },
831
+ { term:'Rebalancing', def:'Adjusting your portfolio back to target allocation by selling overweighted and buying underweighted assets.' },
832
+ { term:'Risk Tolerance', def:'Your ability (financial) and willingness (emotional) to endure market fluctuations.' },
833
+ { term:'Roth IRA', def:'A retirement account funded with after-tax money. Qualified withdrawals are tax-free.' },
834
+ { term:'S&P 500', def:'An index tracking 500 of the largest US publicly traded companies by market cap.' },
835
+ { term:'SIP', def:'Systematic Investment Plan — regular, automated investments at fixed intervals.' },
836
+ { term:'Volatility', def:'The degree of price variation over time. Higher volatility = higher risk and potential reward.' },
837
+ { term:'Yield', def:'Income generated by an investment, usually expressed as a percentage of the investment\'s value.' },
838
+ ];
839
+
840
+ function renderGlossary(filter='') {
841
+ const items = filter ? GLOSSARY.filter(g=>g.term.toLowerCase().includes(filter)||g.def.toLowerCase().includes(filter)) : GLOSSARY;
842
+ document.getElementById('glossary-grid').innerHTML = items.map(g => `
843
+ <div class="glossary-item">
844
+ <div class="glos-term">${g.term}</div>
845
+ <div class="glos-def">${g.def}</div>
846
+ </div>
847
+ `).join('');
848
+ }
849
+
850
+ function filterGlossary() {
851
+ renderGlossary(document.getElementById('glossary-search').value.toLowerCase());
852
+ }
853
+
854
+ document.addEventListener('DOMContentLoaded', () => {
855
+ buildContextSummary();
856
+ renderGlossary();
857
+ });
858
+ </script>
859
+ </body>
860
+ </html>
portfolio.html ADDED
@@ -0,0 +1,673 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — Portfolio Builder</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
+ <style>
10
+ .builder-steps {
11
+ display: flex;
12
+ gap: 8px;
13
+ margin-bottom: 28px;
14
+ background: var(--bg3);
15
+ border-radius: var(--r-sm);
16
+ padding: 4px;
17
+ }
18
+ .step-btn {
19
+ flex: 1;
20
+ padding: 10px 8px;
21
+ border-radius: 6px;
22
+ border: none;
23
+ background: transparent;
24
+ color: var(--text2);
25
+ font-size: 13px;
26
+ font-weight: 600;
27
+ cursor: pointer;
28
+ transition: all var(--transition);
29
+ text-align: center;
30
+ white-space: nowrap;
31
+ }
32
+ .step-btn.active {
33
+ background: var(--card);
34
+ color: var(--cyan);
35
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
36
+ }
37
+ .step-num {
38
+ display: inline-flex;
39
+ width: 20px; height: 20px;
40
+ background: var(--bg3);
41
+ border-radius: 50%;
42
+ align-items: center; justify-content: center;
43
+ font-size: 11px;
44
+ margin-right: 6px;
45
+ }
46
+ .step-btn.active .step-num { background: var(--cyan); color: var(--bg); }
47
+
48
+ .step-panel { display: none; }
49
+ .step-panel.active { display: block; }
50
+
51
+ .goal-cards {
52
+ display: grid;
53
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
54
+ gap: 12px;
55
+ margin-bottom: 24px;
56
+ }
57
+ .goal-card {
58
+ background: var(--bg3);
59
+ border: 2px solid var(--border);
60
+ border-radius: var(--r);
61
+ padding: 20px 16px;
62
+ text-align: center;
63
+ cursor: pointer;
64
+ transition: all var(--transition);
65
+ }
66
+ .goal-card:hover { border-color: var(--border2); transform: translateY(-2px); }
67
+ .goal-card.selected { border-color: var(--cyan); background: rgba(34,211,238,0.08); box-shadow: var(--glow-c); }
68
+ .goal-icon { font-size: 32px; margin-bottom: 8px; }
69
+ .goal-title { font-size: 13px; font-weight: 700; margin-bottom: 4px; }
70
+ .goal-desc { font-size: 11px; color: var(--text2); }
71
+
72
+ .risk-slider-wrap {
73
+ padding: 20px;
74
+ background: var(--bg3);
75
+ border-radius: var(--r);
76
+ margin-bottom: 20px;
77
+ }
78
+ .risk-labels {
79
+ display: flex;
80
+ justify-content: space-between;
81
+ font-size: 12px;
82
+ color: var(--text2);
83
+ margin-top: 10px;
84
+ }
85
+ .risk-profile-display {
86
+ text-align: center;
87
+ padding: 16px;
88
+ background: rgba(34,211,238,0.06);
89
+ border-radius: var(--r-sm);
90
+ border: 1px solid var(--border2);
91
+ margin-top: 16px;
92
+ }
93
+ .rp-icon { font-size: 36px; margin-bottom: 6px; }
94
+ .rp-label { font-family: var(--font-head); font-size: 20px; font-weight: 700; }
95
+ .rp-desc { font-size: 13px; color: var(--text2); margin-top: 4px; }
96
+
97
+ .asset-card {
98
+ display: flex;
99
+ align-items: center;
100
+ gap: 14px;
101
+ padding: 16px;
102
+ background: var(--bg3);
103
+ border-radius: var(--r-sm);
104
+ margin-bottom: 10px;
105
+ border: 1px solid var(--border);
106
+ transition: all var(--transition);
107
+ }
108
+ .asset-card:hover { border-color: var(--border2); }
109
+ .asset-logo {
110
+ width: 42px; height: 42px;
111
+ border-radius: 10px;
112
+ display: flex; align-items: center; justify-content: center;
113
+ font-weight: 800;
114
+ font-size: 11px;
115
+ font-family: var(--font-mono);
116
+ flex-shrink: 0;
117
+ color: var(--bg);
118
+ }
119
+ .asset-info { flex: 1; min-width: 0; }
120
+ .asset-ticker { font-weight: 700; font-size: 15px; }
121
+ .asset-name { font-size: 12px; color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
122
+ .asset-type { margin-top: 3px; }
123
+ .asset-sliders { flex: 2; }
124
+ .asset-pct-val {
125
+ font-family: var(--font-mono);
126
+ font-size: 15px;
127
+ font-weight: 700;
128
+ color: var(--cyan);
129
+ min-width: 46px;
130
+ text-align: right;
131
+ }
132
+ .asset-dollar { font-size: 11px; color: var(--text2); text-align: right; margin-top: 2px; }
133
+ .asset-remove {
134
+ background: rgba(244,63,94,0.1);
135
+ border: none;
136
+ color: var(--rose);
137
+ border-radius: 6px;
138
+ width: 28px; height: 28px;
139
+ display: flex; align-items: center; justify-content: center;
140
+ cursor: pointer;
141
+ font-size: 14px;
142
+ transition: background var(--transition);
143
+ flex-shrink: 0;
144
+ }
145
+ .asset-remove:hover { background: rgba(244,63,94,0.25); }
146
+
147
+ .add-asset-grid {
148
+ display: grid;
149
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
150
+ gap: 8px;
151
+ margin-top: 16px;
152
+ }
153
+ .add-asset-btn {
154
+ padding: 10px 8px;
155
+ background: var(--bg3);
156
+ border: 1px dashed var(--border2);
157
+ border-radius: var(--r-sm);
158
+ color: var(--text2);
159
+ font-size: 12px;
160
+ font-weight: 600;
161
+ cursor: pointer;
162
+ text-align: center;
163
+ transition: all var(--transition);
164
+ font-family: var(--font-body);
165
+ }
166
+ .add-asset-btn:hover { border-color: var(--cyan); color: var(--cyan); background: rgba(34,211,238,0.05); }
167
+ .add-asset-btn.in-portfolio { border-style: solid; border-color: var(--emerald); color: var(--emerald); background: rgba(16,185,129,0.06); }
168
+
169
+ .portfolio-summary-side {
170
+ position: sticky;
171
+ top: 20px;
172
+ }
173
+ .pie-wrap { height: 220px; position: relative; }
174
+ .portfolio-total {
175
+ text-align: center;
176
+ padding: 16px;
177
+ background: var(--bg3);
178
+ border-radius: var(--r-sm);
179
+ margin: 16px 0;
180
+ }
181
+ .pt-label { font-size: 12px; color: var(--text2); text-transform: uppercase; letter-spacing: 0.06em; }
182
+ .pt-value { font-family: var(--font-head); font-size: 28px; font-weight: 800; color: var(--cyan); }
183
+
184
+ .balance-warning {
185
+ display: flex;
186
+ align-items: center;
187
+ gap: 10px;
188
+ padding: 12px 14px;
189
+ border-radius: var(--r-sm);
190
+ font-size: 13px;
191
+ margin-bottom: 16px;
192
+ }
193
+ .balance-warning.ok { background: rgba(16,185,129,0.1); border: 1px solid rgba(16,185,129,0.3); color: var(--emerald); }
194
+ .balance-warning.warn { background: rgba(245,158,11,0.1); border: 1px solid rgba(245,158,11,0.3); color: var(--amber); }
195
+ .balance-warning.err { background: rgba(244,63,94,0.1); border: 1px solid rgba(244,63,94,0.3); color: var(--rose); }
196
+
197
+ .rebal-btn {
198
+ width: 100%;
199
+ padding: 10px;
200
+ background: var(--bg3);
201
+ border: 1px solid var(--border);
202
+ border-radius: var(--r-sm);
203
+ color: var(--text2);
204
+ font-size: 13px;
205
+ font-weight: 600;
206
+ cursor: pointer;
207
+ transition: all var(--transition);
208
+ font-family: var(--font-body);
209
+ margin-bottom: 8px;
210
+ }
211
+ .rebal-btn:hover { border-color: var(--cyan); color: var(--cyan); background: rgba(34,211,238,0.05); }
212
+ </style>
213
+ </head>
214
+ <body>
215
+ <div class="app-shell">
216
+
217
+ <!-- Sidebar -->
218
+ <nav class="sidebar">
219
+ <div class="sidebar-logo">
220
+ <div class="logo-mark">
221
+ <div class="logo-icon">📈</div>
222
+ <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
223
+ </div>
224
+ </div>
225
+ <div class="nav-section">
226
+ <div class="nav-label">Main</div>
227
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
228
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
229
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
230
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
231
+ <div class="nav-label">Tools</div>
232
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
233
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights</a>
234
+ </div>
235
+ <div class="sidebar-footer">
236
+ <div class="market-ticker">Live Market</div>
237
+ <div id="sidebar-tickers"></div>
238
+ </div>
239
+ </nav>
240
+
241
+ <main class="main-content">
242
+ <div class="page-header fade-in">
243
+ <div class="page-title">Portfolio <span>Builder</span></div>
244
+ <div class="page-subtitle">Build a smart, diversified portfolio in 3 easy steps</div>
245
+ </div>
246
+
247
+ <!-- Step Nav -->
248
+ <div class="builder-steps fade-in">
249
+ <button class="step-btn active" data-step="1"><span class="step-num">1</span>Your Goals</button>
250
+ <button class="step-btn" data-step="2"><span class="step-num">2</span>Risk Profile</button>
251
+ <button class="step-btn" data-step="3"><span class="step-num">3</span>Build & Tune</button>
252
+ </div>
253
+
254
+ <div class="grid-60-40">
255
+ <!-- Left: Steps -->
256
+ <div>
257
+
258
+ <!-- Step 1: Goals -->
259
+ <div class="step-panel active" id="step-1">
260
+ <div class="card fade-in">
261
+ <div class="section-title">🎯 What are you investing for?</div>
262
+ <div class="text-muted text-sm" style="margin-bottom:20px">Select one or more goals (you can always change later)</div>
263
+ <div class="goal-cards" id="goal-cards">
264
+ <div class="goal-card" data-goal="Retirement"><div class="goal-icon">🏖️</div><div class="goal-title">Retirement</div><div class="goal-desc">Long-term wealth for a comfortable retirement</div></div>
265
+ <div class="goal-card" data-goal="Wealth Building"><div class="goal-icon">💰</div><div class="goal-title">Wealth Building</div><div class="goal-desc">Grow your money over time</div></div>
266
+ <div class="goal-card" data-goal="Down Payment"><div class="goal-icon">🏠</div><div class="goal-title">Home Purchase</div><div class="goal-desc">Save for a down payment</div></div>
267
+ <div class="goal-card" data-goal="Education"><div class="goal-icon">📚</div><div class="goal-title">Education</div><div class="goal-desc">Fund for college or courses</div></div>
268
+ <div class="goal-card" data-goal="Emergency Fund"><div class="goal-icon">🛡️</div><div class="goal-title">Emergency Fund</div><div class="goal-desc">Safety net for unexpected expenses</div></div>
269
+ <div class="goal-card" data-goal="Passive Income"><div class="goal-icon">💸</div><div class="goal-title">Passive Income</div><div class="goal-desc">Earn dividends & interest</div></div>
270
+ </div>
271
+ <div class="slider-wrap">
272
+ <div class="slider-header">
273
+ <span class="slider-label">💵 How much to invest?</span>
274
+ <span class="slider-val" id="invest-display">$5,000</span>
275
+ </div>
276
+ <input type="range" id="invest-slider" min="500" max="100000" value="5000" step="500">
277
+ <div style="display:flex;justify-content:space-between;font-size:11px;color:var(--text3);margin-top:4px">
278
+ <span>$500</span><span>$100K</span>
279
+ </div>
280
+ </div>
281
+ <button class="btn btn-primary" onclick="goStep(2)">Next: Set Risk Profile →</button>
282
+ </div>
283
+ </div>
284
+
285
+ <!-- Step 2: Risk -->
286
+ <div class="step-panel" id="step-2">
287
+ <div class="card fade-in">
288
+ <div class="section-title">🎯 Your Risk Tolerance</div>
289
+ <div class="text-muted text-sm" style="margin-bottom:16px">Drag the slider to find your comfort level</div>
290
+ <div class="risk-slider-wrap">
291
+ <div class="slider-header">
292
+ <span class="slider-label">Risk Level</span>
293
+ <span class="slider-val" id="risk-val-display">Moderate</span>
294
+ </div>
295
+ <input type="range" id="risk-slider" min="1" max="5" value="3" step="1">
296
+ <div class="risk-labels">
297
+ <span>🐢 Very Conservative</span>
298
+ <span>🦁 Very Aggressive</span>
299
+ </div>
300
+ </div>
301
+ <div class="risk-profile-display" id="risk-profile-box">
302
+ <div class="rp-icon">⚖️</div>
303
+ <div class="rp-label">Moderate Investor</div>
304
+ <div class="rp-desc">Balanced mix of growth and stability. You're OK with some market swings for better long-term returns.</div>
305
+ </div>
306
+ <div class="flex gap-12" style="margin-top:20px">
307
+ <button class="btn btn-ghost" onclick="goStep(1)">← Back</button>
308
+ <button class="btn btn-primary" onclick="goStep(3);buildPortfolio()">Generate Portfolio →</button>
309
+ </div>
310
+ </div>
311
+ </div>
312
+
313
+ <!-- Step 3: Build -->
314
+ <div class="step-panel" id="step-3">
315
+ <div class="card fade-in">
316
+ <div class="flex justify-between items-center" style="margin-bottom:16px">
317
+ <div class="section-title" style="margin-bottom:0">🏗️ Your Portfolio</div>
318
+ <div class="flex gap-8">
319
+ <button class="rebal-btn" style="width:auto;padding:8px 14px" onclick="autoRebalance()">⚖️ Auto-rebalance</button>
320
+ </div>
321
+ </div>
322
+ <div id="asset-balance-warning" class="balance-warning ok" style="margin-bottom:14px">
323
+ ✅ Portfolio is balanced at 100%
324
+ </div>
325
+ <div id="asset-list"></div>
326
+ <div class="divider"></div>
327
+ <div class="section-title" style="font-size:14px">➕ Add Assets</div>
328
+ <div class="add-asset-grid" id="add-asset-grid"></div>
329
+ <div class="flex gap-12" style="margin-top:20px">
330
+ <button class="btn btn-ghost" onclick="goStep(2)">← Back</button>
331
+ <button class="btn btn-emerald btn-full" onclick="savePortfolio_()">💾 Save Portfolio</button>
332
+ </div>
333
+ </div>
334
+ </div>
335
+
336
+ </div>
337
+
338
+ <!-- Right: Live Preview -->
339
+ <div class="portfolio-summary-side">
340
+ <div class="card fade-in-1">
341
+ <div class="card-title">🔴 Live Preview</div>
342
+ <div class="pie-wrap">
343
+ <canvas id="previewPieChart"></canvas>
344
+ </div>
345
+ <div class="portfolio-total">
346
+ <div class="pt-label">Total Value</div>
347
+ <div class="pt-value" id="preview-total">$5,000</div>
348
+ </div>
349
+ <div id="preview-legend" style="margin-top:8px"></div>
350
+ <div class="divider"></div>
351
+ <div class="card-title">📊 Portfolio Metrics</div>
352
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:8px">
353
+ <div style="background:var(--bg3);border-radius:var(--r-sm);padding:12px;text-align:center">
354
+ <div style="font-size:11px;color:var(--text2);margin-bottom:4px">Risk Score</div>
355
+ <div style="font-family:var(--font-head);font-size:20px;font-weight:800;color:var(--amber)" id="preview-risk">—</div>
356
+ </div>
357
+ <div style="background:var(--bg3);border-radius:var(--r-sm);padding:12px;text-align:center">
358
+ <div style="font-size:11px;color:var(--text2);margin-bottom:4px">Diversification</div>
359
+ <div style="font-family:var(--font-head);font-size:20px;font-weight:800;color:var(--emerald)" id="preview-div">—</div>
360
+ </div>
361
+ </div>
362
+ </div>
363
+ </div>
364
+ </div>
365
+
366
+ </main>
367
+ </div>
368
+
369
+ <!-- Mobile Bottom Nav -->
370
+ <nav class="bottom-nav">
371
+ <div class="bottom-nav-inner">
372
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
373
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
374
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
375
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
376
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
377
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
378
+ </div>
379
+ </nav>
380
+
381
+ <script src="shared.js"></script>
382
+ <script>
383
+ // ── State ─────────────────────────────────────────────────────────
384
+ let portfolio = getPortfolio();
385
+ let investAmount = portfolio.totalInvested || 5000;
386
+ let riskLevel = 3; // 1-5
387
+ let selectedGoals = portfolio.goals || ['Wealth Building'];
388
+ let previewChart = null;
389
+
390
+ const RISK_PROFILES = {
391
+ 1: { icon:'🐢', label:'Very Conservative', desc:'Capital preservation is your #1 priority. Heavy bonds & cash equivalents.', template:[30,0,5,0,50,10,5] },
392
+ 2: { icon:'🛡️', label:'Conservative', desc:'Modest growth with low risk. Bonds-heavy with some equity exposure.', template:[20,10,10,5,40,10,5] },
393
+ 3: { icon:'⚖️', label:'Moderate', desc:'Balanced growth & stability. OK with some swings for better returns.', template:[30,20,15,12,13,7,3] },
394
+ 4: { icon:'🚀', label:'Aggressive', desc:'Growth-focused. Higher potential returns, higher volatility accepted.', template:[35,30,20,12,0,3,0] },
395
+ 5: { icon:'🦁', label:'Very Aggressive', desc:'Maximum growth. Concentrated tech & equities. High-risk, high-reward.', template:[20,35,30,15,0,0,0] },
396
+ };
397
+
398
+ const ALL_ASSETS = [
399
+ { ticker:'VOO', name:'Vanguard S&P 500 ETF', price:478.22, color:'#22d3ee', type:'ETF' },
400
+ { ticker:'VTI', name:'Vanguard Total Market', price:240.30, color:'#0ea5e9', type:'ETF' },
401
+ { ticker:'QQQ', name:'Invesco Nasdaq 100', price:456.80, color:'#8b5cf6', type:'ETF' },
402
+ { ticker:'NVDA', name:'NVIDIA Corp', price:875.40, color:'#10b981', type:'Stock' },
403
+ { ticker:'AAPL', name:'Apple Inc.', price:188.60, color:'#f59e0b', type:'Stock' },
404
+ { ticker:'AMZN', name:'Amazon.com Inc.', price:188.90, color:'#0891b2', type:'Stock' },
405
+ { ticker:'TSLA', name:'Tesla Inc.', price:182.30, color:'#f43f5e', type:'Stock' },
406
+ { ticker:'WMT', name:'Walmart Inc.', price: 67.80, color:'#34d399', type:'Stock' },
407
+ { ticker:'MCD', name:"McDonald's Corp", price:281.50, color:'#fb923c', type:'Stock' },
408
+ { ticker:'BND', name:'Vanguard Bond ETF', price: 73.40, color:'#6366f1', type:'Bond' },
409
+ { ticker:'GLD', name:'SPDR Gold Trust', price:218.10, color:'#fbbf24', type:'Commodity' },
410
+ { ticker:'SLV', name:'iShares Silver Trust', price: 28.60, color:'#94a3b8', type:'Commodity' },
411
+ ];
412
+
413
+ // ── Step Navigation ───────────────────────────────────────────────
414
+ function goStep(n) {
415
+ document.querySelectorAll('.step-panel').forEach(p => p.classList.remove('active'));
416
+ document.querySelectorAll('.step-btn').forEach(b => b.classList.remove('active'));
417
+ document.getElementById('step-' + n).classList.add('active');
418
+ document.querySelector(`.step-btn[data-step="${n}"]`).classList.add('active');
419
+ }
420
+
421
+ document.querySelectorAll('.step-btn').forEach(btn => {
422
+ btn.addEventListener('click', () => {
423
+ const s = parseInt(btn.dataset.step);
424
+ if (s === 3) buildPortfolio();
425
+ goStep(s);
426
+ });
427
+ });
428
+
429
+ // ── Goal Cards ────────────────────────────────────────────────────
430
+ document.querySelectorAll('.goal-card').forEach(card => {
431
+ card.addEventListener('click', () => {
432
+ const goal = card.dataset.goal;
433
+ const idx = selectedGoals.indexOf(goal);
434
+ if (idx >= 0) selectedGoals.splice(idx, 1);
435
+ else selectedGoals.push(goal);
436
+ card.classList.toggle('selected');
437
+ });
438
+ });
439
+ // Pre-select saved goals
440
+ selectedGoals.forEach(g => {
441
+ const c = document.querySelector(`.goal-card[data-goal="${g}"]`);
442
+ if (c) c.classList.add('selected');
443
+ });
444
+
445
+ // ── Investment Slider ─────────────────────────────────────────────
446
+ const investSlider = document.getElementById('invest-slider');
447
+ investSlider.value = investAmount;
448
+ document.getElementById('invest-display').textContent = '$' + investAmount.toLocaleString();
449
+ investSlider.addEventListener('input', () => {
450
+ investAmount = parseInt(investSlider.value);
451
+ document.getElementById('invest-display').textContent = '$' + investAmount.toLocaleString();
452
+ document.getElementById('preview-total').textContent = '$' + investAmount.toLocaleString();
453
+ updatePortfolioAmounts();
454
+ });
455
+
456
+ // ── Risk Slider ───────────────────────────────────────────────────
457
+ const riskSlider = document.getElementById('risk-slider');
458
+ riskSlider.addEventListener('input', () => {
459
+ riskLevel = parseInt(riskSlider.value);
460
+ updateRiskDisplay();
461
+ });
462
+
463
+ function updateRiskDisplay() {
464
+ const profile = RISK_PROFILES[riskLevel];
465
+ const box = document.getElementById('risk-profile-box');
466
+ box.querySelector('.rp-icon').textContent = profile.icon;
467
+ box.querySelector('.rp-label').textContent = profile.label;
468
+ box.querySelector('.rp-desc').textContent = profile.desc;
469
+ document.getElementById('risk-val-display').textContent = profile.label;
470
+ }
471
+
472
+ // ── Build Portfolio from template ─────────────────────────────────
473
+ function buildPortfolio() {
474
+ const template = RISK_PROFILES[riskLevel].template;
475
+ const baseAssets = [
476
+ ALL_ASSETS.find(a=>a.ticker==='VOO'),
477
+ ALL_ASSETS.find(a=>a.ticker==='QQQ'),
478
+ ALL_ASSETS.find(a=>a.ticker==='NVDA'),
479
+ ALL_ASSETS.find(a=>a.ticker==='AAPL'),
480
+ ALL_ASSETS.find(a=>a.ticker==='BND'),
481
+ ALL_ASSETS.find(a=>a.ticker==='GLD'),
482
+ ALL_ASSETS.find(a=>a.ticker==='AMZN'),
483
+ ];
484
+
485
+ portfolio.assets = baseAssets.map((a, i) => ({
486
+ ...a,
487
+ pct: template[i],
488
+ shares: parseFloat(((investAmount * template[i] / 100) / a.price).toFixed(3))
489
+ })).filter(a => a.pct > 0);
490
+
491
+ portfolio.totalInvested = investAmount;
492
+ portfolio.goals = selectedGoals;
493
+ portfolio.riskProfile = RISK_PROFILES[riskLevel].label;
494
+
495
+ renderAssetList();
496
+ renderAddGrid();
497
+ renderPreviewChart();
498
+ }
499
+
500
+ // ── Render Asset List ─────────────────────────────────────────────
501
+ function renderAssetList() {
502
+ const container = document.getElementById('asset-list');
503
+ container.innerHTML = '';
504
+
505
+ portfolio.assets.forEach((asset, idx) => {
506
+ const dollarVal = (investAmount * asset.pct / 100).toFixed(0);
507
+ const div = document.createElement('div');
508
+ div.className = 'asset-card';
509
+ div.innerHTML = `
510
+ <div class="asset-logo" style="background:${asset.color}">${asset.ticker}</div>
511
+ <div class="asset-info">
512
+ <div class="asset-ticker">${asset.ticker}</div>
513
+ <div class="asset-name">${asset.name}</div>
514
+ <span class="badge badge-${asset.type==='ETF'?'cyan':asset.type==='Bond'?'violet':asset.type==='Commodity'?'amber':'emerald'}">${asset.type}</span>
515
+ </div>
516
+ <div class="asset-sliders">
517
+ <input type="range" min="0" max="80" value="${asset.pct}" step="1"
518
+ oninput="updateAssetPct(${idx}, this.value)"
519
+ style="width:100%;margin-bottom:4px">
520
+ </div>
521
+ <div>
522
+ <div class="asset-pct-val" id="pct-${idx}">${asset.pct}%</div>
523
+ <div class="asset-dollar" id="dollar-${idx}">$${parseInt(dollarVal).toLocaleString()}</div>
524
+ </div>
525
+ <button class="asset-remove" onclick="removeAsset(${idx})">✕</button>
526
+ `;
527
+ container.appendChild(div);
528
+ });
529
+
530
+ updateBalanceWarning();
531
+ updateMetrics();
532
+ }
533
+
534
+ function updateAssetPct(idx, val) {
535
+ portfolio.assets[idx].pct = parseInt(val);
536
+ const dollarVal = (investAmount * parseInt(val) / 100).toFixed(0);
537
+ document.getElementById('pct-' + idx).textContent = val + '%';
538
+ document.getElementById('dollar-' + idx).textContent = '$' + parseInt(dollarVal).toLocaleString();
539
+ updateBalanceWarning();
540
+ renderPreviewChart();
541
+ updateMetrics();
542
+ }
543
+
544
+ function updatePortfolioAmounts() {
545
+ portfolio.assets.forEach((a, i) => {
546
+ const dEl = document.getElementById('dollar-' + i);
547
+ if (dEl) dEl.textContent = '$' + parseInt(investAmount * a.pct / 100).toLocaleString();
548
+ });
549
+ renderPreviewChart();
550
+ }
551
+
552
+ function updateBalanceWarning() {
553
+ const total = portfolio.assets.reduce((s, a) => s + a.pct, 0);
554
+ const warn = document.getElementById('asset-balance-warning');
555
+ if (total === 100) {
556
+ warn.className = 'balance-warning ok';
557
+ warn.innerHTML = '✅ Portfolio is balanced at 100%';
558
+ } else if (total < 100) {
559
+ warn.className = 'balance-warning warn';
560
+ warn.innerHTML = `⚠️ Under-allocated: ${total}% used, ${100-total}% remaining`;
561
+ } else {
562
+ warn.className = 'balance-warning err';
563
+ warn.innerHTML = `❌ Over-allocated: ${total}% total (exceeds 100% by ${total-100}%)`;
564
+ }
565
+ }
566
+
567
+ function updateMetrics() {
568
+ document.getElementById('preview-risk').textContent = calcRiskScore(portfolio) + '/100';
569
+ document.getElementById('preview-div').textContent = calcDiversification(portfolio) + '/100';
570
+ }
571
+
572
+ function removeAsset(idx) {
573
+ portfolio.assets.splice(idx, 1);
574
+ renderAssetList();
575
+ renderAddGrid();
576
+ renderPreviewChart();
577
+ }
578
+
579
+ function autoRebalance() {
580
+ const total = portfolio.assets.reduce((s, a) => s + a.pct, 0);
581
+ const perAsset = Math.floor(100 / portfolio.assets.length);
582
+ let rem = 100 - perAsset * portfolio.assets.length;
583
+ portfolio.assets.forEach((a, i) => { a.pct = perAsset + (i === 0 ? rem : 0); });
584
+ renderAssetList();
585
+ renderPreviewChart();
586
+ showToast('Portfolio auto-rebalanced to equal weights!');
587
+ }
588
+
589
+ // ── Add Asset Grid ────────────────────────────────────────────────
590
+ function renderAddGrid() {
591
+ const grid = document.getElementById('add-asset-grid');
592
+ grid.innerHTML = ALL_ASSETS.map(a => {
593
+ const inPortfolio = portfolio.assets.some(pa => pa.ticker === a.ticker);
594
+ return `<button class="add-asset-btn ${inPortfolio?'in-portfolio':''}"
595
+ onclick="toggleAsset('${a.ticker}')" title="${a.name}">
596
+ ${inPortfolio ? '✓ ' : '+'} ${a.ticker}
597
+ </button>`;
598
+ }).join('');
599
+ }
600
+
601
+ function toggleAsset(ticker) {
602
+ const exists = portfolio.assets.findIndex(a => a.ticker === ticker);
603
+ if (exists >= 0) {
604
+ removeAsset(exists);
605
+ } else {
606
+ if (portfolio.assets.length >= 10) { showToast('Max 10 assets per portfolio', 'error'); return; }
607
+ const asset = ALL_ASSETS.find(a => a.ticker === ticker);
608
+ portfolio.assets.push({ ...asset, pct: 5, shares: 0 });
609
+ renderAssetList();
610
+ renderPreviewChart();
611
+ renderAddGrid();
612
+ }
613
+ }
614
+
615
+ // ── Preview Pie Chart ─────────────────────────────────────────────
616
+ function renderPreviewChart() {
617
+ const ctx = document.getElementById('previewPieChart').getContext('2d');
618
+ if (previewChart) previewChart.destroy();
619
+
620
+ const validAssets = portfolio.assets.filter(a => a.pct > 0);
621
+ previewChart = new Chart(ctx, {
622
+ type: 'doughnut',
623
+ data: {
624
+ labels: validAssets.map(a => a.ticker),
625
+ datasets: [{
626
+ data: validAssets.map(a => a.pct),
627
+ backgroundColor: validAssets.map(a => a.color),
628
+ borderColor: 'rgba(5,13,26,0.8)',
629
+ borderWidth: 3,
630
+ hoverOffset: 8
631
+ }]
632
+ },
633
+ options: {
634
+ responsive: true, maintainAspectRatio: false,
635
+ cutout: '68%',
636
+ plugins: { legend: { display: false } }
637
+ }
638
+ });
639
+
640
+ const leg = document.getElementById('preview-legend');
641
+ leg.innerHTML = validAssets.map(a => `
642
+ <div class="legend-item">
643
+ <div class="legend-dot" style="background:${a.color}"></div>
644
+ <span class="legend-name">${a.ticker}</span>
645
+ <span class="legend-pct">${a.pct}%</span>
646
+ </div>
647
+ `).join('');
648
+ document.getElementById('preview-total').textContent = '$' + investAmount.toLocaleString();
649
+ }
650
+
651
+ // ── Save Portfolio ────────────────────────────────────────────────
652
+ function savePortfolio_() {
653
+ const total = portfolio.assets.reduce((s,a) => s + a.pct, 0);
654
+ if (total !== 100) { showToast(`Total must be 100% (currently ${total}%)`, 'error'); return; }
655
+ portfolio.assets.forEach(a => {
656
+ a.shares = parseFloat(((investAmount * a.pct / 100) / a.price).toFixed(3));
657
+ });
658
+ savePortfolio(portfolio);
659
+ showToast('✅ Portfolio saved successfully!');
660
+ setTimeout(() => window.location.href = 'index.html', 1500);
661
+ }
662
+
663
+ // ── Init ──────────────────────────────────────────────────────────
664
+ document.addEventListener('DOMContentLoaded', () => {
665
+ applyChartDefaults();
666
+ renderAddGrid();
667
+ renderAssetList();
668
+ renderPreviewChart();
669
+ updateMetrics();
670
+ });
671
+ </script>
672
+ </body>
673
+ </html>
risk.html ADDED
@@ -0,0 +1,618 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — Risk Analyzer</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
+ <style>
10
+ .quiz-card { margin-bottom: 16px; transition: opacity 0.3s; }
11
+ .quiz-card.inactive { opacity: 0.4; pointer-events: none; }
12
+ .question-num {
13
+ font-size: 11px; font-weight: 700; text-transform: uppercase;
14
+ letter-spacing: 0.1em; color: var(--cyan); margin-bottom: 6px;
15
+ }
16
+ .question-text { font-size: 16px; font-weight: 600; margin-bottom: 14px; line-height: 1.4; }
17
+ .answer-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
18
+ .answer-btn {
19
+ padding: 12px 14px;
20
+ background: var(--bg3);
21
+ border: 1px solid var(--border);
22
+ border-radius: var(--r-sm);
23
+ color: var(--text2);
24
+ font-family: var(--font-body);
25
+ font-size: 13px;
26
+ font-weight: 500;
27
+ cursor: pointer;
28
+ text-align: left;
29
+ transition: all var(--transition);
30
+ line-height: 1.3;
31
+ }
32
+ .answer-btn:hover { border-color: var(--border2); color: var(--text); background: var(--card2); }
33
+ .answer-btn.selected { border-color: var(--cyan); background: rgba(34,211,238,0.10); color: var(--cyan); }
34
+
35
+ /* Risk Gauge */
36
+ .gauge-container { position: relative; width: 220px; height: 120px; margin: 0 auto; }
37
+ .gauge-svg { width: 220px; height: 120px; }
38
+ .gauge-needle {
39
+ transform-origin: 110px 110px;
40
+ transition: transform 1.2s cubic-bezier(.34,1.56,.64,1);
41
+ }
42
+ .gauge-center-text {
43
+ position: absolute;
44
+ bottom: 0; left: 50%;
45
+ transform: translateX(-50%);
46
+ text-align: center;
47
+ }
48
+ .gauge-score { font-family: var(--font-head); font-size: 32px; font-weight: 800; }
49
+ .gauge-label { font-size: 12px; color: var(--text2); }
50
+
51
+ /* Heatmap */
52
+ .heatmap-grid {
53
+ display: grid;
54
+ gap: 3px;
55
+ }
56
+ .heatmap-cell {
57
+ height: 40px;
58
+ border-radius: 4px;
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ font-size: 11px;
63
+ font-weight: 700;
64
+ color: white;
65
+ transition: transform var(--transition);
66
+ cursor: default;
67
+ }
68
+ .heatmap-cell:hover { transform: scale(1.05); }
69
+
70
+ .rebal-item {
71
+ display: flex;
72
+ align-items: center;
73
+ gap: 14px;
74
+ padding: 14px;
75
+ background: var(--bg3);
76
+ border-radius: var(--r-sm);
77
+ border: 1px solid var(--border);
78
+ margin-bottom: 10px;
79
+ transition: all var(--transition);
80
+ }
81
+ .rebal-item:hover { border-color: var(--border2); }
82
+ .rebal-action {
83
+ font-size: 11px;
84
+ font-weight: 700;
85
+ text-transform: uppercase;
86
+ letter-spacing: 0.08em;
87
+ margin-bottom: 3px;
88
+ }
89
+ .rebal-desc { font-size: 13px; color: var(--text2); }
90
+ .rebal-arrow { font-size: 20px; margin-left: auto; }
91
+
92
+ .risk-breakdown { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
93
+ .rb-item {
94
+ background: var(--bg3);
95
+ border-radius: var(--r-sm);
96
+ padding: 14px;
97
+ text-align: center;
98
+ }
99
+ .rb-val { font-family: var(--font-head); font-size: 22px; font-weight: 800; }
100
+ .rb-label { font-size: 11px; color: var(--text2); margin-top: 4px; text-transform: uppercase; letter-spacing: 0.06em; }
101
+
102
+ .volatility-bar-wrap { margin-bottom: 12px; }
103
+ .vol-header { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 5px; }
104
+ .vol-name { color: var(--text2); font-weight: 600; }
105
+
106
+ .score-ring {
107
+ width: 80px; height: 80px;
108
+ border-radius: 50%;
109
+ display: flex; align-items: center; justify-content: center;
110
+ font-family: var(--font-head);
111
+ font-size: 24px;
112
+ font-weight: 800;
113
+ margin: 0 auto 12px;
114
+ position: relative;
115
+ }
116
+ .score-ring::before {
117
+ content: '';
118
+ position: absolute;
119
+ inset: 4px;
120
+ border-radius: 50%;
121
+ background: var(--bg2);
122
+ }
123
+ .score-ring span { position: relative; z-index: 1; }
124
+
125
+ .scenario-card {
126
+ background: var(--bg3);
127
+ border-radius: var(--r-sm);
128
+ padding: 16px;
129
+ border: 1px solid var(--border);
130
+ transition: all var(--transition);
131
+ }
132
+ .scenario-card:hover { border-color: var(--border2); }
133
+ .scenario-icon { font-size: 28px; margin-bottom: 8px; }
134
+ .scenario-title { font-weight: 700; font-size: 14px; margin-bottom: 6px; }
135
+ .scenario-impact { font-size: 13px; }
136
+ </style>
137
+ </head>
138
+ <body>
139
+ <div class="app-shell">
140
+ <nav class="sidebar">
141
+ <div class="sidebar-logo">
142
+ <div class="logo-mark">
143
+ <div class="logo-icon">📈</div>
144
+ <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
145
+ </div>
146
+ </div>
147
+ <div class="nav-section">
148
+ <div class="nav-label">Main</div>
149
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
150
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
151
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
152
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
153
+ <div class="nav-label">Tools</div>
154
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
155
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights</a>
156
+ </div>
157
+ <div class="sidebar-footer">
158
+ <div class="market-ticker">Live Market</div>
159
+ <div id="sidebar-tickers"></div>
160
+ </div>
161
+ </nav>
162
+
163
+ <main class="main-content">
164
+ <div class="page-header fade-in">
165
+ <div class="page-title">Risk <span>Analyzer</span></div>
166
+ <div class="page-subtitle">Understand and optimize your portfolio's risk profile</div>
167
+ </div>
168
+
169
+ <div class="grid-60-40">
170
+ <!-- Left Column -->
171
+ <div>
172
+
173
+ <!-- Quiz -->
174
+ <div class="card fade-in" id="quiz-section">
175
+ <div class="section-title">🧠 Risk Tolerance Assessment</div>
176
+ <div class="text-muted text-sm" style="margin-bottom:20px">Answer 5 quick questions — no typing needed</div>
177
+
178
+ <div id="quiz-container"></div>
179
+
180
+ <div id="quiz-result" class="hidden" style="margin-top:16px">
181
+ <div style="text-align:center;padding:20px">
182
+ <div style="font-size:48px;margin-bottom:8px" id="result-icon">⚖️</div>
183
+ <div style="font-family:var(--font-head);font-size:24px;font-weight:800;margin-bottom:8px" id="result-title">Moderate Investor</div>
184
+ <div style="color:var(--text2);font-size:14px;max-width:360px;margin:0 auto" id="result-desc"></div>
185
+ </div>
186
+ </div>
187
+
188
+ <div class="flex gap-12" style="margin-top:16px">
189
+ <button class="btn btn-ghost btn-sm" onclick="resetQuiz()">↺ Retake</button>
190
+ <button class="btn btn-primary" id="analyze-btn" onclick="runAnalysis()">Analyze My Risk →</button>
191
+ </div>
192
+ </div>
193
+
194
+ <!-- Volatility Breakdown -->
195
+ <div class="card fade-in fade-in-2" style="margin-top:20px">
196
+ <div class="section-title">📊 Asset Volatility</div>
197
+ <div id="volatility-bars"></div>
198
+ </div>
199
+
200
+ <!-- Scenarios -->
201
+ <div class="card fade-in fade-in-3" style="margin-top:20px">
202
+ <div class="section-title">🌪️ Scenario Simulation</div>
203
+ <div class="text-muted text-sm" style="margin-bottom:16px">How would your portfolio perform in these scenarios?</div>
204
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px" id="scenarios-grid"></div>
205
+ </div>
206
+
207
+ </div>
208
+
209
+ <!-- Right Column -->
210
+ <div>
211
+
212
+ <!-- Risk Gauge -->
213
+ <div class="card fade-in-1">
214
+ <div class="card-title">🎯 Risk Score</div>
215
+ <div class="gauge-container">
216
+ <svg class="gauge-svg" viewBox="0 0 220 120">
217
+ <!-- Background arcs -->
218
+ <path d="M 20 110 A 90 90 0 0 1 200 110" fill="none" stroke="var(--bg3)" stroke-width="18" stroke-linecap="round"/>
219
+ <!-- Colored sections -->
220
+ <path d="M 20 110 A 90 90 0 0 1 65 29" fill="none" stroke="#10b981" stroke-width="18" stroke-linecap="round" opacity="0.8"/>
221
+ <path d="M 65 29 A 90 90 0 0 1 110 20" fill="none" stroke="#22d3ee" stroke-width="18" opacity="0.8"/>
222
+ <path d="M 110 20 A 90 90 0 0 1 155 29" fill="none" stroke="#f59e0b" stroke-width="18" opacity="0.8"/>
223
+ <path d="M 155 29 A 90 90 0 0 1 200 110" fill="none" stroke="#f43f5e" stroke-width="18" stroke-linecap="round" opacity="0.8"/>
224
+ <!-- Needle -->
225
+ <g class="gauge-needle" id="gauge-needle" style="transform:rotate(-90deg)">
226
+ <line x1="110" y1="110" x2="110" y2="30" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
227
+ <circle cx="110" cy="110" r="6" fill="white"/>
228
+ </g>
229
+ </svg>
230
+ <div class="gauge-center-text">
231
+ <div class="gauge-score" id="gauge-score">—</div>
232
+ <div class="gauge-label">Risk Score</div>
233
+ </div>
234
+ </div>
235
+ <div style="text-align:center;margin-top:16px">
236
+ <span class="badge" id="gauge-badge" style="font-size:13px;padding:6px 16px">Calculating...</span>
237
+ </div>
238
+
239
+ <div class="divider"></div>
240
+ <div class="risk-breakdown">
241
+ <div class="rb-item">
242
+ <div class="rb-val" id="rb-div" style="color:var(--emerald)">—</div>
243
+ <div class="rb-label">Diversification</div>
244
+ </div>
245
+ <div class="rb-item">
246
+ <div class="rb-val" id="rb-concentrate" style="color:var(--amber)">—</div>
247
+ <div class="rb-label">Concentration</div>
248
+ </div>
249
+ <div class="rb-item">
250
+ <div class="rb-val" id="rb-equity" style="color:var(--violet)">—</div>
251
+ <div class="rb-label">Equity %</div>
252
+ </div>
253
+ <div class="rb-item">
254
+ <div class="rb-val" id="rb-safe" style="color:var(--cyan)">—</div>
255
+ <div class="rb-label">Safe Assets</div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+
260
+ <!-- Rebalancing Suggestions -->
261
+ <div class="card fade-in-2" style="margin-top:20px">
262
+ <div class="card-title">⚖️ Rebalancing Suggestions</div>
263
+ <div id="rebal-list">
264
+ <div style="color:var(--text2);font-size:13px;text-align:center;padding:20px">
265
+ Complete the risk assessment to see suggestions
266
+ </div>
267
+ </div>
268
+ </div>
269
+
270
+ <!-- Allocation Type Chart -->
271
+ <div class="card fade-in-3" style="margin-top:20px">
272
+ <div class="card-title">🏦 Asset Type Breakdown</div>
273
+ <div style="height:180px;position:relative">
274
+ <canvas id="typeChart"></canvas>
275
+ </div>
276
+ </div>
277
+
278
+ </div>
279
+ </div>
280
+
281
+ </main>
282
+ </div>
283
+
284
+ <nav class="bottom-nav">
285
+ <div class="bottom-nav-inner">
286
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
287
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
288
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
289
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
290
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
291
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
292
+ </div>
293
+ </nav>
294
+
295
+ <script src="shared.js"></script>
296
+ <script>
297
+ const QUESTIONS = [
298
+ {
299
+ q: "How long do you plan to keep your investments?",
300
+ answers: [
301
+ { text: "⏱️ Less than 1 year", score: 1 },
302
+ { text: "📅 1–3 years", score: 2 },
303
+ { text: "📆 3–10 years", score: 4 },
304
+ { text: "🗓️ 10+ years", score: 5 },
305
+ ]
306
+ },
307
+ {
308
+ q: "If your portfolio dropped 20% suddenly, you would:",
309
+ answers: [
310
+ { text: "😱 Sell everything immediately", score: 1 },
311
+ { text: "😟 Sell some to reduce exposure", score: 2 },
312
+ { text: "😐 Hold and wait for recovery", score: 4 },
313
+ { text: "😏 Buy more at lower prices", score: 5 },
314
+ ]
315
+ },
316
+ {
317
+ q: "What's your primary investment objective?",
318
+ answers: [
319
+ { text: "🛡️ Preserve what I have", score: 1 },
320
+ { text: "🌱 Steady, modest growth", score: 2 },
321
+ { text: "⚖️ Balance growth and safety", score: 3 },
322
+ { text: "🚀 Maximum long-term growth", score: 5 },
323
+ ]
324
+ },
325
+ {
326
+ q: "What % of your monthly income do you invest?",
327
+ answers: [
328
+ { text: "💸 Less than 5%", score: 1 },
329
+ { text: "💰 5–10%", score: 2 },
330
+ { text: "📈 10–25%", score: 4 },
331
+ { text: "🏦 More than 25%", score: 5 },
332
+ ]
333
+ },
334
+ {
335
+ q: "Which describes your investing experience?",
336
+ answers: [
337
+ { text: "🐣 Complete beginner", score: 1 },
338
+ { text: "📖 Read a bit about it", score: 2 },
339
+ { text: "🤓 Have some investments", score: 3 },
340
+ { text: "💹 Active investor / trader", score: 5 },
341
+ ]
342
+ },
343
+ ];
344
+
345
+ let answers = new Array(QUESTIONS.length).fill(null);
346
+ let quizComplete = false;
347
+
348
+ function renderQuiz() {
349
+ const container = document.getElementById('quiz-container');
350
+ container.innerHTML = QUESTIONS.map((q, qi) => `
351
+ <div class="card quiz-card ${answers[qi] === null && qi > 0 && answers[qi-1] === null ? 'inactive' : ''}"
352
+ id="quiz-card-${qi}" style="padding:16px;margin-bottom:12px;background:var(--bg3);border:1px solid var(--border)">
353
+ <div class="question-num">Question ${qi+1} of ${QUESTIONS.length}</div>
354
+ <div class="question-text">${q.q}</div>
355
+ <div class="answer-grid">
356
+ ${q.answers.map((a, ai) => `
357
+ <button class="answer-btn ${answers[qi] === ai ? 'selected' : ''}"
358
+ onclick="selectAnswer(${qi}, ${ai})">${a.text}</button>
359
+ `).join('')}
360
+ </div>
361
+ </div>
362
+ `).join('');
363
+ }
364
+
365
+ function selectAnswer(qi, ai) {
366
+ answers[qi] = ai;
367
+ document.querySelectorAll(`#quiz-card-${qi} .answer-btn`).forEach((btn, i) => {
368
+ btn.classList.toggle('selected', i === ai);
369
+ });
370
+ const next = document.getElementById('quiz-card-' + (qi+1));
371
+ if (next) next.classList.remove('inactive');
372
+
373
+ quizComplete = answers.every(a => a !== null);
374
+ if (quizComplete) {
375
+ document.getElementById('analyze-btn').style.background = 'linear-gradient(135deg,var(--emerald-d),var(--emerald))';
376
+ }
377
+ }
378
+
379
+ function resetQuiz() {
380
+ answers.fill(null);
381
+ quizComplete = false;
382
+ renderQuiz();
383
+ document.getElementById('quiz-result').classList.add('hidden');
384
+ }
385
+
386
+ function getTotalScore() {
387
+ return answers.reduce((s, ai, qi) => s + (ai !== null ? QUESTIONS[qi].answers[ai].score : 0), 0);
388
+ }
389
+
390
+ const RISK_RESULTS = [
391
+ null,
392
+ { icon:'🐢', title:'Very Conservative', desc:'You prefer capital safety above all. Bonds and stable assets suit you best. Low risk, low return.', color:'var(--emerald)' },
393
+ { icon:'🛡️', title:'Conservative', desc:'Modest growth with downside protection. A mix of bonds and blue-chip stocks works for you.', color:'var(--cyan)' },
394
+ { icon:'⚖️', title:'Moderate', desc:'Balanced approach. You accept some volatility for reasonable long-term gains.', color:'var(--amber)' },
395
+ { icon:'🚀', title:'Aggressive', desc:'Growth-focused investor. You can stomach volatility and aim for superior long-term returns.', color:'var(--rose)' },
396
+ { icon:'🦁', title:'Very Aggressive', desc:'Maximum growth seeker. High risk, high reward. Concentrated in equities and high-growth assets.', color:'var(--rose)' },
397
+ ];
398
+
399
+ function getProfileFromScore(score) {
400
+ if (score <= 8) return RISK_RESULTS[1];
401
+ if (score <= 12) return RISK_RESULTS[2];
402
+ if (score <= 16) return RISK_RESULTS[3];
403
+ if (score <= 20) return RISK_RESULTS[4];
404
+ return RISK_RESULTS[5];
405
+ }
406
+
407
+ function runAnalysis() {
408
+ if (!quizComplete) { showToast('Please answer all 5 questions first', 'error'); return; }
409
+
410
+ const score = getTotalScore();
411
+ const profile = getProfileFromScore(score);
412
+ document.getElementById('quiz-result').classList.remove('hidden');
413
+ document.getElementById('result-icon').textContent = profile.icon;
414
+ document.getElementById('result-title').textContent = profile.title;
415
+ document.getElementById('result-desc').textContent = profile.desc;
416
+
417
+ const portfolio = getPortfolio();
418
+ const riskScore = calcRiskScore(portfolio);
419
+ const divScore = calcDiversification(portfolio);
420
+
421
+ // Gauge
422
+ const deg = -90 + (riskScore / 100) * 180;
423
+ document.getElementById('gauge-needle').style.transform = `rotate(${deg}deg)`;
424
+ document.getElementById('gauge-score').textContent = riskScore;
425
+
426
+ const riskColors = ['badge-emerald','badge-cyan','badge-amber','badge-amber','badge-rose'];
427
+ const riskNames = ['Low Risk','Moderate-Low','Moderate','Moderate-High','High Risk'];
428
+ const rIdx = Math.floor(riskScore / 25);
429
+ const badge = document.getElementById('gauge-badge');
430
+ badge.className = 'badge ' + riskColors[rIdx];
431
+ badge.textContent = riskNames[rIdx];
432
+
433
+ // Breakdown
434
+ const equityPct = portfolio.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0);
435
+ const safePct = portfolio.assets.filter(a=>a.type==='Bond').reduce((s,a)=>s+a.pct,0);
436
+ const maxPct = Math.max(...portfolio.assets.map(a=>a.pct));
437
+ document.getElementById('rb-div').textContent = divScore + '%';
438
+ document.getElementById('rb-concentrate').textContent = maxPct + '%';
439
+ document.getElementById('rb-equity').textContent = equityPct + '%';
440
+ document.getElementById('rb-safe').textContent = safePct + '%';
441
+
442
+ renderRebalSuggestions(portfolio, riskScore, equityPct);
443
+ renderVolatility(portfolio);
444
+ renderScenarios(portfolio);
445
+ renderTypeChart(portfolio);
446
+ }
447
+
448
+ function renderRebalSuggestions(portfolio, riskScore, equityPct) {
449
+ const list = document.getElementById('rebal-list');
450
+ const suggestions = [];
451
+
452
+ const maxAsset = portfolio.assets.reduce((m, a) => a.pct > m.pct ? a : m, portfolio.assets[0]);
453
+ if (maxAsset.pct > 35) suggestions.push({
454
+ type:'REDUCE', color:'var(--rose)',
455
+ action: `Reduce ${maxAsset.ticker}`,
456
+ desc: `${maxAsset.ticker} is ${maxAsset.pct}% of your portfolio — consider trimming to under 30% for better balance.`,
457
+ arrow: '↓'
458
+ });
459
+
460
+ const bondPct = portfolio.assets.filter(a=>a.type==='Bond').reduce((s,a)=>s+a.pct,0);
461
+ if (bondPct < 10 && riskScore < 60) suggestions.push({
462
+ type:'ADD', color:'var(--emerald)',
463
+ action: 'Add Bonds (BND)',
464
+ desc: 'Your portfolio has little fixed income. Adding 10-15% bonds reduces volatility significantly.',
465
+ arrow: '+'
466
+ });
467
+
468
+ if (equityPct > 85) suggestions.push({
469
+ type:'DIVERSIFY', color:'var(--amber)',
470
+ action: 'Add Alternative Assets',
471
+ desc: 'Over 85% in equities. Consider Gold (GLD) or REITs for non-correlated returns.',
472
+ arrow: '↗'
473
+ });
474
+
475
+ const hasIntl = portfolio.assets.some(a=>a.ticker==='VEA'||a.ticker==='VXUS');
476
+ if (!hasIntl) suggestions.push({
477
+ type:'CONSIDER', color:'var(--cyan)',
478
+ action: 'Consider International Exposure',
479
+ desc: 'Adding 10-15% international ETFs (VEA, VXUS) can reduce US-market concentration risk.',
480
+ arrow: '🌍'
481
+ });
482
+
483
+ if (suggestions.length === 0) suggestions.push({
484
+ type:'OK', color:'var(--emerald)',
485
+ action: '✅ Portfolio looks healthy!',
486
+ desc: 'Your diversification and risk levels are well-balanced. Keep up with regular contributions.',
487
+ arrow: '👍'
488
+ });
489
+
490
+ list.innerHTML = suggestions.map(s => `
491
+ <div class="rebal-item">
492
+ <div>
493
+ <div class="rebal-action" style="color:${s.color}">${s.type}</div>
494
+ <div style="font-weight:700;font-size:14px;margin-bottom:4px">${s.action}</div>
495
+ <div class="rebal-desc">${s.desc}</div>
496
+ </div>
497
+ <div class="rebal-arrow">${s.arrow}</div>
498
+ </div>
499
+ `).join('');
500
+ }
501
+
502
+ const VOLATILITY_DATA = {
503
+ 'VOO': { vol: 15, beta: 1.00, color: '#22d3ee' },
504
+ 'QQQ': { vol: 22, beta: 1.18, color: '#8b5cf6' },
505
+ 'NVDA': { vol: 48, beta: 1.85, color: '#10b981' },
506
+ 'AAPL': { vol: 25, beta: 1.22, color: '#f59e0b' },
507
+ 'BND': { vol: 4, beta: 0.10, color: '#6366f1' },
508
+ 'GLD': { vol: 12, beta: 0.05, color: '#fbbf24' },
509
+ 'AMZN': { vol: 30, beta: 1.35, color: '#0ea5e9' },
510
+ 'VTI': { vol: 15, beta: 1.00, color: '#34d399' },
511
+ 'TSLA': { vol: 62, beta: 2.10, color: '#f43f5e' },
512
+ 'WMT': { vol: 8, beta: 0.55, color: '#34d399' },
513
+ 'MCD': { vol: 9, beta: 0.70, color: '#fb923c' },
514
+ };
515
+
516
+ function renderVolatility(portfolio) {
517
+ const container = document.getElementById('volatility-bars');
518
+ container.innerHTML = portfolio.assets.map(a => {
519
+ const vdata = VOLATILITY_DATA[a.ticker] || { vol: 20, beta: 1.0, color: '#22d3ee' };
520
+ const volColor = vdata.vol < 10 ? '#10b981' : vdata.vol < 25 ? '#f59e0b' : '#f43f5e';
521
+ return `
522
+ <div class="volatility-bar-wrap">
523
+ <div class="vol-header">
524
+ <span class="vol-name">${a.ticker} <span style="color:var(--text3);font-weight:400;font-size:11px">— ${a.name || ''}</span></span>
525
+ <span style="color:${volColor};font-family:var(--font-mono);font-size:12px">±${vdata.vol}% vol · β ${vdata.beta}</span>
526
+ </div>
527
+ <div class="progress-bar">
528
+ <div class="progress-fill" style="width:${Math.min(vdata.vol*1.2,100)}%;background:${volColor}"></div>
529
+ </div>
530
+ </div>
531
+ `;
532
+ }).join('');
533
+ }
534
+
535
+ function renderScenarios(portfolio) {
536
+ const equity = portfolio.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0)/100;
537
+ const bond = portfolio.assets.filter(a=>a.type==='Bond').reduce((s,a)=>s+a.pct,0)/100;
538
+ const gold = portfolio.assets.filter(a=>a.type==='Commodity').reduce((s,a)=>s+a.pct,0)/100;
539
+
540
+ const scenarios = [
541
+ { icon:'📉', title:'2008 Financial Crisis', impact: ((-38*equity) + (5*bond) + (8*gold)).toFixed(1), desc:'Worst-case equity selloff' },
542
+ { icon:'🦠', title:'2020 COVID Crash', impact: ((-30*equity) + (8*bond) + (5*gold)).toFixed(1), desc:'Fast drop, rapid recovery' },
543
+ { icon:'🔥', title:'Inflation Spike (10%)', impact: ((-15*equity) + (-8*bond) + (20*gold)).toFixed(1), desc:'Rising rates environment' },
544
+ { icon:'🚀', title:'Bull Market (+30%)', impact: ((30*equity) + (3*bond) + (8*gold)).toFixed(1), desc:'Strong economic growth' },
545
+ ];
546
+
547
+ document.getElementById('scenarios-grid').innerHTML = scenarios.map(s => {
548
+ const isPos = parseFloat(s.impact) >= 0;
549
+ return `
550
+ <div class="scenario-card">
551
+ <div class="scenario-icon">${s.icon}</div>
552
+ <div class="scenario-title">${s.title}</div>
553
+ <div class="scenario-impact" style="color:${isPos?'var(--emerald)':'var(--rose)'};font-family:var(--font-mono);font-size:18px;font-weight:700">
554
+ ${isPos?'+':''}${s.impact}%
555
+ </div>
556
+ <div style="font-size:11px;color:var(--text2);margin-top:4px">${s.desc}</div>
557
+ </div>
558
+ `;
559
+ }).join('');
560
+ }
561
+
562
+ function renderTypeChart(portfolio) {
563
+ const types = { ETF:0, Stock:0, Bond:0, Commodity:0 };
564
+ portfolio.assets.forEach(a => { types[a.type] = (types[a.type]||0) + a.pct; });
565
+
566
+ const ctx = document.getElementById('typeChart').getContext('2d');
567
+ new Chart(ctx, {
568
+ type: 'bar',
569
+ data: {
570
+ labels: Object.keys(types),
571
+ datasets: [{
572
+ data: Object.values(types),
573
+ backgroundColor: ['rgba(34,211,238,0.7)','rgba(16,185,129,0.7)','rgba(99,102,241,0.7)','rgba(245,158,11,0.7)'],
574
+ borderRadius: 6,
575
+ borderSkipped: false,
576
+ }]
577
+ },
578
+ options: {
579
+ responsive: true, maintainAspectRatio: false,
580
+ plugins: { legend: { display: false } },
581
+ scales: {
582
+ x: { grid: { display: false } },
583
+ y: { grid: { color: 'rgba(34,211,238,0.06)' }, ticks: { callback: v => v + '%' } }
584
+ }
585
+ }
586
+ });
587
+ }
588
+
589
+ document.addEventListener('DOMContentLoaded', () => {
590
+ applyChartDefaults();
591
+ renderQuiz();
592
+ // Auto-run analysis from saved portfolio
593
+ const portfolio = getPortfolio();
594
+ const riskScore = calcRiskScore(portfolio);
595
+ const deg = -90 + (riskScore/100)*180;
596
+ setTimeout(() => {
597
+ document.getElementById('gauge-needle').style.transform = `rotate(${deg}deg)`;
598
+ document.getElementById('gauge-score').textContent = riskScore;
599
+ const rIdx = Math.floor(riskScore/25);
600
+ const badge = document.getElementById('gauge-badge');
601
+ badge.className = 'badge ' + ['badge-emerald','badge-cyan','badge-amber','badge-amber','badge-rose'][rIdx];
602
+ badge.textContent = ['Low Risk','Moderate-Low','Moderate','Moderate-High','High Risk'][rIdx];
603
+ const equityPct = portfolio.assets.filter(a=>a.type==='Stock'||a.type==='ETF').reduce((s,a)=>s+a.pct,0);
604
+ const safePct = portfolio.assets.filter(a=>a.type==='Bond').reduce((s,a)=>s+a.pct,0);
605
+ const maxPct = Math.max(...portfolio.assets.map(a=>a.pct));
606
+ document.getElementById('rb-div').textContent = calcDiversification(portfolio) + '%';
607
+ document.getElementById('rb-concentrate').textContent = maxPct + '%';
608
+ document.getElementById('rb-equity').textContent = equityPct + '%';
609
+ document.getElementById('rb-safe').textContent = safePct + '%';
610
+ renderRebalSuggestions(portfolio, riskScore, equityPct);
611
+ renderVolatility(portfolio);
612
+ renderScenarios(portfolio);
613
+ renderTypeChart(portfolio);
614
+ }, 400);
615
+ });
616
+ </script>
617
+ </body>
618
+ </html>
shared.css ADDED
@@ -0,0 +1,772 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ================================================================
2
+ FinWise — Shared Design System
3
+ Aesthetic: Dark fintech luxury · Syne + DM Sans typography
4
+ ================================================================ */
5
+
6
+ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;1,400&family=DM+Mono:wght@400;500&display=swap');
7
+
8
+ /* ── Variables ─────────────────────────────────────────────────── */
9
+ :root {
10
+ --bg: #050d1a;
11
+ --bg2: #091525;
12
+ --bg3: #0d1f38;
13
+ --card: rgba(12,24,48,0.85);
14
+ --card2: rgba(18,34,66,0.70);
15
+ --border: rgba(34,211,238,0.12);
16
+ --border2: rgba(34,211,238,0.30);
17
+
18
+ --cyan: #22d3ee;
19
+ --cyan-d: #0891b2;
20
+ --emerald: #10b981;
21
+ --emerald-d: #059669;
22
+ --rose: #f43f5e;
23
+ --amber: #f59e0b;
24
+ --violet: #8b5cf6;
25
+ --indigo: #6366f1;
26
+
27
+ --text: #f0f6ff;
28
+ --text2: #8faac8;
29
+ --text3: #3d5a7a;
30
+
31
+ --nav-w: 240px;
32
+ --r: 14px;
33
+ --r-sm: 8px;
34
+ --r-lg: 20px;
35
+ --shadow: 0 8px 40px rgba(0,0,0,0.5);
36
+ --glow-c: 0 0 30px rgba(34,211,238,0.25);
37
+ --glow-e: 0 0 30px rgba(16,185,129,0.25);
38
+
39
+ --font-head: 'Syne', sans-serif;
40
+ --font-body: 'DM Sans', sans-serif;
41
+ --font-mono: 'DM Mono', monospace;
42
+
43
+ --transition: 0.22s cubic-bezier(.4,0,.2,1);
44
+ }
45
+
46
+ /* ── Reset & Base ──────────────────────────────────────────────── */
47
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
48
+
49
+ html { scroll-behavior: smooth; }
50
+
51
+ body {
52
+ font-family: var(--font-body);
53
+ background: var(--bg);
54
+ color: var(--text);
55
+ min-height: 100vh;
56
+ line-height: 1.6;
57
+ font-size: 15px;
58
+ overflow-x: hidden;
59
+ }
60
+
61
+ /* Subtle grid background */
62
+ body::before {
63
+ content: '';
64
+ position: fixed;
65
+ inset: 0;
66
+ background-image:
67
+ linear-gradient(rgba(34,211,238,0.03) 1px, transparent 1px),
68
+ linear-gradient(90deg, rgba(34,211,238,0.03) 1px, transparent 1px);
69
+ background-size: 40px 40px;
70
+ pointer-events: none;
71
+ z-index: 0;
72
+ }
73
+
74
+ /* ── Layout ────────────────────────────────────────────────────── */
75
+ .app-shell {
76
+ display: flex;
77
+ min-height: 100vh;
78
+ }
79
+
80
+ .main-content {
81
+ margin-left: var(--nav-w);
82
+ flex: 1;
83
+ padding: 32px;
84
+ position: relative;
85
+ z-index: 1;
86
+ max-width: calc(100vw - var(--nav-w));
87
+ }
88
+
89
+ /* ── Sidebar Navigation ────────────────────────────────────────── */
90
+ .sidebar {
91
+ position: fixed;
92
+ left: 0; top: 0; bottom: 0;
93
+ width: var(--nav-w);
94
+ background: linear-gradient(180deg, #071224 0%, #050d1a 100%);
95
+ border-right: 1px solid var(--border);
96
+ display: flex;
97
+ flex-direction: column;
98
+ z-index: 100;
99
+ padding: 0 0 20px;
100
+ overflow-y: auto;
101
+ }
102
+
103
+ .sidebar-logo {
104
+ padding: 28px 24px 24px;
105
+ border-bottom: 1px solid var(--border);
106
+ margin-bottom: 12px;
107
+ }
108
+
109
+ .sidebar-logo .logo-mark {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 10px;
113
+ }
114
+
115
+ .logo-icon {
116
+ width: 36px; height: 36px;
117
+ background: linear-gradient(135deg, var(--cyan), var(--emerald));
118
+ border-radius: 10px;
119
+ display: flex; align-items: center; justify-content: center;
120
+ font-size: 18px;
121
+ box-shadow: var(--glow-c);
122
+ }
123
+
124
+ .logo-text {
125
+ font-family: var(--font-head);
126
+ font-size: 22px;
127
+ font-weight: 800;
128
+ background: linear-gradient(90deg, var(--cyan), var(--emerald));
129
+ -webkit-background-clip: text;
130
+ -webkit-text-fill-color: transparent;
131
+ background-clip: text;
132
+ }
133
+
134
+ .logo-sub {
135
+ font-size: 11px;
136
+ color: var(--text3);
137
+ margin-top: 2px;
138
+ letter-spacing: 0.05em;
139
+ text-transform: uppercase;
140
+ }
141
+
142
+ .nav-section {
143
+ padding: 8px 12px;
144
+ flex: 1;
145
+ }
146
+
147
+ .nav-label {
148
+ font-size: 10px;
149
+ letter-spacing: 0.12em;
150
+ text-transform: uppercase;
151
+ color: var(--text3);
152
+ padding: 8px 12px 4px;
153
+ font-weight: 600;
154
+ }
155
+
156
+ .nav-item {
157
+ display: flex;
158
+ align-items: center;
159
+ gap: 12px;
160
+ padding: 11px 14px;
161
+ border-radius: var(--r-sm);
162
+ color: var(--text2);
163
+ text-decoration: none;
164
+ font-size: 14px;
165
+ font-weight: 500;
166
+ transition: all var(--transition);
167
+ margin-bottom: 2px;
168
+ position: relative;
169
+ overflow: hidden;
170
+ }
171
+
172
+ .nav-item::before {
173
+ content: '';
174
+ position: absolute;
175
+ inset: 0;
176
+ background: linear-gradient(90deg, var(--cyan), transparent);
177
+ opacity: 0;
178
+ transition: opacity var(--transition);
179
+ }
180
+
181
+ .nav-item:hover {
182
+ color: var(--text);
183
+ background: rgba(34,211,238,0.07);
184
+ }
185
+
186
+ .nav-item.active {
187
+ color: var(--cyan);
188
+ background: rgba(34,211,238,0.10);
189
+ border: 1px solid rgba(34,211,238,0.2);
190
+ }
191
+
192
+ .nav-item.active .nav-icon { filter: drop-shadow(0 0 6px var(--cyan)); }
193
+
194
+ .nav-icon {
195
+ font-size: 18px;
196
+ width: 24px;
197
+ text-align: center;
198
+ flex-shrink: 0;
199
+ }
200
+
201
+ .nav-badge {
202
+ margin-left: auto;
203
+ background: var(--cyan);
204
+ color: var(--bg);
205
+ font-size: 10px;
206
+ font-weight: 700;
207
+ padding: 2px 6px;
208
+ border-radius: 20px;
209
+ }
210
+
211
+ .sidebar-footer {
212
+ padding: 16px 20px 0;
213
+ border-top: 1px solid var(--border);
214
+ }
215
+
216
+ .market-ticker {
217
+ font-size: 11px;
218
+ color: var(--text3);
219
+ margin-bottom: 8px;
220
+ letter-spacing: 0.05em;
221
+ }
222
+
223
+ .ticker-item {
224
+ display: flex;
225
+ justify-content: space-between;
226
+ font-size: 12px;
227
+ padding: 3px 0;
228
+ }
229
+
230
+ .ticker-item .up { color: var(--emerald); font-family: var(--font-mono); }
231
+ .ticker-item .down { color: var(--rose); font-family: var(--font-mono); }
232
+ .ticker-name { color: var(--text2); font-weight: 500; }
233
+
234
+ /* ── Bottom Mobile Nav ─────────────────────────────────────────── */
235
+ .bottom-nav {
236
+ display: none;
237
+ position: fixed;
238
+ bottom: 0; left: 0; right: 0;
239
+ background: rgba(5,13,26,0.96);
240
+ backdrop-filter: blur(20px);
241
+ border-top: 1px solid var(--border);
242
+ padding: 8px 0 env(safe-area-inset-bottom);
243
+ z-index: 200;
244
+ }
245
+
246
+ .bottom-nav-inner {
247
+ display: flex;
248
+ justify-content: space-around;
249
+ }
250
+
251
+ .bottom-nav-item {
252
+ display: flex;
253
+ flex-direction: column;
254
+ align-items: center;
255
+ gap: 3px;
256
+ padding: 6px 12px;
257
+ color: var(--text3);
258
+ text-decoration: none;
259
+ font-size: 10px;
260
+ font-weight: 600;
261
+ transition: color var(--transition);
262
+ border-radius: var(--r-sm);
263
+ }
264
+
265
+ .bottom-nav-item .bnav-icon { font-size: 20px; }
266
+ .bottom-nav-item.active { color: var(--cyan); }
267
+ .bottom-nav-item:hover { color: var(--text2); }
268
+
269
+ /* ── Page Header ───────────────────────────────────────────────── */
270
+ .page-header {
271
+ margin-bottom: 32px;
272
+ }
273
+
274
+ .page-title {
275
+ font-family: var(--font-head);
276
+ font-size: 32px;
277
+ font-weight: 800;
278
+ line-height: 1.1;
279
+ margin-bottom: 6px;
280
+ }
281
+
282
+ .page-title span {
283
+ background: linear-gradient(90deg, var(--cyan), var(--emerald));
284
+ -webkit-background-clip: text;
285
+ -webkit-text-fill-color: transparent;
286
+ background-clip: text;
287
+ }
288
+
289
+ .page-subtitle { color: var(--text2); font-size: 15px; }
290
+
291
+ /* ── Cards ─────────────────────────────────────────────────────── */
292
+ .card {
293
+ background: var(--card);
294
+ border: 1px solid var(--border);
295
+ border-radius: var(--r);
296
+ padding: 24px;
297
+ backdrop-filter: blur(12px);
298
+ transition: border-color var(--transition), box-shadow var(--transition);
299
+ }
300
+
301
+ .card:hover {
302
+ border-color: var(--border2);
303
+ box-shadow: var(--glow-c);
304
+ }
305
+
306
+ .card-title {
307
+ font-family: var(--font-head);
308
+ font-size: 14px;
309
+ font-weight: 700;
310
+ color: var(--text2);
311
+ text-transform: uppercase;
312
+ letter-spacing: 0.08em;
313
+ margin-bottom: 16px;
314
+ display: flex;
315
+ align-items: center;
316
+ gap: 8px;
317
+ }
318
+
319
+ /* ── Stat Cards ────────────────────────────────────────────────── */
320
+ .stat-grid {
321
+ display: grid;
322
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
323
+ gap: 16px;
324
+ margin-bottom: 28px;
325
+ }
326
+
327
+ .stat-card {
328
+ background: var(--card);
329
+ border: 1px solid var(--border);
330
+ border-radius: var(--r);
331
+ padding: 22px;
332
+ backdrop-filter: blur(12px);
333
+ transition: all var(--transition);
334
+ position: relative;
335
+ overflow: hidden;
336
+ }
337
+
338
+ .stat-card::after {
339
+ content: '';
340
+ position: absolute;
341
+ top: 0; left: 0; right: 0;
342
+ height: 2px;
343
+ background: var(--accent-color, var(--cyan));
344
+ opacity: 0.7;
345
+ }
346
+
347
+ .stat-card:hover {
348
+ border-color: var(--border2);
349
+ transform: translateY(-2px);
350
+ box-shadow: 0 12px 40px rgba(0,0,0,0.4);
351
+ }
352
+
353
+ .stat-label {
354
+ font-size: 11px;
355
+ font-weight: 600;
356
+ text-transform: uppercase;
357
+ letter-spacing: 0.08em;
358
+ color: var(--text3);
359
+ margin-bottom: 10px;
360
+ }
361
+
362
+ .stat-value {
363
+ font-family: var(--font-head);
364
+ font-size: 26px;
365
+ font-weight: 800;
366
+ color: var(--text);
367
+ line-height: 1;
368
+ margin-bottom: 8px;
369
+ }
370
+
371
+ .stat-change {
372
+ font-size: 12px;
373
+ font-weight: 600;
374
+ display: flex;
375
+ align-items: center;
376
+ gap: 4px;
377
+ }
378
+
379
+ .stat-change.up { color: var(--emerald); }
380
+ .stat-change.down { color: var(--rose); }
381
+
382
+ /* ── Grids ─────────────────────────────────────────────────────── */
383
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
384
+ .grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 20px; }
385
+ .grid-60-40 { display: grid; grid-template-columns: 60fr 40fr; gap: 20px; }
386
+ .grid-40-60 { display: grid; grid-template-columns: 40fr 60fr; gap: 20px; }
387
+
388
+ /* ── Buttons ───────────────────────────────────────────────────── */
389
+ .btn {
390
+ display: inline-flex;
391
+ align-items: center;
392
+ gap: 8px;
393
+ padding: 11px 22px;
394
+ border-radius: var(--r-sm);
395
+ font-family: var(--font-body);
396
+ font-size: 14px;
397
+ font-weight: 600;
398
+ cursor: pointer;
399
+ border: none;
400
+ transition: all var(--transition);
401
+ text-decoration: none;
402
+ }
403
+
404
+ .btn-primary {
405
+ background: linear-gradient(135deg, var(--cyan-d), var(--cyan));
406
+ color: var(--bg);
407
+ box-shadow: 0 4px 16px rgba(34,211,238,0.3);
408
+ }
409
+ .btn-primary:hover {
410
+ box-shadow: 0 6px 24px rgba(34,211,238,0.5);
411
+ transform: translateY(-1px);
412
+ }
413
+
414
+ .btn-secondary {
415
+ background: transparent;
416
+ color: var(--cyan);
417
+ border: 1px solid rgba(34,211,238,0.3);
418
+ }
419
+ .btn-secondary:hover {
420
+ background: rgba(34,211,238,0.08);
421
+ border-color: var(--cyan);
422
+ }
423
+
424
+ .btn-ghost {
425
+ background: rgba(255,255,255,0.04);
426
+ color: var(--text2);
427
+ border: 1px solid var(--border);
428
+ }
429
+ .btn-ghost:hover { background: rgba(255,255,255,0.08); color: var(--text); }
430
+
431
+ .btn-emerald {
432
+ background: linear-gradient(135deg, var(--emerald-d), var(--emerald));
433
+ color: #fff;
434
+ box-shadow: var(--glow-e);
435
+ }
436
+
437
+ .btn-sm { padding: 7px 14px; font-size: 13px; }
438
+ .btn-lg { padding: 14px 28px; font-size: 16px; }
439
+ .btn-full { width: 100%; justify-content: center; }
440
+
441
+ /* ── Choice Buttons ────────────────────────────────────────────── */
442
+ .choice-group {
443
+ display: flex;
444
+ flex-wrap: wrap;
445
+ gap: 10px;
446
+ margin-bottom: 20px;
447
+ }
448
+
449
+ .choice-btn {
450
+ padding: 10px 18px;
451
+ border-radius: 100px;
452
+ border: 1px solid var(--border2);
453
+ background: transparent;
454
+ color: var(--text2);
455
+ font-family: var(--font-body);
456
+ font-size: 13px;
457
+ font-weight: 600;
458
+ cursor: pointer;
459
+ transition: all var(--transition);
460
+ display: flex;
461
+ align-items: center;
462
+ gap: 6px;
463
+ }
464
+
465
+ .choice-btn:hover { border-color: var(--cyan); color: var(--text); }
466
+
467
+ .choice-btn.selected {
468
+ background: rgba(34,211,238,0.12);
469
+ border-color: var(--cyan);
470
+ color: var(--cyan);
471
+ box-shadow: 0 0 16px rgba(34,211,238,0.2);
472
+ }
473
+
474
+ /* ── Sliders ───────────────────────────────────────────────────── */
475
+ .slider-wrap { margin-bottom: 20px; }
476
+ .slider-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
477
+ .slider-label { font-size: 13px; color: var(--text2); font-weight: 500; }
478
+ .slider-val { font-family: var(--font-mono); font-size: 14px; color: var(--cyan); font-weight: 500; }
479
+
480
+ input[type=range] {
481
+ -webkit-appearance: none;
482
+ width: 100%;
483
+ height: 6px;
484
+ border-radius: 3px;
485
+ background: var(--bg3);
486
+ outline: none;
487
+ cursor: pointer;
488
+ }
489
+
490
+ input[type=range]::-webkit-slider-thumb {
491
+ -webkit-appearance: none;
492
+ width: 18px; height: 18px;
493
+ border-radius: 50%;
494
+ background: linear-gradient(135deg, var(--cyan-d), var(--cyan));
495
+ box-shadow: 0 0 10px rgba(34,211,238,0.5);
496
+ cursor: pointer;
497
+ transition: box-shadow var(--transition);
498
+ }
499
+ input[type=range]::-webkit-slider-thumb:hover {
500
+ box-shadow: 0 0 18px rgba(34,211,238,0.8);
501
+ }
502
+
503
+ /* ── Progress Bar ──────────────────────────────────────────────── */
504
+ .progress-bar {
505
+ height: 8px;
506
+ background: var(--bg3);
507
+ border-radius: 4px;
508
+ overflow: hidden;
509
+ margin-bottom: 4px;
510
+ }
511
+
512
+ .progress-fill {
513
+ height: 100%;
514
+ border-radius: 4px;
515
+ transition: width 0.6s cubic-bezier(.4,0,.2,1);
516
+ }
517
+
518
+ /* ── Tags / Badges ─────────────────────────────────────────────── */
519
+ .badge {
520
+ display: inline-flex;
521
+ align-items: center;
522
+ gap: 5px;
523
+ padding: 4px 10px;
524
+ border-radius: 100px;
525
+ font-size: 11px;
526
+ font-weight: 700;
527
+ letter-spacing: 0.04em;
528
+ text-transform: uppercase;
529
+ }
530
+
531
+ .badge-cyan { background: rgba(34,211,238,0.15); color: var(--cyan); }
532
+ .badge-emerald { background: rgba(16,185,129,0.15); color: var(--emerald); }
533
+ .badge-rose { background: rgba(244,63,94,0.15); color: var(--rose); }
534
+ .badge-amber { background: rgba(245,158,11,0.15); color: var(--amber); }
535
+ .badge-violet { background: rgba(139,92,246,0.15); color: var(--violet); }
536
+
537
+ /* ── Table ─────────────────────────────────────────────────────── */
538
+ .table-wrap { overflow-x: auto; }
539
+
540
+ table {
541
+ width: 100%;
542
+ border-collapse: collapse;
543
+ font-size: 14px;
544
+ }
545
+
546
+ thead th {
547
+ padding: 12px 16px;
548
+ text-align: left;
549
+ font-size: 11px;
550
+ font-weight: 700;
551
+ text-transform: uppercase;
552
+ letter-spacing: 0.08em;
553
+ color: var(--text3);
554
+ border-bottom: 1px solid var(--border);
555
+ }
556
+
557
+ tbody tr {
558
+ border-bottom: 1px solid rgba(34,211,238,0.05);
559
+ transition: background var(--transition);
560
+ }
561
+
562
+ tbody tr:hover { background: rgba(34,211,238,0.04); }
563
+
564
+ tbody td {
565
+ padding: 14px 16px;
566
+ color: var(--text2);
567
+ vertical-align: middle;
568
+ }
569
+
570
+ tbody td:first-child { color: var(--text); font-weight: 600; }
571
+
572
+ /* ── Tooltips (simple) ─────────────────────────────────────────── */
573
+ .tooltip-wrap { position: relative; display: inline-flex; }
574
+ .tooltip-icon {
575
+ width: 16px; height: 16px;
576
+ border-radius: 50%;
577
+ background: var(--bg3);
578
+ border: 1px solid var(--border2);
579
+ color: var(--text3);
580
+ font-size: 10px;
581
+ display: inline-flex;
582
+ align-items: center;
583
+ justify-content: center;
584
+ cursor: help;
585
+ font-weight: 700;
586
+ font-style: italic;
587
+ }
588
+
589
+ /* ── Section headings ──────────────────────────────────────────── */
590
+ .section-title {
591
+ font-family: var(--font-head);
592
+ font-size: 18px;
593
+ font-weight: 700;
594
+ margin-bottom: 16px;
595
+ display: flex;
596
+ align-items: center;
597
+ gap: 10px;
598
+ }
599
+
600
+ /* ── Divider ───────────────────────────────────────────────────── */
601
+ .divider { height: 1px; background: var(--border); margin: 24px 0; }
602
+
603
+ /* ── Input Fields ──────────────────────────────────────────────── */
604
+ .field-group { margin-bottom: 16px; }
605
+ .field-label {
606
+ display: block;
607
+ font-size: 12px;
608
+ font-weight: 600;
609
+ color: var(--text2);
610
+ text-transform: uppercase;
611
+ letter-spacing: 0.06em;
612
+ margin-bottom: 6px;
613
+ }
614
+
615
+ input[type=text], input[type=number], select, textarea {
616
+ width: 100%;
617
+ padding: 11px 14px;
618
+ background: var(--bg3);
619
+ border: 1px solid var(--border);
620
+ border-radius: var(--r-sm);
621
+ color: var(--text);
622
+ font-family: var(--font-body);
623
+ font-size: 14px;
624
+ outline: none;
625
+ transition: border-color var(--transition);
626
+ }
627
+
628
+ input[type=text]:focus,
629
+ input[type=number]:focus,
630
+ select:focus,
631
+ textarea:focus {
632
+ border-color: var(--cyan);
633
+ box-shadow: 0 0 0 3px rgba(34,211,238,0.1);
634
+ }
635
+
636
+ select option { background: var(--bg2); }
637
+
638
+ /* ── Tabs ──────────────────────────────────────────────────────── */
639
+ .tab-bar {
640
+ display: flex;
641
+ gap: 4px;
642
+ background: var(--bg3);
643
+ border-radius: var(--r-sm);
644
+ padding: 4px;
645
+ margin-bottom: 24px;
646
+ flex-wrap: wrap;
647
+ }
648
+
649
+ .tab-btn {
650
+ flex: 1;
651
+ min-width: 120px;
652
+ padding: 9px 16px;
653
+ border-radius: 6px;
654
+ border: none;
655
+ background: transparent;
656
+ color: var(--text2);
657
+ font-family: var(--font-body);
658
+ font-size: 13px;
659
+ font-weight: 600;
660
+ cursor: pointer;
661
+ transition: all var(--transition);
662
+ white-space: nowrap;
663
+ text-align: center;
664
+ }
665
+
666
+ .tab-btn.active {
667
+ background: var(--card);
668
+ color: var(--cyan);
669
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
670
+ }
671
+
672
+ .tab-pane { display: none; }
673
+ .tab-pane.active { display: block; }
674
+
675
+ /* ── Risk Meter ────────────────────────────────────────────────── */
676
+ .risk-meter-wrap {
677
+ text-align: center;
678
+ padding: 20px 0;
679
+ }
680
+
681
+ .risk-meter-label {
682
+ font-size: 13px;
683
+ color: var(--text2);
684
+ margin-top: 12px;
685
+ font-weight: 600;
686
+ }
687
+
688
+ /* ── Allocation Bar ────────────────────────────────────────────── */
689
+ .alloc-bar {
690
+ height: 10px;
691
+ border-radius: 5px;
692
+ display: flex;
693
+ overflow: hidden;
694
+ gap: 2px;
695
+ margin: 10px 0;
696
+ }
697
+
698
+ .alloc-segment {
699
+ height: 100%;
700
+ border-radius: 5px;
701
+ transition: width 0.5s cubic-bezier(.4,0,.2,1);
702
+ }
703
+
704
+ /* ── Animations ────────────────────────────────────────────────── */
705
+ @keyframes fadeInUp {
706
+ from { opacity: 0; transform: translateY(16px); }
707
+ to { opacity: 1; transform: translateY(0); }
708
+ }
709
+
710
+ @keyframes pulse-glow {
711
+ 0%, 100% { box-shadow: 0 0 10px rgba(34,211,238,0.2); }
712
+ 50% { box-shadow: 0 0 25px rgba(34,211,238,0.5); }
713
+ }
714
+
715
+ @keyframes counter { from { opacity: 0; } to { opacity: 1; } }
716
+
717
+ .fade-in { animation: fadeInUp 0.5s ease both; }
718
+ .fade-in-1 { animation-delay: 0.1s; }
719
+ .fade-in-2 { animation-delay: 0.2s; }
720
+ .fade-in-3 { animation-delay: 0.3s; }
721
+ .fade-in-4 { animation-delay: 0.4s; }
722
+
723
+ /* ── Utility ───────────────────────────────────────────────────── */
724
+ .up { color: var(--emerald); }
725
+ .down { color: var(--rose); }
726
+ .text-muted { color: var(--text2); }
727
+ .text-sm { font-size: 13px; }
728
+ .mono { font-family: var(--font-mono); }
729
+ .mt-0 { margin-top: 0; }
730
+ .mt-16 { margin-top: 16px; }
731
+ .mt-24 { margin-top: 24px; }
732
+ .mb-16 { margin-bottom: 16px; }
733
+ .mb-24 { margin-bottom: 24px; }
734
+ .flex { display: flex; }
735
+ .flex-col { flex-direction: column; }
736
+ .items-center { align-items: center; }
737
+ .justify-between { justify-content: space-between; }
738
+ .gap-8 { gap: 8px; }
739
+ .gap-12 { gap: 12px; }
740
+ .gap-16 { gap: 16px; }
741
+ .full-width { width: 100%; }
742
+ .text-right { text-align: right; }
743
+ .text-center { text-align: center; }
744
+ .bold { font-weight: 700; }
745
+ .hidden { display: none !important; }
746
+
747
+ /* ── Scroll ────────────────────────────────────────────────────── */
748
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
749
+ ::-webkit-scrollbar-track { background: var(--bg); }
750
+ ::-webkit-scrollbar-thumb { background: var(--bg3); border-radius: 3px; }
751
+ ::-webkit-scrollbar-thumb:hover { background: var(--text3); }
752
+
753
+ /* ── Responsive ────────────────────────────────────────────────── */
754
+ @media (max-width: 900px) {
755
+ .sidebar { display: none; }
756
+ .bottom-nav { display: block; }
757
+ .main-content {
758
+ margin-left: 0;
759
+ padding: 20px 16px 80px;
760
+ max-width: 100vw;
761
+ }
762
+ .grid-2, .grid-3, .grid-60-40, .grid-40-60 {
763
+ grid-template-columns: 1fr;
764
+ }
765
+ .stat-grid { grid-template-columns: 1fr 1fr; }
766
+ .page-title { font-size: 24px; }
767
+ }
768
+
769
+ @media (max-width: 480px) {
770
+ .stat-grid { grid-template-columns: 1fr; }
771
+ .choice-group { flex-direction: column; }
772
+ }
shared.js ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* ================================================================
2
+ FinWise — Shared JavaScript
3
+ Utilities, navigation, localStorage helpers
4
+ ================================================================ */
5
+
6
+ // ── Page detection ────────────────────────────────────────────────
7
+ (function setActiveNav() {
8
+ const page = location.pathname.split('/').pop() || 'index.html';
9
+ document.querySelectorAll('.nav-item, .bottom-nav-item').forEach(el => {
10
+ const href = el.getAttribute('href') || '';
11
+ if (href === page || (page === '' && href === 'index.html')) {
12
+ el.classList.add('active');
13
+ }
14
+ });
15
+ })();
16
+
17
+ // ── LocalStorage Helpers ──────────────────────────────────────────
18
+ const LS = {
19
+ get(key, fallback = null) {
20
+ try { const v = localStorage.getItem(key); return v ? JSON.parse(v) : fallback; }
21
+ catch { return fallback; }
22
+ },
23
+ set(key, val) {
24
+ try { localStorage.setItem(key, JSON.stringify(val)); return true; }
25
+ catch { return false; }
26
+ }
27
+ };
28
+
29
+ // ── Default Portfolio Data ────────────────────────────────────────
30
+ const DEFAULT_PORTFOLIO = {
31
+ assets: [
32
+ { ticker: 'VOO', name: 'Vanguard S&P 500 ETF', pct: 30, price: 478.22, shares: 3.1, color: '#22d3ee', type: 'ETF' },
33
+ { ticker: 'QQQ', name: 'Invesco Nasdaq 100 ETF', pct: 20, price: 456.80, shares: 2.2, color: '#8b5cf6', type: 'ETF' },
34
+ { ticker: 'NVDA', name: 'NVIDIA Corporation', pct: 15, price: 875.40, shares: 0.85, color: '#10b981', type: 'Stock' },
35
+ { ticker: 'AAPL', name: 'Apple Inc.', pct: 12, price: 188.60, shares: 3.2, color: '#f59e0b', type: 'Stock' },
36
+ { ticker: 'BND', name: 'Vanguard Bond ETF', pct: 13, price: 73.40, shares: 8.8, color: '#6366f1', type: 'Bond' },
37
+ { ticker: 'GLD', name: 'SPDR Gold Trust', pct: 7, price: 218.10, shares: 1.6, color: '#f43f5e', type: 'Commodity' },
38
+ { ticker: 'AMZN', name: 'Amazon.com Inc.', pct: 3, price: 188.90, shares: 0.8, color: '#0ea5e9', type: 'Stock' },
39
+ ],
40
+ totalInvested: 12500,
41
+ riskProfile: 'Moderate',
42
+ goals: ['Wealth Building'],
43
+ lastUpdated: new Date().toISOString()
44
+ };
45
+
46
+ function getPortfolio() {
47
+ return LS.get('fw_portfolio', DEFAULT_PORTFOLIO);
48
+ }
49
+
50
+ function savePortfolio(p) {
51
+ p.lastUpdated = new Date().toISOString();
52
+ LS.set('fw_portfolio', p);
53
+ }
54
+
55
+ // ── Simulated Market Data ─────────────────────────────────────────
56
+ const MARKET_PRICES = {
57
+ 'VOO': { price: 478.22, change: +1.24, changePct: +0.26 },
58
+ 'QQQ': { price: 456.80, change: -2.10, changePct: -0.46 },
59
+ 'NVDA': { price: 875.40, change: +18.5, changePct: +2.16 },
60
+ 'AAPL': { price: 188.60, change: +0.80, changePct: +0.43 },
61
+ 'BND': { price: 73.40, change: -0.05, changePct: -0.07 },
62
+ 'GLD': { price: 218.10, change: +3.20, changePct: +1.49 },
63
+ 'AMZN': { price: 188.90, change: +1.60, changePct: +0.86 },
64
+ 'VTI': { price: 240.30, change: +0.94, changePct: +0.39 },
65
+ 'TSLA': { price: 182.30, change: -4.20, changePct: -2.25 },
66
+ 'WMT': { price: 67.80, change: +0.30, changePct: +0.44 },
67
+ 'MCD': { price: 281.50, change: +1.10, changePct: +0.39 },
68
+ };
69
+
70
+ // ── Historical Performance Generator ─────────────────────────────
71
+ function generateHistory(days = 180, startVal = 10000, volatility = 0.012) {
72
+ const data = [];
73
+ let val = startVal;
74
+ const now = Date.now();
75
+ for (let i = days; i >= 0; i--) {
76
+ const date = new Date(now - i * 86400000);
77
+ const change = (Math.random() - 0.46) * volatility;
78
+ val = val * (1 + change);
79
+ data.push({ date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }), value: Math.round(val * 100) / 100 });
80
+ }
81
+ return data;
82
+ }
83
+
84
+ // ── Number Formatting ─────────────────────────────────────────────
85
+ function fmt$(n) { return '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
86
+ function fmtPct(n) { return (n > 0 ? '+' : '') + n.toFixed(2) + '%'; }
87
+ function fmtK(n) { return n >= 1000000 ? '$' + (n/1000000).toFixed(2) + 'M' : n >= 1000 ? '$' + (n/1000).toFixed(1) + 'K' : '$' + n.toFixed(0); }
88
+
89
+ // ── Animated Counter ──────────────────────────────────────────────
90
+ function animateCounter(el, target, prefix = '', suffix = '', duration = 1200) {
91
+ const start = parseFloat(el.textContent.replace(/[^0-9.-]/g, '')) || 0;
92
+ const startTime = performance.now();
93
+ function step(now) {
94
+ const p = Math.min((now - startTime) / duration, 1);
95
+ const ease = 1 - Math.pow(1 - p, 3);
96
+ const val = start + (target - start) * ease;
97
+ el.textContent = prefix + val.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + suffix;
98
+ if (p < 1) requestAnimationFrame(step);
99
+ }
100
+ requestAnimationFrame(step);
101
+ }
102
+
103
+ // ── Chart.js Defaults ─────────────────────────────────────────────
104
+ function applyChartDefaults() {
105
+ if (typeof Chart === 'undefined') return;
106
+ Chart.defaults.color = '#8faac8';
107
+ Chart.defaults.borderColor = 'rgba(34,211,238,0.10)';
108
+ Chart.defaults.font.family = "'DM Sans', sans-serif";
109
+ }
110
+
111
+ // ── Ticker Data for Sidebar ───────────────────────────────────────
112
+ const TICKERS = [
113
+ { sym: 'S&P 500', val: '5,308', chg: '+0.26%', up: true },
114
+ { sym: 'NASDAQ', val: '16,742', chg: '-0.46%', up: false },
115
+ { sym: 'BTC', val: '68,420', chg: '+2.14%', up: true },
116
+ { sym: 'GOLD', val: '2,318', chg: '+1.49%', up: true },
117
+ ];
118
+
119
+ function renderSidebarTickers() {
120
+ const container = document.getElementById('sidebar-tickers');
121
+ if (!container) return;
122
+ container.innerHTML = TICKERS.map(t => `
123
+ <div class="ticker-item">
124
+ <span class="ticker-name">${t.sym}</span>
125
+ <span class="${t.up ? 'up' : 'down'}">${t.chg}</span>
126
+ </div>
127
+ `).join('');
128
+ }
129
+
130
+ // ── Risk Score Calculator ─────────────────────────────────────────
131
+ function calcRiskScore(portfolio) {
132
+ const weights = { ETF: 3, Stock: 5, Bond: 1, Commodity: 4 };
133
+ let score = 0;
134
+ portfolio.assets.forEach(a => {
135
+ score += (weights[a.type] || 3) * (a.pct / 100);
136
+ });
137
+ return Math.round((score / 5) * 100); // 0-100
138
+ }
139
+
140
+ // ── Diversification Score ─────────────────────────────────────────
141
+ function calcDiversification(portfolio) {
142
+ const types = [...new Set(portfolio.assets.map(a => a.type))].length;
143
+ const count = portfolio.assets.length;
144
+ const maxPct = Math.max(...portfolio.assets.map(a => a.pct));
145
+ const concentration = maxPct > 40 ? 0.6 : maxPct > 25 ? 0.8 : 1.0;
146
+ return Math.round(((types / 4) * 0.4 + (Math.min(count, 7) / 7) * 0.4 + concentration * 0.2) * 100);
147
+ }
148
+
149
+ // ── Color Palette ─────────────────────────────────────────────────
150
+ const ASSET_COLORS = ['#22d3ee','#10b981','#8b5cf6','#f59e0b','#f43f5e','#6366f1','#0ea5e9','#34d399','#a78bfa','#fb923c'];
151
+
152
+ // ── Toast Notification ────────────────────────────────────────────
153
+ function showToast(msg, type = 'success') {
154
+ const t = document.createElement('div');
155
+ t.style.cssText = `
156
+ position:fixed; bottom:90px; right:20px; z-index:9999;
157
+ padding:12px 20px; border-radius:10px; font-size:13px; font-weight:600;
158
+ background:${type === 'success' ? 'rgba(16,185,129,0.95)' : 'rgba(244,63,94,0.95)'};
159
+ color:#fff; box-shadow:0 8px 32px rgba(0,0,0,0.4);
160
+ animation: fadeInUp 0.3s ease;
161
+ max-width: 300px;
162
+ `;
163
+ t.textContent = msg;
164
+ document.body.appendChild(t);
165
+ setTimeout(() => t.remove(), 3000);
166
+ }
167
+
168
+ // ── On DOM Ready ──────────────────────────────────────────────────
169
+ document.addEventListener('DOMContentLoaded', () => {
170
+ applyChartDefaults();
171
+ renderSidebarTickers();
172
+ });
tracker.html ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>FinWise — Investment Tracker</title>
7
+ <link rel="stylesheet" href="shared.css">
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
9
+ <style>
10
+ .tracker-header-bar {
11
+ display: flex;
12
+ align-items: center;
13
+ justify-content: space-between;
14
+ flex-wrap: wrap;
15
+ gap: 12px;
16
+ margin-bottom: 20px;
17
+ }
18
+ .holding-row td:first-child { font-weight: 700; }
19
+ .gain-cell { font-family: var(--font-mono); font-weight: 700; }
20
+ .ticker-cell {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: 10px;
24
+ }
25
+ .ticker-logo {
26
+ width: 34px; height: 34px;
27
+ border-radius: 8px;
28
+ display: flex; align-items: center; justify-content: center;
29
+ font-size: 9px;
30
+ font-weight: 800;
31
+ font-family: var(--font-mono);
32
+ color: var(--bg);
33
+ flex-shrink: 0;
34
+ }
35
+ .ticker-name-sub { font-size: 11px; color: var(--text2); font-weight: 400; }
36
+ .mini-chart { width: 80px; height: 34px; }
37
+ .pnl-bar-mini { height: 3px; border-radius: 2px; margin-top: 4px; }
38
+
39
+ .edit-input {
40
+ background: transparent;
41
+ border: none;
42
+ border-bottom: 1px dashed var(--border2);
43
+ color: var(--text);
44
+ font-family: var(--font-mono);
45
+ font-size: 13px;
46
+ width: 80px;
47
+ padding: 2px 4px;
48
+ text-align: right;
49
+ outline: none;
50
+ }
51
+ .edit-input:focus { border-bottom-color: var(--cyan); }
52
+
53
+ .add-holding-form {
54
+ display: grid;
55
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
56
+ gap: 12px;
57
+ padding: 16px;
58
+ background: var(--bg3);
59
+ border-radius: var(--r-sm);
60
+ border: 1px dashed var(--border2);
61
+ margin-top: 16px;
62
+ }
63
+
64
+ .summary-strip {
65
+ display: grid;
66
+ grid-template-columns: repeat(auto-fit, minmax(140px,1fr));
67
+ gap: 12px;
68
+ margin-bottom: 20px;
69
+ }
70
+ .ss-item {
71
+ background: var(--card);
72
+ border: 1px solid var(--border);
73
+ border-radius: var(--r-sm);
74
+ padding: 14px 16px;
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 4px;
78
+ }
79
+ .ss-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text3); font-weight: 700; }
80
+ .ss-val { font-family: var(--font-head); font-size: 20px; font-weight: 800; }
81
+
82
+ .period-tabs {
83
+ display: flex;
84
+ gap: 4px;
85
+ background: var(--bg3);
86
+ border-radius: 8px;
87
+ padding: 3px;
88
+ }
89
+ .period-tab {
90
+ padding: 6px 14px;
91
+ border-radius: 6px;
92
+ border: none;
93
+ background: transparent;
94
+ color: var(--text2);
95
+ font-size: 12px;
96
+ font-weight: 700;
97
+ cursor: pointer;
98
+ font-family: var(--font-body);
99
+ transition: all var(--transition);
100
+ }
101
+ .period-tab.active { background: var(--card); color: var(--cyan); }
102
+
103
+ .sort-header { cursor: pointer; user-select: none; white-space: nowrap; }
104
+ .sort-header:hover { color: var(--cyan); }
105
+ .sort-arrow { margin-left: 4px; opacity: 0.5; font-size: 10px; }
106
+
107
+ .delete-btn {
108
+ background: none;
109
+ border: none;
110
+ color: var(--text3);
111
+ cursor: pointer;
112
+ font-size: 16px;
113
+ padding: 4px;
114
+ border-radius: 4px;
115
+ transition: all var(--transition);
116
+ }
117
+ .delete-btn:hover { color: var(--rose); background: rgba(244,63,94,0.1); }
118
+
119
+ .watch-tag {
120
+ display: inline-flex;
121
+ align-items: center;
122
+ gap: 4px;
123
+ font-size: 10px;
124
+ font-weight: 700;
125
+ padding: 2px 8px;
126
+ border-radius: 100px;
127
+ text-transform: uppercase;
128
+ letter-spacing: 0.05em;
129
+ }
130
+ </style>
131
+ </head>
132
+ <body>
133
+ <div class="app-shell">
134
+ <nav class="sidebar">
135
+ <div class="sidebar-logo">
136
+ <div class="logo-mark">
137
+ <div class="logo-icon">📈</div>
138
+ <div><div class="logo-text">FinWise</div><div class="logo-sub">Smart Investing</div></div>
139
+ </div>
140
+ </div>
141
+ <div class="nav-section">
142
+ <div class="nav-label">Main</div>
143
+ <a href="index.html" class="nav-item"><span class="nav-icon">🏠</span> Dashboard</a>
144
+ <a href="portfolio.html" class="nav-item"><span class="nav-icon">📊</span> Portfolio Builder</a>
145
+ <a href="risk.html" class="nav-item"><span class="nav-icon">🎯</span> Risk Analyzer</a>
146
+ <a href="tracker.html" class="nav-item"><span class="nav-icon">📈</span> Tracker</a>
147
+ <div class="nav-label">Tools</div>
148
+ <a href="calculators.html" class="nav-item"><span class="nav-icon">🧮</span> Calculators</a>
149
+ <a href="insights.html" class="nav-item"><span class="nav-icon">💡</span> Insights</a>
150
+ </div>
151
+ <div class="sidebar-footer">
152
+ <div class="market-ticker">Live Market</div>
153
+ <div id="sidebar-tickers"></div>
154
+ </div>
155
+ </nav>
156
+
157
+ <main class="main-content">
158
+ <div class="page-header fade-in">
159
+ <div class="page-title">Investment <span>Tracker</span></div>
160
+ <div class="page-subtitle">Track your holdings, gains, and portfolio performance</div>
161
+ </div>
162
+
163
+ <!-- Summary Strip -->
164
+ <div class="summary-strip fade-in">
165
+ <div class="ss-item">
166
+ <div class="ss-label">Total Value</div>
167
+ <div class="ss-val" id="total-val" style="color:var(--cyan)">—</div>
168
+ </div>
169
+ <div class="ss-item">
170
+ <div class="ss-label">Total Cost</div>
171
+ <div class="ss-val" id="total-cost">—</div>
172
+ </div>
173
+ <div class="ss-item">
174
+ <div class="ss-label">Total Gain</div>
175
+ <div class="ss-val" id="total-gain">—</div>
176
+ </div>
177
+ <div class="ss-item">
178
+ <div class="ss-label">Gain %</div>
179
+ <div class="ss-val" id="total-gain-pct">—</div>
180
+ </div>
181
+ <div class="ss-item">
182
+ <div class="ss-label">Best Performer</div>
183
+ <div class="ss-val" id="best-performer" style="color:var(--emerald)">—</div>
184
+ </div>
185
+ <div class="ss-item">
186
+ <div class="ss-label">Holdings</div>
187
+ <div class="ss-val" id="holdings-count" style="color:var(--violet)">—</div>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- Performance Chart -->
192
+ <div class="card fade-in fade-in-1" style="margin-bottom:20px">
193
+ <div class="flex justify-between items-center" style="margin-bottom:16px">
194
+ <div class="card-title" style="margin-bottom:0">📈 Performance History</div>
195
+ <div class="period-tabs">
196
+ <button class="period-tab" onclick="setPeriod(7)">1W</button>
197
+ <button class="period-tab" onclick="setPeriod(30)">1M</button>
198
+ <button class="period-tab active" onclick="setPeriod(90)">3M</button>
199
+ <button class="period-tab" onclick="setPeriod(180)">6M</button>
200
+ <button class="period-tab" onclick="setPeriod(365)">1Y</button>
201
+ </div>
202
+ </div>
203
+ <div style="height:220px;position:relative">
204
+ <canvas id="perfChart"></canvas>
205
+ </div>
206
+ </div>
207
+
208
+ <!-- Holdings Table -->
209
+ <div class="card fade-in fade-in-2">
210
+ <div class="tracker-header-bar">
211
+ <div class="section-title" style="margin-bottom:0">📋 Holdings</div>
212
+ <div class="flex gap-8">
213
+ <input type="text" id="search-input" placeholder="🔍 Search ticker..." style="width:160px;padding:8px 12px;font-size:13px">
214
+ <button class="btn btn-primary btn-sm" onclick="toggleAddForm()">+ Add Holding</button>
215
+ </div>
216
+ </div>
217
+
218
+ <div class="table-wrap">
219
+ <table id="holdings-table">
220
+ <thead>
221
+ <tr>
222
+ <th>Asset</th>
223
+ <th class="sort-header" onclick="sortBy('currentPrice')">Price <span class="sort-arrow">↕</span></th>
224
+ <th class="sort-header" onclick="sortBy('shares')">Shares <span class="sort-arrow">↕</span></th>
225
+ <th class="sort-header" onclick="sortBy('totalValue')">Value <span class="sort-arrow">↕</span></th>
226
+ <th>Avg Cost</th>
227
+ <th class="sort-header" onclick="sortBy('gain')">Gain/Loss <span class="sort-arrow">↕</span></th>
228
+ <th class="sort-header" onclick="sortBy('gainPct')">Return <span class="sort-arrow">↕</span></th>
229
+ <th>Alloc</th>
230
+ <th></th>
231
+ </tr>
232
+ </thead>
233
+ <tbody id="holdings-body"></tbody>
234
+ </table>
235
+ </div>
236
+
237
+ <!-- Add Holding Form -->
238
+ <div class="add-holding-form hidden" id="add-form">
239
+ <div class="field-group" style="margin-bottom:0">
240
+ <label class="field-label">Ticker</label>
241
+ <select id="new-ticker" style="padding:9px 12px">
242
+ <option value="">Select…</option>
243
+ <option value="VOO">VOO — Vanguard S&P 500</option>
244
+ <option value="QQQ">QQQ — Nasdaq 100</option>
245
+ <option value="NVDA">NVDA — NVIDIA</option>
246
+ <option value="AAPL">AAPL — Apple</option>
247
+ <option value="AMZN">AMZN — Amazon</option>
248
+ <option value="TSLA">TSLA — Tesla</option>
249
+ <option value="BND">BND — Bond ETF</option>
250
+ <option value="GLD">GLD — Gold Trust</option>
251
+ <option value="WMT">WMT — Walmart</option>
252
+ <option value="MCD">MCD — McDonald's</option>
253
+ <option value="VTI">VTI — Total Market</option>
254
+ </select>
255
+ </div>
256
+ <div class="field-group" style="margin-bottom:0">
257
+ <label class="field-label">Shares</label>
258
+ <input type="number" id="new-shares" placeholder="e.g. 5" min="0.001" step="0.001" style="padding:9px 12px">
259
+ </div>
260
+ <div class="field-group" style="margin-bottom:0">
261
+ <label class="field-label">Avg Buy Price</label>
262
+ <input type="number" id="new-cost" placeholder="e.g. 450.00" min="0" step="0.01" style="padding:9px 12px">
263
+ </div>
264
+ <div style="display:flex;gap:8px;align-items:flex-end">
265
+ <button class="btn btn-emerald btn-full" onclick="addHolding()">Add</button>
266
+ <button class="btn btn-ghost" onclick="toggleAddForm()">✕</button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+
271
+ <!-- Allocation Chart + Distribution -->
272
+ <div class="grid-2 fade-in fade-in-3" style="margin-top:20px">
273
+ <div class="card">
274
+ <div class="card-title">🥧 Portfolio Allocation</div>
275
+ <div style="height:200px;position:relative">
276
+ <canvas id="allocChart"></canvas>
277
+ </div>
278
+ </div>
279
+ <div class="card">
280
+ <div class="card-title">🏆 Top Performers</div>
281
+ <div id="top-performers"></div>
282
+ </div>
283
+ </div>
284
+
285
+ </main>
286
+ </div>
287
+
288
+ <nav class="bottom-nav">
289
+ <div class="bottom-nav-inner">
290
+ <a href="index.html" class="bottom-nav-item"><span class="bnav-icon">🏠</span>Home</a>
291
+ <a href="portfolio.html" class="bottom-nav-item"><span class="bnav-icon">📊</span>Portfolio</a>
292
+ <a href="risk.html" class="bottom-nav-item"><span class="bnav-icon">🎯</span>Risk</a>
293
+ <a href="tracker.html" class="bottom-nav-item"><span class="bnav-icon">📈</span>Track</a>
294
+ <a href="calculators.html" class="bottom-nav-item"><span class="bnav-icon">🧮</span>Calc</a>
295
+ <a href="insights.html" class="bottom-nav-item"><span class="bnav-icon">💡</span>Insights</a>
296
+ </div>
297
+ </nav>
298
+
299
+ <script src="shared.js"></script>
300
+ <script>
301
+ let holdings = [];
302
+ let sortField = 'totalValue';
303
+ let sortAsc = false;
304
+ let perfChart = null, allocChart = null;
305
+ let currentPeriod = 90;
306
+
307
+ const AVG_COSTS = { VOO:420, QQQ:390, NVDA:640, AAPL:170, BND:74, GLD:190, AMZN:165, VTI:210, TSLA:210, WMT:63, MCD:270 };
308
+
309
+ function initHoldings() {
310
+ const portfolio = getPortfolio();
311
+ holdings = portfolio.assets.map(a => ({
312
+ ticker: a.ticker,
313
+ name: a.name,
314
+ color: a.color,
315
+ type: a.type,
316
+ shares: a.shares,
317
+ currentPrice: MARKET_PRICES[a.ticker]?.price || a.price,
318
+ avgCost: AVG_COSTS[a.ticker] || a.price * 0.9,
319
+ }));
320
+ computeAndRender();
321
+ }
322
+
323
+ function computeAndRender() {
324
+ const totalVal = holdings.reduce((s,h) => s + h.shares * h.currentPrice, 0);
325
+
326
+ holdings.forEach(h => {
327
+ h.totalValue = h.shares * h.currentPrice;
328
+ h.totalCost = h.shares * h.avgCost;
329
+ h.gain = h.totalValue - h.totalCost;
330
+ h.gainPct = h.totalCost > 0 ? (h.gain / h.totalCost) * 100 : 0;
331
+ h.alloc = totalVal > 0 ? (h.totalValue / totalVal) * 100 : 0;
332
+ });
333
+
334
+ updateSummary(totalVal);
335
+ renderTable();
336
+ renderPerfChart(currentPeriod);
337
+ renderAllocChart();
338
+ renderTopPerformers();
339
+ }
340
+
341
+ function updateSummary(totalVal) {
342
+ const totalCost = holdings.reduce((s,h) => s + h.totalCost, 0);
343
+ const totalGain = totalVal - totalCost;
344
+ const gainPct = totalCost > 0 ? (totalGain/totalCost)*100 : 0;
345
+ const best = holdings.length > 0 ? holdings.reduce((m,h) => h.gainPct > m.gainPct ? h : m, holdings[0]) : null;
346
+
347
+ document.getElementById('total-val').textContent = fmt$(totalVal);
348
+ document.getElementById('total-cost').textContent = fmt$(totalCost);
349
+ const gainEl = document.getElementById('total-gain');
350
+ gainEl.textContent = (totalGain>=0?'+':'') + fmt$(totalGain);
351
+ gainEl.style.color = totalGain >= 0 ? 'var(--emerald)' : 'var(--rose)';
352
+ const gainPctEl = document.getElementById('total-gain-pct');
353
+ gainPctEl.textContent = fmtPct(gainPct);
354
+ gainPctEl.style.color = gainPct >= 0 ? 'var(--emerald)' : 'var(--rose)';
355
+ document.getElementById('best-performer').textContent = best ? best.ticker : '—';
356
+ document.getElementById('holdings-count').textContent = holdings.length;
357
+ }
358
+
359
+ function renderTable() {
360
+ const query = document.getElementById('search-input').value.toLowerCase();
361
+ let rows = [...holdings].filter(h => h.ticker.toLowerCase().includes(query) || h.name.toLowerCase().includes(query));
362
+ rows.sort((a,b) => sortAsc ? a[sortField]-b[sortField] : b[sortField]-a[sortField]);
363
+
364
+ const body = document.getElementById('holdings-body');
365
+ body.innerHTML = rows.map((h,i) => {
366
+ const mktData = MARKET_PRICES[h.ticker] || {};
367
+ const dayChg = mktData.changePct || 0;
368
+ const badgeClass = h.type==='ETF'?'badge-cyan':h.type==='Bond'?'badge-violet':h.type==='Commodity'?'badge-amber':'badge-emerald';
369
+ return `
370
+ <tr class="holding-row" id="row-${h.ticker}">
371
+ <td>
372
+ <div class="ticker-cell">
373
+ <div class="ticker-logo" style="background:${h.color}">${h.ticker}</div>
374
+ <div>
375
+ <div style="font-weight:700">${h.ticker} <span class="badge ${badgeClass}" style="font-size:9px">${h.type}</span></div>
376
+ <div class="ticker-name-sub">${h.name}</div>
377
+ <div style="font-size:11px;margin-top:2px;color:${dayChg>=0?'var(--emerald)':'var(--rose)'}">${dayChg>=0?'▲':'▼'} ${Math.abs(dayChg).toFixed(2)}% today</div>
378
+ </div>
379
+ </div>
380
+ </td>
381
+ <td class="mono">${fmt$(h.currentPrice)}</td>
382
+ <td class="mono">
383
+ <input class="edit-input" type="number" value="${h.shares.toFixed(3)}" step="0.001" min="0"
384
+ onchange="updateShares('${h.ticker}', this.value)">
385
+ </td>
386
+ <td class="mono bold">${fmt$(h.totalValue)}</td>
387
+ <td class="mono">
388
+ <input class="edit-input" type="number" value="${h.avgCost.toFixed(2)}" step="0.01" min="0"
389
+ onchange="updateAvgCost('${h.ticker}', this.value)">
390
+ </td>
391
+ <td class="gain-cell ${h.gain>=0?'up':'down'}">${h.gain>=0?'+':''}${fmt$(h.gain)}</td>
392
+ <td class="gain-cell ${h.gainPct>=0?'up':'down'}">${fmtPct(h.gainPct)}</td>
393
+ <td style="min-width:90px">
394
+ <div style="font-size:12px;font-family:var(--font-mono)">${h.alloc.toFixed(1)}%</div>
395
+ <div class="pnl-bar-mini" style="width:${Math.min(h.alloc,100)}%;background:${h.color}"></div>
396
+ </td>
397
+ <td><button class="delete-btn" onclick="removeHolding('${h.ticker}')">🗑</button></td>
398
+ </tr>
399
+ `;
400
+ }).join('');
401
+ }
402
+
403
+ function updateShares(ticker, val) {
404
+ const h = holdings.find(h=>h.ticker===ticker);
405
+ if (h) { h.shares = parseFloat(val)||0; computeAndRender(); }
406
+ }
407
+ function updateAvgCost(ticker, val) {
408
+ const h = holdings.find(h=>h.ticker===ticker);
409
+ if (h) { h.avgCost = parseFloat(val)||0; computeAndRender(); }
410
+ }
411
+ function removeHolding(ticker) {
412
+ holdings = holdings.filter(h=>h.ticker!==ticker);
413
+ computeAndRender();
414
+ showToast(`${ticker} removed from tracker`);
415
+ }
416
+
417
+ function sortBy(field) {
418
+ if (sortField === field) sortAsc = !sortAsc;
419
+ else { sortField = field; sortAsc = false; }
420
+ renderTable();
421
+ }
422
+
423
+ function setPeriod(days) {
424
+ currentPeriod = days;
425
+ document.querySelectorAll('.period-tab').forEach(b => b.classList.remove('active'));
426
+ event.target.classList.add('active');
427
+ renderPerfChart(days);
428
+ }
429
+
430
+ function renderPerfChart(days) {
431
+ const totalCost = holdings.reduce((s,h)=>s+h.totalCost,0) || 10000;
432
+ const history = generateHistory(days, totalCost);
433
+ const ctx = document.getElementById('perfChart').getContext('2d');
434
+ if (perfChart) perfChart.destroy();
435
+
436
+ const labels = history.filter((_,i)=> days<=30 ? i%2===0 : days<=90 ? i%6===0 : i%14===0).map(d=>d.date);
437
+ const values = history.filter((_,i)=> days<=30 ? i%2===0 : days<=90 ? i%6===0 : i%14===0).map(d=>d.value);
438
+ const isUp = values[values.length-1] >= values[0];
439
+
440
+ const grad = ctx.createLinearGradient(0,0,0,220);
441
+ grad.addColorStop(0, isUp ? 'rgba(16,185,129,0.25)' : 'rgba(244,63,94,0.25)');
442
+ grad.addColorStop(1, 'rgba(0,0,0,0)');
443
+
444
+ perfChart = new Chart(ctx, {
445
+ type:'line',
446
+ data: { labels, datasets:[{
447
+ data: values,
448
+ borderColor: isUp ? '#10b981' : '#f43f5e',
449
+ backgroundColor: grad,
450
+ borderWidth: 2.5,
451
+ fill: true,
452
+ tension: 0.4,
453
+ pointRadius: 0,
454
+ pointHoverRadius: 5,
455
+ }]},
456
+ options: {
457
+ responsive:true, maintainAspectRatio:false,
458
+ plugins:{ legend:{display:false}, tooltip:{ callbacks:{ label: ctx => ' '+fmt$(ctx.raw) } } },
459
+ scales:{
460
+ x:{ grid:{display:false}, ticks:{font:{size:11}} },
461
+ y:{ grid:{color:'rgba(34,211,238,0.06)'}, ticks:{ callback: v => '$'+(v/1000).toFixed(0)+'K', font:{size:11} } }
462
+ },
463
+ interaction:{ intersect:false, mode:'index' }
464
+ }
465
+ });
466
+ }
467
+
468
+ function renderAllocChart() {
469
+ const ctx = document.getElementById('allocChart').getContext('2d');
470
+ if (allocChart) allocChart.destroy();
471
+ allocChart = new Chart(ctx, {
472
+ type:'doughnut',
473
+ data:{
474
+ labels: holdings.map(h=>h.ticker),
475
+ datasets:[{
476
+ data: holdings.map(h=>h.alloc),
477
+ backgroundColor: holdings.map(h=>h.color),
478
+ borderColor:'rgba(5,13,26,0.8)',
479
+ borderWidth:3,
480
+ hoverOffset:6
481
+ }]
482
+ },
483
+ options:{
484
+ responsive:true, maintainAspectRatio:false,
485
+ cutout:'68%',
486
+ plugins:{ legend:{ position:'right', labels:{ color:'#8faac8', font:{size:11}, boxWidth:12 } } }
487
+ }
488
+ });
489
+ }
490
+
491
+ function renderTopPerformers() {
492
+ const sorted = [...holdings].sort((a,b)=>b.gainPct-a.gainPct);
493
+ document.getElementById('top-performers').innerHTML = sorted.slice(0,5).map((h,i) => `
494
+ <div style="display:flex;align-items:center;gap:12px;padding:10px 0;border-bottom:1px solid rgba(34,211,238,0.06)">
495
+ <div style="font-size:16px">${i===0?'🥇':i===1?'🥈':i===2?'🥉':'📊'}</div>
496
+ <div class="ticker-logo" style="background:${h.color};width:30px;height:30px;font-size:8px">${h.ticker}</div>
497
+ <div style="flex:1">
498
+ <div style="font-weight:700;font-size:14px">${h.ticker}</div>
499
+ <div style="font-size:11px;color:var(--text2)">${fmt$(h.totalValue)}</div>
500
+ </div>
501
+ <div style="font-family:var(--font-mono);font-weight:700;color:${h.gainPct>=0?'var(--emerald)':'var(--rose)'}">
502
+ ${fmtPct(h.gainPct)}
503
+ </div>
504
+ </div>
505
+ `).join('');
506
+ }
507
+
508
+ function toggleAddForm() {
509
+ document.getElementById('add-form').classList.toggle('hidden');
510
+ }
511
+
512
+ function addHolding() {
513
+ const ticker = document.getElementById('new-ticker').value;
514
+ const shares = parseFloat(document.getElementById('new-shares').value);
515
+ const cost = parseFloat(document.getElementById('new-cost').value);
516
+ if (!ticker || !shares || !cost) { showToast('Please fill all fields', 'error'); return; }
517
+ if (holdings.find(h=>h.ticker===ticker)) { showToast('Already in portfolio', 'error'); return; }
518
+
519
+ const mkt = MARKET_PRICES[ticker] || { price: cost };
520
+ const assetDef = { VOO:'Vanguard S&P 500 ETF', QQQ:'Invesco Nasdaq 100', NVDA:'NVIDIA Corp', AAPL:'Apple Inc.',
521
+ AMZN:'Amazon.com Inc.', TSLA:'Tesla Inc.', BND:'Vanguard Bond ETF', GLD:'SPDR Gold Trust',
522
+ WMT:'Walmart Inc.', MCD:"McDonald's Corp", VTI:'Vanguard Total Market' };
523
+ const typeMap = { VOO:'ETF', VTI:'ETF', QQQ:'ETF', BND:'Bond', GLD:'Commodity', SLV:'Commodity' };
524
+
525
+ holdings.push({
526
+ ticker,
527
+ name: assetDef[ticker] || ticker,
528
+ color: ASSET_COLORS[holdings.length % ASSET_COLORS.length],
529
+ type: typeMap[ticker] || 'Stock',
530
+ shares,
531
+ currentPrice: mkt.price,
532
+ avgCost: cost,
533
+ });
534
+
535
+ document.getElementById('new-ticker').value = '';
536
+ document.getElementById('new-shares').value = '';
537
+ document.getElementById('new-cost').value = '';
538
+ toggleAddForm();
539
+ computeAndRender();
540
+ showToast(`✅ ${ticker} added to tracker`);
541
+ }
542
+
543
+ document.getElementById('search-input').addEventListener('input', renderTable);
544
+
545
+ document.addEventListener('DOMContentLoaded', () => {
546
+ applyChartDefaults();
547
+ initHoldings();
548
+ });
549
+ </script>
550
+ </body>
551
+ </html>