Skip to content

Keystone

Keystone is a site for an independent estate and letting agent; it also fits property developers (a scheme’s homes as listings) and luxury realtors. It is set at blue hour: deep ink blue with the faint lines of an architect’s floor plan behind every page, front-door green, brass and a red “Sold” strap, a tall serif and a glass pill nav. The home page opens on one house, photographed from across the street and sold as you scroll, with the rating, the headline, one line, Book a free valuation and Browse homes over it from the first frame. The demo is a fictional agency, Calder & Finch.

Keystone comes in two variants, chosen when you create the site: files/Sanity (static pages, any host) and EmDash (--cms emdash, rendered on request on Cloudflare). The pages are the same in both.

Page Edit it in
Home Pages → Home page (a list of sections)
Buy & rent (every home on the market, with filters) Pages → Properties page
A home (photos, price, numbers, highlights, description, key facts, rooms, floor plan, energy rating, map, viewing, enquiry, holding deposit) Content → Properties
Sold & let (results and what they add up to) Pages → Sold & let page; the homes are Content → Properties with the status Sold or Let agreed
Valuation (what you look at, fees, booking, the valuation form, questions) Pages → Valuation page
Areas, and an area guide each (facts, what it’s like, the guide, homes there now) Pages → Areas page, Content → Areas
Guides Content → Guides (one can be for clients only)
About (story, team, how you work) Pages → About page
Contact (the map, hours, visiting, message form, questions) Pages → Contact page
Name, phone, address, area covered, currency, rating, hours, navigation, profiles, memberships Pages → Site settings
Buttons, labels, filters, boards, chat, emails, game texts Content → Interface texts
Privacy, terms, fees Pages → Legal

In headlines, words wrapped in *stars* are set in italic and lit like a window at dusk, as in “Every home has its *evening*”.

Each home is one entry in Content → Properties: for sale or to let, its status (available, under offer, sold, let agreed), the price in whole units (the asking price, or the rent a month) with an optional qualifier (“Guide price”, “Offers over”), the type, bedrooms, bathrooms, receptions and size in square feet, a summary, highlights, key facts, the rooms with their sizes, the main photo and more photos, a floor plan (any drawing), the energy rating in SAP points (the chart is drawn from them), where it is (latitude and longitude: the street, not the door) and the description as the body. Sold and let homes move to Sold & let and show what they achieved (for example “+4% over asking” and the days on market).

Cards carry the agent’s board (For sale, To let, Under offer) and the results a red strap; the main photo morphs into the home’s page. The listing page’s filters (buy or rent, a price range, bedrooms and the area) run in the browser and keep themselves in the address, so a filtered list can be shared: /properties?market=rent&max=1500. The price steps are in Interface texts → Properties.

Each home is a schema.org Offer for search engines, its item a SingleFamilyResidence or an Apartment with rooms, size, address and coordinates.

The hero is the site’s signature. Four photos of the same house from exactly the same spot (Pages → Home page → Hero → Photos) are revealed one after another as you scroll, while the camera makes a slow approach:

  1. On the market: the house at blue hour, lights off. It is there from the first frame and is the page’s largest image, so it loads first.
  2. The door opens: a blade of warm light widens from the front door as the hall comes on.
  3. Lights on: lamplight blooms from the living room and the library window.
  4. Sold: the street lamps strike, every window glows, moths circle the lamps and a red “Sold” strap is laid across the board.

Evening haze drifts along the street throughout. The board’s words are text over the photo (Hero → The board in the garden), so you can change and translate them; its position is Hero → Where the board is in the photos, in percent. A readout counts what mattered (asking price, viewings booked, days on market, over asking) and the captions follow in a glass card with a progress line; write your own in the hero’s four stages. The later photos load after the page has loaded. With reduced motion, or in a browser without scroll-driven animations, the hero is one still: the house sold, every light on, with the four stages listed under it.

To use your own house, take four photos from a tripod with a blank board in the garden, or generate one and edit it into the other three so nothing moves. The positions of the window glows and the street lamps are at the top of src/components/sections/Hero.astro, in percent of the photo.

Viewings, valuations, enquiries and deposits

Section titled “Viewings, valuations, enquiries and deposits”

Book a viewing and book a valuation use Cal.com (PUBLIC_CALCOM_LINK, and PUBLIC_CALCOM_VALUATION_LINK for a separate valuation event); they load on click, and until they’re connected the page shows a preview of the calendar with your times. Enquiries on a home’s page go to the email in Site settings with the home’s name in the subject; the valuation form adds the address, type, bedrooms and selling or letting. A home to let can take a holding deposit with Stripe (a one-time Price id) or Lemon Squeezy (a variant id) in its Holding deposit fields.

The contact page and the home section “The office” show the streets round your office, drawn from OpenStreetMap in your colors, with a pin, your address and directions links; each home’s page shows the same map closer in, centred on that home. It is part of the page (no embed, no cookies) and follows the theme and your accent. To draw your own, put your office’s coordinates in map.config.json and run pnpm map -- fetch; keep your homes inside its area.

Everything works without keys; each feature switches on with its variables in .env.example.

Feature Where Service
Book a viewing, book a valuation Each home’s page, the valuation page; a preview of the calendar until connected Cal.com
Holding deposit Homes to let Stripe or Lemon Squeezy
Property alerts Home, guides and area pages, with a welcome email Supabase or Mailchimp
Enquiries, valuation requests, messages Home pages, valuation and contact pages; to the email in Site settings Resend
AI chat Every page; answers about the homes, results, fees, valuations and areas; never values a home itself Claude or OpenAI
Clients-only guide Guides Clerk
Search ⌘K or the search button: homes, areas, guides, pages built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics, PostHog
Photos Production builds Cloudinary (optional)

The home page is built from these sections, in any order: Hero, Featured homes, How we sell (a photo held beside the steps while a brass line is drawn down them), Recent results (a band of blue hour), The year in numbers (a ruled ledger whose brass rules draw in), Free valuation (the photo revealed through a front door that swings open), Area guides, Latest guides, Property alerts (a terrace whose windows light up one by one), The office (the map) and Closing call.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (front-door green: buttons, marks, pins), the second (brass: small capitals, rules, lit words and the glow of windows) and the third (sold red: the Sold and Let agreed straps, badges). Buttons and the red are darkened until their white text is readable, brass is adjusted for each theme; a color that can’t get there stops the build with a message. Brand also sets the fonts and the corners. The site opens at blue hour even when the visitor’s system is light; the toggle switches to the day (brochure paper and navy ink) and the browser remembers it. The energy rating’s bands keep their usual colors.

Keystone is ready for more languages: see Languages. Prices are written in the page’s language with the currency symbol from Site settings.

A small game, Asking price: three homes on a street have sold, with their bedrooms, size, garden, parking, condition and price. Set the asking price for a fourth with the slider (or ← → , Shift for bigger steps) and list it. It sells at the price the street’s evidence points to; within 2% earns 100 points, 25% or more off earns nothing. Priced too high it sits on the market, too low it goes in days. Five homes; the best score is kept in the visitor’s browser.

  • AGENTS.md describes the project for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, homes, results, areas and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the agency as a RealEstateAgent (the area it serves, hours, rating, team), the homes as Offers of a SingleFamilyResidence or Apartment (and an ItemList on the listing page), areas as Places, questions as a FAQPage and each guide as a BlogPosting.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
pnpm map -- fetch # draw the map round map.config.json from OpenStreetMap
pnpm cms sanity schema|seed # move content to Sanity (see Content → Sanity)

In the EmDash variant: npm run dev, npm run deploy (Cloudflare) and npm run emdash:schema (after changing the content model).