ASKAR

A family-run trekking agency in Kyrgyzstan — Askar the guide, Aizhan handling contacts and bookings, the wider family running logistics, yurts and horses — was losing 40-60% of every booking to OTA platforms (Kalpak, Indy Guide, GetYourGuide). Their existing site was a generic Wix template: a "built on Wix" banner, leftover Russian text nobody had cleaned up, tours with no listed price, and no real booking path.
The obvious fix looks like "build a booking site." The scoping work said otherwise: nine risks were scored by criticality before a line of code was written, and the two highest were not technical. R4 ("stale content — wrong prices/currency") scored 16/25, and R3 ("poor conversion — visitors who never contact") scored 15/25. Both point at the same root cause: trust, not payment friction, is what stops a family from wiring money to a stranger for a multi-week trek abroad.
Askar and Aizhan directly — a small agency owner-operator who needed to own the customer relationship again, without pretending to be a bigger company than it is. Four personas shaped the scope: Tom, a budget backpacker afraid of being scammed abroad; Lena, a solo photographer who needs to see quality visuals and know the horse-riding difficulty before booking; Marc & Léa, a first-time-on-horseback couple who need to be reassured in their own language and currency; and Dmitry, an experienced rider who wants elevation profiles and group size, not marketing copy.
No online payment. Booking routes through a pre-filled WhatsApp deep-link instead.
If the real friction is trust, adding a payment form does not remove it — it just adds a second thing to trust (the site, and the transaction). Every "Book" button on the tours page opens WhatsApp with the message already written ("Hi! I'm interested in the 3-day tour…"), so the family can answer questions, sound human, and close the sale the way trust actually gets built for this kind of purchase — with mailto and tel as the fallback channels for anyone who avoids WhatsApp.
A content layer that degrades field by field: Sanity CMS → static JSON fallback if no project ID is configured, English fallback per string.
A single missing field, or the CMS being entirely unreachable, should never take down a page. If `NEXT_PUBLIC_SANITY_PROJECT_ID` is unset, the Sanity client is simply `null` and the app reads straight from the JSON files in `src/i18n/messages/` instead — the site works even with zero CMS configured, which matters for a family with no technical staff who might misconfigure or forget to renew something.
11 languages, fully localized — not just UI strings. Every tour, review and page section is translated, with prices converted to 11 currencies via the native `Intl.NumberFormat` API rather than a third-party library.
Persona Marc & Léa's actual blocker was language and price, not the tour itself — a visitor who cannot read the itinerary or understand the cost in their own currency will not contact a stranger to ask. `en.json` alone runs 582 lines of real content, not boilerplate. Currency values themselves are hardcoded (see "what I'd redo") — a trade-off made under the project's time budget, not a technical limit.
One aggregated GROQ projection (`ALL_CONTENT_QUERY`), resolved once with a 60-second revalidation and a `sanity` cache tag, instead of scattered per-page queries.
Every page needs a subset of the same shared content (nav, footer, locale strings, homepage sections). Resolving it once avoids duplicate CMS calls per route and keeps the data shape consistent everywhere it is consumed — and the cache tag means a content edit in the Sanity Studio invalidates cleanly instead of waiting out a blind TTL.



No online payment integration (see decisions above).
Building it would have solved a problem the client did not have — R3 and R4 in the risk register are both about trust and content freshness, not payment friction — while leaving the real one untouched.
No custom booking backend, no database, no transactional system at all.
The feasibility review concluded the residual risk was functional, not technical: WhatsApp/mailto/tel are channels the family already operates daily. Building a reservation database would have added a system to maintain for a volume of bookings a TPE (very small business) with no marketing staff does not need yet.
Prices are versioned in code, not editable through the CMS.
The rest of the content architecture is built so a non-technical owner can edit anything, with the CMS→JSON fallback absorbing failure at every field. Prices are the one exception — a scope call made under the project's 36-person-day time budget, not a technical constraint. It breaks the architecture's own principle: the client cannot change a price without asking me to ship a code change, and R4 (stale content, scored 16/25) is precisely the risk this leaves unmitigated for the one field that changes most often.
The site is not yet carrying real booking traffic, so these are the metrics I'd track once it does, not results I already have. The control-indicator table drawn up during scoping already names them: click-through rate to WhatsApp against total visits, LCP under 2.5s via Speed Insights, and uptime via an external monitor — with a conversion target of 3-5% flagged explicitly as a number to validate with the client, not invented from nowhere. What matters is that a click-through number with no conversation-to-booking rate next to it would prove nothing about whether the trust hypothesis (R3/R4) was actually right.
I'd make prices editable in the CMS behind a structured field with validation, not free text — the same discipline already applied to every other piece of content. The current setup means the client has to message me to change a price, which quietly reintroduces the dependency the whole project was built to remove, and is exactly the failure mode R4 was scored against in the first place.