409 lines
20 KiB
TypeScript
409 lines
20 KiB
TypeScript
import { useState } from "react";
|
|
import { Check, Sparkles } from "lucide-react";
|
|
import { Button } from "@/components/ui/button.tsx";
|
|
import { BookingDialog } from "@/components/booking-dialog.tsx";
|
|
|
|
type PricingPlan = {
|
|
id: number;
|
|
name: string;
|
|
slug: string;
|
|
description: string;
|
|
price: number;
|
|
features: string[];
|
|
priceNote: string;
|
|
highlighted?: boolean;
|
|
};
|
|
|
|
type HostingPlan = {
|
|
id: number;
|
|
name: string;
|
|
description: string;
|
|
priceMonthly: number;
|
|
priceYearly: number;
|
|
features: string[];
|
|
highlighted: boolean;
|
|
}
|
|
|
|
const offerlist: PricingPlan[] = [
|
|
{
|
|
id: 1,
|
|
name: "Das Kompakt-Paket",
|
|
slug: "kompakt",
|
|
description:
|
|
"Perfekt für die schnelle, moderne Präsentation. Ideal für Therapeuten, Berater und regionale Handwerker.",
|
|
price: 1800,
|
|
features: [
|
|
"Alle wichtigen Infos auf einer Seite",
|
|
"Für Smartphone optimiert",
|
|
"SSL-Verschlüsselung inklusive",
|
|
"SEO-Grundlagen (Meta-Tags, schnelle Ladezeit)",
|
|
],
|
|
priceNote: "Aufpreise z. B. für mehrsprachige Seiten oder Terminbuchung.",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Das Klassik-Paket",
|
|
slug: "klassik",
|
|
description:
|
|
"Für Praxen und Betriebe mit breitem Angebot. Maximale Übersicht für Ihre Besucher.",
|
|
price: 2500,
|
|
features: [
|
|
"Separate Leistungs- und Teamseiten",
|
|
"Bildergalerie & Referenzen",
|
|
"Erweiterte SEO-Optimierung",
|
|
"Rechtstexte-Assistenz",
|
|
],
|
|
priceNote: "Aufpreise z. B. für zusätzliche Sprachen oder Shop-Anbindung.",
|
|
highlighted: true,
|
|
}
|
|
];
|
|
|
|
const hostinglist: HostingPlan[] = [
|
|
{
|
|
id: 1,
|
|
name: "Basis",
|
|
description: "",
|
|
priceMonthly: 79,
|
|
priceYearly: 790,
|
|
features: [
|
|
"Premium-Hosting",
|
|
"SSL-Zertifikat",
|
|
"Uptime Monitoring",
|
|
"30 Minuten pro Monat für Textänderungen oder Anpassungen",
|
|
],
|
|
highlighted: false,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Standard",
|
|
description: "",
|
|
priceMonthly: 149,
|
|
priceYearly: 1490,
|
|
features: [
|
|
"Alles aus Basis",
|
|
"1 Stunde pro Monat für inhaltliche Pflege, z. B. neue Texte, Bilder, Teammitglieder",
|
|
"Monatlicher Performance-Report zu Besucherzahlen und Analytics",
|
|
],
|
|
highlighted: true,
|
|
}
|
|
];
|
|
|
|
const priceFormatter = new Intl.NumberFormat("de-DE");
|
|
|
|
type Billing = "monthly" | "yearly";
|
|
|
|
export const Pricing = () => {
|
|
const [billing, setBilling] = useState<Billing>("monthly");
|
|
|
|
return (
|
|
<>
|
|
<section id="pricing" className="mx-auto max-w-6xl px-4 py-16 md:px-6 md:py-24">
|
|
<div className="mx-auto max-w-2xl text-center">
|
|
<span className="island-kicker">Preise</span>
|
|
<h2 className="mt-3 font-heading text-3xl font-bold leading-tight tracking-tight text-(--sea-ink) sm:text-4xl">
|
|
Feste Preise. Keine Überraschungen.
|
|
</h2>
|
|
<p className="mt-4 text-(--sea-ink-soft) sm:text-lg">
|
|
Wählen Sie das Paket, das zu Ihrem Betrieb passt. Nach dem Erstgespräch
|
|
bekommen Sie einen festen Preis - schriftlich, ohne Überraschungen.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mx-auto mt-12 grid max-w-4xl gap-6 lg:grid-cols-2 lg:items-stretch">
|
|
{offerlist.map((offer) => {
|
|
const isHighlighted = offer.highlighted;
|
|
return (
|
|
<article
|
|
key={offer.id}
|
|
className={[
|
|
"relative flex flex-col gap-5 rounded-3xl border p-6 sm:p-7",
|
|
isHighlighted
|
|
? "border-(--lagoon-deep) bg-(--surface-strong) shadow-[0_20px_50px_rgba(30,90,72,0.18)] lg:-translate-y-2"
|
|
: "feature-card border-(--line)",
|
|
].join(" ")}
|
|
>
|
|
{isHighlighted && (
|
|
<span className="absolute -top-3 left-1/2 inline-flex -translate-x-1/2 items-center gap-1 rounded-full bg-(--lagoon-deep) px-3 py-1 text-xs font-semibold text-white shadow">
|
|
<Sparkles className="h-3.5 w-3.5" aria-hidden />
|
|
Empfehlung
|
|
</span>
|
|
)}
|
|
|
|
<header className="space-y-2">
|
|
<h3 className="font-heading text-xl font-bold text-(--sea-ink)">
|
|
{offer.name}
|
|
</h3>
|
|
<p className="text-sm text-(--sea-ink-soft)">{offer.description}</p>
|
|
</header>
|
|
|
|
<div className="flex items-baseline gap-1">
|
|
<span className="text-sm text-(--sea-ink-soft)">ab</span>
|
|
<span className="font-heading text-4xl font-bold text-(--sea-ink)">
|
|
{priceFormatter.format(offer.price)} €
|
|
</span>
|
|
</div>
|
|
<div className="-mt-3 space-y-1 text-xs text-(--sea-ink-soft)">
|
|
<p>zzgl. gesetzl. Umsatzsteuer</p>
|
|
<p>{offer.priceNote}</p>
|
|
</div>
|
|
|
|
<ul className="space-y-2.5 text-sm text-(--sea-ink)">
|
|
{offer.features.map((feature) => (
|
|
<li key={feature} className="flex items-start gap-2">
|
|
<Check
|
|
className="mt-0.5 h-4 w-4 shrink-0 text-(--lagoon-deep)"
|
|
aria-hidden
|
|
/>
|
|
<span>{feature}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<div className="mt-auto pt-2">
|
|
<BookingDialog>
|
|
<Button
|
|
variant={isHighlighted ? "default" : "outline"}
|
|
className="w-full"
|
|
data-track="cta_erstgespraech"
|
|
data-source={`pricing_${offer.slug}`}
|
|
>
|
|
Erstgespräch buchen
|
|
</Button>
|
|
</BookingDialog>
|
|
</div>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="mt-24 border-t border-(--line) pt-16">
|
|
<div className="mx-auto max-w-2xl text-center">
|
|
<span className="island-kicker">Hosting & Betreuung</span>
|
|
<h3 className="mt-3 font-heading text-3xl font-bold leading-tight tracking-tight text-(--sea-ink) sm:text-4xl">
|
|
Rundum betreut - auch nach dem Launch.
|
|
</h3>
|
|
<p className="mt-4 text-(--sea-ink-soft) sm:text-lg">
|
|
Wir übernehmen Technik, Sicherheit und laufende Anpassungen -
|
|
damit Ihre Seite dauerhaft schnell, sicher und aktuell bleibt.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="mt-8 flex justify-center">
|
|
<div
|
|
role="radiogroup"
|
|
aria-label="Abrechnungszeitraum"
|
|
className="inline-flex rounded-full border border-(--line) bg-(--surface-strong) p-1 text-sm shadow-sm"
|
|
>
|
|
<button
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={billing === "monthly"}
|
|
onClick={() => setBilling("monthly")}
|
|
data-track="pricing_billing_toggle"
|
|
data-mode="monthly"
|
|
className={[
|
|
"rounded-full px-4 py-1.5 font-medium transition focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--lagoon-deep)",
|
|
billing === "monthly"
|
|
? "bg-(--lagoon-deep) text-white shadow"
|
|
: "text-(--sea-ink-soft) hover:text-(--sea-ink)",
|
|
].join(" ")}
|
|
>
|
|
Monatlich
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={billing === "yearly"}
|
|
onClick={() => setBilling("yearly")}
|
|
data-track="pricing_billing_toggle"
|
|
data-mode="yearly"
|
|
className={[
|
|
"flex items-center gap-2 rounded-full px-4 py-1.5 font-medium transition focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--lagoon-deep)",
|
|
billing === "yearly"
|
|
? "bg-(--lagoon-deep) text-white shadow"
|
|
: "text-(--sea-ink-soft) hover:text-(--sea-ink)",
|
|
].join(" ")}
|
|
>
|
|
Jährlich
|
|
<span
|
|
className={[
|
|
"rounded-full px-2 py-0.5 text-xs font-semibold",
|
|
billing === "yearly"
|
|
? "bg-white/20 text-white"
|
|
: "bg-(--surface-strong) text-(--lagoon-deep)",
|
|
].join(" ")}
|
|
>
|
|
2 Monate frei
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mx-auto mt-10 grid max-w-4xl gap-6 lg:grid-cols-2 lg:items-stretch">
|
|
{hostinglist.map((plan) => {
|
|
const isHighlighted = plan.highlighted;
|
|
const price =
|
|
billing === "monthly" ? plan.priceMonthly : plan.priceYearly;
|
|
const suffix = billing === "monthly" ? "/ Monat" : "/ Jahr";
|
|
return (
|
|
<article
|
|
key={plan.id}
|
|
className={[
|
|
"relative flex flex-col gap-5 rounded-3xl border p-6 sm:p-7",
|
|
isHighlighted
|
|
? "border-(--lagoon-deep) bg-(--surface-strong) shadow-[0_20px_50px_rgba(30,90,72,0.18)] lg:-translate-y-2"
|
|
: "feature-card border-(--line)",
|
|
].join(" ")}
|
|
>
|
|
{isHighlighted && (
|
|
<span className="absolute -top-3 left-1/2 inline-flex -translate-x-1/2 items-center gap-1 rounded-full bg-(--lagoon-deep) px-3 py-1 text-xs font-semibold text-white shadow">
|
|
<Sparkles className="h-3.5 w-3.5" aria-hidden />
|
|
Empfehlung
|
|
</span>
|
|
)}
|
|
|
|
<header className="space-y-2">
|
|
<h4 className="font-heading text-xl font-bold text-(--sea-ink)">
|
|
{plan.name}
|
|
</h4>
|
|
{plan.description && (
|
|
<p className="text-sm text-(--sea-ink-soft)">
|
|
{plan.description}
|
|
</p>
|
|
)}
|
|
</header>
|
|
|
|
<div className="flex items-baseline gap-1">
|
|
<span className="font-heading text-4xl font-bold text-(--sea-ink)">
|
|
{priceFormatter.format(price)} €
|
|
</span>
|
|
<span className="text-sm text-(--sea-ink-soft)">
|
|
{suffix}
|
|
</span>
|
|
</div>
|
|
<p className="-mt-3 text-xs text-(--sea-ink-soft)">
|
|
zzgl. gesetzl. Umsatzsteuer
|
|
</p>
|
|
|
|
<ul className="space-y-2.5 text-sm text-(--sea-ink)">
|
|
{plan.features.map((feature) => (
|
|
<li key={feature} className="flex items-start gap-2">
|
|
<Check
|
|
className="mt-0.5 h-4 w-4 shrink-0 text-(--lagoon-deep)"
|
|
aria-hidden
|
|
/>
|
|
<span>{feature}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</article>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<p className="mx-auto mt-6 max-w-4xl text-center text-xs text-(--sea-ink-soft)">
|
|
Nicht genutzte Zeitkontingente verfallen zum Monatsende.
|
|
Kündbar zum Ende der jeweiligen Laufzeit.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
id="startup"
|
|
className="mx-auto max-w-6xl px-4 pb-16 md:px-6 md:pb-24"
|
|
>
|
|
<div className="island-shell relative overflow-hidden rounded-3xl p-6 sm:p-10 lg:p-12">
|
|
<div className="grid gap-10 lg:grid-cols-2 lg:items-center">
|
|
<div>
|
|
<span className="island-kicker">Für Startups</span>
|
|
<h2 className="mt-3 font-heading text-3xl font-bold leading-tight tracking-tight text-(--sea-ink) sm:text-4xl">
|
|
Ohne Vorabkosten starten.
|
|
</h2>
|
|
<p className="mt-4 text-(--sea-ink-soft) sm:text-lg">
|
|
Als Startup steckt jeder Euro ins Wachstum. Wir übernehmen
|
|
die Website und finanzieren die Erstellung mit - Sie zahlen
|
|
von Tag eins nur einen festen Monatspreis.
|
|
</p>
|
|
<ul className="mt-6 space-y-2.5 text-sm text-(--sea-ink)">
|
|
{[
|
|
"0 € Einmalkosten für die Erstellung",
|
|
"Klassik-Paket inkl. Hosting, SSL und laufender Pflege",
|
|
"1 Stunde inhaltliche Betreuung pro Monat inklusive",
|
|
"Mindestlaufzeit 24 Monate, danach monatlich kündbar",
|
|
].map((feature) => (
|
|
<li key={feature} className="flex items-start gap-2">
|
|
<Check
|
|
className="mt-0.5 h-4 w-4 shrink-0 text-(--lagoon-deep)"
|
|
aria-hidden
|
|
/>
|
|
<span>{feature}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<p className="mt-4 text-xs text-(--sea-ink-soft)">
|
|
Für Unternehmen mit Gründung innerhalb der letzten 3 Jahre.
|
|
Nachweis über Handelsregister- oder Gewerbeanmeldung.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="rounded-2xl border border-(--lagoon-deep) bg-(--surface-strong) p-6 shadow-[0_20px_50px_rgba(30,90,72,0.18)] sm:p-8">
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div>
|
|
<p className="text-xs uppercase tracking-widest text-(--sea-ink-soft)">
|
|
Startup-Paket
|
|
</p>
|
|
<p className="mt-1 font-heading text-sm text-(--sea-ink-soft)">
|
|
Alles inklusive
|
|
</p>
|
|
</div>
|
|
<span className="inline-flex items-center gap-1 rounded-full bg-(--lagoon-deep) px-3 py-1 text-xs font-semibold text-white shadow">
|
|
<Sparkles className="h-3.5 w-3.5" aria-hidden />
|
|
Angebot
|
|
</span>
|
|
</div>
|
|
|
|
<div className="mt-6 flex items-baseline gap-1">
|
|
<span className="font-heading text-5xl font-bold text-(--sea-ink)">
|
|
300 €
|
|
</span>
|
|
<span className="text-sm text-(--sea-ink-soft)">/ Monat</span>
|
|
</div>
|
|
<p className="mt-1 text-xs text-(--sea-ink-soft)">
|
|
zzgl. gesetzl. Umsatzsteuer
|
|
</p>
|
|
|
|
<dl className="mt-5 grid grid-cols-2 gap-3 text-sm">
|
|
<div className="rounded-xl bg-(--surface-strong) px-3 py-2">
|
|
<dt className="text-xs text-(--sea-ink-soft)">
|
|
Einmalkosten
|
|
</dt>
|
|
<dd className="font-heading font-bold text-(--sea-ink)">
|
|
0 €
|
|
</dd>
|
|
</div>
|
|
<div className="rounded-xl bg-(--surface-strong) px-3 py-2">
|
|
<dt className="text-xs text-(--sea-ink-soft)">Laufzeit</dt>
|
|
<dd className="font-heading font-bold text-(--sea-ink)">
|
|
24 Monate
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
|
|
<BookingDialog>
|
|
<Button
|
|
variant="default"
|
|
className="mt-6 w-full"
|
|
data-track="cta_erstgespraech"
|
|
data-source="pricing_startup"
|
|
>
|
|
Erstgespräch buchen
|
|
</Button>
|
|
</BookingDialog>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</>
|
|
);
|
|
};
|