Add 2026 index investing cheat sheet

D David Veksler Β· 5 days ago 7f943d9e6d1c716e39601e14cabf16cf317efd63
Parent: bf21cd3f7
Add a new interactive cheat-sheet page (index-investing-tax-advantaged.html) and its preview image. The page provides an interactive compound/FIRE calculator, age-based asset-allocation tool, account funding waterfall (401(k)/IRA/Roth/HSA/brokerage) with verified 2026 IRS limits, and tax-loss-harvesting / wash-sale guidance. Uses Bootstrap for layout and includes client-side JS for calculators and theme toggling. Includes accompanying image images/index-investing-tax-advantaged.png.

2 files changed +792 βˆ’0

Diff

diff --git a/images/index-investing-tax-advantaged.png b/images/index-investing-tax-advantaged.png
new file mode 100644
index 0000000..b18e3dc
Binary files /dev/null and b/images/index-investing-tax-advantaged.png differ
diff --git a/index-investing-tax-advantaged.html b/index-investing-tax-advantaged.html
new file mode 100644
index 0000000..74be849
--- /dev/null
+++ b/index-investing-tax-advantaged.html
@@ -0,0 +1,792 @@
+<!DOCTYPE html>
+<html lang="en" data-theme="auto">
+
+<head>
+    <meta charset="utf-8" />
+    <meta content="width=device-width, initial-scale=1.0" name="viewport" />
+    <title>Index Investing &amp; Tax-Advantaged Accounts: 2026 Cheat Sheet</title>
+    <meta
+        content="Interactive guide to index investing and tax-advantaged accounts for 2026: compound-interest and FIRE calculator, asset allocation by age, an account funding waterfall (401k, IRA, Roth, HSA, brokerage) with verified 2026 IRS limits, and tax-loss-harvesting rules including the wash-sale window."
+        name="description" />
+    <meta
+        content="index investing, index funds, ETF, tax-advantaged accounts, 401k 2026, Roth IRA, traditional IRA, HSA, FIRE calculator, compound interest, asset allocation by age, tax loss harvesting, wash sale rule, three-fund portfolio, Bogleheads, contribution limits 2026, backdoor Roth, asset location"
+        name="keywords" />
+    <link href="https://cheatsheets.davidveksler.com/index-investing-tax-advantaged.html" rel="canonical" />
+
+    <!-- Open Graph / Facebook -->
+    <meta content="Index Investing &amp; Tax-Advantaged Accounts: 2026 Cheat Sheet" property="og:title" />
+    <meta
+        content="Compound/FIRE calculator, asset allocation by age, the account funding waterfall, and tax-loss-harvesting rules β€” with verified 2026 IRS contribution limits."
+        property="og:description" />
+    <meta content="website" property="og:type" />
+    <meta content="https://cheatsheets.davidveksler.com/index-investing-tax-advantaged.html" property="og:url" />
+    <meta content="images/index-investing-tax-advantaged.png" property="og:image" />
+    <meta content="Interactive index investing and tax-advantaged account cheat sheet with calculators" property="og:image:alt" />
+    <meta content="1200" property="og:image:width" />
+    <meta content="630" property="og:image:height" />
+
+    <!-- Twitter / X Card -->
+    <meta content="summary_large_image" name="twitter:card" />
+    <meta content="Index Investing &amp; Tax-Advantaged Accounts: 2026 Cheat Sheet" name="twitter:title" />
+    <meta
+        content="Compound/FIRE calculator, asset allocation by age, account funding waterfall, and tax-loss-harvesting rules with verified 2026 IRS limits."
+        name="twitter:description" />
+    <meta content="images/index-investing-tax-advantaged.png" name="twitter:image" />
+    <meta content="@heroiclife" name="twitter:creator" />
+
+    <!-- JSON-LD Structured Data -->
+    <script type="application/ld+json">
+    {
+      "@context": "https://schema.org",
+      "@type": "TechArticle",
+      "headline": "Index Investing & Tax-Advantaged Accounts: 2026 Cheat Sheet",
+      "description": "A comprehensive, interactive reference for index-fund investing and tax-advantaged accounts in 2026, including a compound-interest/FIRE calculator, asset allocation by age, the account funding waterfall across 401(k)/IRA/Roth/HSA/brokerage with verified IRS limits, and tax-loss-harvesting rules.",
+      "author": {"@type": "Person", "name": "David Veksler (AI Generated)"},
+      "publisher": {"@type": "Organization", "name": "David Veksler Cheatsheets"},
+      "datePublished": "2026-06-15",
+      "dateModified": "2026-06-15",
+      "keywords": "index investing, tax-advantaged accounts, 401k 2026 limits, Roth IRA, HSA, FIRE calculator, asset allocation, tax loss harvesting, wash sale rule, three-fund portfolio"
+    }
+    </script>
+
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
+        integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous" />
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css"
+        integrity="sha384-CK2SzKma4jA5H/MXDUU7i1TqZlCFaD4T01vtyDFvPlD97JQyS+IsSh1nI2EFbpyk" crossorigin="anonymous" />
+
+    <style>
+        /* Custom CSS is intentionally unlayered: this <style> follows the Bootstrap CDN
+           <link>, so plain rules win by source order. (Layering would demote us below
+           Bootstrap's own unlayered CDN sheet.) data-bs-theme is synced in JS so native
+           Bootstrap surfaces theme correctly too. */
+            :root {
+                color-scheme: light dark;
+                --accent: light-dark(#0a6b4f, #2bd9a3);
+                --accent-2: light-dark(#1d4ed8, #6ea8ff);
+                --warn: light-dark(#9a6700, #ffcf6e);
+                --danger: light-dark(#b42318, #ff8a7a);
+                --page-bg: light-dark(#f4f6f8, #0f1419);
+                --card-bg: light-dark(#ffffff, #1a212b);
+                --card-bg-2: light-dark(#f0f3f6, #222c38);
+                --ink: light-dark(#1c2530, #e6edf3);
+                --ink-soft: light-dark(#54616e, #9fb0c0);
+                --line: light-dark(#dde3ea, #2c3744);
+                --stock: light-dark(#0a6b4f, #2bd9a3);
+                --bond: light-dark(#1d4ed8, #6ea8ff);
+                --shadow: light-dark(0 8px 28px rgba(20, 40, 60, .10), 0 8px 28px rgba(0, 0, 0, .45));
+                --radius: .8rem;
+            }
+
+            :root[data-theme="light"] { color-scheme: light; }
+            :root[data-theme="dark"] { color-scheme: dark; }
+
+            * { scrollbar-width: thin; }
+
+            body {
+                font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
+                background: var(--page-bg);
+                color: var(--ink);
+                line-height: 1.55;
+            }
+
+            h1, h2, h3, h4 { text-wrap: balance; font-weight: 700; }
+            p, li { text-wrap: pretty; }
+            a { color: var(--accent-2); }
+
+            main { max-width: 1100px; }
+
+            code, kbd {
+                background: var(--card-bg-2);
+                color: var(--accent);
+                padding: .08em .4em;
+                border-radius: .3rem;
+                font-size: .9em;
+            }
+
+            .masthead {
+                background:
+                    radial-gradient(1200px 300px at 12% -10%, light-dark(#cdeede, #103b30) 0%, transparent 60%),
+                    radial-gradient(1000px 300px at 100% 0%, light-dark(#d7e2ff, #16253f) 0%, transparent 55%),
+                    var(--card-bg);
+                border-bottom: 1px solid var(--line);
+            }
+
+            .card-panel {
+                background: var(--card-bg);
+                border: 1px solid var(--line);
+                border-radius: var(--radius);
+                box-shadow: var(--shadow);
+            }
+
+            .tool {
+                background: var(--card-bg);
+                border: 1px solid var(--line);
+                border-left: 4px solid var(--accent);
+                border-radius: var(--radius);
+            }
+
+            .metric {
+                background: var(--card-bg-2);
+                border-radius: .6rem;
+                padding: .65rem .85rem;
+            }
+            .metric .v { font-size: clamp(1.15rem, 1rem + 1.2vw, 1.7rem); font-weight: 700; color: var(--accent); line-height: 1.1; }
+            .metric .k { font-size: .76rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-soft); }
+
+            input[type="range"] { accent-color: var(--accent); width: 100%; }
+            .form-control, .form-select {
+                background: var(--card-bg-2);
+                border-color: var(--line);
+                color: var(--ink);
+            }
+            .form-control:focus, .form-select:focus {
+                background: var(--card-bg-2);
+                color: var(--ink);
+                border-color: var(--accent);
+                box-shadow: 0 0 0 .2rem light-dark(rgba(10, 107, 79, .2), rgba(43, 217, 163, .25));
+            }
+
+            details.ref {
+                background: var(--card-bg);
+                border: 1px solid var(--line);
+                border-radius: var(--radius);
+                margin-bottom: .8rem;
+                overflow: hidden;
+            }
+            details.ref > summary {
+                cursor: pointer;
+                padding: .9rem 1.1rem;
+                font-weight: 600;
+                font-size: 1.05rem;
+                list-style: none;
+                display: flex;
+                align-items: center;
+                gap: .6rem;
+            }
+            details.ref > summary::-webkit-details-marker { display: none; }
+            details.ref > summary::after {
+                content: "\F282";
+                font-family: "bootstrap-icons";
+                margin-left: auto;
+                color: var(--ink-soft);
+                transition: transform .2s ease;
+            }
+            details.ref[open] > summary::after { transform: rotate(90deg); }
+            details.ref > summary:hover { color: var(--accent); }
+            details.ref .body { padding: 0 1.1rem 1.1rem; }
+
+            .alloc-bar { display: flex; height: 2.4rem; border-radius: .5rem; overflow: hidden; font-weight: 700; }
+            .alloc-bar .seg { display: flex; align-items: center; justify-content: center; color: #fff; font-size: .9rem; transition: width .25s ease; }
+            .seg-stock { background: var(--stock); }
+            .seg-bond { background: var(--bond); }
+
+            .waterfall-step {
+                background: var(--card-bg-2);
+                border-radius: .55rem;
+                padding: .7rem .85rem;
+                border: 1px solid var(--line);
+                display: flex;
+                gap: .8rem;
+                align-items: flex-start;
+            }
+            .waterfall-step .num {
+                flex: 0 0 1.9rem; height: 1.9rem;
+                display: grid; place-items: center;
+                background: var(--accent); color: #fff;
+                border-radius: 50%; font-weight: 700;
+            }
+            .waterfall-step.filled { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
+            .waterfall-step .fill-amt { color: var(--accent); font-weight: 700; }
+
+            table { color: var(--ink); }
+            .table > :not(caption) > * > * { background: transparent; color: var(--ink); border-bottom-color: var(--line); }
+            .table thead th { border-bottom: 2px solid var(--line); color: var(--ink-soft); text-transform: uppercase; font-size: .76rem; letter-spacing: .03em; }
+
+            .pill { display: inline-block; padding: .12rem .55rem; border-radius: 1rem; font-size: .76rem; font-weight: 600; }
+            .pill-pre { background: light-dark(#dcefe6, #0f3a2d); color: var(--accent); }
+            .pill-post { background: light-dark(#e2e9ff, #16284a); color: var(--accent-2); }
+            .pill-tri { background: light-dark(#fbe9c6, #3a2e10); color: var(--warn); }
+
+            .theme-toggle {
+                background: var(--card-bg-2);
+                border: 1px solid var(--line);
+                color: var(--ink);
+                border-radius: 2rem;
+                padding: .35rem .8rem;
+                font-size: .85rem;
+            }
+
+            :focus-visible { outline: 3px solid var(--accent-2); outline-offset: 2px; border-radius: 4px; }
+
+            .canvas-wrap { position: relative; width: 100%; }
+            canvas { max-width: 100%; }
+
+            .text-soft { color: var(--ink-soft); }
+            .text-accent { color: var(--accent); }
+            .bg-soft { background: var(--card-bg-2); }
+            .small-caps { text-transform: uppercase; letter-spacing: .05em; font-size: .76rem; }
+
+            @media (prefers-reduced-motion: reduce) {
+                * { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
+            }
+
+            @media print {
+                .no-print { display: none !important; }
+                details.ref[open] .body, details.ref .body { display: block !important; }
+                details.ref > summary::after { display: none; }
+                body { background: #fff; color: #000; }
+                .tool, .card-panel, details.ref { box-shadow: none; border-color: #ccc; }
+            }
+    </style>
+</head>
+
+<body>
+    <header class="masthead py-4 py-md-5 mb-4">
+        <div class="container" style="max-width:1100px">
+            <div class="d-flex justify-content-between align-items-start gap-3">
+                <div>
+                    <p class="small-caps text-soft mb-1"><i class="bi bi-graph-up-arrow"></i> Personal Finance Β· Reference</p>
+                    <h1 class="display-6 mb-2">Index Investing &amp; Tax-Advantaged Accounts</h1>
+                    <p class="lead mb-0 text-soft" style="max-width:62ch">
+                        The whole game in one page: buy the haystack, hold it cheaply, shelter it from tax, and
+                        let compounding do the work. Calculators, the 2026 limits, and the funding order that
+                        beats stock-picking for almost everyone.
+                    </p>
+                </div>
+                <button class="theme-toggle no-print flex-shrink-0" id="themeBtn" type="button" aria-label="Toggle color theme">
+                    <i class="bi bi-circle-half"></i> <span id="themeLbl">Auto</span>
+                </button>
+            </div>
+            <p class="small text-soft mt-3 mb-0">
+                <i class="bi bi-patch-check"></i> <strong>Last verified: 2026-06-15.</strong>
+                2026 figures from IRS Notice 2025-67 (retirement) and Rev. Proc. 2025-19 (HSA/HDHP).
+                Educational only β€” not individualized tax, legal, or investment advice.
+            </p>
+        </div>
+    </header>
+
+    <main class="container pb-5">
+
+        <!-- ===================== QUICK REFERENCE ===================== -->
+        <section class="card-panel p-3 p-md-4 mb-4" aria-labelledby="qr">
+            <h2 id="qr" class="h4 mb-3"><i class="bi bi-lightning-charge-fill text-accent"></i> Quick Reference β€” 2026 Contribution Limits</h2>
+            <div class="table-responsive">
+                <table class="table table-sm align-middle mb-2">
+                    <thead>
+                        <tr><th>Account</th><th>2026 limit</th><th>Catch-up (50+)</th><th>Tax treatment</th><th>Notes</th></tr>
+                    </thead>
+                    <tbody>
+                        <tr>
+                            <td><strong>401(k) / 403(b) / 457(b) / TSP</strong></td>
+                            <td>$24,500</td>
+                            <td>+$8,000 <span class="text-soft">(+$11,250 ages 60–63)</span></td>
+                            <td><span class="pill pill-pre">Pre-tax</span> or <span class="pill pill-post">Roth</span></td>
+                            <td>Employee deferral only; total additions cap $72,000</td>
+                        </tr>
+                        <tr>
+                            <td><strong>Traditional / Roth IRA</strong></td>
+                            <td>$7,500 <span class="text-soft">(combined)</span></td>
+                            <td>+$1,100</td>
+                            <td><span class="pill pill-pre">Pre-tax</span> / <span class="pill pill-post">Roth</span></td>
+                            <td>Roth phases out $153k–$168k single, $242k–$252k MFJ</td>
+                        </tr>
+                        <tr>
+                            <td><strong>HSA</strong> (with HDHP)</td>
+                            <td>$4,400 self / $8,750 family</td>
+                            <td>+$1,000 <span class="text-soft">(age 55+)</span></td>
+                            <td><span class="pill pill-tri">Triple tax-free</span></td>
+                            <td>Best deal in the code; needs an HSA-eligible HDHP</td>
+                        </tr>
+                        <tr>
+                            <td><strong>SIMPLE IRA</strong></td>
+                            <td>$17,000 <span class="text-soft">($18,100 some plans)</span></td>
+                            <td>+$4,000 <span class="text-soft">(+$5,250 ages 60–63)</span></td>
+                            <td><span class="pill pill-pre">Pre-tax</span></td>
+                            <td>Small-employer plan</td>
+                        </tr>
+                        <tr>
+                            <td><strong>Taxable brokerage</strong></td>
+                            <td><span class="text-soft">Unlimited</span></td>
+                            <td>β€”</td>
+                            <td>Cap-gains</td>
+                            <td>LTCG 0% up to $49,450 single / $98,900 MFJ taxable income</td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+            <p class="small text-soft mb-0">Catch-up reminder: starting in 2026, if your prior-year FICA wages exceeded <strong>$150,000</strong>, any 50+ catch-up to a workplace plan must go into the <strong>Roth</strong> (after-tax) source.</p>
+        </section>
+
+        <!-- ===================== TOOL 1: COMPOUND / FIRE ===================== -->
+        <section class="tool p-3 p-md-4 mb-4" aria-labelledby="t1">
+            <h2 id="t1" class="h4 mb-1"><i class="bi bi-fire text-accent"></i> Compound-Interest &amp; FIRE Calculator</h2>
+            <p class="text-soft small mb-3">Returns are entered as <strong>real</strong> (after-inflation) so every figure is in today's dollars. FIRE target = annual spending Γ· withdrawal rate.</p>
+            <div class="row g-4">
+                <div class="col-lg-5">
+                    <div class="row g-3">
+                        <div class="col-6"><label class="form-label small mb-1">Current age</label><input class="form-control" id="f_age" type="number" value="30" min="16" max="80"></div>
+                        <div class="col-6"><label class="form-label small mb-1">Current balance ($)</label><input class="form-control" id="f_bal" type="number" value="25000" min="0" step="1000"></div>
+                        <div class="col-6"><label class="form-label small mb-1">Monthly contribution ($)</label><input class="form-control" id="f_pmt" type="number" value="1500" min="0" step="50"></div>
+                        <div class="col-6"><label class="form-label small mb-1">Annual spending ($)</label><input class="form-control" id="f_spend" type="number" value="60000" min="0" step="1000"></div>
+                        <div class="col-6">
+                            <label class="form-label small mb-1">Real return: <span id="f_ret_l" class="text-accent fw-bold">5.0%</span></label>
+                            <input class="form-range" id="f_ret" type="range" min="1" max="9" step="0.5" value="5">
+                            <div class="d-flex justify-content-between small text-soft"><span>1%</span><span>9%</span></div>
+                        </div>
+                        <div class="col-6">
+                            <label class="form-label small mb-1">Withdrawal rate: <span id="f_wr_l" class="text-accent fw-bold">4.0%</span></label>
+                            <input class="form-range" id="f_wr" type="range" min="2.5" max="6" step="0.25" value="4">
+                            <div class="d-flex justify-content-between small text-soft"><span>2.5%</span><span>6%</span></div>
+                        </div>
+                    </div>
+                    <div class="row g-2 mt-1">
+                        <div class="col-6"><div class="metric"><div class="v" id="o_fire">β€”</div><div class="k">FIRE number</div></div></div>
+                        <div class="col-6"><div class="metric"><div class="v" id="o_age">β€”</div><div class="k">Financially independent at</div></div></div>
+                        <div class="col-6"><div class="metric"><div class="v" id="o_yrs">β€”</div><div class="k">Years to FI</div></div></div>
+                        <div class="col-6"><div class="metric"><div class="v" id="o_65">β€”</div><div class="k">Balance at 65</div></div></div>
+                    </div>
+                    <p class="small text-soft mt-2 mb-0" id="f_note"></p>
+                </div>
+                <div class="col-lg-7">
+                    <div class="canvas-wrap"><canvas id="f_chart" height="320" aria-label="Projected portfolio balance over time"></canvas></div>
+                    <p class="small text-soft text-center mt-1 mb-0"><span class="text-accent">β– </span> Portfolio &nbsp; <span style="color:var(--bond)">β–¬</span> FIRE target line</p>
+                </div>
+            </div>
+        </section>
+
+        <!-- ===================== TOOL 2: ASSET ALLOCATION ===================== -->
+        <section class="tool p-3 p-md-4 mb-4" aria-labelledby="t2">
+            <h2 id="t2" class="h4 mb-1"><i class="bi bi-pie-chart-fill text-accent"></i> Asset Allocation by Age</h2>
+            <p class="text-soft small mb-3">Age-based heuristics for the stock/bond split. They're starting points, not laws β€” risk capacity, job stability, and sleep-at-night tolerance all shift the dial.</p>
+            <div class="row g-4 align-items-center">
+                <div class="col-lg-5">
+                    <label class="form-label mb-1">Your age: <span id="a_age_l" class="text-accent fw-bold fs-5">35</span></label>
+                    <input class="form-range" id="a_age" type="range" min="20" max="80" value="35">
+                    <label class="form-label small mt-2 mb-1">Glide-path rule</label>
+                    <select class="form-select" id="a_rule">
+                        <option value="120">Aggressive β€” 120 βˆ’ age in stocks</option>
+                        <option value="110" selected>Moderate β€” 110 βˆ’ age in stocks</option>
+                        <option value="100">Conservative β€” 100 βˆ’ age in stocks</option>
+                    </select>
+                </div>
+                <div class="col-lg-7">
+                    <div class="alloc-bar mb-2" role="img" aria-label="Stock and bond allocation">
+                        <div class="seg seg-stock" id="a_seg_s" style="width:75%">75% Stocks</div>
+                        <div class="seg seg-bond" id="a_seg_b" style="width:25%">25% Bonds</div>
+                    </div>
+                    <div class="row g-2 text-center">
+                        <div class="col-4"><div class="metric"><div class="v" id="a_stock">75%</div><div class="k">Stocks</div></div></div>
+                        <div class="col-4"><div class="metric"><div class="v" id="a_bond">25%</div><div class="k">Bonds</div></div></div>
+                        <div class="col-4"><div class="metric"><div class="v" id="a_intl">β€”</div><div class="k">Intl. of stocks</div></div></div>
+                    </div>
+                    <p class="small text-soft mt-2 mb-0" id="a_note"></p>
+                </div>
+            </div>
+            <div class="mt-3 p-3 bg-soft rounded">
+                <strong class="small-caps text-soft">Canonical three-fund split (Bogleheads)</strong>
+                <p class="small mb-0 mt-1">Total US market + Total international + Total bond. A common stock split is ~60–70% US / 30–40% international (cap-weighted global is ~60/40). Hold bonds in tax-deferred space when you can.</p>
+            </div>
+        </section>
+
+        <!-- ===================== TOOL 3: FUNDING WATERFALL ===================== -->
+        <section class="tool p-3 p-md-4 mb-4" aria-labelledby="t3">
+            <h2 id="t3" class="h4 mb-1"><i class="bi bi-diagram-3-fill text-accent"></i> Account Funding Waterfall</h2>
+            <p class="text-soft small mb-3">Where should your next dollar go? Enter what you can invest this year and your situation β€” it fills the buckets in priority order (the Bogleheads "prioritizing investments" flow), using 2026 limits.</p>
+            <div class="row g-3 mb-3">
+                <div class="col-md-4"><label class="form-label small mb-1">Annual amount to invest ($)</label><input class="form-control" id="w_amt" type="number" value="30000" min="0" step="500"></div>
+                <div class="col-md-3"><label class="form-label small mb-1">Age</label><input class="form-control" id="w_age" type="number" value="35" min="16" max="80"></div>
+                <div class="col-md-5 d-flex flex-column justify-content-end gap-1">
+                    <div class="form-check"><input class="form-check-input" type="checkbox" id="w_match" checked><label class="form-check-label small" for="w_match">Employer 401(k) match β€” up to <input type="number" id="w_matchamt" value="4500" min="0" step="500" style="width:90px" class="form-control form-control-sm d-inline-block">/yr</label></div>
+                    <div class="form-check"><input class="form-check-input" type="checkbox" id="w_hsa" checked><label class="form-check-label small" for="w_hsa">HSA-eligible (have an HDHP) β€” <select id="w_hsatype" class="form-select form-select-sm d-inline-block" style="width:130px"><option value="self">self-only</option><option value="family">family</option></select></label></div>
+                    <div class="form-check"><input class="form-check-input" type="checkbox" id="w_roth" checked><label class="form-check-label small" for="w_roth">Income allows direct Roth IRA (else use backdoor)</label></div>
+                </div>
+            </div>
+            <div id="w_steps" class="d-grid gap-2"></div>
+            <p class="small text-soft mt-2 mb-0" id="w_note"></p>
+        </section>
+
+        <!-- ===================== TOOL 4: TLH / WASH SALE ===================== -->
+        <section class="tool p-3 p-md-4 mb-4" aria-labelledby="t4">
+            <h2 id="t4" class="h4 mb-1"><i class="bi bi-scissors text-accent"></i> Tax-Loss Harvesting β€” Wash-Sale Window</h2>
+            <p class="text-soft small mb-3">The wash-sale rule (IRC Β§1091) disallows a loss if you buy a <em>substantially identical</em> security within <strong>30 days before or after</strong> the sale β€” a 61-day danger zone. Enter your sale date to see the window.</p>
+            <div class="row g-3 align-items-end">
+                <div class="col-md-4"><label class="form-label small mb-1">Loss sale date</label><input class="form-control" id="ws_date" type="date" value="2026-06-15"></div>
+                <div class="col-md-8">
+                    <div class="p-3 bg-soft rounded">
+                        <div class="small text-soft small-caps mb-1">Do not buy a substantially identical security between</div>
+                        <div class="fs-6"><i class="bi bi-calendar-x text-danger"></i> <strong id="ws_start">β€”</strong> &nbsp;and&nbsp; <strong id="ws_end">β€”</strong></div>
+                        <div class="small text-soft mt-1" id="ws_note"></div>
+                    </div>
+                </div>
+            </div>
+            <div class="row g-2 mt-2">
+                <div class="col-md-4"><div class="metric"><div class="v">$3,000</div><div class="k">Max net loss vs. ordinary income / yr ($1,500 MFS)</div></div></div>
+                <div class="col-md-4"><div class="metric"><div class="v">∞</div><div class="k">Carry-forward of excess losses (indefinite)</div></div></div>
+                <div class="col-md-4"><div class="metric"><div class="v">61 days</div><div class="k">Total wash-sale window (30 + sale + 30)</div></div></div>
+            </div>
+            <p class="small text-soft mt-2 mb-0"><i class="bi bi-exclamation-triangle"></i> A wash sale triggered by a purchase in your <strong>IRA</strong> permanently disallows the loss (Rev. Rul. 2008-5) β€” the basis is <em>not</em> added back. Avoid by holding a different-enough fund (e.g., S&amp;P 500 ↔ total-market) during the window.</p>
+        </section>
+
+        <!-- ===================== REFERENCE SECTIONS ===================== -->
+        <h2 class="h4 mt-5 mb-3"><i class="bi bi-journal-text text-accent"></i> Reference</h2>
+
+        <details class="ref" open>
+            <summary><i class="bi bi-1-circle"></i> Index investing fundamentals</summary>
+            <div class="body">
+                <p><strong>The thesis.</strong> After costs, the average actively managed dollar must underperform the market average β€” it <em>is</em> the market minus fees (Sharpe's "arithmetic of active management"). SPIVA data consistently shows ~85–90% of US large-cap active funds trail the S&amp;P 500 over 15 years. So: own the whole market at minimal cost and stop guessing.</p>
+                <ul>
+                    <li><strong>Index fund:</strong> a fund that mechanically holds every security in an index (e.g., S&amp;P 500, total US market). No manager picking winners β†’ near-zero fees.</li>
+                    <li><strong>Expense ratio:</strong> annual fee as % of assets. Realistic floor today: <code>0.03%</code> (VOO/VTI, FXAIX, SWPPX) β€” i.e. <strong>$3/yr per $10,000</strong>. Anything over ~0.20% for a broad index fund is overpriced. A 1% fee compounds to roughly a quarter of your ending balance over 40 years.</li>
+                    <li><strong>ETF vs. mutual fund:</strong> ETFs trade intraday and are usually more tax-efficient in taxable accounts (in-kind creation/redemption avoids capital-gains distributions). Index <em>mutual</em> funds allow automatic fractional dollar investing. In a 401(k)/IRA the difference is cosmetic.</li>
+                    <li><strong>Total return:</strong> S&amp;P 500 long-run average β‰ˆ <strong>~10% nominal / ~6.5–7% real</strong> (1926–present). Wide dispersion: any given year is rarely "average." Don't extrapolate a single decade.</li>
+                </ul>
+                <div class="table-responsive">
+                    <table class="table table-sm">
+                        <thead><tr><th>Broad index fund</th><th>Ticker</th><th>Expense ratio</th><th>Covers</th></tr></thead>
+                        <tbody>
+                            <tr><td>Vanguard Total US Market</td><td>VTI / VTSAX</td><td>0.03% / 0.04%</td><td>~3,600 US stocks</td></tr>
+                            <tr><td>Vanguard S&amp;P 500</td><td>VOO / VFIAX</td><td>0.03% / 0.04%</td><td>500 US large-caps</td></tr>
+                            <tr><td>Fidelity ZERO Total Market</td><td>FZROX</td><td>0.00%</td><td>US total (Fidelity-only)</td></tr>
+                            <tr><td>Vanguard Total International</td><td>VXUS / VTIAX</td><td>0.05% / 0.09%</td><td>Developed + emerging ex-US</td></tr>
+                            <tr><td>Vanguard Total Bond</td><td>BND / VBTLX</td><td>0.03% / 0.05%</td><td>US investment-grade bonds</td></tr>
+                        </tbody>
+                    </table>
+                </div>
+                <p class="small text-soft mb-0"><i class="bi bi-info-circle"></i> Expense ratios verified from issuer pages as of mid-2026; they change rarely but tag-check before quoting exact basis points.</p>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-2-circle"></i> Account types deep dive (401k Β· IRA Β· Roth Β· HSA Β· brokerage)</summary>
+            <div class="body">
+                <div class="table-responsive">
+                    <table class="table table-sm align-middle">
+                        <thead><tr><th>Account</th><th>Contribution tax</th><th>Growth</th><th>Withdrawal tax</th><th>Key rules</th></tr></thead>
+                        <tbody>
+                            <tr><td><strong>Traditional 401(k)</strong></td><td>Pre-tax (deductible)</td><td>Tax-deferred</td><td>Ordinary income</td><td>RMDs at 73; 10% penalty before 59Β½ (some exceptions, Rule of 55)</td></tr>
+                            <tr><td><strong>Roth 401(k)</strong></td><td>After-tax</td><td>Tax-free</td><td>Tax-free*</td><td>No income limit; no RMDs starting 2024 (SECURE 2.0)</td></tr>
+                            <tr><td><strong>Traditional IRA</strong></td><td>Pre-tax (if eligible)</td><td>Tax-deferred</td><td>Ordinary income</td><td>Deduction phases out if covered by workplace plan</td></tr>
+                            <tr><td><strong>Roth IRA</strong></td><td>After-tax</td><td>Tax-free</td><td>Tax-free*</td><td>Contributions withdrawable anytime; 5-year rule on earnings</td></tr>
+                            <tr><td><strong>HSA</strong></td><td>Pre-tax</td><td>Tax-free</td><td>Tax-free (medical)</td><td>Triple tax-free; after 65 acts like a Traditional IRA for non-medical</td></tr>
+                            <tr><td><strong>Taxable brokerage</strong></td><td>After-tax</td><td>Taxed (dividends/gains)</td><td>Cap-gains on growth</td><td>No limits, no penalties, full liquidity, step-up basis at death</td></tr>
+                        </tbody>
+                    </table>
+                </div>
+                <p class="small text-soft">*Roth tax-free on earnings requires the account be open 5 years <em>and</em> age 59Β½ (or death/disability/first-home $10k).</p>
+                <ul class="mb-0">
+                    <li><strong>HSA = the only triple-tax-advantaged account:</strong> deductible in, grows tax-free, tax-free out for qualified medical. Pay current medical out of pocket, save receipts, and let the HSA compound as a stealth retirement account β€” there's no deadline to reimburse yourself.</li>
+                    <li><strong>Backdoor Roth IRA:</strong> over the income limit? Contribute non-deductible to a Traditional IRA, then convert to Roth. Watch the <em>pro-rata rule</em> β€” pre-tax IRA balances make the conversion partly taxable (Form 8606).</li>
+                    <li><strong>Mega-backdoor Roth:</strong> if your plan allows after-tax (non-Roth) 401(k) contributions + in-plan Roth conversion, you can stuff up to the $72,000 total-additions cap minus deferrals and match into Roth. Plan-dependent.</li>
+                    <li><strong>Roth conversion ladder (early retirement):</strong> convert Traditional β†’ Roth in low-income years; each conversion is penalty-free to withdraw after 5 years β€” the classic FIRE bridge before 59Β½.</li>
+                    <li><strong>Rule of 55:</strong> leave your employer in or after the year you turn 55 and you can tap <em>that</em> 401(k) penalty-free (not IRAs).</li>
+                    <li><strong>72(t) / SEPP:</strong> substantially equal periodic payments let you tap an IRA early without the 10% penalty β€” rigid, locked for 5 years or to 59Β½.</li>
+                </ul>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-3-circle"></i> 2026 limits, phase-outs &amp; thresholds (full tables)</summary>
+            <div class="body">
+                <h3 class="h6 mt-1">Roth IRA β€” contribution phase-out (MAGI, 2026)</h3>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th>Filing status</th><th>Full contribution below</th><th>Phase-out range</th><th>No contribution above</th></tr></thead><tbody>
+                    <tr><td>Single / Head of household</td><td>$153,000</td><td>$153,000 – $168,000</td><td>$168,000</td></tr>
+                    <tr><td>Married filing jointly</td><td>$242,000</td><td>$242,000 – $252,000</td><td>$252,000</td></tr>
+                    <tr><td>Married filing separately</td><td>β€”</td><td>$0 – $10,000</td><td>$10,000</td></tr>
+                </tbody></table></div>
+
+                <h3 class="h6">Traditional IRA β€” deduction phase-out (MAGI, 2026, if covered by a workplace plan)</h3>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th>Situation</th><th>Phase-out range</th></tr></thead><tbody>
+                    <tr><td>Single / HoH, covered by workplace plan</td><td>$81,000 – $91,000</td></tr>
+                    <tr><td>MFJ, contributor covered by plan</td><td>$129,000 – $149,000</td></tr>
+                    <tr><td>MFJ, you're not covered but spouse is</td><td>$242,000 – $252,000</td></tr>
+                    <tr><td>Not covered by any workplace plan</td><td>Fully deductible (no limit)</td></tr>
+                </tbody></table></div>
+
+                <h3 class="h6">HSA &amp; HDHP (2026, Rev. Proc. 2025-19)</h3>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th></th><th>Self-only</th><th>Family</th></tr></thead><tbody>
+                    <tr><td>HSA contribution limit</td><td>$4,400</td><td>$8,750</td></tr>
+                    <tr><td>HSA catch-up (55+)</td><td colspan="2">+$1,000 (each spouse needs own HSA)</td></tr>
+                    <tr><td>HDHP minimum deductible</td><td>$1,700</td><td>$3,400</td></tr>
+                    <tr><td>HDHP max out-of-pocket</td><td>$8,500</td><td>$17,000</td></tr>
+                </tbody></table></div>
+
+                <h3 class="h6">Long-term capital-gains brackets (2026 taxable income)</h3>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th>Rate</th><th>Single</th><th>Married filing jointly</th></tr></thead><tbody>
+                    <tr><td>0%</td><td>up to $49,450</td><td>up to $98,900</td></tr>
+                    <tr><td>15%</td><td>$49,451 – $545,500</td><td>$98,901 – $613,700</td></tr>
+                    <tr><td>20%</td><td>over $545,500</td><td>over $613,700</td></tr>
+                </tbody></table></div>
+                <p class="small text-soft mb-0">Net investment income tax (NIIT) of 3.8% stacks on top above $200k single / $250k MFJ MAGI. Short-term gains (held ≀1 year) are taxed as ordinary income β€” hold &gt;1 year.</p>
+
+                <h3 class="h6 mt-3">Year-by-year change (2025 β†’ 2026)</h3>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th>Item</th><th>2025</th><th>2026</th></tr></thead><tbody>
+                    <tr><td>401(k) deferral</td><td>$23,500</td><td>$24,500</td></tr>
+                    <tr><td>401(k) catch-up (50+)</td><td>$7,500</td><td>$8,000</td></tr>
+                    <tr><td>401(k) super catch-up (60–63)</td><td>$11,250</td><td>$11,250</td></tr>
+                    <tr><td>Total additions (415(c))</td><td>$70,000</td><td>$72,000</td></tr>
+                    <tr><td>IRA limit</td><td>$7,000</td><td>$7,500</td></tr>
+                    <tr><td>IRA catch-up</td><td>$1,000</td><td>$1,100</td></tr>
+                    <tr><td>HSA self / family</td><td>$4,300 / $8,550</td><td>$4,400 / $8,750</td></tr>
+                </tbody></table></div>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-4-circle"></i> Asset location β€” which fund goes in which account</summary>
+            <div class="body">
+                <p>Same dollars, different tax drag depending on <em>where</em> you hold each asset. The principle: put tax-inefficient assets in shelters, tax-efficient assets in taxable.</p>
+                <div class="table-responsive"><table class="table table-sm"><thead><tr><th>Hold here</th><th>Put these assets</th><th>Why</th></tr></thead><tbody>
+                    <tr><td><strong>Taxable brokerage</strong></td><td>Broad US/international stock index funds, municipal bonds</td><td>Qualified dividends + LTCG rates; ETFs shed gains; foreign tax credit on intl.</td></tr>
+                    <tr><td><strong>Traditional (tax-deferred)</strong></td><td>Bonds, REITs, high-turnover funds</td><td>Interest/REIT distributions are ordinary income β€” shield them; you'll pay ordinary rates on withdrawal anyway</td></tr>
+                    <tr><td><strong>Roth (tax-free)</strong></td><td>Highest expected-growth assets (small-cap, emerging, aggressive stocks)</td><td>All growth escapes tax forever β€” maximize the tax-free compounding base</td></tr>
+                </tbody></table></div>
+                <ul class="mb-0">
+                    <li><strong>One-fund simplicity beats perfect location.</strong> If juggling locations stops you from investing, a single target-date or total-world fund in every account is a fine choice β€” behavior &gt; optimization.</li>
+                    <li><strong>Hold international in taxable</strong> to claim the foreign tax credit (lost inside an IRA).</li>
+                    <li><strong>Never hold bonds in a Roth</strong> if you have tax-deferred space β€” you're wasting your most valuable (tax-free) real estate on a low-growth asset.</li>
+                </ul>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-5-circle"></i> Withdrawal strategy, the 4% rule &amp; RMDs</summary>
+            <div class="body">
+                <ul>
+                    <li><strong>The 4% rule (Bengen / Trinity study):</strong> withdraw 4% of the initial portfolio, adjust for inflation yearly; historically survived 30 years in ~95%+ of US scenarios. It's a planning heuristic, not a guarantee β€” sequence-of-returns risk early in retirement is the real danger. Longer horizons (FIRE at 40) lean toward 3.25–3.5%.</li>
+                    <li><strong>Withdrawal order (typical tax-minimizing default):</strong> taxable first β†’ tax-deferred (Traditional) next β†’ Roth last. But blend: realize some Traditional income each year up to the top of a low bracket (and do Roth conversions) so you don't get RMD-bombed at 73.</li>
+                    <li><strong>RMDs (Required Minimum Distributions):</strong> begin at <strong>age 73</strong> (rising to 75 for those born 1960+) on Traditional 401(k)/IRA. Roth IRAs never have RMDs; Roth 401(k)s no longer have them (SECURE 2.0, 2024+). Miss one and the penalty is 25% (10% if corrected promptly).</li>
+                    <li><strong>QCD (Qualified Charitable Distribution):</strong> from 70Β½, send up to $108,000 (2025, inflation-indexed) of IRA money directly to charity β€” satisfies RMDs and stays off your AGI.</li>
+                    <li><strong>Capital-gains harvesting:</strong> in low-income years, realize gains up to the top of the 0% LTCG bracket ($98,900 MFJ taxable income in 2026) to reset basis tax-free.</li>
+                </ul>
+                <p class="small text-soft mb-0">QCD figure is the 2025 indexed amount; the 2026 number tracks inflation β€” verify before quoting.</p>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-6-circle"></i> Tax-loss harvesting β€” the full rules</summary>
+            <div class="body">
+                <ul>
+                    <li><strong>What it is:</strong> sell a position at a loss to bank a capital loss, immediately buy a <em>similar-but-not-identical</em> fund to stay invested. The loss offsets gains and up to $3,000/yr of ordinary income; the rest carries forward forever.</li>
+                    <li><strong>Wash-sale rule (IRC Β§1091):</strong> the loss is disallowed if you buy a substantially identical security within 30 days before or after the sale β€” across <em>all</em> your accounts, including your spouse's and your IRA. The 61-day window is the danger zone.</li>
+                    <li><strong>"Substantially identical" is the art:</strong> selling VOO (S&amp;P 500) and buying VTI (total market) is broadly considered safe; selling VOO and buying IVV (another S&amp;P 500 fund) is risky. Use a different index or provider.</li>
+                    <li><strong>IRA trap:</strong> a replacement buy inside your IRA permanently kills the loss (Rev. Rul. 2008-5) β€” basis is <em>not</em> restored. Turn off auto-reinvest/auto-buy in retirement accounts while harvesting.</li>
+                    <li><strong>Tax-rate arbitrage:</strong> you harvest a short-or-long-term loss now but it offsets future gains. Best when you offset short-term gains (taxed at ordinary rates) or income. Watch out for resetting basis lower β†’ larger gains later (deferral, not free money β€” unless you get a step-up at death).</li>
+                    <li><strong>Don't let the tax tail wag the dog:</strong> a tiny loss isn't worth churning; harvesting matters most in volatile down markets and for large taxable balances. Useless inside tax-advantaged accounts (no taxable gains to offset).</li>
+                </ul>
+                <p class="mb-0"><strong>Worked example:</strong> You bought $50k of VTI; it drops to $42k. Sell (bank an $8k loss), buy $42k of VXUS-heavy total-world or VOO same day. Use $3k against this year's salary, carry $5k forward. You stayed ~100% invested and pocketed a tax asset.</p>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-exclamation-octagon-fill text-danger"></i> Common mistakes &amp; anti-patterns</summary>
+            <div class="body">
+                <ul class="mb-0">
+                    <li><strong>Leaving the match on the table.</strong> Not contributing enough to get the full employer match is a guaranteed 50–100% instant return refused. Always fund to the match first.</li>
+                    <li><strong>Cash drag in the IRA.</strong> Contributing to an IRA but never buying funds β€” the money sits in a settlement account earning nothing. <em>Contributing β‰  investing.</em></li>
+                    <li><strong>Performance-chasing &amp; stock-picking.</strong> Last year's winner is a coin flip next year. The index already owns it.</li>
+                    <li><strong>Timing the market.</strong> Missing the 10 best days over 20 years roughly halves your return; those days cluster next to the worst ones. Time <em>in</em> &gt; timing.</li>
+                    <li><strong>High expense ratios &amp; loads.</strong> A 1% advisor fee + 0.75% fund fee can consume ~40% of your lifetime gains. Insist on low-cost index funds; avoid front-loaded funds entirely.</li>
+                    <li><strong>Selling in a crash.</strong> The single most expensive behavioral error. Write an Investment Policy Statement and automate contributions so you keep buying when it's cheap.</li>
+                    <li><strong>Backdoor Roth pro-rata surprise.</strong> Doing a backdoor Roth while holding a big pre-tax IRA β†’ the conversion is mostly taxable. Roll the pre-tax IRA into a 401(k) first.</li>
+                    <li><strong>Ignoring the HSA.</strong> Treating the HSA as a spending account instead of the best retirement vehicle in the code. Invest it; pay current medical from cash flow.</li>
+                    <li><strong>Over-diversifying into overlap.</strong> Owning five "different" funds that are all 80% the same megacaps. Two or three broad funds is plenty.</li>
+                    <li><strong>Home-country / single-stock concentration.</strong> 100% one country or piling into your employer's stock concentrates risk exactly where your paycheck already is.</li>
+                </ul>
+            </div>
+        </details>
+
+        <details class="ref">
+            <summary><i class="bi bi-card-checklist"></i> The 60-second plan (decision guidance)</summary>
+            <div class="body">
+                <ol class="mb-0">
+                    <li>Build a <strong>1-month emergency buffer</strong> + pay off any &gt;8% debt before investing.</li>
+                    <li>Contribute to the <strong>401(k) up to the full match</strong> (free money).</li>
+                    <li>Max the <strong>HSA</strong> if you have an HDHP (triple tax-free; invest it).</li>
+                    <li>Max a <strong>Roth or Traditional IRA</strong> ($7,500). Backdoor if over the income limit.</li>
+                    <li>Finish maxing the <strong>401(k)</strong> ($24,500 total deferral).</li>
+                    <li>Mega-backdoor Roth if available, then <strong>taxable brokerage</strong> β€” unlimited, flexible.</li>
+                    <li>Buy <strong>broad, cheap index funds</strong> (US + international + bonds), set an age-appropriate split, automate, and ignore the noise for 30 years.</li>
+                </ol>
+            </div>
+        </details>
+
+        <footer class="text-center text-soft small mt-5 pt-4" style="border-top:1px solid var(--line)">
+            <p class="mb-1"><strong>Last verified: 2026-06-15.</strong> Limits per IRS Notice 2025-67 &amp; Rev. Proc. 2025-19.</p>
+            <p class="mb-0">Educational reference, not tax/investment advice. Verify current-year figures at <a href="https://www.irs.gov/retirement-plans/cola-increases-for-dollar-limitations-on-benefits-and-contributions">irs.gov</a> before acting. Part of <a href="https://cheatsheets.davidveksler.com/">David Veksler's Cheatsheets</a> Β· AI generated.</p>
+        </footer>
+    </main>
+
+    <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
+        integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
+    <script>
+        (function () {
+            "use strict";
+            const $ = id => document.getElementById(id);
+            const fmt = n => "$" + Math.round(n).toLocaleString("en-US");
+            const fmtK = n => n >= 1e6 ? "$" + (n / 1e6).toFixed(n >= 1e7 ? 0 : 2).replace(/\.00$/, "") + "M" : "$" + Math.round(n / 1000) + "k";
+
+            /* ---------- Theme toggle ---------- */
+            const modes = ["auto", "light", "dark"], icons = { auto: "bi-circle-half", light: "bi-sun", dark: "bi-moon-stars" };
+            let tIdx = 0;
+            try { const s = localStorage.getItem("iitheme"); if (s) tIdx = Math.max(0, modes.indexOf(s)); } catch (e) {}
+            const prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");
+            function applyTheme() {
+                const m = modes[tIdx];
+                document.documentElement.setAttribute("data-theme", m);
+                const resolved = m === "auto" ? (prefersDark && prefersDark.matches ? "dark" : "light") : m;
+                document.documentElement.setAttribute("data-bs-theme", resolved);
+                $("themeLbl").textContent = m[0].toUpperCase() + m.slice(1);
+                $("themeBtn").querySelector("i").className = "bi " + icons[m];
+                try { localStorage.setItem("iitheme", m); } catch (e) {}
+                drawFire();
+            }
+            $("themeBtn").addEventListener("click", () => { tIdx = (tIdx + 1) % modes.length; applyTheme(); });
+            if (prefersDark && prefersDark.addEventListener) prefersDark.addEventListener("change", () => { if (modes[tIdx] === "auto") applyTheme(); });
+
+            const cssVar = n => getComputedStyle(document.documentElement).getPropertyValue(n).trim();
+
+            /* ---------- Tool 1: Compound / FIRE ---------- */
+            const fAge = $("f_age"), fBal = $("f_bal"), fPmt = $("f_pmt"), fSpend = $("f_spend"), fRet = $("f_ret"), fWr = $("f_wr");
+            function computeFire() {
+                const age = +fAge.value, r = +fRet.value / 100, pmt = +fPmt.value * 12, wr = +fWr.value / 100;
+                const fire = (+fSpend.value) / wr;
+                let bal = +fBal.value, fiAge = null, series = [{ age, bal }];
+                for (let a = age + 1; a <= 100; a++) {
+                    bal = bal * (1 + r) + pmt;
+                    series.push({ age: a, bal });
+                    if (fiAge === null && bal >= fire) fiAge = a;
+                    if (a >= 95) break;
+                }
+                const at65 = (series.find(s => s.age === 65) || series[series.length - 1]).bal;
+                return { fire, fiAge, at65, series, age };
+            }
+            let fireState;
+            function updateFire() {
+                $("f_ret_l").textContent = (+fRet.value).toFixed(1) + "%";
+                $("f_wr_l").textContent = (+fWr.value).toFixed(1) + "%";
+                fireState = computeFire();
+                $("o_fire").textContent = fmtK(fireState.fire);
+                $("o_age").textContent = fireState.fiAge ? fireState.fiAge : ">95";
+                $("o_yrs").textContent = fireState.fiAge ? (fireState.fiAge - fireState.age) : "β€”";
+                $("o_65").textContent = fmtK(fireState.at65);
+                $("f_note").textContent = fireState.fiAge
+                    ? "At a " + (+fWr.value).toFixed(1) + "% withdrawal rate you reach financial independence at " + fireState.fiAge + " β€” " + (fireState.fiAge - fireState.age) + " years of contributing " + fmt(+fPmt.value) + "/mo."
+                    : "Not reaching the FIRE target by 95 at these inputs β€” raise contributions, return, or spending plan.";
+                drawFire();
+            }
+            function drawFire() {
+                if (!fireState) return;
+                const cv = $("f_chart"), wrap = cv.parentElement;
+                const W = cv.width = wrap.clientWidth || 600, H = cv.height = 320, ctx = cv.getContext("2d");
+                ctx.clearRect(0, 0, W, H);
+                const pad = { l: 52, r: 12, t: 14, b: 26 };
+                const s = fireState.series, maxBal = Math.max(fireState.fire * 1.15, s[s.length - 1].bal);
+                const a0 = s[0].age, a1 = s[s.length - 1].age;
+                const x = a => pad.l + (a - a0) / (a1 - a0) * (W - pad.l - pad.r);
+                const y = v => H - pad.b - v / maxBal * (H - pad.t - pad.b);
+                const ink = cssVar("--ink-soft"), line = cssVar("--line"), acc = cssVar("--stock"), bond = cssVar("--bond");
+                // grid + y labels
+                ctx.strokeStyle = line; ctx.fillStyle = ink; ctx.font = "11px system-ui"; ctx.lineWidth = 1;
+                for (let i = 0; i <= 4; i++) {
+                    const v = maxBal * i / 4, yy = y(v);
+                    ctx.beginPath(); ctx.moveTo(pad.l, yy); ctx.lineTo(W - pad.r, yy); ctx.stroke();
+                    ctx.fillText(fmtK(v), 4, yy + 4);
+                }
+                // x labels
+                ctx.textAlign = "center";
+                for (let a = a0; a <= a1; a += Math.ceil((a1 - a0) / 6)) ctx.fillText(a, x(a), H - 8);
+                ctx.textAlign = "left";
+                // FIRE target line
+                ctx.strokeStyle = bond; ctx.setLineDash([6, 4]); ctx.lineWidth = 2;
+                ctx.beginPath(); ctx.moveTo(pad.l, y(fireState.fire)); ctx.lineTo(W - pad.r, y(fireState.fire)); ctx.stroke();
+                ctx.setLineDash([]);
+                // portfolio curve
+                ctx.strokeStyle = acc; ctx.lineWidth = 2.5; ctx.beginPath();
+                s.forEach((p, i) => { const px = x(p.age), py = y(Math.min(p.bal, maxBal)); i ? ctx.lineTo(px, py) : ctx.moveTo(px, py); });
+                ctx.stroke();
+                // FI marker
+                if (fireState.fiAge) {
+                    const p = s.find(q => q.age === fireState.fiAge);
+                    ctx.fillStyle = acc; ctx.beginPath(); ctx.arc(x(p.age), y(Math.min(p.bal, maxBal)), 5, 0, 7); ctx.fill();
+                }
+            }
+            [fAge, fBal, fPmt, fSpend, fRet, fWr].forEach(el => el.addEventListener("input", updateFire));
+            window.addEventListener("resize", drawFire);
+            window.addEventListener("load", drawFire);
+
+            /* ---------- Tool 2: Asset allocation ---------- */
+            const aAge = $("a_age"), aRule = $("a_rule");
+            function updateAlloc() {
+                const age = +aAge.value, rule = +aRule.value;
+                let stock = Math.max(20, Math.min(100, rule - age));
+                const bond = 100 - stock;
+                $("a_age_l").textContent = age;
+                $("a_stock").textContent = stock + "%";
+                $("a_bond").textContent = bond + "%";
+                $("a_intl").textContent = Math.round(stock * 0.35) + "%";
+                $("a_seg_s").style.width = stock + "%"; $("a_seg_s").textContent = stock + "% Stocks";
+                $("a_seg_b").style.width = bond + "%"; $("a_seg_b").textContent = bond + "% Bonds";
+                $("a_note").textContent = age < 40
+                    ? "Long runway: a big equity tilt rides out crashes β€” your biggest asset is future earnings."
+                    : age < 60
+                        ? "Mid-career: start adding bonds to dampen sequence risk as the horizon shortens."
+                        : "Near/in retirement: enough bonds to cover ~2–5 years of spending cushions a bad-timing sell-off.";
+            }
+            [aAge, aRule].forEach(el => el.addEventListener("input", updateAlloc));
+
+            /* ---------- Tool 3: Funding waterfall (2026 limits) ---------- */
+            const L = { k401: 24500, k401catch: 8000, ira: 7500, iracatch: 1100, hsaSelf: 4400, hsaFam: 8750, total: 72000 };
+            function updateWaterfall() {
+                let amt = Math.max(0, +$("w_amt").value), age = +$("w_age").value;
+                const hasMatch = $("w_match").checked, matchAmt = Math.max(0, +$("w_matchamt").value);
+                const hasHsa = $("w_hsa").checked, hsaCap = ($("w_hsatype").value === "family" ? L.hsaFam : L.hsaSelf) + (age >= 55 ? 1000 : 0);
+                const roth = $("w_roth").checked;
+                const k401cap = L.k401 + (age >= 50 ? L.k401catch : 0);
+                const iracap = L.ira + (age >= 50 ? L.iracatch : 0);
+                const steps = [];
+                steps.push({ t: "Emergency fund + high-interest debt", cap: Infinity, note: "Before the waterfall: 1–6 months expenses, kill >8% debt.", pre: true });
+                if (hasMatch) steps.push({ t: "401(k) to the full employer match", cap: matchAmt, note: "Instant 50–100% return β€” never skip." });
+                if (hasHsa) steps.push({ t: "Max the HSA", cap: hsaCap, note: "Triple tax-free; invest it, don't spend it." });
+                steps.push({ t: roth ? "Max Roth IRA" : "Max IRA (backdoor Roth if over limit)", cap: iracap, note: "Broad, flexible, contributions withdrawable." });
+                steps.push({ t: "Finish maxing the 401(k)", cap: k401cap - (hasMatch ? Math.min(matchAmt, k401cap) : 0), note: "Up to $" + k401cap.toLocaleString() + " total employee deferral." });
+                steps.push({ t: "Taxable brokerage", cap: Infinity, note: "Unlimited, fully liquid, LTCG rates β€” the overflow bucket." });
+
+                let remaining = amt, html = "", n = 1, placed = 0;
+                for (const s of steps) {
+                    if (s.pre) { html += stepHtml(n++, s.t, null, s.note, false); continue; }
+                    const fill = Math.min(remaining, s.cap);
+                    remaining -= fill; placed += fill;
+                    html += stepHtml(n++, s.t, fill, s.note, fill > 0, s.cap);
+                }
+                $("w_steps").innerHTML = html;
+                $("w_note").textContent = "Allocated " + fmt(placed) + " of " + fmt(amt) + " across the buckets above"
+                    + (remaining > 0 ? " β€” " + fmt(remaining) + " lands in taxable." : ".");
+            }
+            function stepHtml(n, title, fill, note, filled, cap) {
+                const amtTxt = fill === null ? "" : '<span class="fill-amt ms-2">' + fmt(fill) + (cap && cap !== Infinity ? ' <span class="text-soft fw-normal">/ ' + fmt(cap) + '</span>' : '') + '</span>';
+                return '<div class="waterfall-step ' + (filled ? 'filled' : '') + '"><div class="num">' + n + '</div><div><div class="fw-semibold">' + title + amtTxt + '</div><div class="small text-soft">' + note + '</div></div></div>';
+            }
+            ["w_amt", "w_age", "w_match", "w_matchamt", "w_hsa", "w_hsatype", "w_roth"].forEach(id => {
+                $(id).addEventListener("input", updateWaterfall); $(id).addEventListener("change", updateWaterfall);
+            });
+
+            /* ---------- Tool 4: Wash-sale window ---------- */
+            function updateWash() {
+                const v = $("ws_date").value; if (!v) return;
+                const d = new Date(v + "T00:00:00");
+                const start = new Date(d); start.setDate(start.getDate() - 30);
+                const end = new Date(d); end.setDate(end.getDate() + 30);
+                const opt = { year: "numeric", month: "short", day: "numeric" };
+                $("ws_start").textContent = start.toLocaleDateString("en-US", opt);
+                $("ws_end").textContent = end.toLocaleDateString("en-US", opt);
+                $("ws_note").textContent = "61 calendar days total. Buying a substantially identical security anywhere in this window β€” including a spouse's account or an IRA auto-reinvest β€” disallows the loss.";
+            }
+            $("ws_date").addEventListener("input", updateWash);
+
+            /* ---------- init ---------- */
+            applyTheme(); updateFire(); updateAlloc(); updateWaterfall(); updateWash();
+        })();
+    </script>
+</body>
+
+</html>