Skip to content
All templates

Templates/Oral & Facial Clinic

A quiet, almost monochrome page for a private clinic. Anthracite, white and one very light grey carry everything; the only colour is the gold of the review stars. The hero photograph holds three labels that follow the cursor, the treatment panels grow when you point at them and fold their text open, and the reviews travel past on their own. Generous rounded photo frames, no shadows, no coloured overlays.

Health & PracticeFull page

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

Source code

The page is one file, but it loads 16 images (584 KB) from public/. Copy only the code and they are missing. The package holds both, at the right paths.

import {
  useCallback,
  useEffect,
  useRef,
  useState,
  type CSSProperties,
  type ReactNode,
} from "react";
import {
  motion,
  useInView,
  useMotionValue,
  useReducedMotion,
  useSpring,
  useTransform,
} from "framer-motion";
import {
  ArrowRight,
  ArrowUpRight,
  Award,
  BadgeCheck,
  Building2,
  CalendarDays,
  Check,
  ChevronDown,
  Facebook,
  Instagram,
  Linkedin,
  Mail,
  MapPin,
  Menu,
  Phone,
  Smile,
  Sparkles,
  Star,
  Stethoscope,
  X,
} from "lucide-react";

/*
 * Luma Clinic: a full page for a private oral and facial surgery practice.
 *
 * Nearly monochrome. Anthracite, white and one very light grey carry the whole
 * page; the only colour is the gold of the review stars and the four Google
 * tones in its mark. Sections separate by a change of surface, white against
 * near-white, never by a rule.
 *
 * Two motifs run through it. Photographs sit in generously rounded frames and
 * grow very slowly under the cursor, and every block arrives once with a short
 * lift. Everything else stays still.
 *
 * House rules: no shadows, no coloured overlays on the photographs, buttons
 * with a small radius rather than fully round ones.
 */

const INK = "#3b3e44";
const INK_SOFT = "#717479";
const SURFACE = "#f9fafb";
const SURFACE_2 = "#fafafa";
const LINE = "#e7e8ea";
const GOLD = "#c79a2e";

const SANS = '"Figtree", ui-sans-serif, system-ui, sans-serif';
const HEAD = '"Manrope", ui-sans-serif, system-ui, sans-serif';

const head: CSSProperties = { fontFamily: HEAD, fontWeight: 500, letterSpacing: "-0.02em" };

// A small radius instead of round capsules, the same as in the rest of the design system.
const BUTTON = 6;
const IMAGE = 22;

const SMOOTH = [0.22, 1, 0.36, 1] as const;

/* ------------------------------------------------------------------ content */

const CLINIC = {
  name: "Luma Clinic",
  zusatz: "Oral & Facial Clinic",
  strasse: "123 Example Street",
  place: "12345 Your City",
  telefon: "+1 (555) 123 4567",
  telefonHref: "tel:+15551234567",
  mail: "hello@your-domain.com",
  mailHref: "mailto:hello@your-domain.com",
};

const NAV = [
  { label: "Clinic", href: "#clinic" },
  { label: "Treatments", href: "#treatments", mega: true },
  { label: "Patient care", href: "#audiences" },
  { label: "For referrers", href: "#audiences" },
];

const MEGA = [
  {
    title: "Implantology",
    image: "/templates/luma/menu-implantology.webp",
    anriss: "Fixed restorations with a natural appearance, for quality of life that lasts.",
    points: ["Single implants", "All-on-4", "Bone grafting", "Immediate implants"],
  },
  {
    title: "Oral surgery",
    image: "/templates/luma/menu-surgery.webp",
    anriss: "Precise surgery of the mouth, jaw and face, performed by experienced specialists.",
    points: ["Wisdom teeth", "Jaw surgery", "Cyst removal", "Facial trauma"],
  },
  {
    title: "Aesthetics",
    image: "/templates/luma/menu-aesthetics.webp",
    anriss: "Gentle procedures for a fresh, natural result and a rested appearance.",
    points: ["Botulinum toxin", "Dermal fillers", "Eyelid lift", "PRP therapy"],
  },
];

/* The dark strip on the right of the menu: a way through for everyone who does not know
   which of the three columns their question falls under. */
const MEGA_ADVICE = {
  kicker: "Personal consultation",
  title: "Not sure which treatment fits?",
  text: "Talk to Dr. Reiter directly. In a calm first conversation we work through every detail of your individual treatment plan.",
  knopf: "Book an appointment",
};

const HERO_PILLS = [
  { label: "Implantology", Icon: Smile, pos: "left-5 top-6", faktor: [14, 10] },
  {
    label: "Oral surgery",
    Icon: Stethoscope,
    pos: "right-5 top-1/2 -translate-y-1/2",
    faktor: [-22, 16],
  },
  {
    label: "Aesthetics",
    Icon: Sparkles,
    pos: "bottom-6 left-1/2 -translate-x-1/2",
    faktor: [18, -12],
  },
] as const;

const FIGURES = [
  { value: 15, suffix: "+", label: "Years of experience" },
  { value: 8000, suffix: "+", label: "Procedures" },
  { value: 100, suffix: "%", label: "Private practice" },
];

const FIELDS = [
  {
    kicker: "Implantology",
    title: "Fixed teeth. For a lifetime.",
    text: "High-grade ceramic and titanium implants, planned on a 3D scan and placed with minimally invasive techniques. For a smile that simply stays.",
    image: "/templates/luma/bento-implantology.webp",
    alt: "Close-up of a patient smiling after implant treatment",
  },
  {
    kicker: "Oral surgery",
    title: "Surgery with a steady hand",
    text: "Wisdom teeth, jaw corrections, cysts and trauma. Routine, gentle and supported by a modern operating suite.",
    image: "/templates/luma/bento-surgery.webp",
    alt: "Surgeon at work in the operating suite",
  },
  {
    kicker: "Aesthetics",
    title: "Your natural expression",
    text: "Botulinum toxin, dermal fillers and aesthetic procedures, used with restraint. So you look like yourself, only more rested.",
    image: "/templates/luma/bento-aesthetics.webp",
    alt: "Aesthetic consultation in a bright treatment room",
  },
];

const GALLERY = [
  {
    src: "/templates/luma/clinic-4.webp",
    alt: "Surgeon and assistant reviewing patient notes",
    spalten: "md:col-span-8",
    format: "aspect-[4/3]",
    fokus: "",
  },
  {
    src: "/templates/luma/clinic-2.webp",
    alt: "Surgeon taking a quiet moment in the clinic lounge",
    spalten: "md:col-span-4",
    format: "",
    fokus: "35% center",
  },
  {
    src: "/templates/luma/clinic-3.webp",
    alt: "Surgeon reviewing a panoramic x-ray",
    spalten: "md:col-span-4",
    format: "",
    fokus: "62% center",
  },
  {
    src: "/templates/luma/clinic-5.webp",
    alt: "Consultation in the treatment room",
    spalten: "md:col-span-8",
    format: "aspect-[4/3]",
    fokus: "",
  },
  {
    src: "/templates/luma/clinic-1.webp",
    alt: "Reception area of the clinic",
    spalten: "md:col-span-12",
    format: "aspect-[5/2]",
    fokus: "",
  },
];

