Revision vor Kundenübergabe: Plausibilität, SEO, A11y, OpenPanel
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
// OpenPanel wird ausschließlich im Browser initialisiert. Ein dynamischer Import
|
||||
// verhindert, dass der Konstruktor bereits beim Server-Rendering läuft.
|
||||
export function Analytics() {
|
||||
useEffect(() => {
|
||||
void import("@/openpanel.ts");
|
||||
}, []);
|
||||
return null;
|
||||
}
|
||||
+25
-10
@@ -24,14 +24,25 @@ export const Footer = () => {
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row lg:flex-col">
|
||||
<BookingDialog>
|
||||
<Button data-type="accent" size="lg" className="gap-2">
|
||||
<Button
|
||||
data-type="accent"
|
||||
size="lg"
|
||||
className="gap-2"
|
||||
data-track="cta_erstgespraech"
|
||||
data-source="footer"
|
||||
>
|
||||
Erstgespräch buchen
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</BookingDialog>
|
||||
<Button variant="outline" size="lg" asChild>
|
||||
<a href="tel:+4917622222222" className="no-underline">
|
||||
+49 176 22222222
|
||||
<a
|
||||
href="tel:+491718084830"
|
||||
className="no-underline"
|
||||
data-track="kontakt_telefon"
|
||||
data-source="footer_cta"
|
||||
>
|
||||
+49 171 8084830
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -45,10 +56,10 @@ export const Footer = () => {
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
alt="Placeholder Logo"
|
||||
height="32"
|
||||
width="32"
|
||||
src="https://placehold.co/64"
|
||||
alt=""
|
||||
height="520"
|
||||
width="690"
|
||||
src="/img/logo.png"
|
||||
className="h-8 w-8 rounded-md"
|
||||
/>
|
||||
<span className="font-heading text-lg font-bold text-(--sea-ink)">
|
||||
@@ -66,7 +77,7 @@ export const Footer = () => {
|
||||
Anbieter
|
||||
</h3>
|
||||
<address className="mt-3 text-sm not-italic text-(--sea-ink)">
|
||||
Mathias Hurler Webdesign
|
||||
Hurler Webdesign
|
||||
<br />
|
||||
Untermagerbein 30
|
||||
<br />
|
||||
@@ -83,6 +94,8 @@ export const Footer = () => {
|
||||
<a
|
||||
href="mailto:info@webkulisse.de"
|
||||
className="inline-flex items-center gap-2 no-underline hover:underline"
|
||||
data-track="kontakt_mail"
|
||||
data-source="footer_list"
|
||||
>
|
||||
<Mail className="h-4 w-4" aria-hidden />
|
||||
info@webkulisse.de
|
||||
@@ -90,11 +103,13 @@ export const Footer = () => {
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="tel:+4917622222222"
|
||||
href="tel:+491718084830"
|
||||
className="inline-flex items-center gap-2 no-underline hover:underline"
|
||||
data-track="kontakt_telefon"
|
||||
data-source="footer_list"
|
||||
>
|
||||
<Phone className="h-4 w-4" aria-hidden />
|
||||
+49 176 22222222
|
||||
+49 171 8084830
|
||||
</a>
|
||||
</li>
|
||||
<li className="inline-flex items-center gap-2 text-(--sea-ink-soft)">
|
||||
|
||||
+38
-17
@@ -6,11 +6,11 @@ import { Button } from "@/components/ui/button.tsx";
|
||||
import { authClient } from "@/lib/auth-client.ts";
|
||||
|
||||
const navLinks = [
|
||||
{ href: "#zielgruppen", label: "Für wen" },
|
||||
{ href: "#warum-wir", label: "Warum wir" },
|
||||
{ href: "#ablauf", label: "Ablauf" },
|
||||
{ href: "#pricing", label: "Angebote" },
|
||||
{ href: "#kontakt", label: "Kontakt" },
|
||||
{ hash: "zielgruppen", label: "Für wen" },
|
||||
{ hash: "warum-wir", label: "Warum wir" },
|
||||
{ hash: "ablauf", label: "Ablauf" },
|
||||
{ hash: "pricing", label: "Angebote" },
|
||||
{ hash: "kontakt", label: "Kontakt" },
|
||||
];
|
||||
|
||||
export default function Header() {
|
||||
@@ -52,7 +52,7 @@ export default function Header() {
|
||||
<div className="mx-auto flex max-w-6xl items-center justify-between gap-4 px-4 py-3 md:px-6">
|
||||
<Link to="/" aria-label="Startseite" className="flex items-center gap-2 no-underline">
|
||||
<img
|
||||
alt="Logo Webkulisse"
|
||||
alt=""
|
||||
height="520"
|
||||
width="690"
|
||||
src="/img/logo.png"
|
||||
@@ -66,13 +66,17 @@ export default function Header() {
|
||||
<nav aria-label="Hauptnavigation" className="hidden md:block">
|
||||
<ul className="flex items-center gap-1">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<a
|
||||
href={link.href}
|
||||
<li key={link.hash}>
|
||||
<Link
|
||||
to="/"
|
||||
hash={link.hash}
|
||||
className="nav-link rounded-md px-3 py-2 text-sm font-medium no-underline"
|
||||
data-track="nav_anchor"
|
||||
data-hash={link.hash}
|
||||
data-source="header_desktop"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@@ -89,7 +93,14 @@ export default function Header() {
|
||||
</button>
|
||||
|
||||
<Button asChild variant="default" className="hidden sm:inline-flex">
|
||||
<Link to={loginTarget}>{loginLabel}</Link>
|
||||
<Link
|
||||
to={loginTarget}
|
||||
data-track="nav_kundenlogin"
|
||||
data-source="header_desktop"
|
||||
data-authenticated={session?.user ? "true" : "false"}
|
||||
>
|
||||
{loginLabel}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<button
|
||||
@@ -97,7 +108,7 @@ export default function Header() {
|
||||
onClick={() => setMobileOpen((v) => !v)}
|
||||
aria-expanded={mobileOpen}
|
||||
aria-controls="mobile-nav"
|
||||
aria-label="Menü öffnen"
|
||||
aria-label={mobileOpen ? "Menü schließen" : "Menü öffnen"}
|
||||
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-[var(--line)] bg-[var(--chip-bg)] text-[var(--sea-ink)] md:hidden"
|
||||
>
|
||||
{mobileOpen ? <X className="h-4 w-4" /> : <Menu className="h-4 w-4" />}
|
||||
@@ -113,19 +124,29 @@ export default function Header() {
|
||||
>
|
||||
<ul className="mx-auto flex max-w-6xl flex-col gap-1 px-4 py-3">
|
||||
{navLinks.map((link) => (
|
||||
<li key={link.href}>
|
||||
<a
|
||||
href={link.href}
|
||||
<li key={link.hash}>
|
||||
<Link
|
||||
to="/"
|
||||
hash={link.hash}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
className="nav-link block rounded-md px-3 py-2 text-base font-medium no-underline"
|
||||
data-track="nav_anchor"
|
||||
data-hash={link.hash}
|
||||
data-source="header_mobile"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
<li className="pt-2">
|
||||
<Button asChild variant="default" className="w-full">
|
||||
<Link to={loginTarget} onClick={() => setMobileOpen(false)}>
|
||||
<Link
|
||||
to={loginTarget}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
data-track="nav_kundenlogin"
|
||||
data-source="header_mobile"
|
||||
data-authenticated={session?.user ? "true" : "false"}
|
||||
>
|
||||
{loginLabel}
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
+46
-46
@@ -10,58 +10,58 @@ const trustPoints = [
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className="mx-auto max-w-6xl px-4 pt-12 pb-16 md:px-6 md:pt-20 md:pb-24">
|
||||
<div className="grid items-center gap-10 lg:grid-cols-2 lg:gap-16">
|
||||
<div className="space-y-6 rise-in">
|
||||
<span className="island-kicker inline-flex items-center gap-2">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[var(--lagoon)]" aria-hidden />
|
||||
Für Handwerk, Praxis und Therapie in Bayern
|
||||
</span>
|
||||
<section className="mx-auto max-w-4xl px-4 pt-12 pb-16 md:px-6 md:pt-20 md:pb-24">
|
||||
<div className="space-y-6 rise-in">
|
||||
<span className="island-kicker inline-flex items-center gap-2">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[var(--lagoon)]" aria-hidden />
|
||||
Für Handwerk, Praxis und Therapie in Bayern
|
||||
</span>
|
||||
|
||||
<h1 className="font-heading text-4xl font-bold leading-[1.05] tracking-tight text-[var(--sea-ink)] sm:text-5xl lg:text-6xl">
|
||||
Eine Webseite, die{" "}
|
||||
<span className="text-[var(--lagoon-deep)]">neue Kunden bringt</span> - nicht nur gut aussieht.
|
||||
</h1>
|
||||
<h1 className="font-heading text-4xl font-bold leading-[1.05] tracking-tight text-[var(--sea-ink)] sm:text-5xl lg:text-6xl">
|
||||
Eine Webseite, die{" "}
|
||||
<span className="text-[var(--lagoon-deep)]">neue Kunden bringt</span> - nicht nur gut aussieht.
|
||||
</h1>
|
||||
|
||||
<p className="max-w-xl text-lg text-[var(--sea-ink-soft)] sm:text-xl">
|
||||
Damit neue Kunden Sie zuerst finden - nicht die Konkurrenz in der Nachbarschaft.
|
||||
</p>
|
||||
<p className="max-w-xl text-lg text-[var(--sea-ink-soft)] sm:text-xl">
|
||||
Damit neue Kunden Sie zuerst finden - nicht die Konkurrenz in der Nachbarschaft.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<BookingDialog>
|
||||
<Button data-type="accent" size="lg" className="gap-2">
|
||||
Erstgespräch buchen
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</BookingDialog>
|
||||
<Button variant="ghost" size="lg" asChild>
|
||||
<a href="#pricing" className="no-underline">Pakete ansehen</a>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<BookingDialog>
|
||||
<Button
|
||||
data-type="accent"
|
||||
size="lg"
|
||||
className="gap-2"
|
||||
data-track="cta_erstgespraech"
|
||||
data-source="hero"
|
||||
>
|
||||
Erstgespräch buchen
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-(--sea-ink-soft)">
|
||||
30 Minuten, kostenlos - direkt online buchen, keine Warteschleifen.
|
||||
</p>
|
||||
|
||||
<ul className="flex flex-wrap items-center gap-x-6 gap-y-3 pt-2 text-sm text-[var(--sea-ink-soft)]">
|
||||
{trustPoints.map(({ icon: Icon, label }) => (
|
||||
<li key={label} className="inline-flex items-center gap-2">
|
||||
<Icon className="h-4 w-4 text-[var(--lagoon-deep)]" aria-hidden />
|
||||
{label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</BookingDialog>
|
||||
<Button variant="ghost" size="lg" asChild>
|
||||
<a
|
||||
href="#pricing"
|
||||
className="no-underline"
|
||||
data-track="cta_pakete_ansehen"
|
||||
data-source="hero"
|
||||
>
|
||||
Pakete ansehen
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-sm text-(--sea-ink-soft)">
|
||||
30 Minuten, kostenlos - direkt online buchen, keine Warteschleifen.
|
||||
</p>
|
||||
|
||||
<figure className="island-shell relative overflow-hidden rounded-3xl">
|
||||
<img
|
||||
className="block h-full w-full object-cover"
|
||||
src="https://placehold.co/400x600"
|
||||
alt="Aufgeräumter Schreibtisch aus Holz mit Laptop"
|
||||
width="400"
|
||||
height="600"
|
||||
loading="eager"
|
||||
/>
|
||||
</figure>
|
||||
<ul className="flex flex-wrap items-center gap-x-6 gap-y-3 pt-2 text-sm text-[var(--sea-ink-soft)]">
|
||||
{trustPoints.map(({ icon: Icon, label }) => (
|
||||
<li key={label} className="inline-flex items-center gap-2">
|
||||
<Icon className="h-4 w-4 text-[var(--lagoon-deep)]" aria-hidden />
|
||||
{label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
+279
-17
@@ -1,3 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { Check, Sparkles } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button.tsx";
|
||||
import { BookingDialog } from "@/components/booking-dialog.tsx";
|
||||
@@ -5,6 +6,7 @@ import { BookingDialog } from "@/components/booking-dialog.tsx";
|
||||
type PricingPlan = {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
description: string;
|
||||
price: number;
|
||||
features: string[];
|
||||
@@ -12,16 +14,26 @@ type PricingPlan = {
|
||||
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: 900,
|
||||
price: 1800,
|
||||
features: [
|
||||
"Alle wichtigen Infos auf einer Seite",
|
||||
"Kontaktformular & Anfahrt",
|
||||
"Für Smartphone optimiert",
|
||||
"SSL-Verschlüsselung inklusive",
|
||||
"SEO-Grundlagen (Meta-Tags, schnelle Ladezeit)",
|
||||
@@ -31,40 +43,60 @@ const offerlist: PricingPlan[] = [
|
||||
{
|
||||
id: 2,
|
||||
name: "Das Klassik-Paket",
|
||||
slug: "klassik",
|
||||
description:
|
||||
"Für Praxen und Betriebe mit breitem Angebot. Maximale Übersicht für Ihre Besucher.",
|
||||
price: 1700,
|
||||
price: 2500,
|
||||
features: [
|
||||
"Separate Leistungs- und Teamseiten",
|
||||
"Bildergalerie & Referenzen",
|
||||
"Erweiterte SEO-Optimierung",
|
||||
"Rechtstexte-Assistenz",
|
||||
"3 Monate kostenloser Support",
|
||||
],
|
||||
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: 3,
|
||||
name: "Ihre neue Webapp",
|
||||
description:
|
||||
"Für digitale Lösungen mit echtem Mehrwert. Ideal für Unternehmen, die mehr als eine Website brauchen.",
|
||||
price: 2500,
|
||||
id: 2,
|
||||
name: "Standard",
|
||||
description: "",
|
||||
priceMonthly: 149,
|
||||
priceYearly: 1490,
|
||||
features: [
|
||||
"Login- und Nutzerbereich",
|
||||
"Individuelle Funktionen",
|
||||
"Terminbuchung oder Kundenportal",
|
||||
"SEO-Grundlagen (Meta-Tags, schnelle Ladezeit)",
|
||||
"Skalierbare Infrastruktur",
|
||||
"Alles aus Basis",
|
||||
"1 Stunde pro Monat für inhaltliche Pflege, z. B. neue Texte, Bilder, Teammitglieder",
|
||||
"Monatlicher Performance-Report zu Besucherzahlen und Analytics",
|
||||
],
|
||||
priceNote: "Endpreis abhängig vom Funktionsumfang - Details im Erstgespräch.",
|
||||
},
|
||||
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>
|
||||
@@ -77,7 +109,7 @@ export const Pricing = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 grid gap-6 lg:grid-cols-3 lg:items-stretch">
|
||||
<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 (
|
||||
@@ -132,6 +164,8 @@ export const Pricing = () => {
|
||||
<Button
|
||||
variant={isHighlighted ? "default" : "outline"}
|
||||
className="w-full"
|
||||
data-track="cta_erstgespraech"
|
||||
data-source={`pricing_${offer.slug}`}
|
||||
>
|
||||
Erstgespräch buchen
|
||||
</Button>
|
||||
@@ -141,6 +175,234 @@ export const Pricing = () => {
|
||||
);
|
||||
})}
|
||||
</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-white 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-white 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Header from "@/components/header.tsx";
|
||||
import { Footer } from "@/components/footer.tsx";
|
||||
|
||||
export function PublicShell({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<a
|
||||
href="#main"
|
||||
className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-(--lagoon-deep) focus:px-4 focus:py-2 focus:font-medium focus:text-white focus:shadow-lg"
|
||||
>
|
||||
Direkt zum Hauptinhalt springen
|
||||
</a>
|
||||
<Header />
|
||||
<main id="main" tabIndex={-1} className="flex-1 outline-none">
|
||||
{children}
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user