Programmatic SEO, done right

How Much Would I Have If… — the what-if calculator built to earn its long tail

Pick an asset. Pick a year. See the number — on a fast, honest page made to rank.

The domain name is the search query. People literally type “how much would I have if I invested in Apple in 2010” into Google — so the whole product answers exactly that, on a dedicated, statically-generated page for every asset × every year, then ranks for the long tail. The hard part was never generating ~1,070 pages. It was making each one genuinely substantive, accurate, and worth ranking — fast, accessible, and honest about a Your-Money-Your-Life finance topic.

This is the live production site — pick an asset and a year, switch between all at once, a bit each year, and skip the latte, then open Compare or the leaderboard. Opens in desktop by default; use the toggle for the mobile layout. If the embed is blank, your browser may block framing — open it in a new tab.

The problem: programmatic pages get the whole domain buried

Spin up a thousand near-identical “calculator” pages and Google’s Helpful Content System reads the pattern as thin and demotes the entire site — not just the weak pages.

The easy build is a template that swaps a ticker and a year into the same hollow sentence. It scales to thousands of URLs in an afternoon and ranks for nothing, because every page is a doorway with a number stamped in it. The design problem was the opposite of generation: how do you make a page that was produced programmatically read — to a person and to a crawler — as if it were written deliberately, with real data, a real chart, and something true to say that no other page says? Treat thinness itself as the bug to fix.

The doorway page · what most programmatic SEO ships

Apple since 2010 $X

“If you invested $1,000 in {ASSET} in {YEAR} you would have {NUMBER} today.” One templated sentence, no sourcing, no chart, no context. Identical to 1,000 sibling URLs.

thin & templated

The substantive page · what HMWIH ships

Apple since 2010 $18,200
real prices interactive chart derived insights

A unique narrative computed from real monthly closes — best and worst year, deepest drawdown, inflation-adjusted value — plus an accessible chart and structured data that mirrors what the reader sees.

One question, three honest ways to ask it

The same curiosity — “what would I have?” — has three real shapes. Each gets its own math, not a relabelled version of the same formula.

People don’t actually invest one way. Some imagine a single lump sum left alone; some picture drip-feeding a little every year; some have heard the “skip the daily coffee” cliché and want to see if it holds up. Collapsing all three into one number would be the dishonest shortcut. So the calculator carries three distinct engines and is explicit about which one is answering — because the honest answer to a money question depends entirely on how you asked it.

All at once

A single lump sum on day one, left untouched. The cleanest growth-multiple story — shares bought once, valued today.

A bit each year

Dollar-cost averaging across many entry prices. Solved with a money-weighted return (XIRR), because contributions land at different times and a simple average would lie.

Skip the latte

The famous cliché, made checkable: route a small recurring everyday spend into the asset and see what the trade-off actually came to — no hand-waving.

The core decision: a merge that refuses to mix two price scales

The most dangerous bug never threw an error. Two correct data sources, naively overlaid, produced confident, wildly wrong returns.

Long histories come from an index level; recent, precise history comes from an ETF-proxy feed. They measure the same asset on different scales. The first implementation interleaved them by date — and the seam between scales manufactured phantom swings of hundreds to thousands of percent that looked completely plausible on a chart. On a Your-Money-Your-Life topic, a believable wrong number is far worse than a visible crash. The fix was a deliberate merge(): within the span a fetched feed covers, that feed is authoritative and the seed series is never allowed to bleed in; the older seed only fills history the feed doesn’t reach. One rule, unit-tested, with the failure case pinned so it can’t come back.

Naive overlay · interleave by date

Reported gain +3,981%

Index-level and ETF-proxy points sit on different scales. Interleaving them puts a cliff at the seam — a phantom move the chart renders as real.

plausible & wrong

Deliberate merge · feed wins within its span

Reported gain +239%

The fetched feed is authoritative across the range it covers; the seed history only extends backward where no feed exists. No scale ever crosses another.

unit-tested regression

The phantom-swing case is pinned as a regression test: if a future data source ever reintroduces a scale mismatch, the suite fails before it ships.

An insights engine that turns real data into true, unique prose

Substance can’t be hand-written 1,070 times. It has to be derived — every sentence computed from the same numbers behind the chart, so it’s specific and true by construction.

insights.ts · what every page derives

  • Best & worst year — the single calendar years that helped and hurt most, named with their actual returns.
  • Max drawdown & recovery — the deepest peak-to-trough fall and how long it took to climb back.
  • Volatility → a ride descriptor — the bumpiness is translated into plain language instead of a bare standard deviation.
  • Versus the S&P 500 — every asset is benchmarked against the obvious alternative, so the number has a frame.
  • Real vs nominal — CPI-adjusted “today’s dollars” sits beside the headline so inflation isn’t quietly ignored.

