initial version
This commit is contained in:
+165
@@ -0,0 +1,165 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Net Worth Projection Calculator</title>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Net Worth Projection Calculator</h1>
|
||||||
|
<p class="subtitle">Simulate your net worth over a 30-year period</p>
|
||||||
|
|
||||||
|
<form id="calculator-form">
|
||||||
|
<div class="card">
|
||||||
|
<h2>Portfolio Values</h2>
|
||||||
|
<div class="input-grid">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="pretax-value">Pre-tax Market Value</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="pretax-value" inputmode="numeric" data-dollar value="1,500,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="aftertax-value">After-tax Market Value</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="aftertax-value" inputmode="numeric" data-dollar value="1,500,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="roth-value">Roth Market Value</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="roth-value" inputmode="numeric" data-dollar value="500,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="realestate-value">Real Estate Value</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="realestate-value" inputmode="numeric" data-dollar value="1,200,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Assumptions</h2>
|
||||||
|
<div class="input-grid">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="inflation">Inflation Factor</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input type="number" id="inflation" value="3.0" min="0" max="20" step="0.1">
|
||||||
|
<span class="input-suffix">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="ror">Rate of Return</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input type="number" id="ror" value="7.0" min="-20" max="50" step="0.1">
|
||||||
|
<span class="input-suffix">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="tax-rate">Pre-tax Withdrawal Tax Rate</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<input type="number" id="tax-rate" value="25.0" min="0" max="100" step="0.1">
|
||||||
|
<span class="input-suffix">%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Annual Spending (Year 1)</h2>
|
||||||
|
<div class="input-grid">
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="pretax-spending">Pre-tax Spending</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="pretax-spending" inputmode="numeric" data-dollar value="75,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="aftertax-spending">After-tax Spending</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="aftertax-spending" inputmode="numeric" data-dollar value="100,000" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="roth-spending">Roth Spending</label>
|
||||||
|
<div class="input-wrapper">
|
||||||
|
<span class="input-prefix">$</span>
|
||||||
|
<input type="text" id="roth-spending" inputmode="numeric" data-dollar value="0" min="0" step="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-calculate">Calculate</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="results" class="results hidden">
|
||||||
|
<div class="card">
|
||||||
|
<h2>Net Worth Projection</h2>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="projection-chart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Year-by-Year Breakdown (With Inflation)</h2>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table id="results-table-inflation">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Year</th>
|
||||||
|
<th>Pre-tax</th>
|
||||||
|
<th>After-tax</th>
|
||||||
|
<th>Roth</th>
|
||||||
|
<th>Real Estate</th>
|
||||||
|
<th>Total Net Worth</th>
|
||||||
|
<th>YoY Growth</th>
|
||||||
|
<th>Pre-tax Spend</th>
|
||||||
|
<th>After-tax Spend</th>
|
||||||
|
<th>Roth Spend</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h2>Year-by-Year Breakdown (No Inflation)</h2>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table id="results-table-noinflation">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Year</th>
|
||||||
|
<th>Pre-tax</th>
|
||||||
|
<th>After-tax</th>
|
||||||
|
<th>Roth</th>
|
||||||
|
<th>Real Estate</th>
|
||||||
|
<th>Total Net Worth</th>
|
||||||
|
<th>YoY Growth</th>
|
||||||
|
<th>Pre-tax Spend</th>
|
||||||
|
<th>After-tax Spend</th>
|
||||||
|
<th>Roth Spend</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
let chartInstance = null;
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-dollar]').forEach(function (input) {
|
||||||
|
input.addEventListener('input', function () {
|
||||||
|
var raw = this.value.replace(/[^0-9]/g, '');
|
||||||
|
if (raw === '') {
|
||||||
|
this.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.value = parseInt(raw, 10).toLocaleString('en-US');
|
||||||
|
});
|
||||||
|
|
||||||
|
input.addEventListener('blur', function () {
|
||||||
|
var raw = this.value.replace(/[^0-9]/g, '');
|
||||||
|
if (raw === '') {
|
||||||
|
this.value = '0';
|
||||||
|
} else {
|
||||||
|
this.value = parseInt(raw, 10).toLocaleString('en-US');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('calculator-form').addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const resultsInflation = runProjection(true);
|
||||||
|
const resultsNoInflation = runProjection(false);
|
||||||
|
renderTable(resultsInflation, 'results-table-inflation');
|
||||||
|
renderTable(resultsNoInflation, 'results-table-noinflation');
|
||||||
|
renderChart(resultsInflation);
|
||||||
|
document.getElementById('results').classList.remove('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
function getVal(id) {
|
||||||
|
var el = document.getElementById(id);
|
||||||
|
var raw = el.value.replace(/[^0-9.]/g, '');
|
||||||
|
return parseFloat(raw) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fmt(n) {
|
||||||
|
return n.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
function runProjection(useInflation) {
|
||||||
|
const years = 30;
|
||||||
|
const inflationFactor = useInflation ? 1 + getVal('inflation') / 100 : 1;
|
||||||
|
const growthFactor = 1 + getVal('ror') / 100;
|
||||||
|
const taxRate = getVal('tax-rate') / 100;
|
||||||
|
|
||||||
|
let pretax = getVal('pretax-value');
|
||||||
|
let aftertax = getVal('aftertax-value');
|
||||||
|
let roth = getVal('roth-value');
|
||||||
|
let realEstate = getVal('realestate-value');
|
||||||
|
|
||||||
|
let pretaxSpend = getVal('pretax-spending');
|
||||||
|
let aftertaxSpend = getVal('aftertax-spending');
|
||||||
|
let rothSpend = getVal('roth-spending');
|
||||||
|
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
for (let y = 0; y <= years; y++) {
|
||||||
|
const total = Math.round(pretax + aftertax + roth + realEstate);
|
||||||
|
const prevTotal = rows.length > 0 ? rows[rows.length - 1].total : total;
|
||||||
|
rows.push({
|
||||||
|
year: y,
|
||||||
|
pretax: Math.round(pretax),
|
||||||
|
aftertax: Math.round(aftertax),
|
||||||
|
roth: Math.round(roth),
|
||||||
|
realEstate: Math.round(realEstate),
|
||||||
|
total: total,
|
||||||
|
yoyGrowth: Math.round(total - prevTotal),
|
||||||
|
pretaxSpend: Math.round(pretaxSpend),
|
||||||
|
aftertaxSpend: Math.round(aftertaxSpend),
|
||||||
|
rothSpend: Math.round(rothSpend),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (y === years) break;
|
||||||
|
|
||||||
|
pretax = pretax * growthFactor;
|
||||||
|
aftertax = aftertax * growthFactor;
|
||||||
|
roth = roth * growthFactor;
|
||||||
|
realEstate = realEstate * growthFactor;
|
||||||
|
|
||||||
|
pretaxSpend = Math.min(pretaxSpend, pretax * (1 - taxRate));
|
||||||
|
pretax -= pretaxSpend / (1 - taxRate);
|
||||||
|
pretax = Math.max(0, pretax);
|
||||||
|
|
||||||
|
aftertaxSpend = Math.min(aftertaxSpend, aftertax);
|
||||||
|
aftertax -= aftertaxSpend;
|
||||||
|
aftertax = Math.max(0, aftertax);
|
||||||
|
|
||||||
|
rothSpend = Math.min(rothSpend, roth);
|
||||||
|
roth -= rothSpend;
|
||||||
|
roth = Math.max(0, roth);
|
||||||
|
|
||||||
|
pretaxSpend *= inflationFactor;
|
||||||
|
aftertaxSpend *= inflationFactor;
|
||||||
|
rothSpend *= inflationFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTable(rows, tableId) {
|
||||||
|
const tbody = document.querySelector('#' + tableId + ' tbody');
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
rows.forEach(function (r) {
|
||||||
|
const growthColor = r.yoyGrowth >= 0 ? 'color:#16a34a' : 'color:#dc2626';
|
||||||
|
const growthPrefix = r.yoyGrowth >= 0 ? '+$' : '-$';
|
||||||
|
const growthStr = r.year === 0 ? '—' : '<span style="' + growthColor + '">' + growthPrefix + fmt(Math.abs(r.yoyGrowth)) + '</span>';
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
tr.innerHTML =
|
||||||
|
'<td>' + r.year + '</td>' +
|
||||||
|
'<td>$' + fmt(r.pretax) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.aftertax) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.roth) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.realEstate) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.total) + '</td>' +
|
||||||
|
'<td>' + growthStr + '</td>' +
|
||||||
|
'<td>$' + fmt(r.pretaxSpend) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.aftertaxSpend) + '</td>' +
|
||||||
|
'<td>$' + fmt(r.rothSpend) + '</td>';
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderChart(rows) {
|
||||||
|
const labels = rows.map(function (r) { return 'Year ' + r.year; });
|
||||||
|
|
||||||
|
const datasets = [
|
||||||
|
{
|
||||||
|
label: 'Pre-tax',
|
||||||
|
data: rows.map(function (r) { return r.pretax; }),
|
||||||
|
borderColor: '#2563eb',
|
||||||
|
backgroundColor: 'rgba(37, 99, 235, 0.08)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'After-tax',
|
||||||
|
data: rows.map(function (r) { return r.aftertax; }),
|
||||||
|
borderColor: '#16a34a',
|
||||||
|
backgroundColor: 'rgba(22, 163, 74, 0.08)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Roth',
|
||||||
|
data: rows.map(function (r) { return r.roth; }),
|
||||||
|
borderColor: '#dc2626',
|
||||||
|
backgroundColor: 'rgba(220, 38, 38, 0.08)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Real Estate',
|
||||||
|
data: rows.map(function (r) { return r.realEstate; }),
|
||||||
|
borderColor: '#f59e0b',
|
||||||
|
backgroundColor: 'rgba(245, 158, 11, 0.08)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Total Net Worth',
|
||||||
|
data: rows.map(function (r) { return r.total; }),
|
||||||
|
borderColor: '#7c3aed',
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderWidth: 2.5,
|
||||||
|
tension: 0.3,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = document.getElementById('projection-chart').getContext('2d');
|
||||||
|
chartInstance = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: { labels: labels, datasets: datasets },
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function (context) {
|
||||||
|
return context.dataset.label + ': $' + fmt(context.parsed.y);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
labels: {
|
||||||
|
usePointStyle: true,
|
||||||
|
pointStyle: 'circle',
|
||||||
|
padding: 16,
|
||||||
|
font: { size: 12 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
grid: { display: false },
|
||||||
|
ticks: { maxTicksLimit: 11 },
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
callback: function (v) { return '$' + fmt(v); },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg: #f4f6f9;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--text: #1a1a2e;
|
||||||
|
--text-muted: #6b7280;
|
||||||
|
--primary: #2563eb;
|
||||||
|
--primary-hover: #1d4ed8;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
||||||
|
--radius: 12px;
|
||||||
|
--radius-sm: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 860px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin-bottom: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h2 {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
overflow: hidden;
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper:focus-within {
|
||||||
|
border-color: var(--primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-wrapper input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
padding: 0.55rem 0.75rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-family: inherit;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-prefix,
|
||||||
|
.input-suffix {
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: var(--bg);
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-prefix {
|
||||||
|
border-right: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-suffix {
|
||||||
|
border-left: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-calculate {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.8rem;
|
||||||
|
background: var(--primary);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: inherit;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
transition: background 0.15s, transform 0.1s;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-calculate:hover {
|
||||||
|
background: var(--primary-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-calculate:active {
|
||||||
|
transform: scale(0.99);
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 380px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-wrapper {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th {
|
||||||
|
background: var(--bg);
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--text-muted);
|
||||||
|
padding: 0.6rem 0.5rem;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: right;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:hover {
|
||||||
|
background: rgba(37, 99, 235, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
body {
|
||||||
|
padding: 1rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
height: 260px;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user