Skip to main content

Landing Page Builder

[landing-page] [admin] [branding]

Your own landing page for your provider portal, built directly in licensio, with no external CMS needed. Drag & drop sections, branding from your provider, live preview.

Who it's for​

Admins who want to provide a public marketing/sales page for their license portal. It appears under <tenant>.licensio.io/ as the default start page.

Step by step​

1. Open the builder​

Settings → Landing Page.

2. Add sections​

Available section types:

  • Hero : large headline banner with a call to action
  • Text & Image (About) : text block next to an image
  • Packages : automatically all packages with show_in_join_preview=true as a pricing table
  • Features : feature grid (up to 6 tiles)
  • CTA : a call-to-action block (headline + button)
  • Screenshots : framed screenshot/mockup block
  • Carousel : image slider with auto-play (see notes below)
  • Portal preview : a live preview of the portal (dashboard/products/assets/account)

Header and footer are configured separately and inherit the branding.

3. Drag & drop​

Reorder sections with the mouse. The order is saved live.

4. Branding​

Sections automatically inherit your provider branding (colors, font, logo). Via separate per-section branding options, background types can be changed (solid, gradient, image).

5. Live preview​

The mobile/desktop toggle shows how the page looks on various devices. There are also portal preview arrows to page through the sections.

6. Publish​

Click Save, and the page is live immediately under <tenant>.licensio.io/. No separate deploy step.

Sections in detail​

Hero​

  • Headline (large)
  • Subheadline (small)
  • Primary button (text + link)
  • Background type: color / gradient / image

Packages​

Automatically shows all active packages of your provider where show_in_join_preview=true is set (toggle in the package settings). Each package card shows:

  • Name + description
  • Price (monthly + setup fee if applicable)
  • Feature list (✓ "Unlimited downloads", "Up to 3 printers", "CRM integration", depending on the feature flags)
  • Call to action to register

Upload images + set the order + auto-play interval. Arrows + dot indicators for manual navigation.

Caution: for carousel components with functional state updates, the logic is guarded against an endless-loop render (see commit 63ca931). The arrows trigger a carousel step incrementally.

Final block with headline + button (e.g. "Secure your license now → /join").

Frequently asked questions​

How do I change the header / logo / footer? Branding tab in Settings → logo, font, colors. This affects the landing page and portal at the same time.

Can I insert my own HTML? Currently only through the available section types. A custom HTML block is coming in Phase 2.

How do I link to registration? Direct link: /join (shows self-service packages). If one of your packages has auto_activate=true, the customer goes straight through without any wait.

Does branding take effect immediately? Yes. Saving branding triggers a re-render on the landing page, with no additional step.

Are there templates? Currently you start with an empty page. A template library is coming in Phase 2.

Scroll animations (fade-in)​

Since 2026-04-29, each section fades in gently as you scroll (opacity 0 → 1 + translateY 28px → 0, transition 0.6s). The first section (usually the hero) is already visible on page load; the following 5 get a staggered delay (0.05s per position) for a calm, cascaded effect.

Implementation details (for developers):

  • Intersection observer (threshold: 0.1) in apps/portal/app/components/LandingPage.tsx
  • Sections are direct children of an lp-sections-container div, which makes the nth-child cascading delays work cleanly
  • Header and footer intentionally do not animate: they stay static
  • The preview buttons in the builder (🖥 Desktop / 📱 Mobile) open the real portal page with a token; the animations are visible there identically

Multilingual landing page (develop)​

In addition to the original language (German), the landing page can have an English variant, maintained manually or translated automatically at the push of a button. The end customer sees the page in the language they set in the portal (i18n).

Language tab in the builder​

At the top of the editor area, a toggle DE / EN:

  • DE mode : you maintain the original, exactly as before.
  • EN mode : for each text field, a read-only reference box appears with the German original (🇩🇪 …, italic, beige background) and below it an empty input field for the English version.

Auto-translate​

The button "🔄 Automatically translate to EN" (visible in the DE tab) fills all EN fields at once:

  1. Click → confirmation dialog.
  2. Confirm → licensio sends the texts to Claude (Sonnet) for translation. Takes a few seconds, cost per run ~$0.05 (borne by licensio).
  3. Success → all EN fields are filled, the tab switches automatically to EN. You can refine each text manually and must then still click Save.

Rate limit: a maximum of 3 auto-translate runs per 72 hours and provider. Once the limit is reached, the button is disabled with a note of when it is available again; you can translate manually at any time in the meantime.

"Not translated" hint (NotTranslatedBadge)​

If a visible field (e.g. the hero headline) has no translation yet in EN mode, the builder shows a small "Not translated" / "Not translated" badge right next to it. This way you can see at a glance where there are still gaps, especially when you have added individual fields after an auto-translate run or added new sections.

Important: the badge appears only in the admin preview (builder + preview tab with token URL). End customers on the live page never see it: there it silently falls back to the original (default_locale) so the page looks clean. This way you as the provider get the hint without your customers seeing translation gaps.

What happens with a missing translation?​

The end customer never sees an empty spot: if the EN version of a field is missing, the display automatically falls back to the German original. The translation is thus additive: you can fill it in piece by piece without the page breaking.

What is covered?​

  • Phase 1 (Foundation): data model for translated fields, fallback logic, no migration needed. English texts live as a suffix key in the existing tenants.landing_page JSONB (headline = DE original, headline_en = English version).
  • Phase 2 (Builder UI + Auto-Translate): language tab, side-by-side editors, auto-translate button with rate limit, NotTranslatedBadge. Covers the top-level texts (headline / subline / body text / button text) in all section editors: Hero, About, Packages, Features, CTA, Screenshots, Carousel, portal preview.

Still open (Phase 3 / 4)​

  • Side-by-side also for list items (individual feature cards, carousel items: title + description) and for multiple buttons in the hero.
  • More languages (ES, FR) in the LanguageProvider + dictionary.

For developers​

  • Translate route: POST /api/landing-page/translate (Admin app), uses ANTHROPIC_API_KEY. Without a key → 503 { code: 'API_KEY_MISSING' }. ENV must be set in apps/admin/.env.local (local) and in the Vercel Admin project for Production + Preview.
  • Portal render: getLocalizedField(data, field, locale, defaultLocale) : reads data[field_<locale>] if locale != defaultLocale, otherwise data[field], otherwise the original.
  • Branding : colors, logo, font
  • Packages : show_in_join_preview for the pricing section
  • Custom Domain : own-domain hosting (in preparation)
  • i18n : platform i18n setup