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 complete page as a single file. Copy it into ./components and add the component to your route.

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 = "#8a8d93";
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 kopf: CSSProperties = { fontFamily: HEAD, fontWeight: 500, letterSpacing: "-0.02em" };

/** Ein kleiner Radius statt runder Kapseln, so wie im uebrigen Designsystem. */
const KNOPF = 6;
const BILD = 22;

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

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

const KLINIK = {
  name: "Luma Clinic",
  zusatz: "Oral & Facial Clinic",
  strasse: "123 Example Street",
  ort: "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 = [
  {
    titel: "Implantology",
    bild: "/templates/luma/menu-implantology.webp",
    anriss: "Fixed restorations with a natural appearance, for quality of life that lasts.",
    punkte: ["Single implants", "All-on-4", "Bone grafting", "Immediate implants"],
  },
  {
    titel: "Oral surgery",
    bild: "/templates/luma/menu-surgery.webp",
    anriss: "Precise surgery of the mouth, jaw and face, performed by experienced specialists.",
    punkte: ["Wisdom teeth", "Jaw surgery", "Cyst removal", "Facial trauma"],
  },
  {
    titel: "Aesthetics",
    bild: "/templates/luma/menu-aesthetics.webp",
    anriss: "Gentle procedures for a fresh, natural result and a rested appearance.",
    punkte: ["Botulinum toxin", "Dermal fillers", "Eyelid lift", "PRP therapy"],
  },
];

/** Der dunkle Streifen rechts im Menue: ein Weg fuer alle, die nicht wissen,
 *  unter welche der drei Spalten ihr Anliegen faellt. */
const MEGA_RAT = {
  kicker: "Personal consultation",
  titel: "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 ZAHLEN = [
  { wert: 15, suffix: "+", label: "Years of experience" },
  { wert: 8000, suffix: "+", label: "Procedures" },
  { wert: 100, suffix: "%", label: "Private practice" },
];

const FELDER = [
  {
    kicker: "Implantology",
    titel: "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.",
    bild: "/templates/luma/bento-implantology.webp",
    alt: "Close-up of a patient smiling after implant treatment",
  },
  {
    kicker: "Oral surgery",
    titel: "Surgery with a steady hand",
    text: "Wisdom teeth, jaw corrections, cysts and trauma. Routine, gentle and supported by a modern operating suite.",
    bild: "/templates/luma/bento-surgery.webp",
    alt: "Surgeon at work in the operating suite",
  },
  {
    kicker: "Aesthetics",
    titel: "Your natural expression",
    text: "Botulinum toxin, dermal fillers and aesthetic procedures, used with restraint. So you look like yourself, only more rested.",
    bild: "/templates/luma/bento-aesthetics.webp",
    alt: "Aesthetic consultation in a bright treatment room",
  },
];

