Skip to content

Lido

Lido is a site for a pool service company: weekly pool service, openings and closings, green-to-clean recoveries, repairs and leak detection, equipment and upgrades, and spa and hot tub care. It is sunny and graphic, like a 1950s lido: the home page opens on a short silent film of the floor of a clear pool seen from straight above, edge to edge on every screen, while a pool cover rolls back off the water, with the phone number and a free pool check right under the headline. Every section’s label carries a glazed depth marker (“3 FT”, “4 FT”…) that counts down the page like the markers on a pool deck, and a fine pool-tile grid runs behind every page; the colours are cabana coral, deep-end cobalt and lounger lemon on sun-bleached stone. The demo is a fictional business, Halcyon Pool Co.

Lido 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)
Services, projects, guides, 404 Pages → the page’s own entry
Prices (a weekly visit, plans, a visit step by step, booking, deposits and gift cards, questions) Pages → Prices page
Contact (pool check form, towns, map, hours, details, questions) Pages → Contact page
Services (icon, colour, price from, how long, what’s included, typical jobs, questions, photo) Content → Services
Projects, the pool stories (where, when, what we did, time, visits, cost, the water before and after, photo, story) Content → Projects
Guides (one can be for members only) Content → Guides
Name, phone, address, insurance, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts, the depth marker’s unit Content → Interface texts
Privacy, terms, guarantee Pages → Legal

In headlines, words wrapped in *stars* get a lane rope: a line of coral, white and cobalt floats pays out under them as they scroll into view, as in “Back to blue, *pool by pool*”. On the opening they turn lemon.

The home page’s first section is the film. Its texts are yours: the status line on a glazed tile (“Now booking spring openings”), the small line, the headline, the lead, two buttons (make the first one your phone number as a tel: link and it gets a phone icon) and up to three facts on glazed tiles under them (wide screens). The picture is two files:

  • Photo: the film’s first frame, 16:9 and at least 1920 pixels wide. It shows at once and is what search engines and slow phones see. Set Photo focus to the part that should stay in view on a narrow screen (e.g. 50% 50%).
  • Film: a short silent MP4 in public/media/ (about 2 MB, 1280 wide), e.g. /media/hero.mp4. It starts loading only after the page has, fades in over the photo and loops (make it play forward and then backward, so the loop has no cut). Leave it empty to show the photo only. Visitors who prefer reduced motion or save data see the photo.

The copy sits on a deep-end plate, a cobalt-black shade that reaches well beyond the text, so white text stays readable over any frame, even a white one; the buttons and tiles are solid. On load a slatted pool cover rolls back off the water (not with reduced motion).

Each service card carries a waterline course of small tiles in one of the three colours (coral, cobalt or lemon) with its number on a white tile and its drawing. Pool stories carry the water’s readings, before and after (up to four, e.g. “Free chlorine: 0 ppm → 3 ppm”): two on the card, all of them on the story’s page.

The prices page holds a weekly visit’s price on a big cobalt tile with its small print (larger pools, a spa, filter cleans, the pool check), up to four plans (mark one as the popular one), a weekly visit step by step beside a water test strip whose pads develop their colours as you scroll (also on the home page), booking a free pool check with Cal.com, and deposits and gift cards you take with Stripe or Lemon Squeezy: each one gets a one-time price id. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.

The map is drawn from OpenStreetMap in your colours, with a pin on your shop and a ring for the area you cover. It is part of the page (no embed, no cookies) and follows the theme and your accent. To draw your own, put your shop’s coordinates in map.config.json and run pnpm map -- fetch. detail: "area" (the default in Lido) draws the main roads, railways, water and woods of a whole district, and Contact page → Map → Radius sets the ring; detail: "streets" draws the streets and buildings round one address instead.

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

Feature Where Service
Book a free pool check Prices page, loads on click; a preview of the calendar until connected Cal.com
Deposits and gift cards Prices page (one-time prices) Stripe or Lemon Squeezy
Pool notes by email Home and guides pages, with a welcome email Supabase or Mailchimp
Members-only guides A guide marked members only shows its text after signing in Clerk
Pool check form Contact page; messages go to the email in Site settings Resend
AI chat On every page, answers about services, prices, pool stories and areas; gives prices only as “from”, never gives chemical doses or tells anyone to mix chemicals Claude or OpenAI
Search ⌘K or the search button: services, pool stories, guides, prices, contact built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, PostHog, Google Analytics
Photos Served from your Cloudinary account when it is set Cloudinary

The home page is built from these sections, in any order: Opening (the film and the pool cover), Services (cards with a waterline course), A weekly visit (a test strip, pad by pad), Pool stories (with the water before and after), Numbers (on glazed depth tiles along a stone coping), The area on a map, Guides, Pool notes by email and Closing call (the deep end with a lane line and a photo in a stone coping). With reduced motion nothing moves.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (coral: buttons, links and marked words, the lane-rope floats), the second (cobalt: the deep bands, the hero’s plate, the depth tiles’ lettering and the depth scale) and the third (lemon: chips, the readings after and light fills). The accent and cobalt are darkened until white text on them is readable, the lemon is lightened until dark text on it is, and every shade of the depth scale gets the text colour that reads on it; a color that can’t get there stops the build with a message. Brand also sets the fonts and the corners (the soft square of a pool tile, or round with pill buttons). The site opens on the stone day theme even when the visitor’s system is dark; the toggle switches to the pool after dark, and the browser remembers the choice. The header strip, the tab bar and the deep bands look the same in both. The depth markers’ unit (“FT”, or “M” if you work in metres) is in Content → Interface texts.

Lido is ready for more languages: see Languages.

A small game, Make waves: leaves float on a pool seen from above and the skimmer’s mouth is a gap in one wall. You can’t touch the leaves: tap the water (or move the ring with the arrow keys and press Enter or Space) to drop a ripple, which pushes every leaf it passes away from where it fell, harder close by. Herd every leaf into the skimmer with the ripples you have. Each pool scores 20 per leaf skimmed and 10 per ripple left; three pools, with more leaves and the skimmer on another wall each time. Every pool is checked to be solvable before it is dealt, and 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, services, pool stories and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the business as a HomeAndConstructionBusiness with the areas it serves and its plans, services as Services with their price, pool stories as CreativeWorks, a weekly visit as a HowTo, 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).