const REASONS = [
  {
    Icon: Award,
    kicker: "Experience",
    title: "15+ years alongside our patients",
    text: "Thousands of procedures in implantology, oral and aesthetic facial surgery, carefully planned and calmly performed.",
  },
  {
    Icon: Building2,
    kicker: "The clinic",
    title: "A modern high-tech practice in the heart of the city",
    text: "3D diagnostics, digital implant planning and our own operating suite, so every treatment is safe and comfortable.",
  },
  {
    Icon: Sparkles,
    kicker: "Specialisation",
    title: "Recognised experts in implantology",
    text: "Specialised in complex cases, immediate implants and aesthetically perfect restorations, always with a natural result.",
  },
];

const VOICES = [
  {
    text: "Finally a surgeon who listens. Dr. Reiter took his time over my implant consultation and explained everything calmly. The result looks completely natural.",
    name: "Sarah M.",
    behandlung: "Implant restoration",
    quelle: "Google",
  },
  {
    text: "An outstanding surgeon with a calm, professional manner. The wisdom tooth removal was completely painless and I was back on my feet after two days.",
    name: "Mark T.",
    behandlung: "Wisdom tooth removal",
    quelle: "Verified",
  },
  {
    text: "I look like myself, just fresher. That is exactly what I had hoped for. A very subtle, aesthetic result and a genuinely pleasant team.",
    name: "Caroline B.",
    behandlung: "Aesthetic treatment",
    quelle: "Google",
  },
  {
    text: "The consultation was honest and unhurried. Not a sales pitch, but a real plan for my smile. Today I wear it without thinking about it.",
    name: "Julia R.",
    behandlung: "Immediate implant",
    quelle: "Google",
  },
  {
    text: "From reception to aftercare, everything was at the highest level. You are not one patient among many here, you are genuinely seen.",
    name: "Andrew K.",
    behandlung: "Bone grafting",
    quelle: "Verified",
  },
];

const AUDIENCES = [
  {
    kicker: "For patients",
    title: "Your path to us",
    text: "From the first enquiry to aftercare, we guide you personally. Answers to common questions and details on the procedure, costs and anaesthesia are all here.",
    points: ["Process and booking", "Frequently asked questions", "Getting here and parking"],
    cta: "View patient information",
  },
  {
    kicker: "For referrers",
    title: "A reliable partnership",
    text: "Collegial referrals with clear communication, fast reporting and prompt hand-back. We treat your patients the way you would treat them yourself.",
    points: ["Online referral form", "Digital reporting", "Direct line to the clinical lead"],
    cta: "Start a referral",
  },
];

const FOOT_GROUPS = [
  { title: "Clinic", links: ["About the clinic", "Team", "Careers"] },
  { title: "Treatments", links: ["Implantology", "Oral surgery", "Aesthetics"] },
  { title: "Service", links: ["For patients", "For referrers", "Book an appointment"] },
];

/* --------------------------------------------------------------- primitives */

// Everything arrives the same way: a short lift, a long settle, once.
function Reveal({
  children,
  delay = 0,
  className = "",
  style,
}: {
  children: ReactNode;
  delay?: number;
  className?: string;
  style?: CSSProperties;
}) {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const visible = useInView(ref, { once: true, margin: "-80px" });
  return (
    <motion.div
      ref={ref}
      className={className}
      style={style}
      initial={reduce ? false : { opacity: 0, y: 28 }}
      animate={visible || reduce ? { opacity: 1, y: 0 } : { opacity: 0, y: 28 }}
      transition={{ duration: reduce ? 0 : 0.9, ease: SMOOTH, delay: reduce ? 0 : delay }}
    >
      {children}
    </motion.div>
  );
}

// The wordmark: four letters, one path each, colour from the parent.
function Wordmark({ className = "" }: { className?: string }) {
  return (
    <svg
      viewBox="0 0 264 68"
      role="img"
      aria-label="Luma"
      className={className}
      fill="none"
      stroke="currentColor"
      strokeWidth={3.6}
      strokeLinecap="butt"
      strokeLinejoin="miter"
    >
      <path d="M4 4v60h42" />
      <path d="M68 4v39a21 21 0 0 0 42 0V4" />
      <path d="M132 64V4l28 38 28-38v60" />
      <path d="M210 64 235 4l25 60M219 45h32" />
    </svg>
  );
}

// The four Google colours, as they stand in the brand.
function GoogleMark({ className = "" }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" className={className} role="img" aria-label="Google">
      <path
        fill="#4285F4"
        d="M23.49 12.27c0-.79-.07-1.54-.19-2.27H12v4.51h6.44c-.28 1.48-1.12 2.73-2.39 3.58v2.98h3.86c2.26-2.08 3.58-5.15 3.58-8.8z"
      />
      <path
        fill="#34A853"
        d="M12 24c3.24 0 5.95-1.08 7.93-2.91l-3.86-2.98c-1.07.72-2.45 1.16-4.07 1.16-3.13 0-5.78-2.11-6.73-4.96H1.29v3.09C3.26 21.3 7.31 24 12 24z"
      />
      <path
        fill="#FBBC05"
        d="M5.27 14.31a7.2 7.2 0 010-4.62V6.6H1.29a12 12 0 000 10.8l3.98-3.09z"
      />
      <path
        fill="#EA4335"
        d="M12 4.75c1.77 0 3.35.61 4.6 1.8l3.42-3.42C17.95 1.19 15.24 0 12 0 7.31 0 3.26 2.7 1.29 6.6l3.98 3.09C6.22 6.86 8.87 4.75 12 4.75z"
      />
    </svg>
  );
}

function Stars({ groesse = 16, color = GOLD }: { groesse?: number; color?: string }) {
  return (
    <span className="inline-flex items-center gap-1" style={{ color: color }} aria-hidden>
      {Array.from({ length: 5 }).map((_, i) => (
        <Star
          key={i}
          style={{ width: groesse, height: groesse }}
          fill="currentColor"
          strokeWidth={0}
        />
      ))}
    </span>
  );
}

// The number counts up once when it comes into view.
function Counter({ target, suffix = "" }: { target: number; suffix?: string }) {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLSpanElement>(null);
  const visible = useInView(ref, { once: true, margin: "-60px" });
  const [value, setValue] = useState(0);

  useEffect(() => {
    if (!visible) return;
    if (reduce) {
      setValue(target);
      return;
    }
    const duration = 1400;
    const start = performance.now();
    let id = 0;
    const step = (now: number) => {
      const t = Math.min(1, (now - start) / duration);
      /* Rolling out at the end: a number that stops abruptly reads as a jump, not as a
         counter. */
      setValue(Math.round(target * (1 - Math.pow(1 - t, 3))));
      if (t < 1) id = requestAnimationFrame(step);
    };
    id = requestAnimationFrame(step);
    return () => cancelAnimationFrame(id);
  }, [visible, reduce, target]);

  return (
    <span ref={ref} className="tabular-nums">
      {value.toLocaleString("en-US")}
      {suffix}
    </span>
  );
}

