Skip to content

Homepage Brand Routing

Overview

A single OktaPT-FE codebase serves three distinct marketing experiences at /:

  • Otera Health (oterahealth.com) — full US clinic-buyer homepage
  • OKTA Health (oktahealth.ru) — Russian-market clinic-buyer homepage
  • Custom tenants (12 clinic-branded deployments like knosispt.oterahealth.com, stroma.oktahealth.ru) — minimal tenant-branded login gateway

Each deployment gets its own Next.js build with its own NEXT_PUBLIC_BRAND_NAME env var, so the root route resolves to a different top-level component per brand. Unused branches are tree-shaken out of each bundle.

Every other page (login, dashboards, onboarding, clinic-solutions, etc.) is shared across all brands, except /team, which exists only on the OTERA build, and /careers and /pricing, which exist on the OTERA and OKTA builds with their own content (see Marketing navigation, Team, Careers and Pricing).

Brand Resolution

Two independent mechanisms.

A clinic build reads its name, logo, colour and parent brand at runtime from GET /v2/branding, by the host it is served on (see Tenant Branding), so one build can serve any clinic. /, /join, /patients and /contact are server-rendered per host, so the clinic's name is in the first HTML. The OTERA/OKTA switch below stays build-time.

Build-time switch (pages/index.tsx)

NEXT_PUBLIC_BRAND_NAME is inlined by Webpack at build time. pages/index.tsx is a three-way switch:

if (process.env.NEXT_PUBLIC_BRAND_NAME === "OTERA") return <OteraHomePage />;
if (process.env.NEXT_PUBLIC_BRAND_NAME === "OKTA")  return <OktaHomePage />;
return <CustomTenantHomePage />;

Because the env var is a compile-time constant in each deployment, the inactive branches are never loaded by that build.

Runtime hostname detection (lib/brand/parentBrand.ts)

CustomTenantHomePage renders for 12 different clinic brands. Each needs a "Learn more about [parent brand]" link back to the right marketing site — but the build itself has no way to know whether a given custom tenant lives on OTERA or OKTA infra.

