Tideline
Tideline is a site for a boat company in a real city of canals or rivers: canal cruises, river trips, boat tours, a water taxi, boat hire. The home page opens on a long panorama of house fronts mirrored in the canal, which sails past sideways as you scroll (at dusk in the dark theme, the windows lit). A split-flap departures board lists the boats of the day, and the cruises stand in a row as canal houses, their pictures cut to step, neck and bell gables. Every route is computed along the real canals on a map of your city drawn from OpenStreetMap in a vivid 3D style. Coral, turquoise and sunshine yellow, headings in a round, heavy grotesque. The demo is a fictional small-boat company in Amsterdam, Slow Water Boats.
Tideline 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 on the water:
- 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 dusk, 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 cruises their route (the landmarks in
order, plus waypoints to steer it) and boarding pier. With
"routeOn": "water"inmap.config.json(the default here) routes follow the centre lines of the canals and rivers, and every distance is measured along them. A landmark on a quay is fine: the route passes the nearest point on the water. Set"routeOn": "streets"for tours on foot or by bike.
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) |
| Cruises (every route on the city, the cruises, private hire) | Pages → Cruises page and Content → Cruises |
| A cruise (the route on the plan, sight by sight with the kilometre, the facts, tickets) | Content → Cruises |
| Along the way (every landmark on the map, a filter by kind) | Pages → Along the way page and Content → Landmarks |
| A landmark (what it is, what you see from the boat, nearby on the map) | Content → Landmarks |
| Skippers | Pages → Captains page and Content → Captains |
| Journal | Pages → Journal page and Content → Journal |
| Boarding (the piers on the plan, practical details, the contact form, questions) | Pages → Boarding page |
| Name, city, contact, navigation | Pages → Site settings |
| District and canal names on the map | Pages → The map |
In headlines, words wrapped in *stars* get a wavy sunshine underline.
Cruises, the sights, captains
Section titled “Cruises, the sights, captains”Each cruise has a summary, its route, a duration, when it sails, its departures and departure times (for the board), the boat and seats, the price and a note, the boarding pier, highlights, a picture and a colour. Its page shows the route on the plan with each landmark at its kilometre. Turn on “Sell seats online” and add a Stripe Price id (or a Lemon Squeezy variant id) and the cruise’s button takes guests to checkout; after paying they get an email with the boarding pier. Groups book a private boat with Cal.com.
Landmarks have a kind (the along the way page filters by it without JavaScript), a fact and what you see from the boat. Captains have the boat they skipper.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Tickets | Each cruise’s page | Stripe or Lemon Squeezy |
| Private hire | Cruises page, loads on click | Cal.com |
| Newsletter | Home and journal pages, with a welcome email | Supabase or Mailchimp |
| Contact form | Boarding page; messages go to the email in Site settings | Resend |
| AI chat | Every page; answers from your cruises (with their routes), landmarks, captains, boarding and journal | Claude or OpenAI |
| Search | ⌘K or the search button: cruises, landmarks, captains, journal, boarding | 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 canal front (the signature: the panorama that sails past as you scroll; its day and dusk pictures are in the section), Departures (the split-flap board, from each cruise’s departure times), Cruises (as gabled houses), Along the way (the sights as a strip that passes the boat), Captains, 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
(coral: buttons, pins and the route), the second (turquoise: fills, panel shadows and the evening)
and the third (sunshine: the wave under marked words, the next departure, 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 dusk, unless the visitor picks one with the toggle; the maps switch with it.
Languages
Section titled “Languages”Tideline is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Harbour jam: a 6 × 6 harbour packed with moored boats, each sliding only along its own length. Tap a boat, then the water it should slide to (or arrows, Tab to change boat); one slide is one move however far it goes. Clear a way and get the coral tour boat out through the gap in the harbour wall. The fewest moves for every harbour is found by a search over every position, and you score 100 × the fewest moves ÷ yours. Three harbours, each more crowded. 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, cruises and landmarks for AI assistants, in every language.- Pages carry JSON-LD: the business as a
TravelAgency, cruises asTouristTrips with their itinerary and price, 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).