function Kicker({ children, hell = false }: { children: ReactNode; hell?: boolean }) {
  return (
    <span
      className="text-[11px] uppercase"
      style={{
        fontFamily: SANS,
        letterSpacing: "0.2em",
        color: hell ? "rgba(255,255,255,0.55)" : "rgba(59,62,68,0.75)",
      }}
    >
      {children}
    </span>
  );
}

function Button({
  children,
  href = "#appointment",
  variant = "solid",
  Icon,
}: {
  children: ReactNode;
  href?: string;
  variant?: "solid" | "line";
  Icon?: typeof ArrowRight;
}) {
  const full = variant === "solid";
  return (
    <a
      href={href}
      className="group inline-flex items-center justify-center gap-0 px-6 py-3.5 text-sm transition-colors sm:px-7"
      style={{
        borderRadius: BUTTON,
        fontFamily: SANS,
        background: full ? INK : "#ffffff",
        color: full ? "#ffffff" : INK,
        border: full ? "1px solid transparent" : `1px solid ${LINE}`,
      }}
    >
      {children}
      {Icon ? (
        /* The symbol only appears on hover and does not push the text around while doing
           so: it grows out of zero width. */
        <Icon
          className="h-4 w-4 max-w-0 -translate-x-1 opacity-0 transition-all duration-300 ease-out group-hover:ml-2 group-hover:max-w-[1rem] group-hover:translate-x-0 group-hover:opacity-100"
          strokeWidth={1.75}
        />
      ) : null}
    </a>
  );
}

/* ------------------------------------------------------------------- header */

function Header() {
  const [open, setOpen] = useState(false);
  const [megaOpen, setMegaOpen] = useState(false);
  const [behandlungen, setTreatments] = useState(false);

  useEffect(() => {
    if (!open) return;
    const before = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = before;
    };
  }, [open]);

  return (
    <>
      {/* The scrim lies outside the header. The header has a backdrop blur, and an
          element with backdrop-filter is the containing block for position:fixed in its
          descendants: inside, the scrim would only be as tall as the header itself. */}
      <div
        aria-hidden
        className="pointer-events-none fixed inset-x-0 bottom-0 top-20 z-40 hidden transition-opacity duration-300 lg:block"
        style={{
          background: "rgba(24,26,29,0.4)",
          opacity: behandlungen ? 1 : 0,
          visibility: behandlungen ? "visible" : "hidden",
        }}
      />

      <header
        className="sticky top-0 z-50 backdrop-blur"
        style={{ borderBottom: `1px solid ${LINE}`, background: "rgba(255,255,255,0.9)" }}
      >
        <div className="mx-auto flex h-20 max-w-7xl items-center justify-between px-5 sm:px-6">
          <a href="#top" className="flex items-center gap-3" aria-label={CLINIC.name}>
            <Wordmark className="h-6 w-auto" />
            <span className="hidden h-6 w-px sm:block" style={{ background: LINE }} />
            <span className="hidden text-sm sm:inline" style={{ ...head, color: INK_SOFT }}>
              {CLINIC.zusatz}
            </span>
          </a>

          <nav className="hidden items-center gap-10 lg:flex">
            {NAV.map((entry) =>
              entry.mega ? (
                /* Opening and closing hang on state, not on :hover. That way the scrim can
                   lie outside the header, and the keyboard reaches the same panel through
                   focus. */
                <div
                  key={entry.label}
                  onMouseEnter={() => setTreatments(true)}
                  onMouseLeave={() => setTreatments(false)}
                  onFocus={() => setTreatments(true)}
                  onBlur={(e) => {
                    if (!e.currentTarget.contains(e.relatedTarget as Node)) setTreatments(false);
                  }}
                >
                  <button
                    type="button"
                    aria-expanded={behandlungen}
                    className="inline-flex items-center gap-1 py-7 text-sm transition-colors"
                    style={{ fontFamily: SANS, color: INK_SOFT }}
                  >
                    {entry.label}
                    <ChevronDown
                      className={`h-4 w-4 transition-transform duration-300 ${
                        behandlungen ? "rotate-180" : ""
                      }`}
                    />
                  </button>
                  <MegaMenu open={behandlungen} />
                </div>
              ) : (
                <a
                  key={entry.label}
                  href={entry.href}
                  className="text-sm transition-colors hover:opacity-70"
                  style={{ fontFamily: SANS, color: INK_SOFT }}
                >
                  {entry.label}
                </a>
              ),
            )}
          </nav>

          <div className="flex items-center gap-3">
            <a
              href="#appointment"
              className="hidden px-5 py-2.5 text-sm text-white transition-colors sm:inline-flex"
              style={{ borderRadius: BUTTON, background: INK, fontFamily: SANS }}
            >
              Book appointment
            </a>
            <button
              type="button"
              aria-label={open ? "Close menu" : "Open menu"}
              aria-expanded={open}
              onClick={() => setOpen((v) => !v)}
              className="inline-flex h-10 w-10 items-center justify-center lg:hidden"
              style={{ borderRadius: BUTTON, border: `1px solid ${LINE}`, color: INK_SOFT }}
            >
              {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)] overflow-y-auto bg-white lg:hidden"
            style={{ borderTop: `1px solid ${LINE}` }}
          >
            <nav className="flex flex-col px-5 py-6">
              {NAV.map((entry) =>
                entry.mega ? (
                  <div key={entry.label} style={{ borderBottom: `1px solid ${LINE}` }}>
                    <button
                      type="button"
                      onClick={() => setMegaOpen((v) => !v)}
                      aria-expanded={megaOpen}
                      className="flex w-full items-center justify-between py-4 text-lg"
                      style={{ color: INK, fontFamily: SANS }}
                    >
                      {entry.label}
                      <ChevronDown
                        className={`h-5 w-5 transition-transform duration-300 ${
                          megaOpen ? "rotate-180" : ""
                        }`}
                      />
                    </button>
                    {/* Opening and closing through the row height of the grid: that runs
                        smoothly without knowing a height. */}
                    <div
                      className={`grid transition-all duration-300 ease-out ${
                        megaOpen ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
                      }`}
                    >
                      <div className="overflow-hidden">
                        <div className="flex flex-col gap-6 pb-5">
                          {MEGA.map((group) => (
                            <div key={group.title} className="flex flex-col gap-2">
                              <Kicker>{group.title}</Kicker>
                              <div className="flex flex-col">
                                {group.points.map((dot) => (
                                  <a
                                    key={dot}
                                    href="#treatments"
                                    onClick={() => setOpen(false)}
                                    className="py-2 text-base"
                                    style={{ color: INK, fontFamily: SANS }}
                                  >
                                    {dot}
                                  </a>
                                ))}
                              </div>
                            </div>
                          ))}
                        </div>
                      </div>
                    </div>
                  </div>
                ) : (
                  <a
                    key={entry.label}
                    href={entry.href}
                    onClick={() => setOpen(false)}
                    className="block py-4 text-lg"
                    style={{ borderBottom: `1px solid ${LINE}`, color: INK, fontFamily: SANS }}
                  >
                    {entry.label}
                  </a>
                ),
              )}
              <a
                href="#appointment"
                onClick={() => setOpen(false)}
                className="mt-8 inline-flex items-center justify-center px-6 py-3.5 text-sm text-white"
                style={{ borderRadius: BUTTON, background: INK, fontFamily: SANS }}
              >
                Book appointment
              </a>
            </nav>
          </div>
        ) : null}
      </header>
    </>
  );
}

