Alle Templates
Templates/Medical Clinic
A restrained full-page template for medical practices. Clear typography, plenty of whitespace and gentle motion. A mega menu, animated key figures and a reviews marquee are already included.
Medical & HealthKomplette Seite
Die fertige Seite zum Durchscrollen, umschaltbar zwischen Desktop, Tablet und Handy.
Quellcode
Die komplette Seite als eine Datei. Kopiere sie nach ./components und binde die Komponente in deine Route ein.
import {
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
type ElementType,
type TouchEvent,
} from "react";
import {
motion,
useMotionValue,
useSpring,
useTransform,
useReducedMotion,
type Variants,
} from "framer-motion";
import {
MapPin,
ArrowRight,
CalendarDays,
Smile,
Stethoscope,
Sparkles,
Star,
ArrowUpRight,
Check,
Award,
Building2,
ChevronDown,
Menu,
X,
Phone,
Mail,
Instagram,
Linkedin,
Facebook,
} from "lucide-react";
/**
* Praxisklinik am Park - neutral, fictional template for a specialist clinic
* for oral, maxillofacial and aesthetic facial surgery.
*
* Brand, medical lead, address, phone, email and review portals are entirely
* fictional. No real photos of people are used: all images are neutral
* placeholders from /public/heros. External links point to "#". Self-contained
* via a scoped <style> block.
*/
const BRAND = "Praxisklinik am Park";
const SUBBRAND = "The Clinic";
const DOCTOR = "Dr. Julia Bergmann, MD";
const IMG = {
hero: "/heros/realistic-close-up-portrait-woman.webp",
doctor: "/heros/hyperrealistic-photo-of-a-young-professional-sitting.webp",
avatar1: "/heros/close-up-portrait-of-a-womans-face.webp",
avatar2: "/heros/close-up-smiling-face-wet.webp",
avatar3: "/heros/realistic-close-up-portrait-woman.webp",
implant: "/heros/dental-smile.webp",
mkg: "/heros/skin-macro-shot-golden-hour-lighting-soft.webp",
ästhetik: "/heros/beauty-eyes.webp",
galleryOffice: "/heros/bright-scandinavian-home-office-corner-open.webp",
galleryLounge: "/heros/modern-living-room-with-white-walls-featuring.webp",
galleryGarden: "/heros/architecture-garden.webp",
galleryFacade: "/heros/timeless-european-apartment-building-soft.webp",
};
const scopedStyles = `
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500&family=Figtree:wght@300;400;500;600&display=swap');
.praxisklinik-park {
--ink: oklch(0.27 0.005 250);
--ink-hover: oklch(0.33 0.006 250);
--ink-soft: oklch(0.6 0.005 250);
--ink-70: oklch(0.27 0.005 250 / 0.7);
--ink-60: oklch(0.27 0.005 250 / 0.6);
--ink-55: oklch(0.27 0.005 250 / 0.55);
--ink-35: oklch(0.27 0.005 250 / 0.35);
--ink-10: oklch(0.27 0.005 250 / 0.1);
--ink-85: oklch(0.27 0.005 250 / 0.85);
--ink-40: oklch(0.27 0.005 250 / 0.4);
--ink-90: oklch(0.27 0.005 250 / 0.9);
--font-heading: "Manrope", ui-sans-serif, system-ui, sans-serif;
--font-sans: "Figtree", ui-sans-serif, system-ui, sans-serif;
font-family: var(--font-sans);
color: var(--ink);
}
.praxisklinik-park .font-heading { font-family: var(--font-heading); }
@layer base {
.praxisklinik-park h1,
.praxisklinik-park h2,
.praxisklinik-park h3,
.praxisklinik-park h4,
.praxisklinik-park h5,
.praxisklinik-park h6 {
font-family: var(--font-heading);
font-weight: 500;
letter-spacing: -0.02em;
}
}
.praxisklinik-park .ppk-overlay {
background-image: linear-gradient(to top, var(--ink-85) 0%, var(--ink-40) 50%, var(--ink-10) 100%);
transition: background-image 900ms cubic-bezier(0.22, 1, 0.36, 1);
}
.praxisklinik-park .group:hover .ppk-overlay {
background-image: linear-gradient(to top, var(--ink-90) 0%, var(--ink-55) 55%, var(--ink-10) 100%);
}
@keyframes ppk-marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (min-width: 768px) {
.praxisklinik-park .ppk-marquee { animation: ppk-marquee 40s linear infinite; }
.praxisklinik-park .ppk-marquee:hover { animation-play-state: paused; }
}
@media (prefers-reduced-motion: reduce) {
.praxisklinik-park .ppk-marquee { animation: none; }
}
`;
/* ------------------------------------------------------------------ */
/* Motion helpers */
/* ------------------------------------------------------------------ */
const revealVariants: Variants = {
hidden: { opacity: 0, y: 32 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.9, ease: [0.22, 1, 0.36, 1] },
},
};
function Reveal({
children,
delay = 0,
className,
as: As = "div",
}: {
children: ReactNode;
delay?: number;
className?: string;
as?: ElementType;
}) {
const reduce = useReducedMotion();
const MotionTag = useMemo(() => motion.create(As), [As]);
if (reduce) {
const Tag = As as ElementType;
return <Tag className={className}>{children}</Tag>;
}
return (
<MotionTag
className={className}
initial="hidden"
whileInView="visible"
viewport={{ once: true, margin: "-80px" }}
variants={revealVariants}
transition={{ delay }}
>
{children}
</MotionTag>
);
}
function CountUp({
end,
duration = 1800,
suffix = "",
prefix = "",
decimals = 0,
locale = "en-US",
}: {
end: number;
duration?: number;
suffix?: string;
prefix?: string;
decimals?: number;
locale?: string;
}) {
const reduce = useReducedMotion();
const [value, setValue] = useState(0);
const ref = useRef<HTMLSpanElement | null>(null);
const started = useRef(false);
useEffect(() => {
const node = ref.current;
if (!node) return;
if (reduce) {
setValue(end);
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !started.current) {
started.current = true;
const startTime = performance.now();
const tick = (now: number) => {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = progress === 1 ? 1 : 1 - Math.pow(2, -10 * progress);
setValue(end * eased);
if (progress < 1) requestAnimationFrame(tick);
else setValue(end);
};
requestAnimationFrame(tick);
}
});
},
{ threshold: 0.4 },
);
observer.observe(node);
return () => observer.disconnect();
}, [end, duration, reduce]);
const formatted = value.toLocaleString(locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
});
return (
<span ref={ref}>
{prefix}
{formatted}
{suffix}
</span>
);
}
/* ------------------------------------------------------------------ */
/* Logo (neutral mark) */
/* ------------------------------------------------------------------ */
function Logo({ className }: { className?: string }) {
return (
<svg
viewBox="0 0 40 40"
role="img"
aria-label={BRAND}
className={className}
fill="none"
stroke="currentColor"
>
<circle cx="20" cy="20" r="15" strokeWidth="1.75" />
<path d="M13 18.5a7 7 0 0 0 14 0" strokeWidth="1.75" strokeLinecap="round" />
<circle cx="15" cy="16" r="0.9" fill="currentColor" stroke="none" />
<circle cx="25" cy="16" r="0.9" fill="currentColor" stroke="none" />
</svg>
);
}
/* ------------------------------------------------------------------ */
/* Navbar + Mega menu */
/* ------------------------------------------------------------------ */
const navItems = [
{ label: "The Clinic", href: "#", hasMega: false },
{ label: "Treatments", href: "#", hasMega: true },
{ label: "Patient Services", href: "#", hasMega: false },
{ label: "For Referrers", href: "#", hasMega: false },
];
const leistungenGroups = [
{
title: "Implantology",
items: ["Single Implants", "All on 4", "Bone Grafting", "Immediate Implants"],
},
{
title: "Oral Surgery",
items: ["Wisdom Teeth", "Jaw Surgery", "Cyst Removal", "Trauma Care"],
},
{
title: "Aesthetics",
items: ["Botulinum Toxin", "Dermal Fillers", "Eyelid Lift", "PRP Therapy"],
},
];
const megaColumns = [
{
title: "Implantology",
teaser: "Fixed restorations with a natural look and a real gain in quality of life.",
image: IMG.implant,
items: ["Single Implants", "All on 4", "Bone Grafting", "Immediate Implants"],
},
{
title: "Oral Surgery",
teaser: "Precise procedures on the mouth, jaw and face, performed by experienced hands.",
image: IMG.mkg,
items: ["Wisdom Teeth", "Jaw Surgery", "Cyst Removal", "Trauma Care"],
},
{
title: "Aesthetics",
teaser: "Gentle treatments for a fresh, natural result.",
image: IMG.ästhetik,
items: ["Botulinum Toxin", "Dermal Fillers", "Eyelid Lift", "PRP Therapy"],
},
];
function MegaMenuLeistungen() {
return (
<>
<div
className="pointer-events-none invisible fixed inset-0 top-20 z-30 bg-black/40 opacity-0 transition-opacity duration-300 group-hover:visible group-hover:opacity-100"
aria-hidden="true"
/>
<div className="invisible absolute left-0 right-0 top-full z-40 overflow-hidden rounded-b-3xl border-t border-gray-200 bg-white opacity-0 transition-all duration-200 group-hover:visible group-hover:opacity-100">
<div className="mx-auto w-full max-w-[1600px]">
<div className="flex flex-col lg:flex-row">
<div className="grid flex-1 grid-cols-1 md:grid-cols-3">
{megaColumns.map((col, i) => (
<div
key={col.title}
className={`group/col flex cursor-pointer flex-col gap-6 px-8 py-10 ${
i < megaColumns.length - 1 ? "md:border-r md:border-gray-100" : ""
}`}
>
<div className="aspect-[16/10] w-full overflow-hidden rounded-2xl bg-gray-100">
<img
src={col.image}
alt=""
width={640}
height={360}
loading="lazy"
className="h-full w-full object-cover transition-transform duration-700 ease-out group-hover/col:scale-105"
/>
</div>
<div className="space-y-3">
<h3 className="font-heading text-xl leading-tight text-[var(--ink)]">
{col.title}
</h3>
<p className="text-sm leading-relaxed text-[var(--ink-soft)]">{col.teaser}</p>
<nav className="flex flex-col gap-3 pt-3">
{col.items.map((item) => (
<a
key={item}
href="#"
className="group/link flex items-center justify-between text-sm font-medium text-[var(--ink)] transition-colors hover:text-[var(--ink-70)]"
>
{item}
<ArrowRight className="h-3.5 w-3.5 -translate-x-2 opacity-0 transition-all duration-300 group-hover/link:translate-x-0 group-hover/link:opacity-100" />
</a>
))}
</nav>
</div>
</div>
))}
</div>
<aside className="relative w-full shrink-0 overflow-hidden bg-[var(--ink)] px-8 py-10 lg:w-[360px] lg:px-10">
<div className="relative flex h-full flex-col justify-between gap-10">
<div className="space-y-5">
<span className="inline-block rounded-full bg-white/10 px-3 py-1 text-[10px] font-medium uppercase tracking-[0.2em] text-white/80">
Personal Consultation
</span>
<h4 className="font-heading text-2xl leading-tight text-white">
Not sure which treatment is right for you?
</h4>
<p className="text-sm leading-relaxed text-white/70">
Speak directly with our medical team. In a calm first conversation, we work
through every detail of your treatment plan.
</p>
</div>
<a
href="#"
className="group/cta flex w-full items-center justify-between rounded-full bg-white px-7 py-4 text-sm font-medium text-[var(--ink)] transition-colors hover:bg-gray-50"
>
Book an Appointment
<ArrowUpRight className="h-4 w-4 transition-transform duration-300 group-hover/cta:translate-x-1 group-hover/cta:-translate-y-0.5" />
</a>
</div>
</aside>
</div>
</div>
</div>
</>
);
}
function Navbar() {
const [open, setOpen] = useState(false);
const [leistungenOpen, setLeistungenOpen] = useState(false);
useEffect(() => {
if (open) document.body.style.overflow = "hidden";
else document.body.style.overflow = "";
return () => {
document.body.style.overflow = "";
};
}, [open]);
return (
<header className="sticky top-0 z-50 border-b border-gray-200 bg-white/90 backdrop-blur">
<div className="mx-auto flex h-20 max-w-7xl items-center justify-between px-6">
<a href="#" className="flex items-center gap-3" aria-label={BRAND}>
<Logo className="h-7 w-auto text-[var(--ink)]" />
<span className="hidden h-6 w-px bg-gray-200 sm:block" />
<span className="hidden font-heading text-sm text-[var(--ink-soft)] sm:inline">
{SUBBRAND}
</span>
</a>
<nav className="hidden items-center gap-10 lg:flex">
{navItems.map((item) =>
item.hasMega ? (
<div key={item.label} className="group">
<button
type="button"
className="inline-flex items-center gap-1 py-6 text-sm text-[var(--ink-soft)] transition-colors hover:text-[var(--ink)]"
>
{item.label}
<ChevronDown className="h-4 w-4 transition-transform group-hover:rotate-180" />
</button>
<MegaMenuLeistungen />
</div>
) : (
<a
key={item.label}
href={item.href}
className="text-sm text-[var(--ink-soft)] transition-colors hover:text-[var(--ink)]"
>
{item.label}
</a>
),
)}
</nav>
<div className="flex items-center gap-3">
<a
href="#"
className="hidden rounded-full bg-[var(--ink)] px-6 py-2.5 text-sm font-normal text-white transition-colors hover:bg-[var(--ink-hover)] sm:inline-flex"
>
Book Appointment
</a>
<button
type="button"
aria-label={open ? "Close menu" : "Open menu"}
onClick={() => setOpen((v) => !v)}
className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-gray-200 text-[var(--ink-soft)] lg:hidden"
>
{open ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
</div>
</div>
{open && (
<div className="absolute inset-x-0 top-full z-[60] max-h-[calc(100svh-5rem)] min-h-[calc(100svh-5rem)] overflow-y-auto border-t border-gray-100 bg-white lg:hidden">
<nav className="flex flex-col gap-1 px-6 py-8">
{navItems.map((item) =>
item.hasMega ? (
<div key={item.label} className="border-b border-gray-100">
<button
type="button"
onClick={() => setLeistungenOpen((v) => !v)}
aria-expanded={leistungenOpen}
className="flex w-full items-center justify-between py-4 text-lg text-[var(--ink)]"
>
{item.label}
<ChevronDown
className={`h-5 w-5 transition-transform duration-300 ${leistungenOpen ? "rotate-180" : ""}`}
/>
</button>
<div
className={`grid transition-all duration-300 ease-out ${
leistungenOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
}`}
>
<div className="overflow-hidden">
<div className="flex flex-col gap-6 pb-5 pt-1">
{leistungenGroups.map((group) => (
<div key={group.title} className="flex flex-col gap-2">
<p className="text-[11px] font-medium uppercase tracking-[0.18em] text-[var(--ink-soft)]">
{group.title}
</p>
<div className="flex flex-col">
{group.items.map((sub) => (
<a
key={sub}
href="#"
onClick={() => setOpen(false)}
className="py-2 text-base text-[var(--ink)]"
>
{sub}
</a>
))}
</div>
</div>
))}
</div>
</div>
</div>
</div>
) : (
<a
key={item.label}
href={item.href}
onClick={() => setOpen(false)}
className="block border-b border-gray-100 py-4 text-lg text-[var(--ink)]"
>
{item.label}
</a>
),
)}
<a
href="#"
onClick={() => setOpen(false)}
className="mt-8 inline-flex items-center justify-center rounded-full bg-[var(--ink)] px-6 py-3.5 text-sm font-normal text-white"
>
Book Appointment
</a>
</nav>
</div>
)}
</header>
);
}
/* ------------------------------------------------------------------ */
/* Hero */
/* ------------------------------------------------------------------ */
function HeroImageWithPills() {
const reduce = useReducedMotion();
const ref = useRef<HTMLDivElement | null>(null);
const mx = useMotionValue(0);
const my = useMotionValue(0);
const sx = useSpring(mx, { stiffness: 60, damping: 18, mass: 0.8 });
const sy = useSpring(my, { stiffness: 60, damping: 18, mass: 0.8 });
const handleMove = (e: React.MouseEvent<HTMLDivElement>) => {
if (reduce) return;
const rect = ref.current?.getBoundingClientRect();
if (!rect) return;
const x = (e.clientX - rect.left) / rect.width - 0.5;
const y = (e.clientY - rect.top) / rect.height - 0.5;
mx.set(x * 2);
my.set(y * 2);
};
const handleLeave = () => {
mx.set(0);
my.set(0);
};
const pill1X = useTransform(sx, (v) => v * 14);
const pill1Y = useTransform(sy, (v) => v * 10);
const pill2X = useTransform(sx, (v) => v * -22);
const pill2Y = useTransform(sy, (v) => v * 16);
const pill3X = useTransform(sx, (v) => v * 18);
const pill3Y = useTransform(sy, (v) => v * -12);
return (
<div
ref={ref}
onMouseMove={handleMove}
onMouseLeave={handleLeave}
className="group relative overflow-hidden rounded-3xl bg-gray-100"
>
<img
src={IMG.hero}
alt="Patient at Praxisklinik am Park"
width={1024}
height={1280}
loading="eager"
decoding="async"
className="h-[440px] w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105 sm:h-[620px] lg:h-[760px]"
/>
<div className="pointer-events-none absolute inset-0">
<motion.div
style={{ x: pill1X, y: pill1Y }}
className="absolute left-5 top-6 flex items-center gap-2 rounded-full bg-white/85 px-4 py-2 text-xs font-medium text-[var(--ink)] ring-1 ring-white/60 backdrop-blur-md"
>
<Smile className="h-4 w-4 text-[var(--ink)]" strokeWidth={1.75} />
Implantology
</motion.div>
<div className="absolute right-5 top-1/2 -translate-y-1/2">
<motion.div
style={{ x: pill2X, y: pill2Y }}
className="flex items-center gap-2 rounded-full bg-white/85 px-4 py-2 text-xs font-medium text-[var(--ink)] ring-1 ring-white/60 backdrop-blur-md"
>
<Stethoscope className="h-4 w-4 text-[var(--ink)]" strokeWidth={1.75} />
Oral Surgery
</motion.div>
</div>
<div className="absolute bottom-6 left-1/2 -translate-x-1/2">
<motion.div
style={{ x: pill3X, y: pill3Y }}
className="flex items-center gap-2 rounded-full bg-white/85 px-4 py-2 text-xs font-medium text-[var(--ink)] ring-1 ring-white/60 backdrop-blur-md"
>
<Sparkles className="h-4 w-4 text-[var(--ink)]" strokeWidth={1.75} />
Aesthetics
</motion.div>
</div>
</div>
</div>
);
}
const heroAvatars = [IMG.avatar1, IMG.avatar2, IMG.avatar3];
function Hero() {
return (
<section className="bg-white">
<div className="mx-auto grid grid-cols-1 items-center gap-12 px-5 py-16 sm:px-6 sm:py-24 lg:grid-cols-2 lg:gap-20 lg:px-20 lg:py-32">
<Reveal className="flex flex-col items-center text-center lg:items-start lg:text-left">
<span className="inline-flex w-fit items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-1.5 text-xs tracking-wide text-[var(--ink-60)]">
<MapPin className="h-3.5 w-3.5 text-[var(--ink)]" strokeWidth={1.75} />
{BRAND}
</span>
<h1 className="mt-6 text-4xl leading-[1.1] text-[var(--ink)] sm:mt-8 sm:text-5xl lg:text-7xl lg:leading-[1.05]">
<span className="block">A smile that</span>
<span className="block sm:hidden">feels like you</span>
<span className="hidden sm:block">feels entirely</span>
<span className="hidden sm:block">like you</span>
</h1>
<p className="mt-6 max-w-xl text-base leading-relaxed text-[var(--ink-soft)] sm:mt-8 sm:text-lg">
A specialist clinic for oral, maxillofacial and aesthetic facial surgery in the park
district.
</p>
<div className="mt-8 flex flex-wrap items-center justify-center gap-3 sm:mt-10 sm:gap-4 lg:justify-start">
<a
href="#leistungen"
className="group inline-flex items-center justify-center gap-0 rounded-full bg-[var(--ink)] px-6 py-3.5 text-sm font-normal text-white transition-all hover:gap-2 hover:bg-[var(--ink-hover)] sm:px-8 sm:py-4"
>
Explore Treatments
<ArrowRight
className="h-4 w-4 max-w-0 -translate-x-1 opacity-0 transition-all duration-300 ease-out group-hover:max-w-[1rem] group-hover:translate-x-0 group-hover:opacity-100"
strokeWidth={1.75}
/>
</a>
<a
href="#"
className="group inline-flex items-center justify-center gap-0 rounded-full border border-gray-200 bg-white px-6 py-3.5 text-sm font-normal text-[var(--ink)] transition-all hover:gap-2 hover:bg-gray-50 sm:px-8 sm:py-4"
>
Book an Appointment
<CalendarDays
className="h-4 w-4 max-w-0 -translate-x-1 opacity-0 transition-all duration-300 ease-out group-hover:max-w-[1rem] group-hover:translate-x-0 group-hover:opacity-100"
strokeWidth={1.75}
/>
</a>
</div>
<div className="mt-12 flex flex-wrap items-center justify-center gap-5 lg:justify-start">
<div className="flex -space-x-2">
{heroAvatars.map((src, i) => (
<img
key={i}
src={src}
alt=""
width={40}
height={40}
loading="lazy"
className="h-10 w-10 rounded-full border-2 border-white object-cover"
/>
))}
</div>
<div className="flex flex-col items-center lg:items-start">
<div className="flex items-center gap-1 text-[var(--ink)]">
{Array.from({ length: 5 }).map((_, i) => (
<Star key={i} className="h-4 w-4 fill-current" strokeWidth={0} />
))}
<span className="ml-2 text-sm font-medium text-[var(--ink)]">4.9 / 5</span>
</div>
<p className="mt-1 text-sm text-[var(--ink-soft)]">
from over 380 reviews on independent portals
</p>
</div>
</div>
</Reveal>
<Reveal delay={0.15} className="relative">
<HeroImageWithPills />
</Reveal>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Doctor intro */
/* ------------------------------------------------------------------ */
const doctorStats = [
{ end: 15, suffix: "+", label: "Years of experience" },
{ end: 8000, suffix: "+", label: "Procedures" },
{ end: 100, suffix: "%", label: "Private practice" },
];
function DoctorIntro() {
return (
<section className="bg-[#f9fafb]">
<div className="mx-auto grid max-w-7xl grid-cols-1 gap-12 px-5 py-20 sm:gap-8 sm:px-6 sm:py-24 lg:grid-cols-12 lg:gap-16 lg:py-32">
<Reveal className="order-1 lg:order-2 lg:col-span-7">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
Personal. Precise. Discreet.
</span>
<h2 className="mt-4 text-[28px] leading-tight text-[var(--ink)] sm:text-4xl lg:text-5xl">
{DOCTOR}
</h2>
<p className="mt-3 text-base text-[var(--ink-soft)] sm:text-lg">
Specialist in oral, maxillofacial and facial surgery
</p>
<div className="mt-8 max-w-xl space-y-5 text-[15px] leading-relaxed text-[var(--ink-soft)] sm:mt-8 sm:text-base">
<p>
A good result takes calm. I take the time to talk things through and plan every
procedure so it fits your face and your everyday life.
</p>
<p>
After many years in implantology, oral surgery and aesthetic facial surgery, I know
the difference between a lot and enough. My aim is a result that no one notices,
except you.
</p>
</div>
<div className="mt-10 hidden lg:block">
<a
href="#praxis"
className="inline-flex items-center justify-center rounded-full border border-gray-200 bg-white px-7 py-3 text-sm font-medium text-[var(--ink)] transition-colors hover:bg-gray-50"
>
More about the clinic
</a>
</div>
</Reveal>
<Reveal delay={0.05} className="order-2 lg:order-3 lg:col-span-7">
<div className="grid grid-cols-3 divide-x divide-gray-200 pt-2 sm:gap-10 sm:divide-x-0 sm:border-t sm:border-gray-200 sm:pt-8">
{doctorStats.map((s) => (
<div key={s.label} className="px-3 text-center sm:px-0 sm:text-left">
<div className="font-heading text-2xl font-medium tabular-nums text-[var(--ink)] sm:text-3xl">
<CountUp end={s.end} suffix={s.suffix} />
</div>
<div className="mt-1 text-[11px] leading-tight text-[var(--ink-70)] sm:text-sm">
{s.label}
</div>
</div>
))}
</div>
<div className="mt-10 lg:hidden">
<a
href="#praxis"
className="inline-flex w-full items-center justify-center rounded-full border border-gray-200 bg-white px-7 py-3 text-sm font-medium text-[var(--ink)] transition-colors hover:bg-gray-50 sm:w-auto"
>
More about the clinic
</a>
</div>
</Reveal>
<Reveal delay={0.1} className="order-3 lg:order-1 lg:col-span-5 lg:row-span-2">
<div className="group relative overflow-hidden rounded-3xl bg-gray-100">
<img
src={IMG.doctor}
alt={DOCTOR}
width={1024}
height={1280}
loading="lazy"
className="aspect-[4/5] w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105 lg:aspect-[3/4]"
/>
<span className="absolute left-4 top-4 inline-flex items-center rounded-full border border-white/60 bg-white/80 px-4 py-1.5 text-xs tracking-wide text-[var(--ink)] backdrop-blur sm:left-5 sm:top-5">
Your doctor
</span>
</div>
</Reveal>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Services bento */
/* ------------------------------------------------------------------ */
const servicePanels = [
{
kicker: "Implantology",
title: "Fixed teeth that last.",
body: "Ceramic or titanium implants, planned on a 3D scan and placed gently. For a bite you never have to think about again.",
image: IMG.implant,
},
{
kicker: "Oral Surgery",
title: "Surgery with a steady hand",
body: "Wisdom teeth, jaw corrections, cysts and injuries. Treated with experience, modern equipment and clear aftercare.",
image: IMG.mkg,
},
{
kicker: "Aesthetics",
title: "The same you, just fresher",
body: "Botulinum toxin, dermal fillers and small procedures, used sparingly. So you look the way you do on a good day.",
image: IMG.ästhetik,
},
];
function ServicesBento() {
return (
<section id="leistungen" className="bg-white">
<div className="mx-auto max-w-7xl px-5 py-16 sm:px-6 sm:py-24 lg:py-32">
<Reveal>
<div className="grid grid-cols-1 items-end gap-10 lg:grid-cols-12">
<div className="lg:col-span-7">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
Our Treatments
</span>
<h2 className="mt-5 text-3xl text-[var(--ink)] sm:text-4xl lg:text-5xl">
Three focus areas, one standard
</h2>
</div>
<p className="text-base text-[var(--ink-soft)] lg:col-span-5">
Implantology, oral surgery and aesthetic facial surgery under one roof. Calm, careful
and tailored to your situation.
</p>
</div>
</Reveal>
<Reveal delay={0.1}>
<div className="mt-10 flex flex-col gap-3 sm:mt-14 md:h-[560px] md:flex-row lg:h-[620px]">
{servicePanels.map((p) => (
<article
key={p.kicker}
style={{ transitionTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)" }}
className="group relative h-[420px] flex-1 overflow-hidden rounded-3xl transition-[flex-grow] duration-[900ms] hover:md:flex-grow-[2.4] md:h-auto"
>
<img
src={p.image}
alt={p.kicker}
loading="lazy"
className="absolute inset-0 h-full w-full object-cover transition-transform duration-[1600ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:scale-[1.06]"
/>
<div className="ppk-overlay absolute inset-0" />
<div className="relative flex h-full flex-col justify-between p-7 text-white lg:p-10">
<div className="flex items-start justify-end">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-full border border-white/40 text-white opacity-0 transition-opacity duration-500 group-hover:opacity-100">
<ArrowUpRight className="h-4 w-4" />
</span>
</div>
<div>
<div className="text-[11px] font-light uppercase tracking-[0.3em] text-white/55">
{p.kicker}
</div>
<h3 className="mt-4 max-w-md font-heading text-2xl leading-tight text-white lg:text-[2rem]">
{p.title}
</h3>
<div className="grid grid-rows-[1fr] transition-[grid-template-rows] duration-[1100ms] ease-[cubic-bezier(0.22,1,0.36,1)] md:grid-rows-[0fr] md:group-hover:grid-rows-[1fr]">
<div className="overflow-hidden">
<p className="mt-5 max-w-md text-[13px] font-light leading-relaxed text-white/75 transition-all duration-[800ms] ease-[cubic-bezier(0.22,1,0.36,1)] md:translate-y-2 md:opacity-0 md:group-hover:translate-y-0 md:group-hover:opacity-100 md:group-hover:delay-200 lg:text-sm">
{p.body}
</p>
</div>
</div>
</div>
</div>
</article>
))}
</div>
</Reveal>
<Reveal delay={0.2}>
<div className="relative mt-6 flex flex-col items-start justify-between gap-6 overflow-hidden rounded-3xl bg-[#f9fafb] p-6 sm:flex-row sm:items-center sm:p-8 lg:p-10">
<div className="relative">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
Appointment
</span>
<h3 className="mt-3 max-w-xl text-xl text-[var(--ink)] sm:text-2xl lg:text-[1.75rem]">
A first consultation usually within 48 hours. In person and without a long wait.
</h3>
</div>
<a
href="#"
className="relative inline-flex items-center gap-2 rounded-full bg-[var(--ink)] px-7 py-3.5 text-sm font-normal text-white transition-colors hover:bg-[var(--ink-hover)]"
>
Book an Appointment
<ArrowUpRight className="h-4 w-4" />
</a>
</div>
</Reveal>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Clinic gallery */
/* ------------------------------------------------------------------ */
const galleryImages = [
{ src: IMG.galleryOffice, alt: "Bright reception area by the window", aspect: "aspect-[4/3]" },
{ src: IMG.galleryLounge, alt: "Open waiting and lounge area", aspect: "aspect-[4/3]" },
{ src: IMG.implant, alt: "Treatment in a calm setting", aspect: "aspect-[4/3]" },
{ src: IMG.galleryGarden, alt: "Clinic building with garden grounds", aspect: "aspect-[4/3]" },
{ src: IMG.galleryFacade, alt: "Facade in the park district", aspect: "aspect-[3/2]" },
{ src: IMG.hero, alt: "A relaxed moment before treatment", aspect: "aspect-[3/2]" },
];
function PraxisEinblicke() {
const [activeIndex, setActiveIndex] = useState(0);
const touchStartX = useRef<number | null>(null);
const handleTouchStart = (event: TouchEvent<HTMLDivElement>) => {
touchStartX.current = event.touches[0]?.clientX ?? null;
};
const handleTouchEnd = (event: TouchEvent<HTMLDivElement>) => {
if (touchStartX.current === null) return;
const endX = event.changedTouches[0]?.clientX;
if (endX === undefined) return;
const swipeDistance = endX - touchStartX.current;
if (Math.abs(swipeDistance) >= 40) {
setActiveIndex((current) => {
if (swipeDistance < 0) return Math.min(current + 1, galleryImages.length - 1);
return Math.max(current - 1, 0);
});
}
touchStartX.current = null;
};
return (
<section id="praxis" className="overflow-x-hidden bg-[#f9fafb]">
<div className="mx-auto max-w-7xl px-5 py-16 sm:px-6 sm:py-24 lg:py-32">
<Reveal className="grid grid-cols-1 items-end gap-10 lg:grid-cols-12">
<div className="lg:col-span-7">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
A Look Inside
</span>
<h2 className="mt-5 text-3xl text-[var(--ink)] sm:text-4xl lg:text-5xl">
A place you are glad to arrive at
</h2>
</div>
<p className="max-w-md text-base leading-relaxed text-[var(--ink-soft)] lg:col-span-5">
Bright rooms, a calm atmosphere and a team that takes its time. So a visit feels less
like an appointment.
</p>
</Reveal>
{/* Mobile slider */}
<div
className="mt-10 touch-pan-y overflow-hidden overscroll-x-contain md:hidden"
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
<div
className="flex will-change-transform"
style={{
transform: `translate3d(-${activeIndex * 100}%, 0, 0)`,
transition: "transform 420ms cubic-bezier(0.22, 1, 0.36, 1)",
}}
>
{galleryImages.map((img, i) => (
<div key={i} className="w-full flex-shrink-0 px-1">
<div className="group overflow-hidden rounded-3xl bg-gray-100">
<img
src={img.src}
alt={img.alt}
loading="lazy"
className={`${img.aspect} w-full object-cover`}
/>
</div>
</div>
))}
</div>
</div>
<div className="mt-4 flex justify-center gap-2 md:hidden">
{galleryImages.map((_, i) => (
<button
key={i}
onClick={() => setActiveIndex(i)}
className={`h-2 rounded-full transition-all duration-300 ${
i === activeIndex
? "w-6 bg-[var(--ink)]"
: "w-2 bg-[var(--ink-10)] hover:bg-[var(--ink-40)]"
}`}
aria-label={`Image ${i + 1}`}
/>
))}
</div>
{/* Desktop grid */}
<div className="mt-10 hidden grid-cols-12 gap-3 sm:mt-16 sm:gap-4 md:grid lg:gap-6">
<Reveal className="col-span-12 md:col-span-8">
<div className="group overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[0].src}
alt={galleryImages[0].alt}
loading="lazy"
className="aspect-[4/3] w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
<Reveal delay={0.1} className="col-span-12 md:col-span-4">
<div className="group h-full overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[1].src}
alt={galleryImages[1].alt}
loading="lazy"
className="h-full w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
<Reveal delay={0.05} className="col-span-12 md:col-span-4">
<div className="group h-full overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[2].src}
alt={galleryImages[2].alt}
loading="lazy"
className="h-full w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
<Reveal delay={0.15} className="col-span-12 md:col-span-8">
<div className="group overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[3].src}
alt={galleryImages[3].alt}
loading="lazy"
className="aspect-[4/3] w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
<Reveal delay={0.1} className="col-span-12 md:col-span-7">
<div className="group overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[4].src}
alt={galleryImages[4].alt}
loading="lazy"
className="aspect-[3/2] w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
<Reveal delay={0.2} className="col-span-12 md:col-span-5">
<div className="group h-full overflow-hidden rounded-3xl bg-gray-100">
<img
src={galleryImages[5].src}
alt={galleryImages[5].alt}
loading="lazy"
className="h-full w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105"
/>
</div>
</Reveal>
</div>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Stats (Why) */
/* ------------------------------------------------------------------ */
const statItems = [
{
icon: Award,
kicker: "Experience",
title: "Many years alongside our patients",
body: "Thousands of procedures in implantology, oral and aesthetic facial surgery, carefully planned and calmly performed.",
},
{
icon: Building2,
kicker: "The Clinic",
title: "Modern rooms with an in-house surgical suite",
body: "3D diagnostics, digital implant planning and a dedicated procedure room, so every treatment is safe and comfortable.",
},
{
icon: Sparkles,
kicker: "Focus",
title: "Specialised in demanding cases",
body: "Immediate implants, complex restorations and refined aesthetic results, always with a natural standard.",
},
];
function Stats() {
return (
<section className="bg-white">
<div className="mx-auto max-w-7xl px-5 py-16 sm:px-6 sm:py-20 lg:py-28">
<Reveal>
<div className="mx-auto max-w-2xl text-center">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
Why {BRAND}
</span>
<h2 className="mt-5 text-2xl text-[var(--ink)] sm:text-3xl lg:text-4xl">
Three reasons that speak for themselves
</h2>
</div>
</Reveal>
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-14 sm:gap-6 md:grid-cols-3">
{statItems.map((item, i) => {
const Icon = item.icon;
return (
<Reveal key={item.kicker} delay={i * 0.08}>
<div
className="group relative h-auto overflow-hidden rounded-3xl bg-[#fafafa] md:h-[340px]"
style={{ transitionTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)" }}
>
<div
className="relative flex flex-col items-center gap-6 p-8 text-center transition-all duration-[900ms] ease-[cubic-bezier(0.22,1,0.36,1)] md:absolute md:inset-0 md:justify-between md:group-hover:-translate-y-3 md:group-hover:opacity-0 lg:p-10"
style={{ backgroundColor: "#fafafa" }}
>
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-white text-[var(--ink)] transition-transform duration-[900ms] ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:-translate-y-1">
<Icon className="h-5 w-5" strokeWidth={1.5} />
</div>
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
{item.kicker}
</span>
<h3 className="mt-4 text-xl text-[var(--ink)] sm:text-2xl lg:text-[1.625rem] lg:leading-snug">
{item.title}
</h3>
</div>
<p className="text-sm leading-relaxed text-[var(--ink-soft)] md:hidden">
{item.body}
</p>
</div>
<div className="absolute inset-0 hidden translate-y-4 flex-col items-center justify-between rounded-3xl bg-[var(--ink)] p-8 text-center text-white opacity-0 transition-all duration-[900ms] ease-[cubic-bezier(0.22,1,0.36,1)] md:flex md:group-hover:translate-y-0 md:group-hover:opacity-100 lg:p-10">
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-white/10 text-white">
<Icon className="h-5 w-5" strokeWidth={1.5} />
</div>
<p className="text-base leading-relaxed text-white/85">{item.body}</p>
<span className="text-xs uppercase tracking-[0.2em] text-white/60">
{item.kicker}
</span>
</div>
</div>
</Reveal>
);
})}
</div>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Reviews */
/* ------------------------------------------------------------------ */
function StarBadge({ className = "" }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" className={className} aria-label="Review portal" fill="none">
<rect width="24" height="24" rx="6" fill="var(--ink)" />
<path
d="M12 6l1.7 3.5 3.8.5-2.8 2.7.7 3.8L12 14.9 8.6 16.5l.7-3.8-2.8-2.7 3.8-.5L12 6z"
fill="#fff"
/>
</svg>
);
}
function CheckBadge({ className = "" }: { className?: string }) {
return (
<svg viewBox="0 0 24 24" className={className} aria-label="Patient portal" fill="none">
<rect width="24" height="24" rx="6" fill="var(--ink-soft)" />
<path
d="M7.5 12.5l3 3 6-6.5"
stroke="#fff"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
const reviews = [
{
quote:
"Finally someone who listens. The consultation was thorough, everything was explained calmly, and the result looks completely natural.",
author: "Sabine M.",
treatment: "Implant Restoration",
source: "score",
},
{
quote:
"Calm, professional and honest. The wisdom tooth surgery was painless, and I was back on my feet after two days.",
author: "Markus T.",
treatment: "Wisdom Teeth",
source: "portal",
},
{
quote: "I look like myself, just more rested. A subtle result and a genuinely pleasant team.",
author: "Carolin B.",
treatment: "Aesthetic Treatment",
source: "score",
},
{
quote: "Not a sales pitch, but an honest plan for my smile. Today I wear it with ease.",
author: "Julia R.",
treatment: "Immediate Implant",
source: "score",
},
{
quote:
"Everything from check-in to aftercare was well thought through. You really feel in good hands here.",
author: "Andreas K.",
treatment: "Bone Grafting",
source: "portal",
},
];
function Reviews() {
const loop = [...reviews, ...reviews];
return (
<section className="bg-white">
<div className="mx-auto px-5 py-16 sm:px-6 sm:py-24 lg:px-12 lg:py-32">
<div className="grid grid-cols-1 gap-12 lg:grid-cols-12 lg:gap-16">
<Reveal className="lg:col-span-4">
<div className="lg:sticky lg:top-28">
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
Reviews
</span>
<h2 className="mt-5 text-3xl text-[var(--ink)] sm:text-4xl lg:text-5xl">
What our patients say
</h2>
<p className="mt-6 max-w-sm text-base leading-relaxed text-[var(--ink-soft)]">
Over 380 reviews on independent portals. We thank our patients for their trust.
</p>
<div className="mt-10 flex items-center gap-5">
<div className="flex items-baseline gap-2">
<span className="font-heading text-5xl text-[var(--ink)]">4.9</span>
<span className="text-sm text-[var(--ink-60)]">/ 5</span>
</div>
<div className="h-10 w-px bg-[var(--ink-10)]" />
<div>
<div className="flex items-center gap-1 text-[#f5b400]">
{Array.from({ length: 5 }).map((_, k) => (
<Star key={k} className="h-3.5 w-3.5 fill-current" strokeWidth={0} />
))}
</div>
<div className="mt-1 text-xs text-[var(--ink-60)]">from over 380 reviews</div>
</div>
</div>
</div>
</Reveal>
<div className="lg:col-span-8">
<div className="relative -mx-6 overflow-hidden lg:-mr-12">
<div
aria-hidden
className="pointer-events-none absolute inset-y-0 left-0 z-10 hidden w-16 bg-gradient-to-r from-white to-transparent sm:w-24 md:block"
/>
<div
aria-hidden
className="pointer-events-none absolute inset-y-0 right-0 z-10 hidden w-16 bg-gradient-to-l from-white to-transparent sm:w-24 md:block"
/>
<div className="ppk-marquee flex snap-x snap-mandatory gap-5 overflow-x-auto overscroll-x-contain px-6 pb-4 [-ms-overflow-style:none] [scrollbar-width:none] md:w-max md:snap-none md:overflow-visible [&::-webkit-scrollbar]:hidden">
{loop.map((r, i) => (
<article
key={`${r.author}-${i}`}
className="flex h-full w-[85vw] max-w-[360px] shrink-0 snap-center flex-col rounded-3xl p-10 md:w-[360px] md:snap-none"
style={{ backgroundColor: "#fafafa" }}
>
<div className="flex items-center gap-1 text-[#f5b400]">
{Array.from({ length: 5 }).map((_, k) => (
<Star key={k} className="h-4 w-4 fill-current" strokeWidth={0} />
))}
</div>
<p className="mt-6 text-base leading-relaxed text-[var(--ink-soft)]">
{r.quote}
</p>
<div className="mt-auto pt-8">
<div className="flex items-center gap-4 pt-4">
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-white">
{r.source === "score" ? (
<StarBadge className="h-6 w-6" />
) : (
<CheckBadge className="h-6 w-6" />
)}
</div>
<div className="min-w-0">
<div className="text-sm font-medium text-[var(--ink)]">{r.author}</div>
<div className="mt-1 text-xs uppercase tracking-[0.18em] text-[var(--ink-35)]">
{r.treatment}
</div>
</div>
</div>
</div>
</article>
))}
</div>
</div>
</div>
</div>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Split audiences */
/* ------------------------------------------------------------------ */
const audienceBoxes = [
{
kicker: "For Patients",
title: "Your path to us",
body: "From your first enquiry to aftercare, we guide you personally. Find answers on the process, costs and anaesthesia right here.",
bullets: ["Process and scheduling", "Frequently asked questions", "Getting here and parking"],
cta: "View patient information",
},
{
kicker: "For Referrers",
title: "Dependable collaboration",
body: "Collegial referrals with clear communication, fast reporting and return referral. Your patients are in good hands with us.",
bullets: [
"Online referral form",
"Digital report delivery",
"A direct line to the medical lead",
],
cta: "Start a referral",
},
];
function SplitAudiences() {
return (
<section className="bg-white">
<div className="mx-auto max-w-7xl px-5 py-16 sm:px-6 sm:py-24 lg:py-32">
<div className="grid grid-cols-1 gap-3 sm:gap-4 md:grid-cols-2">
{audienceBoxes.map((b, i) => (
<Reveal key={b.kicker} delay={i * 0.1}>
<article
className="flex h-full flex-col justify-between rounded-3xl p-7 sm:p-10 lg:p-14"
style={{ backgroundColor: "#fafafa" }}
>
<div>
<span className="text-xs uppercase tracking-[0.2em] text-[var(--ink-60)]">
{b.kicker}
</span>
<h3 className="mt-5 text-2xl text-[var(--ink)] sm:text-3xl lg:text-4xl">
{b.title}
</h3>
<p className="mt-5 max-w-lg text-base leading-relaxed text-[var(--ink-soft)]">
{b.body}
</p>
<ul className="mt-8 space-y-4">
{b.bullets.map((line) => (
<li
key={line}
className="flex items-center gap-3 text-sm text-[var(--ink-soft)]"
>
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full border border-gray-200 bg-white text-[var(--ink)]">
<Check className="h-3.5 w-3.5" strokeWidth={2} />
</span>
{line}
</li>
))}
</ul>
</div>
<a
href="#"
className="group/link mt-10 inline-flex w-fit items-center gap-2 rounded-full bg-[var(--ink)] px-6 py-3 text-sm font-normal text-white transition-colors hover:bg-[var(--ink-hover)]"
>
{b.cta}
<ArrowUpRight className="h-4 w-4 transition-transform group-hover/link:-translate-y-0.5 group-hover/link:translate-x-0.5" />
</a>
</article>
</Reveal>
))}
</div>
</div>
</section>
);
}
/* ------------------------------------------------------------------ */
/* Footer */
/* ------------------------------------------------------------------ */
const footerGroups = [
{
title: "Clinic",
links: [
{ label: "About the clinic", href: "#" },
{ label: "Team", href: "#" },
{ label: "Careers", href: "#" },
],
},
{
title: "Treatments",
links: [
{ label: "Implantology", href: "#" },
{ label: "Oral Surgery", href: "#" },
{ label: "Aesthetics", href: "#" },
],
},
{
title: "Services",
links: [
{ label: "For Patients", href: "#" },
{ label: "For Referrers", href: "#" },
{ label: "Book an Appointment", href: "#" },
],
},
];
function Footer() {
return (
<footer className="relative overflow-hidden bg-[var(--ink)] text-white">
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-white/20 to-transparent"
/>
<div className="mx-auto max-w-7xl px-6 pt-20 lg:pt-28">
<Reveal>
<div className="grid grid-cols-1 gap-14 pb-16 lg:grid-cols-12 lg:gap-10">
<div className="lg:col-span-4">
<div className="flex items-center gap-3">
<Logo className="h-7 w-auto text-white" />
<span className="h-6 w-px bg-white/20" />
<span className="font-heading text-sm text-white/70">{SUBBRAND}</span>
</div>
<p className="mt-6 max-w-xs text-sm leading-relaxed text-white/60">
Personal, precise, discreet. Your clinic for implantology, oral surgery and
aesthetic treatments in the park district.
</p>
<div className="mt-5 text-xs text-white/40">
<span className="text-white/50">Mon to Fri</span> 08:00 to 18:00 ·{" "}
<span className="text-white/50">Sat</span> by appointment
</div>
</div>
<div className="grid grid-cols-1 gap-10 sm:grid-cols-3 lg:col-span-5">
{footerGroups.map((g) => (
<div key={g.title}>
<div className="text-xs uppercase tracking-[0.2em] text-white/40">{g.title}</div>
<ul className="mt-6 space-y-3">
{g.links.map((l) => (
<li key={l.label}>
<a
href={l.href}
className="group inline-flex items-center text-sm text-white/80 transition-colors hover:text-white"
>
<span className="relative">
{l.label}
<span className="absolute -bottom-0.5 left-0 h-px w-0 bg-white transition-all duration-300 group-hover:w-full" />
</span>
</a>
</li>
))}
</ul>
</div>
))}
</div>
<div className="lg:col-span-3">
<div className="text-xs uppercase tracking-[0.2em] text-white/40">Contact</div>
<address className="mt-6 not-italic">
<ul className="space-y-5 text-sm leading-relaxed text-white/70">
<li className="flex items-center gap-4">
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-white/15">
<MapPin className="h-4 w-4 text-white/80" strokeWidth={1.5} />
</span>
<span>
<span className="block text-white">Parkallee 12</span>
<span className="block text-white/60">10245 Musterstadt, by Parkforum</span>
</span>
</li>
<li className="flex items-center gap-4">
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-white/15">
<Phone className="h-4 w-4 text-white/80" strokeWidth={1.5} />
</span>
<a href="#" className="text-white transition-colors hover:text-white/70">
030 1234560
</a>
</li>
<li className="flex items-center gap-4">
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-white/15">
<Mail className="h-4 w-4 text-white/80" strokeWidth={1.5} />
</span>
<a href="#" className="text-white transition-colors hover:text-white/70">
kontakt@praxisklinik-am-park.de
</a>
</li>
</ul>
</address>
</div>
</div>
</Reveal>
<div className="flex flex-col items-start justify-between gap-6 border-t border-white/10 py-8 sm:flex-row sm:items-center">
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-xs text-white/50">
<span>
© {new Date().getFullYear()} {BRAND}
</span>
<a href="#" className="transition-colors hover:text-white">
Legal Notice
</a>
<a href="#" className="transition-colors hover:text-white">
Privacy
</a>
<a href="#" className="transition-colors hover:text-white">
Careers
</a>
</div>
<div className="flex items-center gap-2">
{[
{ label: "Instagram", icon: Instagram },
{ label: "LinkedIn", icon: Linkedin },
{ label: "Facebook", icon: Facebook },
].map(({ label, icon: Icon }) => (
<a
key={label}
href="#"
aria-label={label}
className="flex h-9 w-9 items-center justify-center rounded-full border border-white/10 text-white/70 transition-colors hover:border-white/30 hover:text-white"
>
<Icon className="h-4 w-4" strokeWidth={1.5} />
</a>
))}
</div>
</div>
</div>
</footer>
);
}
/* ------------------------------------------------------------------ */
/* Page */
/* ------------------------------------------------------------------ */
export function PraxisklinikPage() {
return (
<div className="praxisklinik-park min-h-screen bg-white text-[var(--ink)]">
<style>{scopedStyles}</style>
<Navbar />
<main>
<Hero />
<DoctorIntro />
<ServicesBento />
<PraxisEinblicke />
<Stats />
<Reviews />
<SplitAudiences />
</main>
<Footer />
</div>
);
}
export default PraxisklinikPage;
FAQ
Häufige Fragen
Eine kuratierte Landingpage aus aufeinander abgestimmten Sections der Library, in sinnvoller Reihenfolge von Hero bis Footer. Du kopierst die Sections einzeln und setzt sie mit dem Kompositions-Snippet zusammen.
Deine Frage war nicht dabei? Wir helfen gern weiter.
Kontakt aufnehmen