Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesMinisitesMinisites

Minisites

Per-organization marketing sites rendered by Astro at the org’s custom host (apps/minisites). Distinct from Taikan’s own marketing site (apps/marketing); both share the Astro stack but the minisites app is multi-tenant and dynamic.

What

Each Taikan organization can publish a multi-page site that combines:

  • Owner-authored content (hero copy, about-us, gallery, testimonials, FAQ, social, contact).
  • Live platform data fetched from the Taikan API at request time (programs, upcomingSessions, plans, courses, coaches, events).

The site resolves itself from the incoming Host header against the minisite_content table, either a custom domain (fit.gymname.com) or a *.taikan.fit subdomain. Since FIT-209 the renderer is multi-page: a fixed page set (Home, plus owner-toggleable Courses, Workshops, About, Contact) routed by URL slug.

Why

  • Gym owners can publish a public site without leaving the Taikan dashboard or buying a separate Squarespace plan.
  • Live data (class schedule, plans, courses) stays in sync automatically — no double-entry between Taikan and a separate CMS.
  • Astro keeps each page <100KB and CDN-cacheable.

Who

  • Gym owners edit content via the Taikan dashboard minisite editor; publishing flips the is_published flag and snapshots contentpublished_content.
  • Prospective members land on the minisite from search, social, or QR codes — see schedule, pricing, and submit a contact form that lands as a lead.

Persona impact

PersonaImpact
Gym ownerOwns their public web presence inside Taikan, no third-party site needed.
CoachSurfaced on the trainers section; their availability + program affiliation shows automatically.
ProspectDiscovers a gym’s offering and books an intro through the contact form (lead → CRM).

Capabilities

  • Multi-tenant resolution from Host header, either custom_domain or subdomain.
  • Multi-page composition: a fixed page set (Home always-on, plus owner-toggleable Courses, Workshops, About, Contact). Each page is slug-routed and carries its own ordered section list.
  • Section composition per page: hero, about, classes, schedule, pricing, courses, trainers, contact, gallery, testimonials, faq, social, events. Each toggled per-org and per-page.
  • Social section: manual Instagram / TikTok / Facebook / YouTube links plus curated post thumbnails.
  • Events / workshops: dated, promotable entries authored in the editor and surfaced via the new minisite_events table (lead-capture, not purchasable).
  • Timed lead-capture popup: fires after a delay or on exit intent, posts name + phone as a lead.
  • Pricing section (FIT-287): server-gated intro pricing terms and effective seat availability on plan cards, plus an optional purchase CTA that links a plan card straight into the public join chain instead of the contact form. Both are double/triple-gated (PostHog flags + per-site editor opt-in) — a site that hasn’t opted in renders byte-identical to before. See behavior.md.
  • Theme controls: primary / secondary colors, body + heading font families, button style, corner style, section density, favicon, SEO title/description. Default template is a warm copper editorial look; headingFontFamily, cornerStyle, and sectionDensity are wired into the layout as of the FIT-287 design rework (previously stored/editable but ignored by the renderer).
  • Draft / publish workflow: content is the working draft, published_content is the live snapshot.
  • Locale-aware (he / en / ru), defaults to the org’s locale and falls back to Hebrew.
  • Top nav switches to page links on multi-page sites and stays as in-page section anchors for single-page sites.
  • Floating WhatsApp button when the contact section provides a number.
  • Hero image preloaded via <link rel="preload"> to shorten LCP.
  • marketing-site — shares the Astro toolchain.
  • webhooks — Clerk doesn’t gate minisite reads; they’re fully public.
  • minisites schema lives in libs/db/src/lib/schema/minisites.ts.
  • API resolve endpoint: GET /minisites/resolve?host=... (public).
  • public-join-links (FIT-251) — the purchase CTA (FIT-287) links into this chain (/join/:token?plan=); it does not add a new checkout surface.
  • plan-groups / effective seat capacity (FIT-289) — the minisite reads the same binding plan-vs-group seat computation the dashboard plan cap enforcement uses, via shared helpers in apps/api/src/plans/plan-capacity.util.ts.

Status

Shipped. Editor lives in the dashboard. Minisite rendering app deploys independently to Vercel.

Gaps

  • No A/B testing harness, every visitor sees published_content as-is.
  • No page-level analytics (PostHog page views only). Conversion funnel through the contact form lacks lifecycle attribution.
  • The page set is fixed (Home, Courses, Workshops, About, Contact). Owners can enable/disable and reorder these but cannot author arbitrary new pages, and there is no blog / case-studies page type yet.
  • Events are lead-capture only, not purchasable. There is no payment or entitlement coupling and no per-event RSVP/registration ledger.
  • Event registration reuses the generic org lead capture (source: 'minisite'), so a lead is not attributed to the specific event that drove it.
  • Theme tokens are still template-level, no per-section font overrides and no CSS variable escape hatch for power users.
  • Custom domain SSL relies on Vercel’s automated provisioning; misconfigured CNAMEs surface a generic 404, not a setup hint.
  • Purchase-mode “sold out” CTA is cosmetic: it routes to the same generic contact form as everything else, with no campaignRef/prefill threading the lead as a waitlist signup for that specific plan.
  • Plan/seat edits do not bust the resolve cache (only publish/unpublish/domain/visibility/events do) — a sold-out plan can look open for up to ~5-6 minutes; true enforcement happens at purchase time in the subscription-insert transaction, so this is a staleness annoyance, not a correctness bug.
  • External-payment-link orgs (plans with paymentLinkUrl, mutually exclusive with intro/caps) cannot use purchase mode — the joinUrl gate requires an active in-app payment config, so those orgs keep the lead-form CTA even with ctaMode: 'purchase' selected.
  • No stats/highlights authoring exists yet for the About section or hero-adjacent dark bento cards — the design system has the .card-dark primitive but nothing populates it (owner-authored numbers only, never invented).
  • No screenshot-based visual QA pass has been run yet against the FIT-287 design rework (RTL, cornerStyle/sectionDensity variants, reduced-motion, no-JS) — see qa-plan.md.