const GALERIE = [
  {
    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 GRUENDE = [
  {
    Icon: Award,
    kicker: "Experience",
    titel: "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",
    titel: "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",
    titel: "Recognised experts in implantology",
    text: "Specialised in complex cases, immediate implants and aesthetically perfect restorations, always with a natural result.",
  },
];

const STIMMEN = [
  {
    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 ZIELGRUPPEN = [
  {
    kicker: "For patients",
    titel: "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.",
    punkte: ["Process and booking", "Frequently asked questions", "Getting here and parking"],
    cta: "View patient information",
  },
  {
    kicker: "For referrers",
    titel: "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.",
    punkte: ["Online referral form", "Digital reporting", "Direct line to the clinical lead"],
    cta: "Start a referral",
  },
];

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

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

/** Alles kommt gleich: ein kurzer Hub, ein langes Auslaufen, einmal. */
function Reveal({
  children,
  delay = 0,
  className = "",
  style,
}: {
  children: ReactNode;
  delay?: number;
  className?: string;
  style?: CSSProperties;
}) {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const sichtbar = useInView(ref, { once: true, margin: "-80px" });
  return (
    <motion.div
      ref={ref}
      className={className}
      style={style}
      initial={reduce ? false : { opacity: 0, y: 28 }}
      animate={sichtbar || reduce ? { opacity: 1, y: 0 } : { opacity: 0, y: 28 }}
      transition={{ duration: reduce ? 0 : 0.9, ease: WEICH, delay: reduce ? 0 : delay }}
    >
      {children}
    </motion.div>
  );
}

/** Die Wortmarke: vier Buchstaben aus je einem Pfad, Farbe vom Elternteil. */
function Wortmarke({ 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>
  );
}

/** Die vier Google-Farben, so wie sie in der Marke stehen. */
function GoogleMarke({ 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 Sterne({ groesse = 16, farbe = GOLD }: { groesse?: number; farbe?: string }) {
  return (
    <span className="inline-flex items-center gap-1" style={{ color: farbe }} aria-hidden>
      {Array.from({ length: 5 }).map((_, i) => (
        <Star
          key={i}
          style={{ width: groesse, height: groesse }}
          fill="currentColor"
          strokeWidth={0}
        />
      ))}
    </span>
  );
}

/** Die Zahl zaehlt einmal hoch, wenn sie ins Bild kommt. */
function Zaehler({ ziel, suffix = "" }: { ziel: number; suffix?: string }) {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLSpanElement>(null);
  const sichtbar = useInView(ref, { once: true, margin: "-60px" });
  const [wert, setWert] = useState(0);

  useEffect(() => {
    if (!sichtbar) return;
    if (reduce) {
      setWert(ziel);
      return;
    }
    const dauer = 1400;
    const start = performance.now();
    let id = 0;
    const schritt = (jetzt: number) => {
      const t = Math.min(1, (jetzt - start) / dauer);
      // Am Ende ausrollen: eine Zahl, die abrupt stehen bleibt, wirkt wie ein
      // Sprung, nicht wie ein Zaehler.
      setWert(Math.round(ziel * (1 - Math.pow(1 - t, 3))));
      if (t < 1) id = requestAnimationFrame(schritt);
    };
    id = requestAnimationFrame(schritt);
    return () => cancelAnimationFrame(id);
  }, [sichtbar, reduce, ziel]);

  return (
    <span ref={ref} className="tabular-nums">
      {wert.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.6)",
      }}
    >
      {children}
    </span>
  );
}

function Knopf({
  children,
  href = "#appointment",
  variante = "voll",
  Icon,
}: {
  children: ReactNode;
  href?: string;
  variante?: "voll" | "linie";
  Icon?: typeof ArrowRight;
}) {
  const voll = variante === "voll";
  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: KNOPF,
        fontFamily: SANS,
        background: voll ? INK : "#ffffff",
        color: voll ? "#ffffff" : INK,
        border: voll ? "1px solid transparent" : `1px solid ${LINE}`,
      }}
    >
      {children}
      {Icon ? (
        // Das Symbol erscheint erst beim Hover und schiebt dabei den Text nicht
        // um: es waechst aus der Breite null heraus.
        <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 Kopfzeile() {
  const [offen, setOffen] = useState(false);
  const [megaOffen, setMegaOffen] = useState(false);
  const [behandlungen, setBehandlungen] = useState(false);

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

  return (
    <>
      {/* Der Schleier liegt ausserhalb des Kopfs. Der Kopf hat einen
          Hintergrund-Weichzeichner, und ein Element mit backdrop-filter ist der
          Bezugsrahmen fuer position:fixed seiner Nachfahren: drinnen waere der
          Schleier nur so hoch wie der Kopf selbst. */}
      <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={KLINIK.name}>
            <Wortmarke 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={{ ...kopf, color: INK_SOFT }}>
              {KLINIK.zusatz}
            </span>
          </a>

          <nav className="hidden items-center gap-10 lg:flex">
            {NAV.map((eintrag) =>
              eintrag.mega ? (
                // Auf- und zugehen haengt am Zustand, nicht an :hover. So kann
                // der Schleier ausserhalb des Kopfs liegen, und die Tastatur
                // kommt ueber den Fokus an dieselbe Tafel.
                <div
                  key={eintrag.label}
                  onMouseEnter={() => setBehandlungen(true)}
                  onMouseLeave={() => setBehandlungen(false)}
                  onFocus={() => setBehandlungen(true)}
                  onBlur={(e) => {
                    if (!e.currentTarget.contains(e.relatedTarget as Node)) setBehandlungen(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 }}
                  >
                    {eintrag.label}
                    <ChevronDown
                      className={`h-4 w-4 transition-transform duration-300 ${
                        behandlungen ? "rotate-180" : ""
                      }`}
                    />
                  </button>
                  <Megamenue offen={behandlungen} />
                </div>
              ) : (
                <a
                  key={eintrag.label}
                  href={eintrag.href}
                  className="text-sm transition-colors hover:opacity-70"
                  style={{ fontFamily: SANS, color: INK_SOFT }}
                >
                  {eintrag.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: KNOPF, background: INK, fontFamily: SANS }}
            >
              Book appointment
            </a>
            <button
              type="button"
              aria-label={offen ? "Close menu" : "Open menu"}
              aria-expanded={offen}
              onClick={() => setOffen((v) => !v)}
              className="inline-flex h-10 w-10 items-center justify-center lg:hidden"
              style={{ borderRadius: KNOPF, border: `1px solid ${LINE}`, color: INK_SOFT }}
            >
              {offen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
            </button>
          </div>
        </div>

        {offen ? (
          <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((eintrag) =>
                eintrag.mega ? (
                  <div key={eintrag.label} style={{ borderBottom: `1px solid ${LINE}` }}>
                    <button
                      type="button"
                      onClick={() => setMegaOffen((v) => !v)}
                      aria-expanded={megaOffen}
                      className="flex w-full items-center justify-between py-4 text-lg"
                      style={{ color: INK, fontFamily: SANS }}
                    >
                      {eintrag.label}
                      <ChevronDown
                        className={`h-5 w-5 transition-transform duration-300 ${
                          megaOffen ? "rotate-180" : ""
                        }`}
                      />
                    </button>
                    {/* Auf- und zuklappen ueber die Zeilenhoehe des Rasters: das
                      laeuft weich, ohne eine Hoehe zu kennen. */}
                    <div
                      className={`grid transition-all duration-300 ease-out ${
                        megaOffen ? "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((gruppe) => (
                            <div key={gruppe.titel} className="flex flex-col gap-2">
                              <Kicker>{gruppe.titel}</Kicker>
                              <div className="flex flex-col">
                                {gruppe.punkte.map((punkt) => (
                                  <a
                                    key={punkt}
                                    href="#treatments"
                                    onClick={() => setOffen(false)}
                                    className="py-2 text-base"
                                    style={{ color: INK, fontFamily: SANS }}
                                  >
                                    {punkt}
                                  </a>
                                ))}
                              </div>
                            </div>
                          ))}
                        </div>
                      </div>
                    </div>
                  </div>
                ) : (
                  <a
                    key={eintrag.label}
                    href={eintrag.href}
                    onClick={() => setOffen(false)}
                    className="block py-4 text-lg"
                    style={{ borderBottom: `1px solid ${LINE}`, color: INK, fontFamily: SANS }}
                  >
                    {eintrag.label}
                  </a>
                ),
              )}
              <a
                href="#appointment"
                onClick={() => setOffen(false)}
                className="mt-8 inline-flex items-center justify-center px-6 py-3.5 text-sm text-white"
                style={{ borderRadius: KNOPF, background: INK, fontFamily: SANS }}
              >
                Book appointment
              </a>
            </nav>
          </div>
        ) : null}
      </header>
    </>
  );
}

/** Das grosse Menue unter "Treatments". Nur ab lg, per Hover. */
function Megamenue({ offen }: { offen: boolean }) {
  return (
    <div
      className="absolute inset-x-0 top-full z-40 overflow-hidden transition-all duration-200"
      style={{
        visibility: offen ? "visible" : "hidden",
        opacity: offen ? 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((gruppe, i) => (
            <div
              key={gruppe.titel}
              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 }}
              >
                {/* Das Bild waechst nur beim Anfahren der Spalte, langsam,
                      wie ueberall sonst auf dieser Seite. */}
                <img
                  src={gruppe.bild}
                  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={{ ...kopf, color: INK }}>
                  {gruppe.titel}
                </h3>
                <p
                  className="mt-2 text-sm leading-relaxed"
                  style={{ fontFamily: SANS, color: INK_SOFT }}
                >
                  {gruppe.anriss}
                </p>
              </div>

              <nav className="flex flex-col gap-3">
                {gruppe.punkte.map((punkt) => (
                  <a
                    key={punkt}
                    href="#treatments"
                    className="group/zeile flex items-center justify-between text-sm transition-colors hover:opacity-70"
                    style={{ fontFamily: SANS, color: INK }}
                  >
                    {punkt}
                    {/* Der Pfeil faehrt von links ein, statt einfach da zu
                          sein: die Zeile bekommt eine Richtung. */}
                    <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_RAT.kicker}
              </span>
              <h4 className="mt-5 text-[22px] leading-tight" style={{ ...kopf, color: "#ffffff" }}>
                {MEGA_RAT.titel}
              </h4>
              <p
                className="mt-4 text-sm leading-relaxed"
                style={{ fontFamily: SANS, color: "rgba(255,255,255,0.7)" }}
              >
                {MEGA_RAT.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: KNOPF, background: "#ffffff", color: INK, fontFamily: SANS }}
            >
              {MEGA_RAT.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: KNOPF,
              border: `1px solid ${LINE}`,
              color: "rgba(59,62,68,0.62)",
              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={{ ...kopf, 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">
            <Knopf href="#treatments" Icon={ArrowRight}>
              Explore treatments
            </Knopf>
            <Knopf href="#appointment" variante="linie" Icon={CalendarDays}>
              Book an appointment
            </Knopf>
          </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">
                <Sterne 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">
          <HeldBild />
        </Reveal>
      </div>
    </section>
  );
}

/**
 * Das Heldenbild mit drei Schildern darauf. Sie folgen der Maus, jedes mit
 * eigenem Faktor und eigener Richtung: gleiche Faktoren saehen aus wie eine
 * verschobene Ebene, verschiedene wie drei Dinge im Raum.
 */
function HeldBild() {
  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 bewegen = 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 zurueck = 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 bewegungen = [
    { x: x1, y: y1 },
    { x: x2, y: y2 },
    { x: x3, y: y3 },
  ];

  return (
    <div
      ref={ref}
      onMouseMove={bewegen}
      onMouseLeave={zurueck}
      className="group relative overflow-hidden"
      style={{ borderRadius: BILD, 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: bewegungen[i].x,
              y: bewegungen[i].y,
              borderRadius: KNOPF,
              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>
  );
}

/* -------------------------------------------------------------------- arzt */

function Arzt() {
  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={{ ...kopf, 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">
            <Knopf href="#impressions" variante="linie">
              More about the clinic
            </Knopf>
          </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}` }}
          >
            {ZAHLEN.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={{ ...kopf, color: INK }}>
                  <Zaehler ziel={z.wert} suffix={z.suffix} />
                </div>
                <div
                  className="mt-1 text-[11px] leading-tight sm:text-sm"
                  style={{ fontFamily: SANS, color: "rgba(59,62,68,0.7)" }}
                >
                  {z.label}
                </div>
              </div>
            ))}
          </div>

          <div className="mt-10 lg:hidden">
            <Knopf href="#impressions" variante="linie">
              More about the clinic
            </Knopf>
          </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: BILD, 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: KNOPF,
                background: "rgba(255,255,255,0.82)",
                color: INK,
                fontFamily: SANS,
              }}
            >
              Your surgeon
            </span>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

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

function Behandlungen() {
  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={{ ...kopf, 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}>
          {/* Ab md teilen sich die drei Tafeln die Breite und die angefahrene
              waechst auf gut das Doppelte. Darunter stehen sie untereinander,
              weil eine Ziehharmonika auf dem Telefon nichts zu greifen hat. */}
          <div className="mt-10 flex flex-col gap-3 sm:mt-14 md:h-[560px] md:flex-row lg:h-[620px]">
            {FELDER.map((feld) => (
              <article
                key={feld.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: BILD,
                  transitionTimingFunction: "cubic-bezier(0.22,1,0.36,1)",
                }}
              >
                <img
                  src={feld.bild}
                  alt={feld.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)" }}
                />
                {/* Nur so viel Verlauf, wie die weisse Schrift braucht, und in
                    der Farbe des Textes statt in einer bunten. */}
                <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: KNOPF, 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)",
                      }}
                    >
                      {feld.kicker}
                    </div>
                    <h3
                      className="mt-4 max-w-md text-2xl leading-tight lg:text-[2rem]"
                      style={{ ...kopf, color: "#ffffff" }}
                    >
                      {feld.titel}
                    </h3>
                    {/* Der Satz steht auf dem Telefon immer, am Schreibtisch
                        klappt er beim Anfahren auf: 0fr zu 1fr, ohne feste
                        Hoehe. */}
                    <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)",
                          }}
                        >
                          {feld.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: BILD, 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={{ ...kopf, 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: KNOPF, background: INK, fontFamily: SANS }}
            >
              Book an appointment
              <ArrowUpRight className="h-4 w-4" />
            </a>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

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

function Galerie() {
  const [aktiv, setAktiv] = 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={{ ...kopf, 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>

        {/* Telefon: ein Bild je Ansicht, mit dem Finger weiterschieben. */}
        <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 ende = e.changedTouches[0]?.clientX;
            if (ende === undefined) return;
            const weg = ende - start.current;
            if (Math.abs(weg) >= 40) {
              setAktiv((i) => (weg < 0 ? Math.min(i + 1, GALERIE.length - 1) : Math.max(i - 1, 0)));
            }
            start.current = null;
          }}
        >
          <div
            className="flex will-change-transform"
            style={{
              transform: `translate3d(-${aktiv * 100}%, 0, 0)`,
              transition: "transform 420ms cubic-bezier(0.22, 1, 0.36, 1)",
            }}
          >
            {GALERIE.map((bild) => (
              <div key={bild.src} className="w-full shrink-0 px-1">
                <div
                  className="overflow-hidden"
                  style={{ borderRadius: BILD, background: "#eef0f2" }}
                >
                  <img
                    src={bild.src}
                    alt={bild.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">
          {GALERIE.map((bild, i) => (
            <button
              key={bild.src}
              type="button"
              onClick={() => setAktiv(i)}
              aria-label={`Image ${i + 1}`}
              className="h-2 rounded-full transition-all duration-300"
              style={{
                width: i === aktiv ? 24 : 8,
                background: i === aktiv ? INK : "rgba(59,62,68,0.2)",
              }}
            />
          ))}
        </div>

        {/* Schreibtisch: ein Raster aus sechs Bildern in drei Reihen. */}
        <div className="mt-10 hidden grid-cols-12 gap-3 sm:mt-16 sm:gap-4 md:grid lg:gap-6">
          {GALERIE.map((bild, i) => (
            <Reveal key={bild.src} delay={(i % 2) * 0.08} className={`col-span-12 ${bild.spalten}`}>
              <div
                className="group h-full overflow-hidden"
                style={{ borderRadius: BILD, background: "#eef0f2" }}
              >
                <img
                  src={bild.src}
                  alt={bild.alt}
                  loading="lazy"
                  style={bild.fokus ? { objectPosition: bild.fokus } : undefined}
                  className={`${bild.format || "h-full"} w-full object-cover transition-transform duration-[1200ms] ease-out group-hover:scale-105`}
                />
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------------ gruende */

function Gruende() {
  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={{ ...kopf, 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">
          {GRUENDE.map((grund, i) => (
            <Reveal key={grund.kicker} delay={i * 0.08}>
              {/* Zwei Lagen uebereinander: die helle geht hoch und weg, die
                  dunkle kommt hoch und bleibt. Auf dem Telefon steht beides
                  ohne Wechsel untereinander. */}
              <div
                className="group relative h-auto overflow-hidden md:h-[340px]"
                style={{ borderRadius: BILD, 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 }}
                  >
                    <grund.Icon className="h-5 w-5" strokeWidth={1.5} />
                  </div>
                  <div>
                    <Kicker>{grund.kicker}</Kicker>
                    <h3
                      className="mt-4 text-xl sm:text-2xl lg:text-[1.625rem] lg:leading-snug"
                      style={{ ...kopf, color: INK }}
                    >
                      {grund.titel}
                    </h3>
                  </div>
                  <p
                    className="text-sm leading-relaxed md:hidden"
                    style={{ fontFamily: SANS, color: INK_SOFT }}
                  >
                    {grund.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: BILD,
                    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)" }}
                  >
                    <grund.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)" }}
                  >
                    {grund.text}
                  </p>
                  <Kicker hell>{grund.kicker}</Kicker>
                </div>
              </div>
            </Reveal>
          ))}
        </div>
      </div>
    </section>
  );
}

/* ------------------------------------------------------------------ stimmen */

function Stimmen() {
  const reduce = useReducedMotion();
  const doppelt = [...STIMMEN, ...STIMMEN];

  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={{ ...kopf, 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={{ ...kopf, 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>
                  <Sterne 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))" }}
              />
              {/* Telefon: schieben. Schreibtisch: eine ruhige Wanderung, die
                  beim Anfahren stehen bleibt. Bei reduzierter Bewegung steht
                  sie ganz. */}
              <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 }}
              >
                {doppelt.map((stimme, i) => (
                  <article
                    key={`${stimme.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: BILD, background: SURFACE_2 }}
                  >
                    <Sterne groesse={15} />
                    <p
                      className="mt-6 text-base leading-relaxed"
                      style={{ fontFamily: SANS, color: INK_SOFT }}
                    >
                      {stimme.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" }}
                      >
                        {stimme.quelle === "Google" ? (
                          <GoogleMarke 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 }}
                        >
                          {stimme.name}
                        </div>
                        <div
                          className="mt-1 text-[11px] uppercase"
                          style={{
                            fontFamily: SANS,
                            letterSpacing: "0.18em",
                            color: "rgba(59,62,68,0.4)",
                          }}
                        >
                          {stimme.behandlung}
                        </div>
                      </div>
                    </div>
                  </article>
                ))}
              </motion.div>
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* --------------------------------------------------------------- zielgruppen */

function Zielgruppen() {
  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">
          {ZIELGRUPPEN.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: BILD, background: SURFACE_2 }}
              >
                <div>
                  <Kicker>{box.kicker}</Kicker>
                  <h3
                    className="mt-5 text-2xl sm:text-3xl lg:text-4xl"
                    style={{ ...kopf, color: INK }}
                  >
                    {box.titel}
                  </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.punkte.map((punkt) => (
                      <li
                        key={punkt}
                        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: KNOPF, background: "#ffffff", color: INK }}
                        >
                          <Check className="h-3.5 w-3.5" strokeWidth={2} />
                        </span>
                        {punkt}
                      </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: KNOPF, 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>
  );
}

/* ------------------------------------------------------------------- fusszeile */

function Fusszeile() {
  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">
                <Wortmarke className="h-6 w-auto" />
                <span className="h-6 w-px" style={{ background: "rgba(255,255,255,0.2)" }} />
                <span className="text-sm" style={{ ...kopf, color: "rgba(255,255,255,0.7)" }}>
                  {KLINIK.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.42)" }}
              >
                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">
              {FUSS_GRUPPEN.map((gruppe) => (
                <div key={gruppe.titel}>
                  <Kicker hell>{gruppe.titel}</Kicker>
                  <ul className="mt-6 space-y-3">
                    {gruppe.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: KNOPF, 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">{KLINIK.strasse}</span>
                      <span className="block" style={{ color: "rgba(255,255,255,0.6)" }}>
                        {KLINIK.ort}
                      </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: KNOPF, border: "1px solid rgba(255,255,255,0.16)" }}
                    >
                      <Phone className="h-4 w-4" strokeWidth={1.5} />
                    </span>
                    <a
                      href={KLINIK.telefonHref}
                      className="text-white transition-colors hover:opacity-75"
                    >
                      {KLINIK.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: KNOPF, border: "1px solid rgba(255,255,255,0.16)" }}
                    >
                      <Mail className="h-4 w-4" strokeWidth={1.5} />
                    </span>
                    <a
                      href={KLINIK.mailHref}
                      className="text-white transition-colors hover:opacity-75"
                    >
                      {KLINIK.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.5)" }}
          >
            <span>© 2026 {KLINIK.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: KNOPF,
                  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 }}>
      <Kopfzeile />
      <main>
        <Held />
        <Arzt />
        <Behandlungen />
        <Galerie />
        <Gruende />
        <Stimmen />
        <Zielgruppen />
      </main>
      <Fusszeile />
    </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