Maak een dynamische landingspagina voor de studio.
== API endpoint ==
Haal alle content op met fetch("https://caistudio.nl/api/v1/landing-page/st_0192845773a793bbc19c4b7b04f37515") bij page load.
Toon een loading state terwijl de data wordt opgehaald. Render NIETS uit hardcoded waarden — al het zichtbare moet uit deze API komen.
== Response shape ==
{
"name": string,
"title": string | null,
"valueProposition": string | null,
"description": string | null,
"imageUrl": string,
"logoUrl": string | null,
"isFree": boolean,
"price": string | null, // pre-formatted euro amount, e.g. "29.95"
"trialPeriodDays": integer | null,
"recurringInterval": string, // "monthly" | "yearly" | "weekly" | …
"checkoutUrl": string,
"checkoutEnabled": boolean,
"ownerName": string,
"ownerBio": string | null,
"assistants": [
{ "name": string, "description": string | null, "imageUrl": string }
],
"legalDocuments": [
{ "label": string, "url": string }
],
"theme": {
"accent": string, // hex, e.g. "#f43f5e"
"accentHover": string,
"accentDark": string,
"scale": { "50": string, "100": string, …, "900": string, "950": string }
} | null
}
== Vereiste secties (in deze volgorde) ==
data.name, data.title, data.valueProposition, data.imageUrl / data.logoUrl, en een prominente CTA naar data.checkoutUrl.data.assistants als kaarten met name, description, imageUrl. Aantal teksten zoals "X AI-assistenten" gebruiken data.assistants.length — NOOIT een vast getal.data.ownerName (en data.ownerBio indien aanwezig).data.description.data.description en data.assistants.data.checkoutUrl.data.legalDocuments als links (label + url, open in nieuw tabblad).== Regels ==
data.assistants.length, NOOIT een vast getal. Ook geen "7 AI-assistenten" o.i.d. — altijd dynamisch.data.isFree === false. Format: €{data.price} (data.price is al pre-formatted) gevolgd door een label op basis van data.recurringInterval ("monthly" / "yearly" / etc.). Toon proefperiode alleen als data.trialPeriodDays !== null.data.checkoutUrl, maar ALLEEN als data.checkoutEnabled === true. Anders toon "Binnenkort beschikbaar" als placeholder.data.theme.accent toe op alle CTA / interactieve elementen via CSS variables of inline style. Gebruik data.theme.scale (key/value: shade-naam → hex) voor achtergronden, borders en accenten. data.theme kan null zijn — gebruik dan een neutrale default (slate/zinc).@import 'shadcn/tailwind.css' in globals.css — dit veroorzaakt "Failed to load shadcn from /app/globals.css" errors. Gebruik alleen inline Tailwind CSS classes.Nu ga ik de pagina bouwen. Ik zal meerdere component-bestanden aanmaken voor een nette structuur.
Now I'll write the main client page that fetches data, applies the theme, and renders all sections with a skeleton loading state.
De landingspagina is volledig gebouwd en opgedeeld in negen losstaande componenten. Bij page load fetcht LandingClient de API, past applyTheme() de accent- en schaal-kleuren toe als CSS-variabelen op :root, en rendert daarna alle secties in de juiste volgorde — Hero, Assistenten, De Maker, Probleem→Transformatie, Voordelen, Social Proof, FAQ, CTA en Footer. Alle zichtbare waarden (naam, prijs, aantallen, knoppen, afbeeldingen, juridische links) komen uitsluitend uit de API; er is nul hardcoded content behalve de Nederlandstalige placeholder-teksten voor testimonials en FAQ die later eenvoudig te vervangen zijn.