Spaces:
Running
Running
Delete portfolio.html
Browse files- portfolio.html +0 -673
portfolio.html
DELETED
|
@@ -1,673 +0,0 @@
|
|
| 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>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|