Pace
Pace is a site for a running event in a real city: a city race, a night run, a charity run, a running club’s race weekend. Your city is drawn from OpenStreetMap in a vivid 3D style (pastel walls and terracotta roofs by day, electric violet and neon windows at night), and on the home page the course draws itself along the real streets as you scroll: the camera follows a lime runner from landmark to landmark and an odometer counts the kilometres. Hot magenta, electric violet and lime, headings in a condensed athletic face. The demo is a fictional night run through Lisbon, the Luz Night Run.
Pace 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.
Your city
Section titled “Your city”The template draws the map itself, so it can be any city:
- Set the area in
map.config.json:areawith its south, west, north and east edges in degrees (right-click on openstreetmap.org → “Show address” gives you a point’s latitude and longitude). About 2-3 km across works best. - Run
npm run map -- fetch. It downloads the area from OpenStreetMap (one request, up to a minute), builds the map’s geometry insrc/data/map/and draws four base maps intopublic/map/: the city in 3D and as a plan, each by day and at night, saved as AVIF in every width the site uses. A city on the sea or an estuary is mapped only by its coastline; the map command closes it into water. - Give your landmarks their latitude and longitude, your races their course (the landmarks in order, plus waypoints to steer it) and start line. Courses are found along the real streets when the site builds, and every distance is measured along them, so name your races by what they measure.
The map’s colours are in map.config.json → colors; after changing them, npm run map -- render
redraws the maps without downloading again. Map data is © OpenStreetMap contributors, under the Open
Database License. Every map shows the attribution; keep it.
Pages and their content
Section titled “Pages and their content”| Page | Edit it in |
|---|---|
| Home | Pages → Home page (a list of sections) |
| Races (every course on the city, the races, teams and clubs) | Pages → Races page and Content → Races |
| A race (the course on the plan, landmark by landmark with the kilometre, the facts, entry) | Content → Races |
| The course (every landmark on the map, a filter by kind) | Pages → Course page and Content → Landmarks |
| A landmark (what it is, what runners find there on race night, nearby on the map) | Content → Landmarks |
| Pacers | Pages → Pacers page and Content → Pacers |
| Journal | Pages → Journal page and Content → Journal |
| Race day (the start lines, bib pick-up, getting there, the contact form, questions) | Pages → Race day page |
| Name, city, contact, navigation | Pages → Site settings |
| District and river names on the map | Pages → The map |
In headlines, words wrapped in *stars* sit on a band of lime, like the tape at the finish.
Races, the course, pacers
Section titled “Races, the course, pacers”Each race has a summary, its course, a time limit, the start date and time, start waves, places, the entry fee and a note, the start line, highlights, a picture and a colour. Its page shows the course on the plan with each landmark at its kilometre. Turn on “Sell entries online” and add a Stripe Price id (or a Lemon Squeezy variant id) and the race’s button takes runners to checkout; after paying they get an email with the start line. Teams and clubs book a call with Cal.com.
Landmarks have a kind (the course page filters by it without JavaScript), a fact and what runners find there on race night (water, music, a cheer zone). Pacers have the time they hold.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Entries | Each race’s page | Stripe or Lemon Squeezy |
| Teams and clubs | Races page, loads on click | Cal.com |
| Newsletter (when entries open) | Home and journal pages, with a welcome email | Supabase or Mailchimp |
| Contact form | Race day page; messages go to the email in Site settings | Resend |
| AI chat | Every page; answers from your races (with their courses), landmarks, pacers, race day and journal | Claude or OpenAI |
| Search | ⌘K or the search button: races, the course, pacers, journal, race day | built in, or Algolia |
| Analytics | Every page; Google Analytics only after consent | Plausible, Umami, Google Analytics, PostHog |
| Members-only posts | Journal | Clerk |
Sections
Section titled “Sections”The home page is built from these sections, in any order: The course (the signature: the course draws itself on the 3D city as you scroll, the runner leads it and the odometer counts), Races, On the course (landmarks as cards), Pacers, Numbers, Journal, Newsletter and a closing call over a photo from edge to edge.
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (hot
magenta: buttons, pins and the course), the second (electric violet: fills and the night) and the
third (lime: the finish-line band, the runner, highlights). Buttons are darkened until their white
text reads; every color is adjusted for each theme, and one that can’t get there stops the build with a
message. Brand also sets the fonts (text, headings, labels) and the corners. The theme follows the
system, day or night, unless the visitor picks one with the toggle; the maps switch with it.
Languages
Section titled “Languages”Pace is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Measure the course: a grid of streets, each with its length; mark out a loop from the start line and back that measures exactly the distance asked for, using no street twice (tap the corners one after another, or arrows and Enter; U undoes). The loop of that exact length with the fewest turns is found for every course, and you score 100 × the fewest turns ÷ yours. Three courses, each grid bigger. The best score is kept in the visitor’s browser.
AI assistants and search engines
Section titled “AI assistants and search engines”AGENTS.mddescribes the project for coding assistants (Claude Code reads it throughCLAUDE.md, Cursor through.cursor/rules/)./llms.txtlists your pages, races and landmarks for AI assistants, in every language.- Pages carry JSON-LD: the organiser as a
SportsOrganization, races asSportsEvents with their entry fee, landmarks asTouristAttractions, questions as aFAQPage, posts asBlogPostings.
Commands
Section titled “Commands”pnpm dev # local site, editor at /keystaticpnpm map # draw your city (npm run map -- fetch | build | render)pnpm build # production buildpnpm 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).