Where I Left Off

The first post covered three static calculators. The second covered the Astro rebuild into a platform: guides, a glossary, salary and price pages, a first set of neighborhoods, and an affordability index.

Since then the site has gone from “here’s your number” to “here’s what limits your number, what would move it, and where every figure came from.” Most of that landed over October 2–4 as a numbered series of phases, each a separate pull request with its own entry in the repo’s change log. The test suite grew to 337 passing tests and the repo to roughly 190 commits.

This post is mostly about why: why I kept building, why so much of it is about search, how Claude fit in, and why the people using it are friends who are buying, or trying to buy, in New York.

Why I Built It

Buying in New York is confusing in ways national tools don’t handle. A co-op board looks at debt-to-income and post-closing liquidity, not just a down payment. Condos carry a mortgage recording tax that co-ops don’t. There’s a mansion tax above $1M, a flip tax on some sales, and maintenance or common charges that behave differently from rent. Renting has its own rules now, with the FARE Act changing who pays a broker.

A generic “how much house can I afford” calculator gets most of that wrong. I wanted one place that models the NYC-specific rules, shows its sources, and tells you what is actually limiting you.

Why So Much of This Is About SEO

Most people don’t start with a calculator. They start with a question typed into a search box: “what salary do I need for a $700K co-op,” “how much cash to close on a condo in Brooklyn,” “what is a flip tax.” A site with three calculator pages can’t answer those, so nobody lands on it.

The strategy was to turn the same calculation engine into pages that answer those questions directly:

  • Salary and price pages (/income/<amount>/, /buy/<price>/, salary and required-salary pages), generated from the engine so every number matches the calculators. Rent pages gained income-rule and FARE Act scenarios, and salary pages gained pay-period and housing sections.
  • 32 guides and 45 glossary terms, each with a worked example and dated sources. This phase added eleven guides and twenty terms.
  • Borough hubs for Manhattan, Brooklyn, and Queens, and 10 neighborhood pages, each with cited rent and sale figures.
  • Tools aimed at later-stage questions, covered below.

The sitemap is past 170 URLs, but the count isn’t the point. Search engines, and AdSense’s reviewers, are right to be suspicious of thousands of near-identical generated pages, and I’d already been flagged for thin content once. So the rule became: a page only exists if it has a real number, a source, and a reason to exist.

What “SEO” meant in practice

Almost none of it was keyword tricks. It was infrastructure that makes a content-heavy site trustworthy and crawlable:

  • Structured data. Per-page JSON-LD (Article for guides, now with an author, and reviewedBy only once a real reviewer exists) and a navigation schema generated from the same data that renders the nav, so they can’t disagree.
  • A truthful sitemap. Every guide, glossary term, and neighborhood carries a lastmod from its own updated date, and hubs use their newest entry’s date. A sitemap that lies about freshness is worse than none.
  • An /explore/ directory generated from the same collections as the pages, so every new page is automatically linked from somewhere crawlable.
  • Internal linking by design. Glossary terms link to guides, guides link to the calculator that computes the answer, and landing pages link to their nearest neighbors.
  • Honest markup. When visible breadcrumbs came off the calculators, I left the breadcrumb JSON-LD off too, rather than mark up content that isn’t on the page.
  • Journey-based navigation, organized by what someone is trying to do rather than by page type.
  • Per-page Open Graph images, so shared links look right.
  • Trust pages as credibility work: About, Privacy, Methodology, a public changelog and corrections page, and a byline on every guide.

One Engine, Many Tools

The first phase was unglamorous and everything else depends on it. The co-op, condo, and rent math used to live in separate places, so the calculators, the static income/price pages, and the guide examples could each drift. I pulled them onto one shared engine with an assumptions registry (each default carries a value, source, and as-of date) and golden tests. Guide worked examples are checked against the engine too, so a guide can’t quote a number the calculators wouldn’t produce.

Every later tool is the engine re-run with one input changed:

  • How do I afford more? (/afford-more/): which single changes raise your ceiling the most.
  • Down Payment Savings Planner (/savings-planner/): how long a cash target takes at a given monthly savings rate.
  • NYC Rent vs Buy (/rent-vs-buy/): the buyer and the renter both invest whatever the cheaper path saves each month, and the buyer “sells” at each year-end and pays NYC seller costs. It reports which path is ahead, the break-even year, and the break-even rent, with a net-worth chart. Growth and return rates are labeled placeholders, and a “what’s left out” section lists what the model ignores (income taxes, refinancing, assessments, the value of flexibility).
  • Rate & maintenance sensitivity (/rate-sensitivity/): how the ceiling moves as mortgage rates and monthly charges change.
  • Cost to move (/cost-to-move/): every dollar needed on move-in day for a rental, co-op, or condo, sorted into spent, becomes equity, comes back later (deposits), and stays in your account (co-op reserves). It reconciles with the rent calculator, the savings planner’s cash target, and the reserves guide.
  • My NYC Plan (/plan/): reads what’s saved in the browser and, for renting, a co-op, and a condo, shows the ceiling, whether income or cash is the binding limit, what the other side alone would allow, and up to three single changes that actually help. Changes that wouldn’t help aren’t listed. The lift amounts are tested to the dollar: the stated amount is enough, and one dollar less isn’t.
  • Charts that keep cited and calculated figures apart on /neighborhoods/, so a chart never presents a number the site computed as if it were a cited market figure.