// The large menu under "Treatments". Only from lg on, on hover.
function MegaMenu({ open }: { open: boolean }) {
  return (
    <div
      className="absolute inset-x-0 top-full z-40 overflow-hidden transition-all duration-200"
      style={{
        visibility: open ? "visible" : "hidden",
        opacity: open ? 1 : 0,
        background: "#ffffff",
        borderTop: `1px solid ${LINE}`,
        borderBottomLeftRadius: 24,
        borderBottomRightRadius: 24,
      }}
    >
      <div className="mx-auto flex w-full max-w-[1600px] flex-col lg:flex-row">
        <div className="grid flex-1 grid-cols-1 md:grid-cols-3">
          {MEGA.map((group, i) => (
            <div
              key={group.title}
              className="group/spalte flex cursor-pointer flex-col gap-5 px-8 py-9"
              style={i < MEGA.length - 1 ? { borderRight: `1px solid ${LINE}` } : undefined}
            >
              <div
                className="aspect-[16/10] w-full overflow-hidden"
                style={{ borderRadius: 16, background: SURFACE }}
              >
                {/* The picture only grows when the column is hovered, slowly, as
                    everywhere else on this page. */}
                <img
                  src={group.image}
                  alt=""
                  aria-hidden
                  loading="lazy"
                  className="h-full w-full object-cover transition-transform duration-700 ease-out group-hover/spalte:scale-[1.05]"
                />
              </div>

              <div>
                <h3 className="text-[19px] leading-tight" style={{ ...head, color: INK }}>
                  {group.title}
                </h3>
                <p
                  className="mt-2 text-sm leading-relaxed"
                  style={{ fontFamily: SANS, color: INK_SOFT }}
                >
                  {group.anriss}
                </p>
              </div>

              <nav className="flex flex-col gap-3">
                {group.points.map((dot) => (
                  <a
                    key={dot}
                    href="#treatments"
                    className="group/zeile flex items-center justify-between text-sm transition-colors hover:opacity-70"
                    style={{ fontFamily: SANS, color: INK }}
                  >
                    {dot}
                    {/* The arrow drives in from the left instead of simply being there:
                        the line gets a direction. */}
                    <ArrowRight className="h-3.5 w-3.5 -translate-x-2 opacity-0 transition-all duration-300 group-hover/zeile:translate-x-0 group-hover/zeile:opacity-100" />
                  </a>
                ))}
              </nav>
            </div>
          ))}
        </div>

        <aside
          className="w-full shrink-0 px-8 py-9 lg:w-[340px] lg:px-9"
          style={{ background: INK }}
        >
          <div className="flex h-full flex-col justify-between gap-9">
            <div>
              <span
                className="inline-block px-3 py-1 text-[10px] uppercase"
                style={{
                  borderRadius: 999,
                  background: "rgba(255,255,255,0.12)",
                  color: "rgba(255,255,255,0.8)",
                  fontFamily: SANS,
                  letterSpacing: "0.2em",
                }}
              >
                {MEGA_ADVICE.kicker}
              </span>
              <h4 className="mt-5 text-[22px] leading-tight" style={{ ...head, color: "#ffffff" }}>
                {MEGA_ADVICE.title}
              </h4>
              <p
                className="mt-4 text-sm leading-relaxed"
                style={{ fontFamily: SANS, color: "rgba(255,255,255,0.7)" }}
              >
                {MEGA_ADVICE.text}
              </p>
            </div>

            <a
              href="#appointment"
              className="group/rat flex w-full items-center justify-between px-6 py-3.5 text-sm transition-opacity hover:opacity-90"
              style={{ borderRadius: BUTTON, background: "#ffffff", color: INK, fontFamily: SANS }}
            >
              {MEGA_ADVICE.knopf}
              <ArrowUpRight className="h-4 w-4 transition-transform duration-300 group-hover/rat:-translate-y-0.5 group-hover/rat:translate-x-0.5" />
            </a>
          </div>
        </aside>
      </div>
    </div>
  );
}

/* --------------------------------------------------------------------- hero */