detectParentBrandFromHostname() resolves this at runtime from window.location.hostname:

  • *.oktahealth.ru or *.oktahealth.com → OKTA (https://oktahealth.ru)
  • *.oterahealth.com → OTERA (https://oterahealth.com)
  • Anything else (fully custom domain) → OTERA (default)

SSR renders the OTERA fallback so the server markup is stable; the real hostname is re-evaluated in a useEffect on the client.

OteraHomePage

Eighteen sections. US market, vision-led narrative ("less screen time, more hands-on care"). Every product claim references a shipped feature or a service the team delivers today (EMR integrations, billing and claims through Stedi, the AI call agent). Every visual is either a text-built product mock with fictional people or an AI-generated picture (the clinic photos under the hero and in the screen-time section, and one family of warm clay-style illustrations used on the platform cards, the loop's nodes, the How-it-works steps, the AI band and the compliance strip), so the page shows no real patient and reads in both languages. Eyebrow labels appear only where the title alone would not say who a section is for (the owner/therapist sections).

Sections, in order (anchor ids in brackets):

  1. OteraHero — light hero with demo and free-trial CTAs over the photo stage (HeroPhotoStage): a warm clinic photo (a therapist supporting her patient's outstretched arms) in which the tablet on the stool behind them shows the same movement on a 3D anatomy model, and a live visit note, a finished AI call and, under the tablet, a reviewed screening floating at its edges. Nothing is drawn over the patient and the hero shows no angle. No fine print under the buttons and no patient link: the page speaks to clinics
  2. OteraTrustBar — clinic logo strip
  3. OteraProductModules — 8 platform cards, one per product (#product)
  4. OteraConnectedLoop — how it all connects (#how-it-connects): the clinic's roles on the left, the patient's touchpoints on the right, the six-step loop of care around one patient record in the middle (a ring on wide screens, a numbered list on phones), and the EMR and clearinghouse partners under it (ConnectedLoop)
  5. OteraScreenTimeProblem — why clinics switch: a photo of hands-on care beside the headline and three figures
  6. OteraScreenings — objective movement screenings, with a frame of the clip (a photo of a patient at home with the skeleton overlay and goniometer arc drawn on it) above the results card (#screenings)
  7. OteraTelehealth — video visits with live notes, with the visit card showing the patient exercising at home, the therapist in the picture-in-picture and the exercise the therapist has put on both screens (#telehealth)
  8. OteraRtmDeepDive — the RTM billing board mock beside the shared RtmCalculator with live CMS 2025 rate math (#rtm-billing)
  9. OteraIntegrations — billing and claims management through the Stedi clearinghouse (claims list mock) and EMR integration with Epic, Raintree, Stride and PtCare (note-to-chart mock), with the vendors' own logos (VendorLogo, files under public/images/integrations/; PtCare has no public logo and is set as a wordmark) (#integrations)
  10. OteraAiAgents — the AI call agent, with a finished-call card (transcript and outcomes) (#ai-agents)
  11. OteraAiEngine — four AI capabilities, on a dark band (#ai-engine)
  12. OteraForOwners — clinic-owner persona section with the clinic insights mock (#owners)
  13. OteraForTherapists — therapist persona section with the therapist app in a phone bezel (#therapists)
  14. OteraTestimonials — named clinic quotes
  15. OteraHowItWorks — onboarding steps
  16. OteraCompliance — four-item trust strip
  17. OteraFAQ — eight questions
  18. OteraDemoCTA — dark closing panel with the demo calendar link (#demo)

The look: white heroes with a faint sunrise wash at the top and the hero photo floating in a white bezel with a warm-tinted shadow, sections alternating white with a light paper that has a hint of the brand blue (bg-marketing-paper), serif display headlines (Lora, via next/font, balanced so no line is left with one word) over the global Nunito body text, white rounded cards, section visuals on a flat pale-blue stage (bg-marketing-stage), and navy panels (bg-marketing-navy) for the AI band, the closing call to action, the highlighted pricing plan and the footer. The colors are the marketing.* tokens in tailwind.config.js (see "Marketing pages" in the design system); the marketing components use no raw hex classes. No pastel radial glows on light surfaces: that was what made the page look like sword.com. Buttons have 12 px corners; the two remaining eyebrow labels (the owner and therapist sections) are plain brand-colored text with a dot; the heroes have none.

OktaHomePage

Thirteen sections: Hero, TrustBar, ProductModules (6 cards), ConnectedLoop (owner and specialists, client touchpoints, a six-step loop without billing and no partners row), Screenings, Telehealth, AiEngine, ForOwners, ForTherapists, HowItWorks, Compliance, FAQ (8 questions), DemoCTA. The hero stage, the screening clip, the insights panel and the phone mock are the same components as on Otera with center-flavoured data (no money, no claims, no AI call agent). Nothing about RTM, billing, claims, EMR, RCM or HIPAA renders on OKTA; testimonials are omitted because the named quotes are US clinics.

All copy is sourced from public/locales/{en,ru}/clinic-home.json via next-i18next.

CustomTenantHomePage

Single-screen login gateway for non-flagship tenants. Renders for any deployment whose NEXT_PUBLIC_BRAND_NAME isn't OTERA or OKTA: any clinic build; the clinic's name comes from its tenant.

Layout:

  • Tenant logo (GET /v2/branding/logo)
  • Tenant display name headline
  • "Runs on {Otera|Okta}" subtitle — parent brand resolved at runtime from hostname
  • Two login CTAs: "I'm a patient" / "I'm a therapist" (both go to /login)
  • "Learn more about Otera/Okta" link pointing back to the parent marketing site

Marketing navigation, Team, Careers and Pricing

Visitors who are not signed in get a marketing navigation: Platform, Pricing, Team, Careers, Contact on OTERA; Platform, Pricing, Careers and Contact on OKTA (no Team); nothing on tenant builds. "Platform" is a menu listing every product with a line each (PRODUCT_LINKS in components/header/marketingLinks.ts, labels under header.products.* in common), each linking to its section of the home page; the mobile menu lists the same products under a "Platform" heading. The desktop links come from components/header/MarketingNavigation.tsx. The navigation renders only once the auth state is anonymous (nothing while it is still resolving), so signed-in users keep their app navigation unchanged. Pages where a visitor is in the middle of a task (/login, /signup, /permission-consent, /reset-password, /start-workout, /logout and the therapist-promo funnel) show no marketing menu on any width (showsMarketingLinks in marketingLinks.ts), so a patient opening a workout from an email or resetting a password is not pulled toward Pricing or Careers. The marketing pages follow the visitor's reduce-motion setting (MotionConfig reducedMotion="user"), and the home hero's headline and photo are painted from the server HTML without an entrance animation, so a phone shows them before the scripts load.

The header's fit check (useNavFit in components/Header.tsx) treats these links like the app's: when they don't fit, the menu button stands in for them. A visitor keeps Log in and Sign up (and the language switcher) in the bar at every width: beside the menu button on tablets (768–1023 px) and when the menu has replaced the links, since the menu itself has no Log in. From 1280 px, once the links fit, the row becomes a three-column grid so the links sit exactly in the middle of the page, over the centered heroes. MainNavigation renders nothing when the user has no pages (a visitor), so it doesn't take a slot in the row.

/team renders only on the OTERA build; /careers and /pricing render on the OTERA and OKTA builds. On every other build (and /team on OKTA) they render a <meta http-equiv="refresh"> plus router.replace("/"), like /clinic-solutions. Their content lives in typed files with { en, ru } labels, with a US set and an OKTA set where the builds differ, and the page chrome under pricing / pricingOkta and careers / careersOkta in clinic-home:

  • OktaPT-FE/lib/content/team.ts — the people, in two groups (leadership, advisors): name, role, a short bio, an optional fullBio that opens behind a "Read more" toggle on the card, linkedinUrl and photo (a square image under public/images/team/; initials show until one is added).
  • OktaPT-FE/lib/content/careers.ts — US roles (OPEN_ROLES_OTERA) and roles based in Russia (OPEN_ROLES_OKTA, remote within Russia or hybrid in Moscow, applications to okta.team@oktahealth.com); OPEN_ROLES and CAREERS_EMAIL resolve to the build's set. Originally: the open roles. Applications are mailto: links to CAREERS_EMAIL; an empty OPEN_ROLES shows a "no open roles" note.
  • OktaPT-FE/lib/content/pricing.ts — OTERA: the three plans (Starter, Clinic, Practice) with their monthly per-therapist prices in USD, the two add-ons (AI call agents, priced per clinic; billing & claims management at 10% of collections), the feature comparison table and the pricing questions. OKTA: Старт, Центр and Сеть in rubles per specialist (1 490 ₽, 4 990 ₽, per location), no add-ons, a comparison table and questions without any RTM, billing, EMR or AI-call content. TIERS, COMPARISON, ADD_ONS and PRICING_FAQ resolve to the build's set; formatMonthly prints $19 or 1 490 ₽.

All three routes, and /contact, are in publicRoutes and the matcher of middleware.ts (so a direct hit gets the locale redirect; before, a direct visit to /contact rendered with the placeholder default locale and an empty language switcher) and in PUBLIC_ROUTES of lib/utils/routes.ts (so the auth handler resolves an anonymous visitor without a request).

The shared Footer has Product, Company, Legal and Contact columns. Product (anchors into the home page, plus Pricing) exists only on the OTERA and OKTA builds, and OKTA's omits RTM billing, integrations and AI call agents. Company lists Team and Careers on OTERA, Careers on OKTA, plus Contact and Feedback on every build. Legal keeps the hostname rule: Terms and SMS consent appear on oterahealth.com hosts and localhost.

The /patients Route

Before the redesign, / was a patient-first landing page. After the redesign, / became the clinic-buyer marketing page, so the old patient-first page was moved verbatim to /patients. This preserves the SEO and external links pointing at patient-intent copy, and gives patients who land on the marketing site a dedicated destination.

Both / and /patients are listed in the publicRoutes array in middleware.ts, so they bypass auth and render for logged-out visitors.

Shared Marketing Components

Used by both OTERA and OKTA homepages to avoid duplication:

  • RtmCalculator — live reimbursement calculator with CMS 2025 CPT rates baked in. Also exports RtmCptCodeList for the code breakdown UI
  • PersonaSection — two-column section template (copy and bullets beside a visual on a tinted stage) used by *Screenings, *Telehealth, *ForOwners and *ForTherapists on both brands
  • FeatureModuleCard — product module card used in the product-modules grids
  • DemoCTACard — dark rounded closing panel with primary/secondary CTAs
  • MarketingButton — the one button of the marketing pages (primary, secondary, onDark, onDarkSecondary)
  • SectionHeading — title and subtitle block used by every section, with an optional eyebrow; also exports Eyebrow, the small brand-colored label (used only by the owner/therapist sections)
  • ComplianceStrip — four-item trust strip (OteraCompliance, OktaCompliance)
  • ScreeningResultsMock — text-built screening results card (degrees per side, the left–right difference), with an optional frame of the clip above the rows (mocks/ScreeningVideoMock)
  • LiveNotesMock — text-built video visit with live notes under SOAP headings; says in words that AI wrote them
  • HeroPhotoStage — the stage under both heroes: the clinic photo (public/images/marketing/hero-screening.jpg) in a white bezel, with up to three cards floating at its edges on wide screens (mocks/HeroFloatCards); they hang up to 4rem past the photo but never come closer than 1.5rem to the window edge. The tablet's screen is part of the picture: an AI-generated anatomy figure in the patient's pose (Higgsfield Soul), set into the screen in perspective with the glass's own reflections kept on top, so it scales and crops with the photo. To change it, composite onto the original 2048 × 1152 render again rather than editing the JPEG. On phones the photo is cropped to 4:3 around the people, so the tablet is out of frame there
  • mocks/ — the text-built product mocks with fictional people: PoseOverlay (the live-screening skeleton drawn over the screening clip: thin bones, small landmark dots, the measured joint ringed, a goniometer arc and a dark angle chip), ScreeningVideoMock (a photo of a patient at home with the skeleton and goniometer arc drawn over it), RtmBoardMock (billing board), ClaimsMock, EmrSyncMock (with the vendor logos), CallAgentMock, ClinicInsightsMock (owner panel), DoctorAppMock (therapist app in a phone bezel), plus the StatusChip, Meter and Avatar primitives. All strings come in as props from the sections, so every mock is translated; none of them is a screenshot
  • ConnectedLoop — the "how it fits together" map: role lists for the clinic and the patient, the loop of care around the record (SVG ring with chips at wide widths, a list below lg), and an optional partners block with vendor logos
  • VendorLogo — the logos of the systems Otera is connected to (Epic, Raintree, Stride, Stedi; PtCare as a wordmark), shown in one monochrome weight
  • Photos under public/images/marketing/ are AI-generated pictures of no real person, made for this page and reused by the OKTA page. The screening clip and the exercise demo thumbnail are rendered from the same reference model the API's MedBridge video generation uses (OktaPT-API/assets/medbridge-video-generation/model-1.png), so the person in the screening and in the exercise videos is one and the same; the video-visit patient (exercising at home) and therapist and the hands-on-care photo are text-to-image renders
  • marketingLinks.ts — the demo calendar, doctor sign-up and /patients hrefs
  • displayFont.ts — the Lora display face (Latin and Cyrillic) for headlines; brandTint.ts — the brand color over transparency (used on the dark panels and the loop)

Translations

All clinic-homepage copy lives in the clinic-home i18n namespace. Files:

  • public/locales/en/clinic-home.json
  • public/locales/ru/clinic-home.json

pages/index.tsx's getStaticProps loads both common and clinic-home namespaces for every locale. See Translations for the {{variable}} interpolation syntax and validation script.

clinic-home also holds the page chrome of /team and /careers (top-level team and careers objects) and each home page's <title> and description (otera.meta, okta.meta). The roster and job posts themselves are bilingual objects in the content files above, not translation keys. The marketing navigation and footer labels live in common (header.navigation.*, footer.*).

Key Files

File Role
OktaPT-FE/pages/index.tsx Build-time three-way brand switch
OktaPT-FE/pages/patients.tsx Preserved patient-first landing page
OktaPT-FE/lib/brand/parentBrand.ts Runtime hostname → parent brand resolver
OktaPT-FE/components/landing/otera/OteraHomePage.tsx Otera top-level homepage
OktaPT-FE/components/landing/otera/sections/ Otera section components (18)
OktaPT-FE/components/landing/okta/OktaHomePage.tsx OKTA top-level homepage
OktaPT-FE/components/landing/okta/sections/ OKTA section components (13)
OktaPT-FE/components/landing/custom/CustomTenantHomePage.tsx Custom-tenant login gateway
OktaPT-FE/components/landing/shared/RtmCalculator.tsx Reusable RTM reimbursement calculator
OktaPT-FE/components/landing/shared/mocks/ Text-built product mocks used as the page's visuals
OktaPT-FE/components/landing/shared/PersonaSection.tsx Reusable owner/therapist persona layout
OktaPT-FE/components/landing/shared/FeatureModuleCard.tsx Reusable product-module card
OktaPT-FE/components/landing/shared/DemoCTACard.tsx Reusable demo CTA closer
OktaPT-FE/public/locales/en/clinic-home.json English clinic-home strings
OktaPT-FE/public/locales/ru/clinic-home.json Russian clinic-home strings
OktaPT-FE/pages/team.tsx Team page (OTERA build only; redirects to / elsewhere)
OktaPT-FE/pages/careers.tsx Careers page (OTERA build only; redirects to / elsewhere)
OktaPT-FE/lib/content/team.ts Team roster: leadership and advisors, bios, LinkedIn links, photos under public/images/team/
OktaPT-FE/lib/content/careers.ts Open roles and CAREERS_EMAIL
OktaPT-FE/components/header/MarketingNavigation.tsx Desktop marketing links for anonymous visitors (items in marketingLinks.ts)
OktaPT-FE/components/landing/Footer.tsx Shared footer: Product, Company, Legal and Contact columns
OktaPT-FE/middleware.ts publicRoutes whitelist and matcher (include /, /patients, /team, /careers)