Each is a pure function in src/lib/ with its own tests; the page scripts only render.

Public Data, and a Site That Checks Itself

/data/ publishes five datasets (cited figures, assumptions, AMI tables, income-needed grids) as JSON and CSV, generated from the same files the pages read, so a download can’t disagree with the site.

A site full of dated figures is only as good as its freshness, and a static site has no backend to refresh them. So I added scheduled GitHub Actions jobs with one deliberate constraint: they only ever open pull requests.

  • Weekly: reads Freddie Mac’s PMMS history and, when there’s a newer 30-year rate than the site’s default, updates the registry and the rate inputs, runs the tests, and opens a draft PR listing the golden tests that need new values, the guide sentences quoting the old rate, and every remaining line that mentions it.
  • Monthly: parses HPD’s AMI chart and opens a draft PR if the year or any figure differs from the site’s table.
  • Yearly: opens a checklist issue for federal, NYS, and NYC tax tables, which can’t be scraped reliably.

The jobs never merge, never push to the default branch, and don’t force-push over a human’s commits. The parsers throw on any format change instead of guessing, with sanity checks on top (AMI must rise with household size, and the 1-person to 4-person ratio must be about 0.7). On October 4 the weekly job moved the default rate from 6.95% to 7.28% and the PR named the 30 tests and 24 lines that quoted the old number.

Why Claude, and How I Used It

I’m one person with a day job. A site like this has hundreds of pages, a real calculation engine, and a long tail of “is this number still true” work. An AI pair is genuinely useful for that, but only if it’s pointed at the right things. What worked:

  • Phases, not sprawl. Everything shipped as numbered phases, each its own PR with its own change-log entry, small enough that I could read every diff.
  • The engine first, content second. Every page inherits correct numbers from the shared engine or fails a test.
  • Quality gates in the build, not in my head. A neighborhood page can’t build unless each market figure carries its own source and date. A test fails if a changelog “Fix:” entry has no matching public correction. A script hashes every inline script and fails the build if the Content-Security-Policy would block it, which came from a real bug where the CSP was blocking the nav dropdowns.
  • Tests first for the risky parts. On /plan/, rules like “don’t quote a $100,000/mo cash ceiling for a renter” and “don’t suggest an income lift that more than doubles income” were written as failing tests before the code. I also deliberately broke things (a typo’d path, an unlinked fix) to confirm tests caught them.
  • Real browser checks. Unit tests passed while the A/B comparison showed +$116,128 beside two values $116,129 apart, because differences were taken before rounding. I only found it by using the page. When I refactored /compare/ onto a shared module, I diffed every result cell against the previous build across four states, not just the happy path.
  • Automation that proposes, never decides, as above.

The catch on the SEO side: Claude makes it cheap to produce a lot of pages, which is exactly the risk. The discipline was spending the saved time on sources, tests, and gates so volume didn’t become thin content. Along the way that also caught real errors, such as the NYS 6.85% bracket threshold for single and head-of-household filers, the AMI table not matching HPD’s 2026 chart, and a renter move-in fee that belongs only to co-op and condo buildings. Each is listed publicly on the corrections page.

Privacy Is Part of Trust

People type their income, debts, and savings into these tools, so I tightened what happens to that data:

  • No personal finances in URLs. The retired co-op domain used to pass saved balances in a URL fragment. Now only an allowlist of 12 calculator settings travels, and personal figures are offered as a local download. The real Worker page is tested in a node:vm sandbox against fake saved data, and the new tests fail against the old Worker. Share links carry scenario inputs, not income.
  • Your Saved Data (/my-data/) lists everything the site stores in your browser, marks what can hold personal figures, and supports named scenarios, export/import with a preview, an A/B comparison of two scenarios (anything a scenario didn’t save is tagged default, so an old scenario isn’t silently compared at today’s rate), and a confirmed delete-everything. No network requests, no ads.
  • A public changelog and corrections page list every change that could move a number, what was wrong, what’s right now, and the PR. Each guide carries a byline and an explicit “not reviewed by a licensed attorney, accountant, or mortgage professional” line. A named reviewer is still an open question, and the site says so rather than implying one.

What Friends Are Telling Me

The best feedback so far has come from friends in the middle of their own NYC home-buying journeys. They tell me it’s useful, and what keeps coming up lines up with why I built it: seeing what actually limits them (income or cash), learning about co-op and condo costs they didn’t know existed, and having numbers they can trace to a source instead of taking on faith.

Their questions also steer the roadmap. When a friend asks something the site can’t answer, that’s the next guide, glossary term, or tool, which is a better content strategy than guessing at keywords.

What I Learned

Search traffic and usefulness turned out to be the same problem. The pages that help a friend decide whether to keep saving or start looking are the same pages that match what someone types into Google, and both depend on the numbers being right, sourced, and current.

It would also have been easy to let a job update the rate and merge it. Opening a draft PR that lists everything the change touches takes a minute to review, and that minute is where a wrong parse gets caught.

What’s Next

A named professional reviewer for the guides is the main open question. After that: more neighborhoods outside Brooklyn at the same sourcing bar, and extending the A/B comparison to the rent-vs-buy and savings-planner scenarios.

More Information