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