All work
Case study

Century Home Inspections

A static-first rebuild of centuryinspections.com for a licensed Maryland home inspector: every page prerendered for local search, a county coverage checker and map that always agree, and an “Ask an Inspector” assistant that answers only from the client’s own 200-question FAQ, never from a language model.

Visit centuryinspections.com
Century Home Inspections homepage on desktop: the headline “We Don’t Cut Corners, We Inspect Them.” beside a photo of a craftsman-style house, with a “1000+ inspections per year” badge
Client

Century Home Inspections

Platform

Astro + React islands, Vercel

Services

Design implementation, development, content architecture, SEO, accessibility, hosting

The challenge

Century Home Inspections is a licensed home and commercial inspector in Sykesville, Maryland, covering six counties and performing more than a thousand inspections a year. The site had three jobs: rank locally for inspection searches, answer a buyer’s questions before they pick up the phone, and turn a visit into a booked inspection.

The previous site was a single-page React app. One HTML document, every route drawn in the browser, so search engines had less to index and every visitor downloaded a framework before reading a word. For a business that lives on local search, the delivery model was working against the goal.

Static first, JavaScript on demand

We rebuilt the site on Astro. Every route is prerendered to finished HTML at build time with its own title, description, canonical URL, Open Graph image, and JSON-LD, including a HomeAndConstructionBusiness schema with the office’s geo, hours, and address. About, Services, News, and the 404 page ship no framework JavaScript at all; a static page loads about 1.2 kB of gzipped script.

React loads only for the pieces that need real state: the coverage checker, the service-area map, the live pace band, the contact form, and the chat panel. Each one hydrates on its own schedule, and two of those schedules are custom. One waits until the page has settled after load, or hydrates at once on the first pointer, focus, or key event, capturing and replaying any click made in between. The other downloads the chat only when a visitor points at or focuses its launcher. Mapbox, close to half a megabyte gzipped, arrives only when the map nears the viewport, and its stylesheet is injected on mount so it never blocks the home page from rendering.

An assistant that cannot make things up

“Ask an Inspector” answers from the client’s own library of 200 questions across 43 categories, transcribed from their master document. It deliberately uses no language model. It can return one of the client’s answers word for word, or hand the question to a person. Nothing in between, so there is no risk of an invented answer about someone’s house.

Matching is a hand-written scorer that weights question text over category and answer body, with stemming, a stopword list, a synonym map (“AC” finds air conditioner, “townhouse” finds row home), and a bonus for matching word pairs. A match has to clear both a minimum score and a share of the query’s words, so one common word like “inspection” cannot pull in an unrelated answer. Both thresholds were tuned by hand against about fifty everyday phrasings.

A hit returns the answer with two related follow-ups. A miss opens a capture form prefilled with the question, and the visitor chooses a text or email reply. Price questions get a fixed “call us” response, so the widget never produces a number. The conversation persists as the visitor moves between pages, and the whole panel, answers included, costs nothing until someone reaches for it.

A coverage checker and a map that always agree

Visitors type an address, town, or ZIP and get an instant yes or no with that county’s lead time. The input is geocoded through Mapbox, biased toward Maryland, then tested against real US Census county polygons. The map shades the same polygons, so nobody is told “no” about a spot that looks covered on screen: one source of geometry feeds both the test and the map layers.

The checker and the map sit in different regions of the Service Areas page, so they are separate islands sharing one result through a small publish-and-subscribe store. When the checker matches a county, the map flies to it, and a match made before the map has hydrated is applied the moment it does. A reset aborts any lookup still in flight, so a stale result can never land after the address has changed. Without a map token, both degrade to a card with the phone number.

One content source, many outputs

All copy and data live in typed TypeScript modules rather than a CMS: business details and hours, the FAQ library, the twelve inspected systems with their add-ons and a prep checklist, eleven news and education articles, and the county polygons. Pages, the sitemap, an llms.txt file for AI agents, and the chat assistant all read the same modules, so they cannot drift apart.

The What We Inspect page lays out all twelve systems as an accordion with a sticky jump nav, scroll-spy, and deep links that open the linked system. Opening a row pins the clicked header in place so closing a panel above it never jerks the page. The prep checklist remembers ticks per browser and is the only thing on the page that prints. With JavaScript off, all twelve systems render as open prose.

The home page’s pace band projects annual inspection volume onto Maryland business hours in Eastern time, whatever the visitor’s timezone. Clock-dependent figures render empty on the server and fill after mount with reserved height, so there is no hydration mismatch and no layout shift, and the copy labels them as estimates. That disclosure is documented in the code as something that must never be removed or softened.

A small server surface and a careful migration

The only server code is the contact endpoint, which runs as a single Vercel Function and delivers form and chat submissions through Resend. Its secrets are declared in a typed schema; if the email key is missing, the route answers with a 503 instead of failing the build. The form carries a honeypot, per-field length limits, HTML escaping, and field-level error states.

Fourteen permanent redirects preserve the old site’s URLs and rankings, and a trailing-slash policy keeps one URL per page. The deployment adds a strict Content Security Policy, HSTS preload, and immutable caching for hashed assets and the two self-hosted variable fonts, which are Latin-subset and preloaded. Images run through Astro’s pipeline with per-image focal points, SVGs through SVGO, and analytics use Vercel’s Astro entrypoint so the static pages never pick up React just to count a page view.

Accessibility, stated precisely

Keyboard and screen-reader work went into the header, the forms, the coverage checker, the map, and the FAQ, and FAQ text contrast was raised to meet WCAG AA. Reflow is handled for landscape phones and 400% zoom, where the sticky nav stops sticking. Every enhancement degrades gracefully: content is readable with JavaScript off, and the map falls back to a phone card without a token.

To be precise about the claim: these are the patterns we built and checked, and the dated Lighthouse run below reports a 100 for accessibility. We have not published a formal conformance audit of the site.

AstroReact islandsLocal SEOMapboxVercel
Measured, not claimed

Google PageSpeed Insights

Run the report yourself (opens in a new tab)
100
Performance
100
Accessibility
100
Best Practices
100
SEO
3 of 3
Agentic Browsing

Core metrics

First Contentful Paint
0.3s
Largest Contentful Paint
0.4s
Total Blocking Time
0ms
Cumulative Layout Shift
0
Speed Index
0.5s

centuryinspections.com · Lighthouse 13.5.0, emulated desktop, captured Sep 26, 2026. Scores vary between runs.

Have a project like this one?

Start a project