Corporate site · Wellness

NulusA breathwork site with an events calendar, local SEO and structured data from a single source

Breathwork site in Guayaquil: 35 URLs across services, cities, industries and an events calendar, built with static Astro and local SEO.

Technologies

  • Astro 7
  • TypeScript
  • CSS with design tokens
  • Fontsource (Newsreader, Inter, Geist Mono)
  • sharp
  • JSON-LD
  • Docker + nginx
  • Railway
  • Google Analytics 4
  • Microsoft Clarity
Nulus website home page, breathwork workshops and sessions in Guayaquil
  • 35

    sitemap URLs

  • 8

    event pages

  • 6

    industry landings

  • 5

    local pages

My role

Development of the site at Azirgo SAS from the design and the SEO briefs, plus the implementation of technical SEO, structured data and measurement.

The problem

Breathwork touches on health - the nervous system, anxiety, contraindications - so visitors need to understand what it is, how it differs from meditation or therapy, who leads it and when the next date is. There are also two different buyers: the person looking for a workshop in their city and the company looking for a program for its team. A one-page site cannot answer either of them in the detail they need.

What I built

  • A home and five service pages: group workshops, one-to-one sessions, retreats, facilitator training and corporate breathwork.
  • Five local pages: Guayaquil as the home base and the parent page of local SEO, with its map, plus Samborondón, Quito, Cuenca and the online format.
  • Six industry landings for corporate sales - tech and startups, banking and insurance, health and clinics, education, call centers and sales, athletes and high performance - each with the problems specific to its sector.
  • Two comparisons with the direct answer right under the H1: breathwork vs meditation and breathwork vs psychological therapy.
  • An events calendar with eight event pages of their own, each with date, venue, price, spots, facilitator and FAQ.
  • Method, pricing, stories and “about us”, with the team marked up as people linked to the organization.
  • Contact with a form that drafts the message and opens WhatsApp, a lazy-loaded map, terms and privacy policy.
  • Structured data generated from shared functions - Organization, LocalBusiness, Service, Offer, Event, Course, FAQPage, Article, BreadcrumbList, ContactPage and AboutPage - plus sitemap, llms.txt and manifest as endpoints.

How it's structured

Astro 7 with static output and no UI framework: there are no islands to hydrate, and JavaScript is limited to small inline scripts for the menu, the form and the map, plus measurement. Business data lives in src/data modules, the JSON-LD builders in src/lib/schema.ts, and each route is an .astro file. The build is served by nginx inside Docker on Railway.

  • Services: /servicios/talleres-de-breathwork, one-to-one sessions, retreats, facilitator training and corporate breathwork.
  • Local SEO: /breathwork/guayaquil (the home base, with map and LocalBusiness), Samborondón, Quito, Cuenca and online.
  • Industries: six landings under /industrias/ for tech, banking, health, education, call centers and sports.
  • Decision: /metodo, two comparisons under /comparativas/, /precios and /historias.
  • Calendar: /eventos and one page per event at /eventos/[slug], the site's only dynamic route.
  • Trust and contact: /sobre-nosotros, /contacto, terms and privacy.
  • Single sources: site.ts (NAP, socials, menus and measurement IDs), eventos.ts, precios.ts and equipo.ts.
  • Endpoints generated at build time: /sitemap.xml, /llms.txt and /site.webmanifest, read from the same data.

A calendar that feeds the whole site

Events are not written into each page: they live in a single typed module that /eventos, the city pages, the service pages, /metodo and the JSON-LD filter. A date or a spot changes in one place.

  • Every event has its own URL, with date, venue, price, facilitator, spots and an FAQ generated from its own data.
  • The type enforces dates in Ecuador's time zone (-05:00) and real available spots.
  • Event in JSON-LD with Offer, InStock or SoldOut depending on the spots, and VirtualLocation for online sessions.
  • A per-date detail field, so two workshops with the same venue, price and facilitator do not publish near-identical pages.
  • The “Reserve” button on the Guayaquil page goes to the event page, not the listing, and /metodo links to the eight event pages on its topic.
  • Prices come from a separate module: what is displayed and what travels in Offer.price are the same data.
  • In the sitemap, the calendar declares the build date because it is regenerated on every deploy; every other page declares the date of the last commit that touched it.

Inside the site

  • Nulus: Method page: the four pillars of the practice
    Method page: the four pillars of the practice.
  • Nulus: Corporate breathwork landing for companies
    Corporate breathwork landing for companies.

Technical decisions

  • Astro with no UI framework and CSS always inlined: with no islands to hydrate, the first paint waits for no stylesheet. The cost - CSS is not cached across pages - was accepted deliberately, because most visits to a local business are first visits.
  • Business data lives in single sources: the NAP is identical character for character in the footer, the JSON-LD and the Guayaquil page, and prices and the calendar change in one file that the pages and the markup inherit from.
  • A contact form that delivers to WhatsApp: the site is static with no backend, so the form drafts the message and opens the chat, works even without JavaScript and fires generate_lead in GA4.
  • LocalBusiness only where there is a physical business to talk about - home, Guayaquil and contact. Samborondón, Quito and Cuenca are declared as Service with their service area, so no nonexistent locations are invented.
  • GA4 and Clarity load on the visitor's first gesture, which keeps them out of the load PageSpeed measures. The cost is written into the code: a visit that touches nothing is not recorded.
  • Nothing invented in the markup: sameAs only with profiles that exist, no twitter:site while there is no account, and each event's available spots are real data, never fabricated scarcity.

Challenges

  • Content was not being extracted: without an <article> element, extractors picked a block at random and on the Cuenca page read 60 of 1,248 words. Wrapping the content fixed “thin” content, “invisible” FAQs and unseen authorship in one go.
  • Two workshop pages with the same venue, price and facilitator came out 96% identical; a per-date detail field brought them down to 61%.
  • The design briefs linked to 35 destinations that were never published: 161 links across 25 files were remapped one by one to the page that actually covers each topic.
  • The newly added content security policy was silently blocking the map and the form's hand-off to WhatsApp; only what Google Maps and wa.me need was opened up.

Result

The site is in production with 35 URLs in the sitemap - services, cities, industries, comparisons and eight event pages - structured data built from the same data shown on screen, and GA4 and Clarity measurement running.

Need something like this?

Tell me what you're building and I'll come back with scope, stack and a realistic timeline.