Zum Inhalt springen
Alle Templates

Templates/Klinik für Kieferchirurgie

Eine ruhige, fast einfarbige Seite für eine Privatklinik. Anthrazit, Weiß und ein sehr helles Grau tragen alles; die einzige Farbe ist das Gold der Bewertungssterne. Auf dem Heldenbild liegen drei Schilder, die dem Zeiger folgen, die Behandlungs-Tafeln wachsen beim Anfahren und klappen ihren Text auf, und die Bewertungen wandern von allein vorbei. Großzügig gerundete Bildrahmen, keine Schatten, keine bunten Überlagerungen.

Gesundheit & PraxisKomplette Seite

Die fertige Seite zum Durchscrollen, umschaltbar zwischen Desktop, Tablet und Handy.

Quellcode

Die komplette Seite als eine Datei. Kopiere sie nach ./components und binde die Komponente in deine Route ein.

import {
  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

Häufige Fragen

Eine kuratierte Landingpage aus aufeinander abgestimmten Sections der Library, in sinnvoller Reihenfolge von Hero bis Footer. Du kopierst die Sections einzeln und setzt sie mit dem Kompositions-Snippet zusammen.

Deine Frage war nicht dabei? Wir helfen gern weiter.

Kontakt aufnehmen