Where I Left Off

The first post about NYC Affordability described a small, static tool: three single-page calculators — co-op, condo, and rent — built in plain HTML, CSS, and JavaScript, served through a Cloudflare Worker with no build step and no dependencies.

That description is now out of date. Over the following months the project grew into a much larger site: a shared affordability engine exposed through calculators, sourced guides, a glossary, neighborhood pages, salary/price landing pages, and a monthly market index — all rebuilt on Astro instead of hand-written HTML. Most of that work happened in an extended series of sessions with Claude Code, and this post is about both what changed and how that collaboration actually worked.

Why I Moved Off Static HTML

The original “no framework, no build step” architecture was a good constraint early on, but it stopped scaling once the site had more than a handful of pages. Every calculator repeated the same header, footer, SEO meta tags, and structured data by hand, so a shared change — like updating the footer links or the JSON-LD schema — meant touching every file individually and hoping nothing drifted.

I migrated the site to Astro in static output mode: npm run build compiles everything in src/pages/ into plain HTML/CSS/JS, and Cloudflare Workers Static Assets serves it exactly the way it served the hand-written files before — no server rendering, nothing leaves the browser. What changed is that the header, footer, SEO head, and ad slots are now shared components, and adding a new page is one file that reuses them instead of a copy-pasted HTML document. The Cloudflare Worker at functions/[[path]].js still does the host-based routing between domains; the migration didn’t touch that layer at all.

From Calculators to a Platform

The calculators were always the interactive core, but they only answer the question for someone who’s ready to run their own numbers. A lot of the site’s traffic potential was in questions people ask before that — “what can I afford on $120k in NYC,” “what does closing on a $700k condo actually cost,” “is Astoria affordable on my income.” Answering those meant expanding past three calculator pages:

  • Guides (/guides/<slug>/) — sourced, citation-backed explainers on renting, buying, co-ops, and affordable housing, grouped by category and cross-linked to the relevant calculator.
  • Glossary (/glossary/<slug>/) — short definitions for terms like AMI, DTI, flip tax, and post-closing liquidity, each with a worked numeric example rather than a bare definition.
  • Salary and price landing pages (/income/<amount>/, /buy/<price>/) — the calculator math exposed as static, indexable content for a fixed set of incomes and prices, computing an income ceiling only (no cash/reserve check) and linking back to the live calculator for a real number.
  • Neighborhood pages (/neighborhoods/<slug>/) — median rent and sale price by neighborhood, each figure carrying its own source and as-of date rather than one shared snapshot date, plus a “what it takes to live here” card computed against that neighborhood’s own cited numbers.
  • NYC Affordability Index (/affordability-index/) — a hand-edited, versioned array of monthly market snapshots with a hand-rolled inline SVG sparkline, honest that “monthly” can’t be an automated cadence on a static site with no backend.
  • Housing Reality Check (/reality-check/) — a lighter, single-savings-number sibling to the full /compare/ dashboard, returning a plain verdict (Comfortable / Stretch / Unlikely, or a max value and the binding constraint) for someone who just wants to know where to start.
  • “What if…?” scenario sliders on /compare/ — salary, savings, and mortgage-rate deltas that recompute all three calculators live without touching a visitor’s saved profile.
  • A Sale Net Proceeds calculator (/sell/) for sellers, and shareable results (Web Share API with a clipboard fallback) wired into every calculator.

Guides, glossary entries, and neighborhoods are Astro content collections — Markdown files with a schema enforced in src/content.config.ts — so adding one is writing a file, not writing a new page.

Working With Claude Code

Most of the last few months of this project happened as a back-and-forth with Claude Code rather than solo coding. A few things about that process stood out:

It caught real bugs, not just style issues. The automatic PMI calculation for the condo and co-op calculators went through two review rounds before a tier-boundary bug in the down-payment search loop surfaced — the kind of off-by-one that’s easy to miss by eye but shows up immediately once you push on edge cases. The most recent fix was similar: the cash estimate on /buy/<price>/ was missing the NYC mortgage recording tax, caught and corrected as a small, isolated PR rather than a rewrite.

Big changes shipped in phases, and each phase was reviewable on its own. The Astro migration, the guides/glossary rollout, the Housing Reality Check and what-if sliders, and the neighborhood pages plus affordability index each landed as their own pull request, in that order, over about a week and a half — large enough to matter, small enough that I could actually read the diff and sanity-check the reasoning before merging.

Copilot’s automated review became a second pass, not a formality. Several PRs — the shared cross-calculator profile, the compare dashboard, the affordable housing finder, the PMI work — went through explicit “address Copilot review feedback” follow-up commits. That two-reviewer loop (Claude proposing, Copilot flagging, a follow-up commit resolving it) caught things like divide-by-zero cases when reserve months is set to zero and stale computed values in the comparison grid before they reached production.

Documentation stayed close to the code, not bolted on after. Rather than a separate wiki, the project’s README carries detailed notes next to each architectural decision — why afford.ts deliberately duplicates rather than imports the DOM-coupled calculator logic, why Reality Check’s liquid savings figure lives in its own localStorage key instead of the shared profile, why the neighborhoods collection refuses to cite a live IDX-feed page. That habit of writing down the why next to the what made it much easier to hand off later changes — including this migration — without re-deriving context every time.

SEO and Trust, Round Two

The first post talked about targeting specific search intent per calculator. That work continued, but the bigger addition was trust signal, not keyword coverage. Google AdSense flagged the early /compare/ page and parts of the site as low-value or thin content, which turned into real work: About and Privacy pages, an expanded /compare/ page, security headers, a dynamically generated sitemap (Astro’s sitemap integration now picks up every new page automatically instead of a hand-maintained file), and a global navbar for basic usability. That flag was a useful forcing function — the fixes it prompted were changes the site needed regardless of AdSense.

What I Learned

The biggest lesson wasn’t about affordability math this time — it was about scope discipline in an AI-assisted workflow. It’s easy to let a single session sprawl into “also let’s redesign the homepage while we’re in here.” Phasing the work into distinct, mergeable PRs — even when the underlying model could plausibly do it all in one pass — kept each change reviewable and made it obvious where a regression came from when one showed up.

The other lesson: static-site constraints keep paying off even as the site grows. Astro’s build step gives shared components without giving up “no server, no database, no user data leaving the browser” — the same promise the original static-HTML version made, just without the copy-paste maintenance cost.

What’s Next

Neighborhood coverage is intentionally small right now — five neighborhoods, chosen because they had a genuinely citable median rent and sale price, not because they’re the most popular searches. Expanding that list without lowering the sourcing bar is the next real content project. Beyond that: a citywide median condo price for the affordability index (deliberately left blank until a real citywide source turns up), and continuing to compare rent-versus-buy scenarios more directly across the calculators.

More Information