function Held() {
  return (
    <section id="top" className="bg-white">
      <div className="mx-auto grid max-w-7xl 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: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 px-3.5 py-1.5 text-xs"
            style={{
              borderRadius: BUTTON,
              border: `1px solid ${LINE}`,
              color: "rgba(59,62,68,0.75)",
              fontFamily: SANS,
              letterSpacing: "0.02em",
            }}
          >
            <MapPin className="h-3.5 w-3.5" style={{ color: INK }} strokeWidth={1.75} />
            In the heart of the city
          </span>

          <h1
            className="mt-6 text-4xl leading-[1.08] sm:mt-8 sm:text-5xl lg:text-[4.25rem] lg:leading-[1.04]"
            style={{ ...head, color: INK }}
          >
            <span className="block">Natural beauty</span>
            <span className="block sm:hidden">begins with your smile</span>
            <span className="hidden sm:block">begins with</span>
            <span className="hidden sm:block">your smile</span>
          </h1>

          <p
            className="mt-6 max-w-xl text-base leading-relaxed sm:mt-8 sm:text-lg"
            style={{ fontFamily: SANS, color: INK_SOFT }}
          >
            The clinic for implantology, oral surgery and aesthetic facial surgery.
          </p>

          <div className="mt-8 flex flex-wrap items-center justify-center gap-3 sm:mt-10 lg:justify-start">
            <Button href="#treatments" Icon={ArrowRight}>
              Explore treatments
            </Button>
            <Button href="#appointment" variant="line" Icon={CalendarDays}>
              Book an appointment
            </Button>
          </div>

          <div className="mt-12 flex flex-wrap items-center justify-center gap-5 lg:justify-start">
            <div className="flex -space-x-2">
              {[
                "/templates/luma/avatar-1.webp",
                "/templates/luma/avatar-2.webp",
                "/templates/luma/bento-surgery.webp",
              ].map((src) => (
                <img
                  key={src}
                  src={src}
                  alt=""
                  aria-hidden
                  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-2">
                <Stars groesse={15} />
                <span className="text-sm" style={{ fontFamily: SANS, color: INK, fontWeight: 500 }}>
                  4.9 / 5
                </span>
              </div>
              <p className="mt-1 text-sm" style={{ fontFamily: SANS, color: INK_SOFT }}>
                from more than 380 verified patient reviews
              </p>
            </div>
          </div>
        </Reveal>

        <Reveal delay={0.15} className="relative">
          <HeldImage />
        </Reveal>
      </div>
    </section>
  );
}

/* The hero picture with three plates on it. They follow the mouse, each with its own factor
   and its own direction: equal factors would look like one shifted layer, different ones
   like three things in space. */
function HeldImage() {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(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 move = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      if (reduce) return;
      const r = ref.current?.getBoundingClientRect();
      if (!r) return;
      mx.set(((e.clientX - r.left) / r.width - 0.5) * 2);
      my.set(((e.clientY - r.top) / r.height - 0.5) * 2);
    },
    [mx, my, reduce],
  );

  const back = useCallback(() => {
    mx.set(0);
    my.set(0);
  }, [mx, my]);

  const x1 = useTransform(sx, (v) => v * HERO_PILLS[0].faktor[0]);
  const y1 = useTransform(sy, (v) => v * HERO_PILLS[0].faktor[1]);
  const x2 = useTransform(sx, (v) => v * HERO_PILLS[1].faktor[0]);
  const y2 = useTransform(sy, (v) => v * HERO_PILLS[1].faktor[1]);
  const x3 = useTransform(sx, (v) => v * HERO_PILLS[2].faktor[0]);
  const y3 = useTransform(sy, (v) => v * HERO_PILLS[2].faktor[1]);
  const motions = [
    { x: x1, y: y1 },
    { x: x2, y: y2 },
    { x: x3, y: y3 },
  ];

  return (
    <div
      ref={ref}
      onMouseMove={move}
      onMouseLeave={back}
      className="group relative overflow-hidden"
      style={{ borderRadius: IMAGE, background: SURFACE }}
    >
      <img
        src="/templates/luma/hero-portrait.webp"
        alt="Three patients of the clinic, close together"
        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-[720px]"
      />

      <div className="pointer-events-none absolute inset-0">
        {HERO_PILLS.map((pill, i) => (
          <motion.div
            key={pill.label}
            style={{
              x: motions[i].x,
              y: motions[i].y,
              borderRadius: BUTTON,
              background: "rgba(255,255,255,0.88)",
              color: INK,
              fontFamily: SANS,
            }}
            className={`absolute ${pill.pos} flex items-center gap-2 px-3.5 py-2 text-xs font-medium backdrop-blur-md`}
          >
            <pill.Icon className="h-4 w-4" strokeWidth={1.75} />
            {pill.label}
          </motion.div>
        ))}
      </div>
    </div>
  );
}

/* ------------------------------------------------------------------ doctor */

function Doctor() {
  return (
    <section id="clinic" style={{ background: SURFACE }}>
      <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">
          <Kicker>Personal. Precise. Discreet.</Kicker>
          <h2
            className="mt-4 text-[28px] leading-tight sm:text-4xl lg:text-5xl"
            style={{ ...head, color: INK }}
          >
            Dr. Jonas Reiter
          </h2>
          <p className="mt-3 text-base sm:text-lg" style={{ fontFamily: SANS, color: INK_SOFT }}>
            Consultant in oral and maxillofacial surgery
          </p>

          <div
            className="mt-8 max-w-xl space-y-5 text-[15px] leading-relaxed sm:text-base"
            style={{ fontFamily: SANS, color: INK_SOFT }}
          >
            <p>
              Beauty is never the goal for me, it is the result of a careful procedure. I take my
              time, I listen, and together with every patient I develop a plan that suits their face
              and their story.
            </p>
            <p>
              With more than 15 years in implantology, oral surgery and aesthetic facial surgery, I
              combine medical precision with a fine sense for natural results.
            </p>
          </div>

          <div className="mt-10 hidden lg:block">
            <Button href="#impressions" variant="line">
              More about the clinic
            </Button>
          </div>
        </Reveal>

        <Reveal delay={0.05} className="order-2 lg:order-3 lg:col-span-7">
          <div
            className="grid grid-cols-3 pt-2 sm:gap-10 sm:pt-8"
            style={{ borderTop: `1px solid ${LINE}` }}
          >
            {FIGURES.map((z, i) => (
              <div
                key={z.label}
                className="px-3 text-center sm:px-0 sm:text-left"
                style={i > 0 ? { borderLeft: `1px solid ${LINE}` } : undefined}
              >
                <div className="text-2xl sm:text-3xl" style={{ ...head, color: INK }}>
                  <Counter target={z.value} suffix={z.suffix} />
                </div>
                <div
                  className="mt-1 text-[11px] leading-tight sm:text-sm"
                  style={{ fontFamily: SANS, color: "rgba(59,62,68,0.75)" }}
                >
                  {z.label}
                </div>
              </div>
            ))}
          </div>

          <div className="mt-10 lg:hidden">
            <Button href="#impressions" variant="line">
              More about the clinic
            </Button>
          </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"
            style={{ borderRadius: IMAGE, background: "#eef0f2" }}
          >
            <img
              src="/templates/luma/doctor-portrait.webp"
              alt="Dr. Jonas Reiter in the consulting room"
              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 px-3.5 py-1.5 text-xs backdrop-blur sm:left-5 sm:top-5"
              style={{
                borderRadius: BUTTON,
                background: "rgba(255,255,255,0.82)",
                color: INK,
                fontFamily: SANS,
              }}
            >
              Your surgeon
            </span>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

/* --------------------------------------------------------------- treatments */

function Treatments() {
  return (
    <section id="treatments" 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">
              <Kicker>Our treatments</Kicker>
              <h2 className="mt-5 text-3xl sm:text-4xl lg:text-5xl" style={{ ...head, color: INK }}>
                Three specialties, one standard
              </h2>
            </div>
            <p className="text-base lg:col-span-5" style={{ fontFamily: SANS, color: INK_SOFT }}>
              Implantology, oral surgery and aesthetic facial surgery under one roof. Specialised.
              Discreet. At the highest level.
            </p>
          </div>
        </Reveal>

        <Reveal delay={0.1}>
          {/* From md on, the three panels share the width and the hovered one grows to a
              good double. Below that they stand one under the other, because an accordion
              has nothing to grab on a phone. */}
          <div className="mt-10 flex flex-col gap-3 sm:mt-14 md:h-[560px] md:flex-row lg:h-[620px]">
            {FIELDS.map((field) => (
              <article
                key={field.kicker}
                className="group relative h-[420px] flex-1 overflow-hidden transition-[flex-grow] duration-[900ms] md:h-auto md:hover:grow-[2.4]"
                style={{
                  borderRadius: IMAGE,
                  transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)",
                }}
              >
                <img
                  src={field.image}
                  alt={field.alt}
                  loading="lazy"
                  className="absolute inset-0 h-full w-full object-cover transition-transform duration-[1600ms] group-hover:scale-[1.06]"
                  style={{ transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)" }}
                />
                {/* Only as much gradient as the white type needs, and in the colour of
                    the text rather than a coloured one. */}
                <div
                  className="absolute inset-0"
                  style={{
                    background:
                      "linear-gradient(to top, rgba(31,33,37,0.86) 0%, rgba(31,33,37,0.42) 46%, rgba(31,33,37,0.1) 100%)",
                  }}
                />

                <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 opacity-0 transition-opacity duration-500 group-hover:opacity-100"
                      style={{ borderRadius: BUTTON, border: "1px solid rgba(255,255,255,0.4)" }}
                    >
                      <ArrowUpRight className="h-4 w-4" />
                    </span>
                  </div>

                  <div>
                    <div
                      className="text-[11px] uppercase"
                      style={{
                        fontFamily: SANS,
                        letterSpacing: "0.3em",
                        color: "rgba(255,255,255,0.6)",
                      }}
                    >
                      {field.kicker}
                    </div>
                    <h3
                      className="mt-4 max-w-md text-2xl leading-tight lg:text-[2rem]"
                      style={{ ...head, color: "#ffffff" }}
                    >
                      {field.title}
                    </h3>
                    {/* On the phone the sentence is always there, on the desktop it opens
                        on hover: 0fr to 1fr, without a fixed height. */}
                    <div
                      className="grid grid-rows-[1fr] transition-[grid-template-rows] duration-[1100ms] md:grid-rows-[0fr] md:group-hover:grid-rows-[1fr]"
                      style={{ transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)" }}
                    >
                      <div className="overflow-hidden">
                        <p
                          className="mt-5 max-w-md text-[13px] leading-relaxed transition-all duration-[800ms] 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"
                          style={{
                            fontFamily: SANS,
                            color: "rgba(255,255,255,0.78)",
                            transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)",
                          }}
                        >
                          {field.text}
                        </p>
                      </div>
                    </div>
                  </div>
                </div>
              </article>
            ))}
          </div>
        </Reveal>

        <Reveal delay={0.2}>
          <div
            id="appointment"
            className="relative mt-6 flex flex-col items-start justify-between gap-6 overflow-hidden p-6 sm:flex-row sm:items-center sm:p-8 lg:p-10"
            style={{ borderRadius: IMAGE, background: SURFACE }}
          >
            <img
              src="/templates/luma/brand-mark.svg"
              alt=""
              aria-hidden
              className="pointer-events-none absolute -right-10 -top-12 h-48 w-auto opacity-[0.06] sm:h-56 lg:h-64"
            />
            <div className="relative">
              <Kicker>Appointment</Kicker>
              <h3
                className="mt-3 max-w-xl text-xl sm:text-2xl lg:text-[1.75rem]"
                style={{ ...head, color: INK }}
              >
                First consultation within 48 hours. In person, with no waiting.
              </h3>
            </div>
            <a
              href="#contact"
              className="relative inline-flex items-center gap-2 px-6 py-3.5 text-sm text-white transition-colors"
              style={{ borderRadius: BUTTON, background: INK, fontFamily: SANS }}
            >
              Book an appointment
              <ArrowUpRight className="h-4 w-4" />
            </a>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------------ galerie */

function Gallery() {
  const [active, setActive] = useState(0);
  const start = useRef<number | null>(null);

  return (
    <section id="impressions" className="overflow-x-hidden" style={{ background: SURFACE }}>
      <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">
            <Kicker>Inside the clinic</Kicker>
            <h2 className="mt-5 text-3xl sm:text-4xl lg:text-5xl" style={{ ...head, color: INK }}>
              A place where people feel at ease
            </h2>
          </div>
          <p
            className="max-w-md text-base leading-relaxed lg:col-span-5"
            style={{ fontFamily: SANS, color: INK_SOFT }}
          >
            Bright rooms, a calm atmosphere and a team that takes its time, so every visit feels
            like less of an appointment.
          </p>
        </Reveal>

        {/* Phone: one picture per view, pushed on with the finger. */}
        <div
          className="mt-10 touch-pan-y overflow-hidden overscroll-x-contain md:hidden"
          onTouchStart={(e) => {
            start.current = e.touches[0]?.clientX ?? null;
          }}
          onTouchEnd={(e) => {
            if (start.current === null) return;
            const end = e.changedTouches[0]?.clientX;
            if (end === undefined) return;
            const weg = end - start.current;
            if (Math.abs(weg) >= 40) {
              setActive((i) =>
                weg < 0 ? Math.min(i + 1, GALLERY.length - 1) : Math.max(i - 1, 0),
              );
            }
            start.current = null;
          }}
        >
          <div
            className="flex will-change-transform"
            style={{
              transform: `translate3d(-${active * 100}%, 0, 0)`,
              transition: "transform 420ms cubic-bezier(0.22, 1, 0.36, 1)",
            }}
          >
            {GALLERY.map((image) => (
              <div key={image.src} className="w-full shrink-0 px-1">
                <div
                  className="overflow-hidden"
                  style={{ borderRadius: IMAGE, background: "#eef0f2" }}
                >
                  <img
                    src={image.src}
                    alt={image.alt}
                    loading="lazy"
                    className="aspect-[4/3] w-full object-cover"
                  />
                </div>
              </div>
            ))}
          </div>
        </div>

        <div className="mt-4 flex justify-center gap-2 md:hidden">
          {GALLERY.map((image, i) => (
            <button
              key={image.src}
              type="button"
              onClick={() => setActive(i)}
              aria-label={`Image ${i + 1}`}
              className="h-2 rounded-full transition-all duration-300"
              style={{
                width: i === active ? 24 : 8,
                background: i === active ? INK : "rgba(59,62,68,0.2)",
              }}
            />
          ))}
        </div>

        {/* Desktop: a grid of six pictures in three rows. */}
        <div className="mt-10 hidden grid-cols-12 gap-3 sm:mt-16 sm:gap-4 md:grid lg:gap-6">
          {GALLERY.map((image, i) => (
            <Reveal
              key={image.src}
              delay={(i % 2) * 0.08}
              className={`col-span-12 ${image.spalten}`}
            >
              <div
                className="group h-full overflow-hidden"
                style={{ borderRadius: IMAGE, background: "#eef0f2" }}
              >
                <img
                  src={image.src}
                  alt={image.alt}
                  loading="lazy"
                  style={image.fokus ? { objectPosition: image.fokus } : undefined}
                  className={`${image.format || "h-full"} w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105`}
                />
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------------ reasons */

function Reasons() {
  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">
            <Kicker>Why Luma</Kicker>
            <h2 className="mt-5 text-2xl sm:text-3xl lg:text-4xl" style={{ ...head, color: INK }}>
              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">
          {REASONS.map((ground, i) => (
            <Reveal key={ground.kicker} delay={i * 0.08}>
              {/* Two layers on top of each other: the light one goes up and away, the
                  dark one comes up and stays. On the phone both stand below each other
                  without a change. */}
              <div
                className="group relative h-auto overflow-hidden md:h-[340px]"
                style={{ borderRadius: IMAGE, background: SURFACE_2 }}
              >
                <div
                  className="relative flex flex-col items-center gap-6 p-8 text-center transition-all duration-[900ms] md:absolute md:inset-0 md:justify-between md:group-hover:-translate-y-3 md:group-hover:opacity-0 lg:p-10"
                  style={{
                    background: SURFACE_2,
                    transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)",
                  }}
                >
                  <div
                    className="flex h-12 w-12 items-center justify-center transition-transform duration-[900ms] group-hover:-translate-y-1"
                    style={{ borderRadius: 14, background: "#ffffff", color: INK }}
                  >
                    <ground.Icon className="h-5 w-5" strokeWidth={1.5} />
                  </div>
                  <div>
                    <Kicker>{ground.kicker}</Kicker>
                    <h3
                      className="mt-4 text-xl sm:text-2xl lg:text-[1.625rem] lg:leading-snug"
                      style={{ ...head, color: INK }}
                    >
                      {ground.title}
                    </h3>
                  </div>
                  <p
                    className="text-sm leading-relaxed md:hidden"
                    style={{ fontFamily: SANS, color: INK_SOFT }}
                  >
                    {ground.text}
                  </p>
                </div>

                <div
                  className="absolute inset-0 hidden translate-y-4 flex-col items-center justify-between p-8 text-center opacity-0 transition-all duration-[900ms] md:flex md:group-hover:translate-y-0 md:group-hover:opacity-100 lg:p-10"
                  style={{
                    borderRadius: IMAGE,
                    background: INK,
                    color: "#ffffff",
                    transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)",
                  }}
                >
                  <div
                    className="flex h-12 w-12 items-center justify-center"
                    style={{ borderRadius: 14, background: "rgba(255,255,255,0.12)" }}
                  >
                    <ground.Icon className="h-5 w-5" strokeWidth={1.5} />
                  </div>
                  <p
                    className="text-base leading-relaxed"
                    style={{ fontFamily: SANS, color: "rgba(255,255,255,0.86)" }}
                  >
                    {ground.text}
                  </p>
                  <Kicker hell>{ground.kicker}</Kicker>
                </div>
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------------- voices */

function Voices() {
  const reduce = useReducedMotion();
  const double = [...VOICES, ...VOICES];

  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">
              <Kicker>Reviews</Kicker>
              <h2 className="mt-5 text-3xl sm:text-4xl lg:text-5xl" style={{ ...head, color: INK }}>
                What our patients say
              </h2>
              <p
                className="mt-6 max-w-sm text-base leading-relaxed"
                style={{ fontFamily: SANS, color: INK_SOFT }}
              >
                More than 380 verified reviews on Google and independent platforms. Thank you to our
                patients for their trust.
              </p>

              <div className="mt-10 flex items-center gap-5">
                <div className="flex items-baseline gap-2">
                  <span className="text-5xl" style={{ ...head, color: INK }}>
                    4.9
                  </span>
                  <span className="text-sm" style={{ fontFamily: SANS, color: INK_SOFT }}>
                    / 5
                  </span>
                </div>
                <div className="h-10 w-px" style={{ background: LINE }} />
                <div>
                  <Stars groesse={14} />
                  <div className="mt-1 text-xs" style={{ fontFamily: SANS, color: INK_SOFT }}>
                    from more than 380 reviews
                  </div>
                </div>
              </div>
            </div>
          </Reveal>

          <div className="lg:col-span-8">
            <div className="relative -mx-5 overflow-hidden sm:-mx-6 lg:-mr-12">
              <div
                aria-hidden
                className="pointer-events-none absolute inset-y-0 left-0 z-10 hidden w-16 sm:w-24 md:block"
                style={{ background: "linear-gradient(to right, #ffffff, rgba(255,255,255,0))" }}
              />
              <div
                aria-hidden
                className="pointer-events-none absolute inset-y-0 right-0 z-10 hidden w-16 sm:w-24 md:block"
                style={{ background: "linear-gradient(to left, #ffffff, rgba(255,255,255,0))" }}
              />
              {/* Phone: push. Desktop: a quiet drift that stops on hover. Under reduced
                  motion it stands still. */}
              <motion.div
                className="flex gap-5 snap-x snap-mandatory overflow-x-auto overscroll-x-contain px-5 pb-4 sm:px-6 md:w-max md:snap-none md:overflow-visible [&::-webkit-scrollbar]:hidden"
                style={{ scrollbarWidth: "none" }}
                animate={reduce ? undefined : { x: ["0%", "-50%"] }}
                transition={reduce ? undefined : { duration: 46, ease: "linear", repeat: Infinity }}
              >
                {double.map((voice, i) => (
                  <article
                    key={`${voice.name}-${i}`}
                    className="flex w-[85vw] max-w-[360px] shrink-0 snap-center flex-col p-9 md:w-[360px] md:snap-none"
                    style={{ borderRadius: IMAGE, background: SURFACE_2 }}
                  >
                    <Stars groesse={15} />
                    <p
                      className="mt-6 text-base leading-relaxed"
                      style={{ fontFamily: SANS, color: INK_SOFT }}
                    >
                      {voice.text}
                    </p>
                    <div className="mt-auto flex items-center gap-4 pt-10">
                      <span
                        className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full"
                        style={{ background: "#ffffff" }}
                      >
                        {voice.quelle === "Google" ? (
                          <GoogleMark className="h-6 w-6" />
                        ) : (
                          <BadgeCheck
                            className="h-6 w-6"
                            style={{ color: INK }}
                            strokeWidth={1.5}
                          />
                        )}
                      </span>
                      <div className="min-w-0">
                        <div
                          className="text-sm"
                          style={{ fontFamily: SANS, color: INK, fontWeight: 500 }}
                        >
                          {voice.name}
                        </div>
                        <div
                          className="mt-1 text-[11px] uppercase"
                          style={{
                            fontFamily: SANS,
                            letterSpacing: "0.18em",
                            color: "rgba(59,62,68,0.75)",
                          }}
                        >
                          {voice.behandlung}
                        </div>
                      </div>
                    </div>
                  </article>
                ))}
              </motion.div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ----------------------------------------------------------------- audiences */

function Audiences() {
  return (
    <section id="audiences" className="bg-white">
      <div className="mx-auto max-w-7xl px-5 pb-16 sm:px-6 sm:pb-24 lg:pb-32">
        <div className="grid grid-cols-1 gap-3 sm:gap-4 md:grid-cols-2">
          {AUDIENCES.map((box, i) => (
            <Reveal key={box.kicker} delay={i * 0.1} className="h-full">
              <article
                className="flex h-full flex-col justify-between p-7 sm:p-10 lg:p-14"
                style={{ borderRadius: IMAGE, background: SURFACE_2 }}
              >
                <div>
                  <Kicker>{box.kicker}</Kicker>
                  <h3
                    className="mt-5 text-2xl sm:text-3xl lg:text-4xl"
                    style={{ ...head, color: INK }}
                  >
                    {box.title}
                  </h3>
                  <p
                    className="mt-5 max-w-lg text-base leading-relaxed"
                    style={{ fontFamily: SANS, color: INK_SOFT }}
                  >
                    {box.text}
                  </p>
                  <ul className="mt-8 space-y-4">
                    {box.points.map((dot) => (
                      <li
                        key={dot}
                        className="flex items-center gap-3 text-sm"
                        style={{ fontFamily: SANS, color: INK_SOFT }}
                      >
                        <span
                          className="flex h-6 w-6 shrink-0 items-center justify-center"
                          style={{ borderRadius: BUTTON, background: "#ffffff", color: INK }}
                        >
                          <Check className="h-3.5 w-3.5" strokeWidth={2} />
                        </span>
                        {dot}
                      </li>
                    ))}
                  </ul>
                </div>
                <a
                  href="#contact"
                  className="group/link mt-10 inline-flex w-fit items-center gap-2 px-6 py-3 text-sm text-white transition-colors"
                  style={{ borderRadius: BUTTON, background: INK, fontFamily: SANS }}
                >
                  {box.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 */

function Footer() {
  return (
    <footer
      id="contact"
      className="relative overflow-hidden"
      style={{ background: INK, color: "#ffffff" }}
    >
      <div className="mx-auto max-w-7xl px-5 pt-20 sm:px-6 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">
                <Wordmark className="h-6 w-auto" />
                <span className="h-6 w-px" style={{ background: "rgba(255,255,255,0.2)" }} />
                <span className="text-sm" style={{ ...head, color: "rgba(255,255,255,0.7)" }}>
                  {CLINIC.zusatz}
                </span>
              </div>
              <p
                className="mt-6 max-w-xs text-sm leading-relaxed"
                style={{ fontFamily: SANS, color: "rgba(255,255,255,0.6)" }}
              >
                Personal. Precise. Discreet. Your clinic for implantology, oral surgery and
                aesthetic treatments.
              </p>
              <div
                className="mt-5 text-xs"
                style={{ fontFamily: SANS, color: "rgba(255,255,255,0.6)" }}
              >
                Mon to Fri 08:00 to 18:00 · Sat by appointment
              </div>
            </div>

            <div className="grid grid-cols-1 gap-10 sm:grid-cols-3 lg:col-span-5">
              {FOOT_GROUPS.map((group) => (
                <div key={group.title}>
                  <Kicker hell>{group.title}</Kicker>
                  <ul className="mt-6 space-y-3">
                    {group.links.map((link) => (
                      <li key={link}>
                        <a
                          href="#top"
                          className="group inline-flex items-center text-sm transition-colors"
                          style={{ fontFamily: SANS, color: "rgba(255,255,255,0.8)" }}
                        >
                          <span className="relative">
                            {link}
                            <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">
              <Kicker hell>Contact</Kicker>
              <address className="mt-6 not-italic">
                <ul
                  className="space-y-5 text-sm leading-relaxed"
                  style={{ fontFamily: SANS, color: "rgba(255,255,255,0.7)" }}
                >
                  <li className="flex items-center gap-4">
                    <span
                      className="flex h-9 w-9 shrink-0 items-center justify-center"
                      style={{ borderRadius: BUTTON, border: "1px solid rgba(255,255,255,0.16)" }}
                    >
                      <MapPin className="h-4 w-4" strokeWidth={1.5} />
                    </span>
                    <span>
                      <span className="block text-white">{CLINIC.strasse}</span>
                      <span className="block" style={{ color: "rgba(255,255,255,0.6)" }}>
                        {CLINIC.place}
                      </span>
                    </span>
                  </li>
                  <li className="flex items-center gap-4">
                    <span
                      className="flex h-9 w-9 shrink-0 items-center justify-center"
                      style={{ borderRadius: BUTTON, border: "1px solid rgba(255,255,255,0.16)" }}
                    >
                      <Phone className="h-4 w-4" strokeWidth={1.5} />
                    </span>
                    <a
                      href={CLINIC.telefonHref}
                      className="text-white transition-colors hover:opacity-75"
                    >
                      {CLINIC.telefon}
                    </a>
                  </li>
                  <li className="flex items-center gap-4">
                    <span
                      className="flex h-9 w-9 shrink-0 items-center justify-center"
                      style={{ borderRadius: BUTTON, border: "1px solid rgba(255,255,255,0.16)" }}
                    >
                      <Mail className="h-4 w-4" strokeWidth={1.5} />
                    </span>
                    <a
                      href={CLINIC.mailHref}
                      className="text-white transition-colors hover:opacity-75"
                    >
                      {CLINIC.mail}
                    </a>
                  </li>
                </ul>
              </address>
            </div>
          </div>
        </Reveal>

        <div
          className="flex flex-col items-start justify-between gap-6 py-8 sm:flex-row sm:items-center"
          style={{ borderTop: "1px solid rgba(255,255,255,0.1)" }}
        >
          <div
            className="flex flex-wrap items-center gap-x-6 gap-y-2 text-xs"
            style={{ fontFamily: SANS, color: "rgba(255,255,255,0.6)" }}
          >
            <span>© 2026 {CLINIC.name}</span>
            <a href="#top" className="transition-colors hover:text-white">
              Legal notice
            </a>
            <a href="#top" className="transition-colors hover:text-white">
              Privacy
            </a>
            <a href="#top" 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 }) => (
              <a
                key={label}
                href="#top"
                aria-label={label}
                className="flex h-9 w-9 items-center justify-center transition-colors hover:text-white"
                style={{
                  borderRadius: BUTTON,
                  border: "1px solid rgba(255,255,255,0.12)",
                  color: "rgba(255,255,255,0.7)",
                }}
              >
                <Icon className="h-4 w-4" strokeWidth={1.5} />
              </a>
            ))}
          </div>
        </div>
      </div>
    </footer>
  );
}

/* --------------------------------------------------------------------- page */

export function LumaClinicPage() {
  return (
    <div style={{ background: "#ffffff", fontFamily: SANS, color: INK }}>
      <Header />
      <main>
        <Held />
        <Doctor />
        <Treatments />
        <Gallery />
        <Reasons />
        <Voices />
        <Audiences />
      </main>
      <Footer />
    </div>
  );
}

export default LumaClinicPage;
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