feldstein.travel
A luxury travel-advisor site built like software: a hand-rolled design system, real logic at the edge, a grounded AI concierge, and a compliance discipline that holds up in a regulated category.
An independent luxury travel advisor needed a site that does three jobs at once. It has to look as refined as the cruise product it sells, turn browsers into qualified inquiries, and answer detailed pre-sale questions without a human on call. It also has to stay strictly compliant in an affiliate-marketing category, where a wrong phrase or an invented price is a real liability. Off-the-shelf themes and generic chatbots fail all three.
The answer was to build the marketing site like a piece of software. The result is feldstein.travel: a live, named site that reads as a polished luxury brand and runs as a real engineering system. It is owned by the practice and shown here with permission.
A hand-rolled design system, no framework
There is no Tailwind and no UI kit. The brand is held together by one CSS-custom-properties file of roughly three hundred lines: a palette, a fluid type scale, a spacing scale, and a small set of components. That single file keeps the look consistent across every page with no framework bloat, and it bakes accessibility into the tokens rather than bolting it on later.
--paper #faf8f3 sand-leaning page field --sand #e8ddc8 warm surface --taupe #8b7e6a muted neutral --ocean #0f2d44 primary deep blue --brass #b8924a accent --brass-text #7a5d23 AA 4.5:1 on sand Cormorant Garamond
Inter, the working sans for body and labels
A fluid scale runs from a 16px base up to an 88px hero step, set once as tokens and reused everywhere.
Accessibility lives inside the system, not in a checklist. The brass used for
small text is a deliberately darker token documented in the source as
clearing AA 4.5:1 contrast on sand surfaces. There is a global
:focus-visible ring, a skip link to the main content, and a data
table that collapses into stacked spec cards on narrow screens instead of
forcing a horizontal scroll. The design discipline is the point, not a theme.
The tokens, verbatim
The accent-text token carries its own contrast note in the source:
:root {
--paper: #faf8f3; /* sand-leaning page field */
--sand: #e8ddc8;
--taupe: #8b7e6a;
--ocean: #0f2d44; /* primary deep blue */
--brass: #b8924a; /* accent */
--brass-text: #7a5d23; /* clears AA 4.5:1 for small
text on sand surfaces */
--font-serif: "Cormorant Garamond", Georgia, serif;
--font-sans: "Inter", system-ui, sans-serif;
--step-0: 1rem; /* 16 body */
--step-6: 4rem; /* 64 display */
--step-7: 5.5rem; /* 88 hero */
}
Real logic at the edge
The site is Astro 5 static output deployed to Cloudflare Pages. Voyage, region, port, and line-comparison pages are generated from structured data through programmatic routes rather than hand-built one by one, which is how a small set of templates becomes a large, cohesive site. The work that cannot be static lives in Cloudflare Pages Functions, the TypeScript edge layer. There are nine API functions, plus a routing middleware, doing genuine work rather than echoing form posts.
The inquiry endpoint refuses to lie
The lead-capture endpoint is hardened on purpose. It checks a honeypot field, validates the email with a strict single-address pattern that blocks header injection into outbound mail fields, and writes to Airtable with an upsert-by-email so a repeat inquiry updates the existing lead instead of creating a duplicate. It then sends an internal notification and a lead confirmation through a transactional email service. The senior detail is the failure path: if the lead persisted nowhere and no notification went out, the function returns a 503 rather than a cheerful thank-you page. It will not report success that did not happen.
A concierge that is grounded, not guessing
The on-site AI concierge, the Purser, is built for trust by construction. It embeds the visitor’s question with a Workers AI model, retrieves the most relevant passages from a curated knowledge base in Cloudflare Vectorize, and passes those passages to Claude as document blocks with citations turned on, routed through a Cloudflare AI Gateway. Before any reply reaches the visitor it runs through an output compliance gate. And there is a hard rule underneath all of it: fares never come from the model. The bot answers from sources or it says it cannot, which is exactly the AI-with-guardrails posture a regulated category demands.
Static output, living data
A static site does not have to mean stale content. A daily pipeline scrapes fares, refreshes foreign-exchange rates with a fallback, logs price history, and rebuilds the search index and the concierge catalog. Each build is gated by a content linter and a site-crawl linter, so nothing ships red. Deploys are a push to main on GitHub Actions, which builds and ships to Cloudflare Pages. The output is static and fast; the data behind it is fresh every day.
Multi-domain, one canonical home
Several domains point at the same brand, and the policy that resolves them
lives in the repo, not in a dashboard. A Pages middleware issues a 301 from
the alternate hosts and the www variant to the canonical
feldstein.travel, and it carries a small map of renamed slugs so
old, previously-indexed URLs redirect cleanly to their new homes. That keeps
search equity in one place and keeps the canonicalization rules versioned
alongside the code.
Why this is the hard part
Travel advising sits in an affiliate-marketing category with real compliance stakes, and the site treats that as an engineering constraint rather than a legal afterthought. The AI is grounded by retrieval and citations, every machine-written reply passes an output gate, and a verbatim independent-advisor disclosure appears on every page. The same compliance module that gates the live bot also runs in the content linter at build time, so the rules cannot drift between what gets written and what gets shipped.
The stack
This case study documents the engineering and design of the live site. It does not publish traffic, conversion, or performance figures, none of which were measured for this write-up.