All 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 & HealthFull page

The finished page to scroll through, switchable between desktop, tablet and mobile.

Source code

The complete page as a single file. Copy it into ./components and add the component to your route.

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

Frequently asked questions

A curated landing page made of matching library sections in a sensible order from hero to footer. You copy the sections one by one and assemble them with the composition snippet.

Didn't find your question? We're happy to help.

Get in touch