Money forecast: calm optimistic start, rising plateau, thin solid projected lines
All checks were successful
Deploy production / deploy (push) Successful in 1m13s

- Optimistic: no crash or correction through 2031 (5 years from today), earning the
  9.5% average; cycle resumes after (correction 2035, crash 2040). Salary plateau
  grows 2%/yr real so pay never stalls.
- Realistic keeps its single -30% crash in 2029; pessimistic unchanged.
- Projected lines are now thin, slightly lighter solid lines in every view; legend updated.
- Explanation text lists the crash/correction years and recovery time.
This commit is contained in:
2026-10-26 08:25:52 +00:00
parent d3b9bb9ba2
commit 78b69c7d8f
6 changed files with 135 additions and 38 deletions

View File

@@ -15,8 +15,8 @@ export type ChartSeries = {
color: string;
points: ChartPoint[];
emphasis?: boolean;
/** Points after this date are drawn dashed (projected). */
dashedFrom?: string;
/** Points after this date are projected: drawn as a thinner, lighter solid line. */
projectedFrom?: string;
};
const palette = ['#0070f3', '#e5484d', '#46a758', '#ad7f00', '#8e4ec6', '#0091ad', '#e56910'];
@@ -283,7 +283,7 @@ export function wealthProjection(
/**
* Thins long series for drawing: per time bucket only the lowest and highest point stay (in
* date order), so a daily projection over decades keeps its spikes without plotting every day.
* The first and last points and the solid/dashed join are always kept.
* The first and last points and the actual/projected join are always kept.
*/
export function decimateSeries(series: ChartSeries[], buckets: number): ChartSeries[] {
const limit = Math.max(2, Math.floor(buckets)) * 2;
@@ -301,8 +301,8 @@ export function decimateSeries(series: ChartSeries[], buckets: number): ChartSer
const keep = new Uint8Array(line.points.length);
keep[0] = 1;
keep[line.points.length - 1] = 1;
if (line.dashedFrom) {
const join = line.points.findLastIndex((point) => point.date <= line.dashedFrom!);
if (line.projectedFrom) {
const join = line.points.findLastIndex((point) => point.date <= line.projectedFrom!);
if (join >= 0) keep[join] = 1;
}
let bucket = -1;
@@ -334,7 +334,7 @@ export function decimateSeries(series: ChartSeries[], buckets: number): ChartSer
export type TimelineView = 'total' | 'split' | 'accounts';
/**
* Joins actual history (solid, through today) with the wealth projection (dashed, after
* Joins actual history (solid, through today) with the wealth projection (thin solid line, after
* today) so both read as one continuous line per measure.
*/
export function wealthTimeline(
@@ -379,7 +379,7 @@ export function wealthTimeline(
label: 'Total',
color: 'var(--site-text)',
emphasis: true,
dashedFrom: today,
projectedFrom: today,
points: join(history.at(-1)!.points, totalProjection?.points ?? [])
};
if (view === 'accounts')
@@ -395,13 +395,13 @@ export function wealthTimeline(
{
label: 'Spendable',
color: '#46a758',
dashedFrom: today,
projectedFrom: today,
points: join(sumKind(false), spendableProjection?.points ?? [])
},
{
label: 'Invested',
color: '#8e4ec6',
dashedFrom: today,
projectedFrom: today,
points: join(sumKind(true), investedProjection?.points ?? [])
}
];