One source, two surfaces

The same derived object feeds the visible prose and the structured data. The page a person reads and the page a crawler parses can’t drift apart, because they’re rendered from one computation.

0 data errors 14 legit big moves coverage → 2026

Verification flagged fourteen extreme moves; each was checked and confirmed real (crashes, COVID, the 2022 drawdown), not artifacts. Proxy and pair adjustments cancel out of the ratios, so they never touch a reported return.

The hardest call: accessible interactive charts, no charting library

A charting library would have shipped in an hour — and quietly failed keyboard users, screen readers, and the page-weight budget on ~1,070 static pages.

The charts are hand-built SVG. A single useChartHover hook drives mouse, touch, and keyboard — arrow keys to move point-to-point, Home and End to jump to the extremes, Escape to dismiss — with an aria-live region narrating each value, a touch-action: pan-y rule so the page still scrolls on mobile, and a reduced-motion path. One sharp gotcha: CSS custom properties don’t resolve inside SVG presentation attributes, so theme colours rendered as invisible lines. A small chartColor() resolver reads the CSS variable and hands SVG a real hex value — the kind of bug a library hides until it doesn’t.

Shipped

  • Keyboard navigation: arrows, Home/End, Escape
  • aria-live value announcements
  • Touch hover that still allows vertical scroll
  • Reduced-motion fallback
  • chartColor() CSS-var → hex resolver

Skipped on purpose

  • A 100 KB+ charting dependency on every static page
  • Canvas rendering a screen reader can’t see
  • Default tooltips that trap touch scrolling
  • Animations with no reduced-motion escape hatch

Built to rank honestly: structured data that mirrors the page

Three content pillars carry the SEO, and the structured data describes exactly what the reader sees — never more.

Scenario pages

Every asset × every year — the long-tail core, each statically generated and individually substantive.

Canonical matchups

“A vs B” comparisons with one canonical order; the reverse order returns a deliberate 404 instead of splitting rank.

The Learn cluster

Concept pages that catch informational queries and link inward — topical authority, not keyword stuffing.

Structured data · mirrors the visible answer

FAQPage + the asset’s computed figures, rendered from the same insights object as the prose. Share-image variants are noindex with a canonical back to the real page, so social previews never compete with the page that should rank.

The rule: structured data may describe what’s on the page — never assert a number the reader can’t also see.

v1 → shipped: defending substance against the easy shortcut

The arc isn’t a feature changelog. Each step defends the same thesis — every page must earn its rank with real data and something true to say — against the cheaper, thinner version of itself.

It begins with the data spine (two sources of truth), then forces substance into every templated page, then builds the content graph that makes the long tail cohere, then makes the data verifiably real, then kills the one bug that could have made the whole thing dishonest, then earns trust through brand and accessibility — and ships.

  • v1

    Two sources of truth.

    An ASSETS registry plus a prices store — the data spine everything else computes from.

  • v2

    Substance over templates.

    The insights engine turns each page from a filled-in sentence into a derived, specific narrative.

  • v3

    The content graph.

    Canonical matchups and the Learn cluster turn isolated pages into an interlinked, crawlable structure.

  • v4

    Real, verified data.

    Monthly closes plus CPI, run through a verifier: 0 errors, 14 big moves confirmed legitimate, coverage to mid-2026.

  • v5

    The dangerous bug, fixed.

    The scale-mixing merge bug — phantom thousand-percent swings — caught, corrected, and pinned with a regression test.

  • v6

    Brand & accessibility.

    The “Cobalt Ledger” identity, and the hand-built charts made fully keyboard- and screen-reader-navigable.

  • live

    Shipped to production.

    ~1,070 static pages on Vercel at a custom domain, data current to mid-2026, with consent-mode analytics. Now indexing and accruing the long tail.

Honest caveat: the site is brand new, so rankings and traffic aren’t meaningful yet — that story will be written by the index over the coming months. This case study is about the engineering and architecture decisions that give those pages a fair chance to earn it.

Explore more work

More real, shipped products — each a different take on making something genuinely useful, honest, and built with craft.

Criterion case study cover
Criterion — the AI interview coach that shows its work

An anti-black-box assessment product: every score traces to evidence and every score is contestable.

View case study
1QR case study cover
1QR — one code, every link

A dynamic QR product that turns a single printed code into a destination you can change after it ships.

View case study
Estately case study cover
Estately — calmer real-estate decisions

A home-search experience that organizes the messy, emotional parts of buying into something legible and calm.

View case study