/* global React, ReactDOM */
const { useState, useEffect, useRef, useMemo } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#FBBF24",
  "heroVariant": "editorial",
  "showStripes": true,
  "darkness": 8
}/*EDITMODE-END*/;

// --- Location SEO config ---------------------------------------------------
// Per-city copy for the SEO landing pages. The active key is set by each city's
// HTML file via `window.__LOCATION_KEY__`. Homepage sets nothing → null.
const LOCATIONS = {
  unterhaching: {
    slug: "autoaufbereitung-unterhaching",
    name: "Unterhaching",
    nameGenitive: "Unterhachings",
    distance: "8 Min.",
    direction: "südlich",
    eyebrow: "Putzbrunner Straße 89 · 8 Min. von Unterhaching",
    heroLine1: "Autoaufbereitung",
    heroLine2Pre: "in",
    heroLine2Em: "Unterhaching",
    heroLede: "Premium Fahrzeugaufbereitung für Unterhachings Fahrzeugbesitzer — Lackkorrektur, Keramikversiegelung und Innenreinigung in Manufaktur-Qualität. Nur 8 Minuten Anfahrt aus Unterhaching, mit Hol- und Bringdienst möglich.",
    localTitle: "Ihre Manufaktur für Autoaufbereitung in Unterhaching.",
    localBody: [
      "Unterhaching liegt direkt an unserer Werkstatt in der Putzbrunner Straße — keine 8 Minuten über die Tegernseer Landstraße. Für Kunden aus Fasanenpark, Am Dorfanger oder rund um die S3-Station bringen wir das Fahrzeug auf Wunsch persönlich vom Hof ab und retour.",
      "Von der einstufigen Politur am Daily-Driver bis zur mehrstufigen Lackkorrektur mit Keramikversiegelung am Liebhaberfahrzeug: Wir arbeiten ausschließlich von Hand, mit Lackmessung und unter Tageslicht-Endkontrolle. Kratzer entfernen, Innenreinigung, Lederpflege oder Smart-Repair — Sie bekommen vorab eine ehrliche Einschätzung, kein Pauschalangebot."
    ],
    nearby: ["taufkirchen", "oberhaching"],
    metaTitle: "Autoaufbereitung Unterhaching | Glanzmanufaktur München",
    metaDescription: "Premium Autoaufbereitung in Unterhaching: Lackkorrektur, Keramikversiegelung & Innenreinigung. 5,0 ★ Google · Manufaktur in München-Perlach, 8 Min. von Unterhaching. Jetzt Termin sichern.",
  },
  taufkirchen: {
    slug: "autoaufbereitung-taufkirchen",
    name: "Taufkirchen",
    nameGenitive: "Taufkirchens",
    distance: "9 Min.",
    direction: "südlich",
    eyebrow: "Putzbrunner Straße 89 · 9 Min. von Taufkirchen",
    heroLine1: "Autoaufbereitung",
    heroLine2Pre: "in",
    heroLine2Em: "Taufkirchen",
    heroLede: "Manufaktur-Aufbereitung für Fahrzeuge aus Taufkirchen — Innen- und Außenreinigung, Politur und Keramikversiegelung von Hand. Nur 9 Minuten Anfahrt über die Tegernseer Landstraße, persönliche Beratung inklusive.",
    localTitle: "Ihre Anlaufstelle für Fahrzeugaufbereitung in Taufkirchen.",
    localBody: [
      "Aus Taufkirchen erreichen Sie uns über die B13 oder Tegernseer Landstraße in unter zehn Minuten. Ob aus Bergstraße, Am Wald oder rund um den S-Bahnhof Taufkirchen — wir nehmen Ihr Fahrzeug entgegen, dokumentieren den Ist-Zustand mit Fotos und besprechen offen, was wirklich notwendig ist.",
      "Unsere Spezialität: mehrstufige Lackkorrektur an Premium- und Sportfahrzeugen, schonend und mit Lackdickenmessung. Dazu Innenreinigung mit Tornador, Lederpflege, Scheinwerferaufbereitung und Keramikversiegelung mit 5+ Jahren Schutz. Faires Preis-Leistungs-Verhältnis, transparente Doku, Termin oft schon innerhalb weniger Tage."
    ],
    nearby: ["unterhaching", "ottobrunn"],
    metaTitle: "Autoaufbereitung Taufkirchen | Glanzmanufaktur München",
    metaDescription: "Professionelle Autoaufbereitung in Taufkirchen: Lackkorrektur, Innenreinigung, Keramikversiegelung. Manufaktur-Qualität, 9 Min. Anfahrt. 5,0 ★ Google. Jetzt Termin anfragen.",
  },
  unterfoehring: {
    slug: "autoaufbereitung-unterfoehring",
    name: "Unterföhring",
    nameGenitive: "Unterföhrings",
    distance: "25 Min.",
    direction: "nördlich",
    eyebrow: "Putzbrunner Straße 89 · Premium-Aufbereitung für Unterföhring",
    heroLine1: "Autoaufbereitung",
    heroLine2Pre: "in",
    heroLine2Em: "Unterföhring",
    heroLede: "Manufaktur für Fahrzeugaufbereitung — auch für Kunden aus Unterföhring. Lackkorrektur, Keramikversiegelung und Innenreinigung in Concours-Qualität. Auf Wunsch mit Hol- und Bringdienst direkt ab Unterföhring.",
    localTitle: "Premium-Aufbereitung für Fahrzeuge aus Unterföhring.",
    localBody: [
      "Unterföhring ist Heimat anspruchsvoller Fahrzeugbesitzer — viele unserer Stammkunden aus den Medien-, Versicherungs- und Logistikunternehmen rund um die Allianz-Arena fahren für die Aufbereitung bewusst nach München-Perlach. Auf Wunsch holen wir Ihr Fahrzeug direkt am Wohn- oder Firmensitz ab und liefern es nach Abschluss zurück.",
      "Wir arbeiten ausschließlich in Manufakturqualität: Handwäsche, Lackmessung vor jeder Politur, Tageslicht-Endkontrolle, lückenlose Foto-Dokumentation. Ob mehrstufige Defektkorrektur an einem Geschäftswagen, Keramikbeschichtung am Neuwagen oder gründliche Aufbereitung vor Leasing-Rückgabe — Sie bekommen ein Ergebnis, das messbar besser ist als die Auslieferung vom Händler."
    ],
    nearby: ["ottobrunn", "unterhaching"],
    metaTitle: "Autoaufbereitung Unterföhring | Glanzmanufaktur München",
    metaDescription: "Premium Fahrzeugaufbereitung für Unterföhring: Lackkorrektur, Keramikversiegelung, Innenreinigung. Manufaktur in München, Hol- und Bringdienst möglich. 5,0 ★ Google. Termin sichern.",
  },
  ottobrunn: {
    slug: "autoaufbereitung-ottobrunn",
    name: "Ottobrunn",
    nameGenitive: "Ottobrunns",
    distance: "7 Min.",
    direction: "südöstlich",
    eyebrow: "Putzbrunner Straße 89 · 7 Min. von Ottobrunn",
    heroLine1: "Autoaufbereitung",
    heroLine2Pre: "in",
    heroLine2Em: "Ottobrunn",
    heroLede: "Premium Fahrzeugaufbereitung für Ottobrunns Fahrzeugbesitzer — Lackkorrektur, Keramikversiegelung und Innenreinigung von Hand. Nur 7 Minuten Anfahrt über die Putzbrunner Straße. Persönliche Beratung, transparente Preise.",
    localTitle: "Autoaufbereitung in Ottobrunn — direkt nebenan.",
    localBody: [
      "Aus Ottobrunn sind wir über die Ottostraße oder Rosenheimer Landstraße in sieben Minuten erreichbar. Egal ob aus Riemerling, Waldperlach oder Ottobrunn-Mitte — Sie sind schneller bei uns als bei jeder Waschstraße.",
      "Was uns von einer Waschanlage trennt: jedes Fahrzeug wird einzeln behandelt, jeder Defekt einzeln bewertet. Lackmessung statt Pauschal-Politur, Tornador-Tiefenreinigung statt Sprühschaum, Keramikversiegelung mit echtem Mehrjahresschutz statt Wachs-Versprechen. Faire Preise, dokumentierter Ablauf, eine Beratung, die Ihnen sagt was Ihr Fahrzeug braucht — nicht was wir verkaufen wollen."
    ],
    nearby: ["taufkirchen", "unterhaching"],
    metaTitle: "Autoaufbereitung Ottobrunn | Glanzmanufaktur München",
    metaDescription: "Professionelle Autoaufbereitung in Ottobrunn: Lackpolitur, Keramikversiegelung, Innenreinigung. Manufaktur 7 Min. entfernt, 5,0 ★ Google. Jetzt persönlich beraten lassen.",
  },
  oberhaching: {
    slug: "autoaufbereitung-oberhaching",
    name: "Oberhaching",
    nameGenitive: "Oberhachings",
    distance: "12 Min.",
    direction: "südlich",
    eyebrow: "Putzbrunner Straße 89 · 12 Min. von Oberhaching",
    heroLine1: "Autoaufbereitung",
    heroLine2Pre: "in",
    heroLine2Em: "Oberhaching",
    heroLede: "Manufaktur-Aufbereitung für Fahrzeuge aus Oberhaching — Lackkorrektur, Keramikversiegelung, Innenreinigung in Concours-Qualität. Nur 12 Minuten Anfahrt, auf Wunsch mit Hol- und Bringdienst.",
    localTitle: "Premium-Fahrzeugaufbereitung für Oberhaching.",
    localBody: [
      "Oberhaching liegt nur 12 Minuten von unserer Manufaktur an der Putzbrunner Straße entfernt. Aus Deisenhofen, Oberbiberg oder Furth — die Anfahrt über die A8/B11 ist schnell, oder wir holen Ihr Fahrzeug direkt ab. Termin meist innerhalb einer Woche.",
      "Wir sind keine Schnellwerkstatt. Eine sorgfältige Lackkorrektur kann 8 bis 30 Stunden Handarbeit bedeuten — und genau diese Zeit nehmen wir uns. Vorher Lackmessung und ehrliche Bestandsaufnahme, danach gemeinsame Endkontrolle bei Tageslicht. Kein Auto verlässt uns, ohne dass wir selbst zufrieden sind."
    ],
    nearby: ["unterhaching", "taufkirchen"],
    metaTitle: "Autoaufbereitung Oberhaching | Glanzmanufaktur München",
    metaDescription: "Premium Autoaufbereitung für Oberhaching: Lackkorrektur, Keramikversiegelung, Innenreinigung in Manufaktur-Qualität. 12 Min. Anfahrt, 5,0 ★ Google. Termin sichern.",
  },
};
const LOCATION_KEY = (typeof window !== "undefined" && window.__LOCATION_KEY__) || null;
const LOC = LOCATION_KEY && LOCATIONS[LOCATION_KEY] ? LOCATIONS[LOCATION_KEY] : null;
// Stable order for the footer "Standorte" column.
const LOCATION_ORDER = ["unterhaching", "taufkirchen", "unterfoehring", "ottobrunn", "oberhaching"];

const HERO_SLIDES = [
  { src: "images/hero-audi-s3.png", glow: "#1f63d8" },
  { src: "images/hero-taycan.png",  glow: "#7a8290" },
  { src: "images/hero-ineos.png",   glow: "#4f9ab1" },
  { src: "images/hero-rangerover.png", glow: "#7a3a32" },
];
function HeroSlideshow() {
  const [i, setI] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setI((x) => (x + 1) % HERO_SLIDES.length), 4500);
    return () => clearInterval(id);
  }, []);
  return (
    <div style={{ position: "relative" }}>
      {HERO_SLIDES.map((slide, idx) => (
        <div
          key={"glow-" + slide.src}
          aria-hidden
          style={{
            position: "absolute",
            left: "50%", top: "-60%",
            transform: "translateX(-50%)",
            width: "130%", height: "150%",
            background: `radial-gradient(ellipse 55% 50% at 50% 55%, ${slide.glow} 0%, ${slide.glow}dd 20%, ${slide.glow}88 42%, ${slide.glow}33 62%, transparent 80%)`,
            filter: "blur(120px)",
            mixBlendMode: "screen",
            opacity: idx === i ? 1 : 0,
            pointerEvents: "none",
            transition: "opacity 1600ms cubic-bezier(0.4, 0, 0.2, 1)",
            zIndex: 0,
          }}
        />
      ))}
      <div className="placeholder hero-slideshow" style={{ aspectRatio: "21/8", overflow: "hidden", position: "relative", zIndex: 1, borderRadius: "12px" }}>
        {HERO_SLIDES.map((slide, idx) => (
          <img
            key={slide.src}
            src={slide.src}
            alt=""
            style={{
              position: "absolute", inset: 0,
              width: "100%", height: "100%", objectFit: "cover",
              opacity: idx === i ? 1 : 0,
              transform: idx === i ? "scale(1.0)" : "scale(1.04)",
              transition: "opacity 1600ms cubic-bezier(0.4, 0, 0.2, 1), transform 5000ms ease-out",
            }}
          />
        ))}
        <div style={{
          position: "absolute", inset: 0, pointerEvents: "none",
          background: "linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.55) 78%, rgba(0,0,0,0.92) 100%)",
        }} />
      </div>
    </div>
  );
}

// --- Striped placeholder for car imagery -------------------------------------
function Placeholder({ label, ratio = "16/9", className = "", dark = true }) {
  const stripeStroke = dark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.06)";
  const bg = dark ? "rgba(255,255,255,0.025)" : "rgba(0,0,0,0.025)";
  const uid = React.useId().replace(/:/g, "");
  const patternId = `stripe-${uid}`;
  return (
    <div
      className={`placeholder ${className}`}
      style={{ aspectRatio: ratio, background: bg, borderColor: dark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.08)" }}
    >
      <svg className="placeholder-stripes" width="100%" height="100%" preserveAspectRatio="none">
        <defs>
          <pattern id={patternId} width="14" height="14" patternUnits="userSpaceOnUse" patternTransform="rotate(-30)">
            <line x1="0" y1="0" x2="0" y2="14" stroke={stripeStroke} strokeWidth="1" />
          </pattern>
        </defs>
        <rect width="100%" height="100%" fill={`url(#${patternId})`} />
      </svg>
      <span className="placeholder-label">{label}</span>
    </div>
  );
}

// --- Top nav ----------------------------------------------------------------
function Nav({ onBook }) {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 24);
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const links = [
    ["Leistungen", "#leistungen"],
    ["Konfigurator", "#konfigurator"],
    ["Instagram", "#galerie"],
    ["Videos", "#reels"],
    ["Rezensionen", "#rezensionen"],
    ["Kontakt", "#kontakt"],
  ];

  return (
    <header className={`nav ${scrolled ? "nav--scrolled" : ""}`}>
      <div className="nav-inner">
        <div className="nav-brand-group">
          <a href="#top" className="brand">
            <span className="brand-mark">GM</span>
            <span className="brand-word">
              <span>Glanzmanufaktur</span>
              <span className="brand-sub">München · seit 2024</span>
            </span>
          </a>
          <a
            href="https://crm.rvertising.com/login"
            target="_blank"
            rel="noopener"
            className="nav-login nav-login--desktop"
            aria-label="Lead-CRM Login"
            title="Login (intern)"
          >
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <circle cx="12" cy="8" r="4" />
              <path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8" />
            </svg>
          </a>
        </div>
        <nav className="nav-links">
          {links.map(([label, href]) => (
            <a key={href} href={href}>{label}</a>
          ))}
        </nav>
        <div className="nav-cta">
          <a href="tel:+4915734828543" className="nav-phone">+49 157 34 828 543</a>
          <button className="btn btn--gold" onClick={onBook}>Termin buchen</button>
        </div>
        <a
          href="https://crm.rvertising.com/login"
          target="_blank"
          rel="noopener"
          className="nav-login nav-login--mobile"
          aria-label="Lead-CRM Login"
          title="Login (intern)"
        >
          <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <circle cx="12" cy="8" r="4" />
            <path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8" />
          </svg>
        </a>
        <button className={`nav-burger ${open ? "is-open" : ""}`} onClick={() => setOpen(!open)} aria-label="Menü">
          <span></span><span></span>
        </button>
      </div>
      {open && (
        <div className="nav-mobile">
          {links.map(([label, href]) => (
            <a key={href} href={href} onClick={() => setOpen(false)}>{label}</a>
          ))}
          <button className="btn btn--gold" onClick={() => { setOpen(false); onBook(); }}>Termin buchen</button>
        </div>
      )}
    </header>
  );
}

// --- Hero -------------------------------------------------------------------
function Hero({ onBook, variant }) {
  const eyebrow = LOC ? LOC.eyebrow : "Putzbrunner Straße 89 · München Südost";
  return (
    <section className={`hero hero--${variant} ${LOC ? "hero--location" : ""}`} id="top">
      <div className="hero-grid">
        <div className="hero-meta">
          <span className="eyebrow">
            <span className="eyebrow-dot" />
            {eyebrow}
          </span>
        </div>

        <h1 className="hero-title">
          {LOC ? (
            <>
              <span className="hero-title-line">{LOC.heroLine1}</span>
              <span className="hero-title-line">
                {LOC.heroLine2Pre} <em>{LOC.heroLine2Em}</em>.
              </span>
            </>
          ) : (
            <>
              <span className="hero-title-line">Perfektion</span>
              <span className="hero-title-line">
                bis ins <em>Detail</em>.
              </span>
            </>
          )}
        </h1>

        <div className="hero-side">
          <p className="hero-lede">
            {LOC ? LOC.heroLede : (
              <>Manufaktur für Fahrzeugaufbereitung, Lackkorrektur und
              Keramikversiegelung. Handarbeit aus München —
              für alle, die ihr Fahrzeug nicht nur fahren, sondern lieben.</>
            )}
          </p>
          <div className="hero-cta">
            <button className="btn btn--gold btn--lg" onClick={onBook}>Termin anfragen</button>
            <a href="#konfigurator" className="btn btn--ghost btn--lg">Zum Konfigurator →</a>
          </div>
        </div>

        <div className="hero-strip">
          <CarMarquee />
          <div className="hero-strip-overlay">
            <div>
              <div className="kpi">200+</div>
              <div className="kpi-label">Fahrzeuge veredelt</div>
            </div>
            <div>
              <div className="kpi">30h</div>
              <div className="kpi-label">Ø Detailing-Aufwand</div>
            </div>
            <div>
              <div className="kpi">5,0</div>
              <div className="kpi-label">Google Bewertung</div>
            </div>
          </div>
        </div>

      </div>
    </section>
  );
}

function ServicesMarquee() {
  return (
    <div className="hero-marquee">
      <div className="marquee-track">
        {Array(2).fill(0).map((_, i) => (
          <div key={i} className="marquee-row">
            <span>Innenreinigung</span><span>·</span>
            <span>Außenreinigung</span><span>·</span>
            <span>Lackkorrektur</span><span>·</span>
            <span>Keramikversiegelung</span><span>·</span>
            <span>Lederpflege</span><span>·</span>
            <span>Scheinwerferaufbereitung</span><span>·</span>
            <span>Ozonbehandlung</span><span>·</span>
            <span>Smart Repair</span><span>·</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// --- Services Section -------------------------------------------------------
const SERVICES = [
  {
    id: "innen",
    no: "01",
    title: "Innenreinigung",
    intro: "Sauberer, frischer und angenehmer Innenraum — bis in die letzte Naht.",
    image: "images/taycan-seat-clean.png",
    packages: [
      {
        name: "Interieur Basis",
        from: "ab 69 €",
        items: [
          "Aussaugen Innen- und Kofferraum",
          "Tiefenreinigung mit Tornador",
          "Oberflächenreinigung",
          "Scheibenreinigung innen",
        ],
      },
      {
        name: "Interieur Premium",
        from: "ab 199 €",
        featured: true,
        items: [
          "Aussaugen Innen- und Kofferraum",
          "Tiefenreinigung mit Tornador",
          "Oberflächenreinigung & -pflege",
          "Pinseln aller Kanten und Ritzen",
          "Schamponieren von Teppich, Fußraum, Sitzen",
          "Leder/Alcantara Reinigung & Pflege",
          "Scheibenreinigung innen",
        ],
      },
    ],
  },
  {
    id: "aussen",
    no: "02",
    title: "Außenreinigung",
    intro: "Schonend von Hand — Ihr Fahrzeug erstrahlt wieder in neuem Glanz.",
    image: "images/ineos-front.png",
    packages: [
      {
        name: "Exterieur Basis",
        from: "ab 49 €",
        items: [
          "Außenwäsche von Hand",
          "Felgenreinigung (Sichtseite)",
          "Scheibenreinigung außen",
          "Trocknen mit Mikrofasertuch",
        ],
      },
      {
        name: "Exterieur Premium",
        from: "ab 99 €",
        featured: true,
        items: [
          "Außenwäsche von Hand",
          "Insekten- & Flugrostentfernung",
          "Felgenreinigung (Sichtseite)",
          "Reinigung der Radkästen",
          "Pinseln aller Kanten und Ritzen",
          "Türholme & Rahmen",
          "Reifen- & Kunststoffpflege",
          "Trocknen mittels Druckluft",
        ],
      },
    ],
  },
  {
    id: "lack",
    no: "03",
    title: "Lackkorrektur",
    intro: "Kratzer entfernen, Tiefenglanz herstellen — ein- bis dreistufig.",
    image: "images/audi-s3-side.png",
    packages: [
      { name: "1-stufige Politur", from: "ab 289 €", items: ["Holo­gramm­entfernung", "Auffrischen matter Stellen", "Glanz­steigerung", "Vorbereitung für Versiegelung"] },
      { name: "2-stufige Politur", from: "ab 439 €", featured: true, items: ["Defektkorrektur", "Swirl-Entfernung", "Tiefenglanz", "Hochglanz-Finish"] },
      { name: "3-stufige Politur", from: "ab 589 €", items: ["Vollständige Defektkorrektur", "Tiefere Kratzer schleifen", "Mehrstufiger Glanzaufbau", "Concours-Finish"] },
    ],
  },
  {
    id: "schutz",
    no: "04",
    title: "Versiegelung",
    intro: "Langanhaltender Schutz für Lack, Glas, Felgen, Leder und Stoff.",
    image: "images/rangerover-front.png",
    packages: [
      { name: "Keramikversiegelung", from: "Preis auf Anfrage", featured: true, items: ["Lack", "Glas", "Kunststoff", "Felgen & Bremssättel", "5+ Jahre Schutz"] },
      { name: "Textil & Leder", from: "Preis auf Anfrage", items: ["Lederimprägnierung", "Alcantaraimprägnierung", "Stoffimprägnierung", "Verdeckimprägnierung"] },
    ],
  },
  {
    id: "sonstiges",
    no: "05",
    title: "Sonstiges",
    intro: "Zusatzleistungen rund ums Fahrzeug — auf Wunsch im Paket oder einzeln.",
    image: "images/audi-s3-side.png",
    services: [
      { name: "Ozonbehandlung",          from: "ab 49 €" },
      { name: "Scheinwerferaufbereitung", from: "ab 79 €" },
      { name: "Bremssattellackierung",   from: "ab 119 €" },
      { name: "Motorwäsche",             from: "ab 35 €" },
      { name: "Tierhaarentfernung",      from: "ab 59 €" },
      { name: "Trockeneisstrahlen",      from: "Preis auf Anfrage" },
      { name: "Dellenreparatur",         from: "Preis auf Anfrage" },
      { name: "Smart-Repair",            from: "Preis auf Anfrage" },
      { name: "Hol- und Bringdienst",    from: "ab 25 €" },
      { name: "Mietfahrzeug",            from: "ab 35 €/Tag" },
    ],
  },
];

function ServiceDetailBody({ service }) {
  return (
    <div className="service-detail-inner">
      <div className="service-detail-head">
        <div>
          <span className="service-detail-no">{service.no} / {String(SERVICES.length).padStart(2, "0")}</span>
          <h3 className="service-detail-title">{service.title}</h3>
          <p className="service-detail-intro">{service.intro}</p>
        </div>
        {service.image ? (
          <div className="placeholder service-detail-img" style={{ aspectRatio: "4/3", overflow: "hidden" }}>
            <img src={service.image} alt={service.title} style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
          </div>
        ) : (
          <Placeholder label={`${service.title.toUpperCase()} · Detailaufnahme`} ratio="4/3" className="service-detail-img" />
        )}
      </div>

      {service.services ? (
        <ul className="service-list">
          {service.services.map((s, i) => (
            <li key={i} className="service-list-row">
              <span className="service-list-name">{s.name}</span>
              <span className="service-list-price">{s.from}</span>
            </li>
          ))}
        </ul>
      ) : (
        <div className="package-grid">
          {service.packages.map((p, i) => (
            <article key={i} className={`package ${p.featured ? "package--featured" : ""}`}>
              <header className="package-head">
                <h4>{p.name}</h4>
                <span className="package-price">{p.from}</span>
              </header>
              <ul className="package-items">
                {p.items.map((it, j) => (
                  <li key={j}>
                    <span className="check">+</span>
                    <span>{it}</span>
                  </li>
                ))}
              </ul>
              {p.featured && <span className="package-badge">Empfehlung</span>}
            </article>
          ))}
        </div>
      )}
    </div>
  );
}

function Services() {
  const [active, setActive] = useState("innen");
  const [autoplay, setAutoplay] = useState(true);
  const [minH, setMinH] = useState(0);
  const [isMobile, setIsMobile] = useState(false);
  const detailRef = useRef(null);
  const current = SERVICES.find(s => s.id === active);

  useEffect(() => {
    const mq = window.matchMedia('(max-width: 768px)');
    const update = () => setIsMobile(mq.matches);
    update();
    mq.addEventListener('change', update);
    return () => mq.removeEventListener('change', update);
  }, []);

  useEffect(() => {
    if (!autoplay || isMobile) return;
    const id = setInterval(() => {
      setActive(prev => {
        const idx = SERVICES.findIndex(s => s.id === prev);
        return SERVICES[(idx + 1) % SERVICES.length].id;
      });
    }, 3000);
    return () => clearInterval(id);
  }, [autoplay, isMobile]);

  // Tablet (>768 ≤960): container wächst auf den höchsten je gemessenen Tab — keine Layout-Sprünge mehr
  useEffect(() => {
    if (isMobile || !detailRef.current) return;
    if (document.documentElement.clientWidth > 960) { setMinH(0); return; }
    const id = setTimeout(() => {
      const inner = detailRef.current?.querySelector('.service-detail-inner');
      if (!inner) return;
      const h = inner.offsetHeight;
      setMinH(prev => Math.max(prev, h));
    }, 700);
    return () => clearTimeout(id);
  }, [active, isMobile]);

  if (isMobile) {
    return (
      <section className="services" id="leistungen">
        <div className="section-head">
          <span className="section-no">— Leistungen</span>
          <h2 className="section-title">Vier Disziplinen.<br/>Ein Anspruch: <em>Perfektion.</em></h2>
        </div>

        <div className="service-accordion" id="pakete">
          {SERVICES.map((s, i) => {
            const isOpen = active === s.id;
            return (
              <div key={s.id} className={`service-acc-item ${isOpen ? "is-open" : ""}`} style={{ "--nudge-delay": `${i * 0.4}s` }}>
                <button
                  type="button"
                  className="service-acc-head"
                  aria-expanded={isOpen}
                  onClick={() => { setAutoplay(false); setActive(isOpen ? null : s.id); }}
                >
                  <span className="service-tab-no">{s.no}</span>
                  <span className="service-tab-title">{s.title}</span>
                  <span className="service-acc-chev" aria-hidden="true">
                    <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                      <path d="M3.5 5.25L7 8.75L10.5 5.25" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  </span>
                </button>
                {isOpen && (
                  <div className="service-acc-body">
                    <ServiceDetailBody service={s} />
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </section>
    );
  }

  return (
    <section className="services" id="leistungen">
      <div className="section-head">
        <span className="section-no">— Leistungen</span>
        <h2 className="section-title">Vier Disziplinen.<br/>Ein Anspruch: <em>Perfektion.</em></h2>
      </div>

      <div className="service-tabs">
        {SERVICES.map(s => (
          <button
            key={s.id}
            className={`service-tab ${active === s.id ? "is-active" : ""}`}
            onClick={() => { setAutoplay(false); setActive(s.id); }}
          >
            <span className="service-tab-no">{s.no}</span>
            <span className="service-tab-title">{s.title}</span>
            <span className="service-tab-arrow">↗</span>
          </button>
        ))}
      </div>

      <div className="service-detail" id="pakete" ref={detailRef} style={minH ? { minHeight: `${minH}px` } : undefined}>
        <div key={active}>
          <ServiceDetailBody service={current} />
        </div>
      </div>
    </section>
  );
}

// --- Process ---------------------------------------------------------------
const PROCESS = [
  { n: "01", t: "Anfrage & Beratung", d: "Wir besprechen Zustand und Ziel — telefonisch oder vor Ort. Kein Pauschalangebot, sondern eine individuelle Einschätzung." },
  { n: "02", t: "Begutachtung", d: "Lackmessung, Defektanalyse, Oberflächentest. Wir wissen vorher, was wir nachher liefern können." },
  { n: "03", t: "Detailing", d: "Handarbeit von 3 bis 30+ Stunden. Jede Naht, jede Kante, jede Reflexion." },
  { n: "04", t: "Übergabe", d: "Gemeinsame Endkontrolle bei Tageslicht. Pflegehinweise inklusive." },
];

function Process() {
  return (
    <section className="process" id="manufaktur">
      <div className="section-head">
        <span className="section-no">— Manufaktur-Prinzip</span>
        <h2 className="section-title">Vier Schritte.<br/>Keine Abkürzungen.</h2>
      </div>
      <ol className="process-list">
        {PROCESS.map(p => (
          <li key={p.n} className="process-item">
            <span className="process-no">{p.n}</span>
            <h3 className="process-title">{p.t}</h3>
            <p className="process-desc">{p.d}</p>
          </li>
        ))}
      </ol>
    </section>
  );
}

// --- Car Marquee -----------------------------------------------------------
function CarMarquee() {
  // Top row: Kunde bei der Arbeit (wild verteilt, einige wiederholt für gleiche Dichte wie vorher)
  const rowA = [
    7, 3, 11, 1, 8, 5, 12, 2, 10, 6, 4, 9, 11, 3, 7, 8, 1,
  ].map(n => `images/Arbeit/${n}.webp`);
  // Bottom row: Marquee-Bilder (16 wie ursprünglich)
  const rowB = [
    13, 31, 3, 18, 24, 10, 29, 16, 2, 20, 32, 8, 26, 12, 23, 5,
  ].map(n => `images/marquee/m${n}.webp`);
  const dup = (arr) => [...arr, ...arr];
  return (
    <section className="car-marquee" aria-hidden="true">
      <div className="car-marquee-track car-marquee-track--a">
        {dup(rowA).map((src, i) => (
          <figure className="car-marquee-item" key={`a${i}`}>
            <img src={src} alt="" loading="lazy" />
          </figure>
        ))}
      </div>
      <div className="car-marquee-track car-marquee-track--b">
        {dup(rowB).map((src, i) => (
          <figure className="car-marquee-item" key={`b${i}`}>
            <img src={src} alt="" loading="lazy" />
          </figure>
        ))}
      </div>
    </section>
  );
}

// --- Configurator ----------------------------------------------------------
const CONFIG_STEPS = [
  {
    key: "carClass",
    q: "Fahrzeugklasse?",
    opts: [
      { v: "kompakt", l: "Kompakt", sub: "Polo, A1, Mini …", mult: 1 },
      { v: "mittel", l: "Mittelklasse", sub: "3er, A4, C-Klasse …", mult: 1.15 },
      { v: "suv", l: "SUV / Van", sub: "X5, Q7, GLE …", mult: 1.35 },
      { v: "luxus", l: "Luxus / Sport", sub: "RS, AMG, Porsche …", mult: 1.6 },
    ],
  },
  {
    key: "service",
    q: "Welches Programm?",
    multi: true,
    groups: [
      {
        label: "Innenreinigung",
        opts: [
          { v: "innen-basis", l: "Basis", sub: "ab 69 €", base: 69 },
          { v: "innen-premium", l: "Premium", sub: "ab 199 €", base: 199 },
        ],
      },
      {
        label: "Außenreinigung",
        opts: [
          { v: "aussen-basis", l: "Basis", sub: "ab 49 €", base: 49 },
          { v: "aussen-premium", l: "Premium", sub: "ab 99 €", base: 99 },
        ],
      },
      {
        label: "Lackpolitur",
        opts: [
          { v: "politur-1", l: "1-stufig", sub: "ab 289 €", base: 289 },
          { v: "politur-2", l: "2-stufig", sub: "ab 439 €", base: 439 },
          { v: "politur-3", l: "3-stufig", sub: "ab 589 €", base: 589 },
        ],
      },
    ],
  },
  {
    key: "extra",
    q: "Versiegelung & Imprägnierung?",
    multi: true,
    groups: [
      {
        label: "Keramikversiegelung",
        opts: [
          { v: "k-lack", l: "Nur Lack", sub: "+ ab 590 €", add: 590 },
          { v: "k-alles", l: "Alles", sub: "+ ab 990 €", hint: "Lack · Glas · Felgen · Kunststoff", add: 990 },
        ],
      },
      {
        label: "Imprägnierung",
        opts: [
          { v: "i-leder", l: "Leder", sub: "+ ab 149 €", add: 149 },
          { v: "i-alcantara", l: "Alcantara", sub: "+ ab 169 €", add: 169 },
          { v: "i-stoff", l: "Stoff (Sitze)", sub: "+ ab 129 €", add: 129 },
          { v: "i-verdeck", l: "Verdeck (Cabrio)", sub: "+ ab 199 €", add: 199 },
        ],
      },
      {
        label: "Keine",
        opts: [
          { v: "none", l: "Nein, danke", sub: "—", add: 0, exclusive: true },
        ],
      },
    ],
  },
];

const stepOpts = (s) => s.opts || s.groups.flatMap(g => g.opts);
const findOpt = (s, v) => stepOpts(s).find(o => o.v === v);
const findGroup = (s, o) => s.groups?.find(g => g.opts.includes(o));

function Configurator({ onBook }) {
  const [step, setStep] = useState(0);
  const [picks, setPicks] = useState({});
  const [contact, setContact] = useState({ firstName: "", lastName: "", phone: "", email: "" });
  const [consent, setConsent] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [sent, setSent] = useState(false);
  const [error, setError] = useState(null);

  const total = useMemo(() => {
    const cls = findOpt(CONFIG_STEPS[0], picks.carClass);
    const svcs = (picks.service || []).map(v => findOpt(CONFIG_STEPS[1], v)).filter(Boolean);
    const exts = (picks.extra || []).map(v => findOpt(CONFIG_STEPS[2], v)).filter(Boolean);
    if (!cls || svcs.length === 0) return null;
    const base = svcs.reduce((s, o) => s + o.base, 0) * cls.mult;
    const add = exts.reduce((s, o) => s + o.add, 0);
    return Math.round((base + add) / 10) * 10;
  }, [picks]);

  const updateContact = (k, v) => setContact({ ...contact, [k]: v });
  const restart = () => {
    setStep(0);
    setPicks({});
    setContact({ firstName: "", lastName: "", phone: "", email: "" });
    setConsent(false);
    setSent(false);
    setError(null);
  };

  const canSubmitLead =
    contact.firstName.trim().length >= 2 &&
    contact.lastName.trim().length >= 2 &&
    contact.phone.trim().length >= 6 &&
    consent &&
    !submitting;

  const submitConfigLead = async (e) => {
    if (e) e.preventDefault();
    if (!canSubmitLead) return;
    setSubmitting(true);
    setError(null);

    // Map konfigurator picks → CRM lead fields
    const cls = findOpt(CONFIG_STEPS[0], picks.carClass);
    const svcLabel = (picks.service || [])
      .map(v => {
        const o = findOpt(CONFIG_STEPS[1], v);
        const g = findGroup(CONFIG_STEPS[1], o);
        return g ? `${g.label} · ${o.l}` : o.l;
      })
      .join(", ");
    const extLabel = (picks.extra || [])
      .map(v => {
        const o = findOpt(CONFIG_STEPS[2], v);
        const g = findGroup(CONFIG_STEPS[2], o);
        return g ? `${g.label} · ${o.l}` : o.l;
      })
      .filter(s => s && !s.includes("Nein, danke")) // skip "Keine · Nein, danke"
      .join(", ");

    // Note holds anything not mapped to a primary CRM column
    const noteLines = ["Konfigurator-Anfrage"];
    if (extLabel) noteLines.push(`Versiegelung: ${extLabel}`);
    if (total != null) noteLines.push(`Richtpreis ab: ${total} €`);

    const params = new URLSearchParams(window.location.search);

    try {
      const res = await fetch(CRM_API, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          tenant: CRM_TENANT,
          name: `${contact.firstName.trim()} ${contact.lastName.trim()}`,
          phone: contact.phone.trim(),
          email: contact.email.trim() || null,
          vehicle: cls?.l || null,
          service: svcLabel || null,
          note: noteLines.join("\n"),
          revenue_cents: total != null ? total * 100 : null,
          utm_source: params.get("utm_source") || null,
          utm_medium: params.get("utm_medium") || null,
          utm_campaign: params.get("utm_campaign") || null,
        }),
      });
      if (!res.ok) {
        const j = await res.json().catch(() => ({}));
        throw new Error(j.error || `Server-Fehler (${res.status})`);
      }
      setSent(true);
    } catch (err) {
      setError(err && err.message ? err.message : "Senden fehlgeschlagen — bitte später erneut versuchen oder telefonisch melden.");
    } finally {
      setSubmitting(false);
    }
  };

  const cur = CONFIG_STEPS[step];
  const done = step >= CONFIG_STEPS.length;
  const canAdvance = !cur?.multi || (picks[cur?.key]?.length > 0);

  const isPicked = (o) => cur.multi
    ? (picks[cur.key] || []).includes(o.v)
    : picks[cur.key] === o.v;

  const handlePick = (o) => {
    if (cur.multi) {
      const arr = picks[cur.key] || [];
      let next;
      if (arr.includes(o.v)) {
        next = arr.filter(v => v !== o.v);
      } else if (o.exclusive) {
        next = [o.v];
      } else {
        const others = stepOpts(cur).filter(x => x.exclusive).map(x => x.v);
        next = [...arr.filter(v => !others.includes(v)), o.v];
      }
      setPicks({ ...picks, [cur.key]: next });
    } else {
      setPicks({ ...picks, [cur.key]: o.v });
      setTimeout(() => setStep(step + 1), 220);
    }
  };

  const renderOpt = (o) => (
    <button
      key={o.v}
      className={`config-opt ${isPicked(o) ? "is-picked" : ""}`}
      onClick={() => handlePick(o)}
    >
      <span className="config-opt-l">{o.l}</span>
      <span className="config-opt-sub">{o.sub}</span>
      {o.hint && <span className="config-opt-hint">{o.hint}</span>}
    </button>
  );

  return (
    <section className="config" id="konfigurator">
      <div className="config-grid">
        <div className="config-side">
          <span className="section-no">— Konfigurator</span>
          <h2 className="section-title">In 30 Sekunden zum <em>Richtpreis.</em></h2>
          <p className="config-lede">
            Drei Fragen, eine erste Hausnummer. Der finale Preis ergibt sich nach
            Begutachtung — fair und nachvollziehbar.
          </p>
          <div className="config-progress">
            {CONFIG_STEPS.map((s, i) => (
              <span key={s.key} className={`config-dot ${i <= step ? "is-on" : ""}`} />
            ))}
          </div>
        </div>

        <div className="config-main">
          {!done ? (
            <>
              <div className="config-q">
                <span className="config-step">{String(step + 1).padStart(2, "0")} / {String(CONFIG_STEPS.length).padStart(2, "0")}</span>
                <h3>{cur.q}</h3>
              </div>
              {cur.groups ? (
                <div className="config-groups">
                  {cur.groups.map(g => (
                    <div key={g.label} className="config-group" data-cols={g.opts.length}>
                      <span className="config-group-label">{g.label}</span>
                      <div className="config-opts">
                        {g.opts.map(renderOpt)}
                      </div>
                    </div>
                  ))}
                </div>
              ) : (
                <div className="config-opts">
                  {cur.opts.map(renderOpt)}
                </div>
              )}
              <div className="config-nav">
                {step > 0 && (
                  <button className="config-back" onClick={() => setStep(step - 1)}>← Zurück</button>
                )}
                {cur.multi && (
                  <button
                    className="btn btn--gold config-next"
                    onClick={() => setStep(step + 1)}
                    disabled={!canAdvance}
                  >
                    {step === CONFIG_STEPS.length - 1 ? "Richtpreis berechnen" : "Weiter →"}
                  </button>
                )}
              </div>
            </>
          ) : sent ? (
            <div className="config-result config-done">
              <div className="booking-done-mark">✓</div>
              <h3 className="config-done-title">Anfrage gesendet.</h3>
              <p className="config-done-text">
                Vielen Dank, {contact.firstName}. Wir melden uns innerhalb von 24h
                telefonisch unter {contact.phone} bei Ihnen.
              </p>
              <button className="btn btn--ghost" onClick={restart}>Neu konfigurieren</button>
            </div>
          ) : (
            <div className="config-result">
              <span className="config-step">Ergebnis</span>
              <div className="config-total">
                <span className="config-total-label">Richtpreis ab</span>
                <span className="config-total-num">{total} €</span>
              </div>
              <ul className="config-summary">
                <li><span>Fahrzeugklasse</span><span>{findOpt(CONFIG_STEPS[0], picks.carClass).l}</span></li>
                <li>
                  <span>Programm</span>
                  <span>{(picks.service || []).map(v => { const o = findOpt(CONFIG_STEPS[1], v); const g = findGroup(CONFIG_STEPS[1], o); return g ? `${g.label} · ${o.l}` : o.l; }).join(", ") || "—"}</span>
                </li>
                <li>
                  <span>Versiegelung</span>
                  <span>{(picks.extra || []).map(v => findOpt(CONFIG_STEPS[2], v).l).join(", ") || "—"}</span>
                </li>
              </ul>
              <p className="config-note">
                Richtpreis ohne Begutachtung. Jedes Fahrzeug ist individuell —
                den finalen Preis nennen wir nach kurzer Sichtung vor Ort.
              </p>

              <form className="config-lead-form" onSubmit={submitConfigLead}>
                <span className="config-step">Termin anfragen</span>
                <h4 className="booking-q">Wen dürfen wir kontaktieren?</h4>

                <div className="booking-name-row">
                  <label className="field">
                    <span>Vorname</span>
                    <input
                      value={contact.firstName}
                      onChange={e => updateContact("firstName", e.target.value)}
                      placeholder="Max"
                      autoComplete="given-name"
                      required
                    />
                  </label>
                  <label className="field">
                    <span>Nachname</span>
                    <input
                      value={contact.lastName}
                      onChange={e => updateContact("lastName", e.target.value)}
                      placeholder="Mustermann"
                      autoComplete="family-name"
                      required
                    />
                  </label>
                </div>

                <label className="field">
                  <span>Telefon</span>
                  <input
                    type="tel"
                    value={contact.phone}
                    onChange={e => updateContact("phone", e.target.value)}
                    placeholder="+49 157 …"
                    autoComplete="tel"
                    required
                  />
                </label>

                <label className="field">
                  <span>E-Mail <em className="field-optional">— optional</em></span>
                  <input
                    type="email"
                    value={contact.email}
                    onChange={e => updateContact("email", e.target.value)}
                    placeholder="max@example.com"
                    autoComplete="email"
                  />
                </label>

                {error && (
                  <div className="booking-error" role="alert">{error}</div>
                )}

                <label className="field-consent">
                  <input
                    type="checkbox"
                    checked={consent}
                    onChange={e => setConsent(e.target.checked)}
                    required
                  />
                  <span>
                    Ich akzeptiere die <a href="datenschutz.html" target="_blank" rel="noopener">Datenschutzerklärung</a>. Meine Angaben werden ausschließlich zur Bearbeitung meiner Anfrage verwendet und nicht an Dritte weitergegeben.
                  </span>
                </label>

                <div className="config-actions">
                  <button type="submit" className="btn btn--gold btn--lg" disabled={!canSubmitLead}>
                    {submitting ? "Sende …" : "Anfrage senden"}
                  </button>
                  <button type="button" className="btn btn--ghost" onClick={restart}>Neu starten</button>
                </div>
              </form>
            </div>
          )}
        </div>
      </div>
    </section>
  );
}

// --- Instagram Feed --------------------------------------------------------
function formatCount(n) {
  if (n == null) return "";
  if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(".0", "") + "k";
  return String(n);
}

const IG_COLOR_CACHE = new Map();
function extractDominantColor(img) {
  if (IG_COLOR_CACHE.has(img.src)) return IG_COLOR_CACHE.get(img.src);
  try {
    const canvas = document.createElement("canvas");
    const w = 14, h = 18;
    canvas.width = w; canvas.height = h;
    const ctx = canvas.getContext("2d", { willReadFrequently: true });
    ctx.drawImage(img, 0, 0, w, h);
    const data = ctx.getImageData(0, 0, w, h).data;
    let r = 0, g = 0, b = 0, n = 0;
    for (let i = 0; i < data.length; i += 4) {
      // Bias toward saturated pixels — ignore near-greys to get vivid ambient
      const rr = data[i], gg = data[i+1], bb = data[i+2];
      const max = Math.max(rr, gg, bb), min = Math.min(rr, gg, bb);
      const sat = max === 0 ? 0 : (max - min) / max;
      const weight = 0.3 + sat * 1.7;
      r += rr * weight; g += gg * weight; b += bb * weight; n += weight;
    }
    let cr = r/n, cg = g/n, cb = b/n;
    // Normalize so the brightest channel hits 230 — gives the glow consistent intensity
    // regardless of how dark the source image is.
    const peak = Math.max(cr, cg, cb);
    if (peak > 8) {
      const scale = 230 / peak;
      cr *= scale; cg *= scale; cb *= scale;
    }
    const color = [Math.min(255, Math.round(cr)), Math.min(255, Math.round(cg)), Math.min(255, Math.round(cb))];
    IG_COLOR_CACHE.set(img.src, color);
    return color;
  } catch {
    return [120, 120, 120];
  }
}

function InstagramFeed() {
  const [data, setData] = useState(null);
  const stageRef = useRef(null);
  const leftGlowRef = useRef(null);
  const rightGlowRef = useRef(null);

  useEffect(() => {
    fetch("data/instagram.json", { cache: "no-cache" })
      .then(r => r.ok ? r.json() : null)
      .then(setData)
      .catch(() => setData(null));
  }, []);

  // Marquee-Animationen pausieren wenn nicht im Viewport — spart GPU-Cycles auf Mobile
  useEffect(() => {
    const stage = stageRef.current;
    if (!stage || typeof IntersectionObserver === "undefined") return;
    const io = new IntersectionObserver(([entry]) => {
      const tracks = stage.querySelectorAll(".ig-feed-track");
      tracks.forEach(t => { t.style.animationPlayState = entry.isIntersecting ? "running" : "paused"; });
    }, { rootMargin: "200px" });
    io.observe(stage);
    return () => io.disconnect();
  }, [data]);

  useEffect(() => {
    if (!data || !stageRef.current) return;
    let raf;
    let lastLeft = [120, 120, 120];
    let lastRight = [120, 120, 120];

    const lerp = (a, b, t) => a + (b - a) * t;
    const lerpColor = (a, b, t) => [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)];

    function findCenteredCard(colSelector, stageRect) {
      const cards = stageRef.current.querySelectorAll(`${colSelector} .ig-card`);
      const stageCenter = stageRect.top + stageRect.height / 2;
      let best = null, bestDist = Infinity;
      for (const c of cards) {
        const r = c.getBoundingClientRect();
        if (r.bottom < stageRect.top || r.top > stageRect.bottom) continue;
        const dist = Math.abs((r.top + r.height / 2) - stageCenter);
        if (dist < bestDist) { bestDist = dist; best = c; }
      }
      return best;
    }

    function tick() {
      const stage = stageRef.current;
      if (!stage) return;
      const stageRect = stage.getBoundingClientRect();

      const leftCard = findCenteredCard(".ig-feed-col--0", stageRect);
      const rightCard = findCenteredCard(".ig-feed-col--4", stageRect);

      if (leftCard) {
        const img = leftCard.querySelector("img");
        if (img && img.complete && img.naturalWidth > 0) {
          const target = extractDominantColor(img);
          lastLeft = lerpColor(lastLeft, target, 0.04);
          if (leftGlowRef.current) {
            leftGlowRef.current.style.setProperty("--ig-glow", `rgb(${lastLeft.map(v => Math.round(v)).join(",")})`);
          }
        }
      }
      if (rightCard) {
        const img = rightCard.querySelector("img");
        if (img && img.complete && img.naturalWidth > 0) {
          const target = extractDominantColor(img);
          lastRight = lerpColor(lastRight, target, 0.04);
          if (rightGlowRef.current) {
            rightGlowRef.current.style.setProperty("--ig-glow", `rgb(${lastRight.map(v => Math.round(v)).join(",")})`);
          }
        }
      }
      raf = requestAnimationFrame(tick);
    }
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [data]);

  const posts = data?.posts || [];
  const profile = data?.profile;
  const handle = profile?.username || "glanzmanufaktur.weileder";
  const profileUrl = `https://www.instagram.com/${handle}/`;

  // Distribute posts across 5 columns round-robin so heights stay balanced
  const columns = useMemo(() => {
    const cols = [[], [], [], [], []];
    posts.forEach((p, i) => cols[i % 5].push(p));
    return cols;
  }, [posts]);

  function Card({ post }) {
    return (
      <a href={post.permalink} target="_blank" rel="noopener" className="ig-card" key={post.id}>
        <div className="ig-card-media">
          <img src={post.thumbnail} alt="" loading="lazy" />
          {post.isVideo && (
            <span className="ig-card-icon" aria-hidden>
              <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
            </span>
          )}
          {post.isCarousel && (
            <span className="ig-card-icon" aria-hidden>
              <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2"><rect x="6" y="6" width="13" height="13" rx="2"/><path d="M3 16V5a2 2 0 0 1 2-2h11"/></svg>
            </span>
          )}
        </div>
        <div className="ig-card-overlay">
          <div className="ig-card-stats">
            <span>♥ {formatCount(post.likes)}</span>
            <span>💬 {formatCount(post.comments)}</span>
          </div>
          {post.caption && (
            <p className="ig-card-caption">{post.caption.split("\n")[0].slice(0, 140)}</p>
          )}
        </div>
      </a>
    );
  }

  return (
    <section className="ig-feed" id="galerie">
      <div className="section-head">
        <span className="section-no">— Aus der Werkstatt</span>
        <h2 className="section-title">Live aus <em>Instagram.</em></h2>
      </div>

      {posts.length === 0 ? (
        <div className="ig-feed-empty">Lade Beiträge …</div>
      ) : (
        <div className="ig-feed-stage" ref={stageRef}>
          <div className="ig-feed-glow ig-feed-glow--left" ref={leftGlowRef} aria-hidden />
          <div className="ig-feed-glow ig-feed-glow--right" ref={rightGlowRef} aria-hidden />
          <div className="ig-feed-columns">
            {columns.map((col, ci) => (
              <div key={ci} className={`ig-feed-col ig-feed-col--${ci}`}>
                <div className="ig-feed-track">
                  {col.map(p => <Card key={"a-" + p.id} post={p} />)}
                  {col.map(p => <Card key={"b-" + p.id} post={p} />)}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      <div className="gallery-foot">
        <a href={profileUrl} target="_blank" rel="noopener" className="btn btn--ghost btn--lg">
          Mehr auf Instagram @{handle} →
        </a>
      </div>
    </section>
  );
}

// --- Reels Showcase --------------------------------------------------------
const REELS_STAGES = [
  { key: 'bmw',    label: 'BMW',    car: 'images/cars/bmw.webp',    videos: ['videos-web/bmw-reel-1.mp4',    'videos-web/bmw-arbeit-1.mp4'] },
  { key: 'audi',   label: 'Audi',   car: 'images/cars/audi.webp',   videos: ['videos-web/audi-reel-2.mp4',   'videos-web/audi-arbeit-2.mp4'] },
  { key: 'subaru', label: 'Subaru', car: 'images/cars/subaru.webp', videos: ['videos-web/subaru-reel-3.mp4', 'videos-web/subaru-arbeit-3.mp4'] },
];

function ReelsShowcase() {
  const wrapRef = useRef(null);
  const carRef = useRef(null);
  const blurRef = useRef(null);
  const manualLockRef = useRef(false);
  const [stage, setStage] = useState(0);
  const [videoIdx, setVideoIdx] = useState(0);
  const [revealKey, setRevealKey] = useState(0);
  const [muted, setMuted] = useState(true);
  const REVEAL_PX = 700;
  const current = REELS_STAGES[stage];
  // Flat sequence over all (stage, videoIdx) pairs — used to populate stacked back cards.
  const flatIndex = stage * 2 + videoIdx;
  const totalSteps = REELS_STAGES.length * 2;
  const videoAt = (offset) => {
    const i = (flatIndex + offset) % totalSteps;
    return REELS_STAGES[Math.floor(i / 2)].videos[i % 2];
  };

  // Scroll-pinned reveal: on first entry, drive transforms via scroll progress.
  useEffect(() => {
    let raf = 0;
    const apply = () => {
      if (manualLockRef.current) return;
      const el = wrapRef.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const scrollable = r.height - window.innerHeight;
      const p = scrollable <= 0 ? 0 : Math.min(1, Math.max(0, -r.top / scrollable));
      const car = carRef.current;
      const blur = blurRef.current;
      if (car) {
        car.style.opacity = Math.min(1, p * 2.4);
        car.style.transform = `translateY(${(1 - p) * 70}%) scale(${0.94 + p * 0.06})`;
      }
      if (blur) {
        blur.style.opacity = Math.min(1, p * 1.6) * 0.55;
        blur.style.transform = `translateX(${-(1 - p) * 80}%)`;
      }
    };
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => { raf = 0; apply(); });
    };
    apply();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', apply);
    return () => {
      if (raf) cancelAnimationFrame(raf);
      window.removeEventListener('scroll', onScroll);
      window.removeEventListener('resize', apply);
    };
  }, []);

  // On stage change (revealKey > 0), auto-replay the reveal animation.
  useEffect(() => {
    if (revealKey === 0) return;
    const car = carRef.current;
    const blur = blurRef.current;
    manualLockRef.current = true;
    if (car) {
      car.style.transition = 'none';
      car.style.opacity = '0';
      car.style.transform = 'translateY(70%) scale(0.94)';
    }
    if (blur) {
      blur.style.transition = 'none';
      blur.style.opacity = '0';
      blur.style.transform = 'translateX(-80%)';
    }
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        if (car) {
          car.style.transition = '';
          car.style.opacity = '1';
          car.style.transform = 'translateY(0%) scale(1)';
        }
        if (blur) {
          blur.style.transition = '';
          blur.style.opacity = '0.55';
          blur.style.transform = 'translateX(0%)';
        }
      });
    });
    const t = setTimeout(() => { manualLockRef.current = false; }, 1000);
    return () => clearTimeout(t);
  }, [revealKey]);

  // Slide car + blur out to the right (forward), then run `next` to switch stage.
  const playExit = (then) => {
    const car = carRef.current;
    const blur = blurRef.current;
    manualLockRef.current = true;
    if (car) {
      car.style.transition = 'transform .65s cubic-bezier(.4,0,.2,1), opacity .55s ease';
      car.style.transform = 'translateX(130%) scale(1.02)';
      car.style.opacity = '0';
    }
    if (blur) {
      blur.style.transition = 'transform .65s cubic-bezier(.4,0,.2,1), opacity .55s ease';
      blur.style.transform = 'translateX(80%)';
      blur.style.opacity = '0';
    }
    setTimeout(then, 650);
  };

  const advance = () => {
    if (videoIdx === 0) {
      setVideoIdx(1);
      return;
    }
    playExit(() => {
      setVideoIdx(0);
      setStage((s) => (s + 1) % REELS_STAGES.length);
      setRevealKey((k) => k + 1);
    });
  };
  const goBack = () => {
    if (videoIdx === 1) {
      setVideoIdx(0);
      return;
    }
    playExit(() => {
      setVideoIdx(1);
      setStage((s) => (s - 1 + REELS_STAGES.length) % REELS_STAGES.length);
      setRevealKey((k) => k + 1);
    });
  };

  return (
    <div ref={wrapRef} className="reels-showcase-wrap" style={{ height: `calc(100vh + ${REVEAL_PX}px)` }}>
      <section className="reels-showcase" id="reels" aria-label="Reels">
        {/* Blur stays outside the centered frame so it can extend full viewport without clipping. */}
        <div ref={blurRef} className="reels-car-blur" key={`blur-${current.key}-${revealKey}`}>
          <img src={current.car} alt="" aria-hidden="true" />
        </div>
        <div className="reels-area">
        <div ref={carRef} className="reels-car" key={`car-${current.key}-${revealKey}`}>
          <img src={current.car} alt={current.label} />
        </div>

        <div className="reels-content">
          <div className="reels-text">
            <p className="reels-eyebrow">— Reels</p>
            <h2 className="reels-title">
              Aus unserem<br/>
              <em>Studio</em>.
            </h2>
            <p className="reels-sub">
              Echte Fahrzeuge, echte Arbeit. So sehen Ergebnisse aus, wenn Detail wirklich Detail bedeutet.
            </p>
            <div className="reels-stage-dots">
              {REELS_STAGES.map((s, i) => (
                <span key={s.key} className={`reels-dot${i === stage ? ' is-active' : ''}`} aria-label={s.label} />
              ))}
            </div>
          </div>

          <div className="reels-cards">
            <ReelCard
              key={`card-front-${current.key}-${videoIdx}`}
              src={videoAt(0)}
              onEnded={advance}
              variant="front"
              muted={muted}
              autoPlay
            />
            <ReelCard key={`card-back1-${flatIndex}`} src={videoAt(1)} variant="back-1" muted />
            <ReelCard key={`card-back2-${flatIndex}`} src={videoAt(2)} variant="back-2" muted />
            <ReelCard key={`card-back3-${flatIndex}`} src={videoAt(3)} variant="back-3" muted />
            <button
              type="button"
              className="reel-mute"
              aria-label={muted ? 'Ton einschalten' : 'Ton ausschalten'}
              onClick={() => setMuted((m) => !m)}
            >
              {muted ? (
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4z"/><line x1="22" y1="9" x2="16" y2="15"/><line x1="16" y1="9" x2="22" y2="15"/></svg>
              ) : (
                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 5 6 9H2v6h4l5 4z"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/></svg>
              )}
            </button>
            <div className="reel-nav">
              <button type="button" aria-label="Vorheriges Video" onClick={goBack}>‹</button>
              <button type="button" aria-label="Nächstes Video" onClick={advance}>›</button>
            </div>
          </div>
        </div>
        </div>
      </section>
    </div>
  );
}

function ReelCard({ src, onEnded, variant, autoPlay, muted = true }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!autoPlay || !ref.current) return;
    const v = ref.current;
    const tryPlay = () => v.play().catch(() => {});
    tryPlay();
  }, [src, autoPlay]);
  useEffect(() => {
    if (ref.current) ref.current.muted = muted;
  }, [muted]);
  return (
    <div className={`reel-card reel-card--${variant}`}>
      <video
        ref={ref}
        src={src}
        muted={muted}
        playsInline
        preload={autoPlay ? "auto" : "metadata"}
        onEnded={onEnded}
      />
    </div>
  );
}

// --- Local Area Section (city pages only) ---------------------------------
function LocalAreaSection() {
  if (!LOC) return null;
  const nearby = (LOC.nearby || []).map(k => LOCATIONS[k]).filter(Boolean);
  return (
    <section className="local-area" id="standort">
      <div className="section-head">
        <span className="section-no">— Standort {LOC.name}</span>
        <h2 className="section-title">{LOC.localTitle}</h2>
      </div>
      <div className="local-area-body">
        {LOC.localBody.map((p, i) => <p key={i}>{p}</p>)}
      </div>
      {nearby.length > 0 && (
        <div className="local-area-nearby">
          <span className="local-area-nearby-label">Auch in der Region:</span>
          <div className="local-area-nearby-links">
            {nearby.map(n => (
              <a key={n.slug} href={`/${n.slug}`} className="local-area-nearby-link">
                Autoaufbereitung {n.name} ↗
              </a>
            ))}
            <a href="/" className="local-area-nearby-link">Autoaufbereitung München ↗</a>
          </div>
        </div>
      )}
    </section>
  );
}

// --- Why / Manifesto -------------------------------------------------------
function Manifesto() {
  return (
    <section className="manifesto">
      <p className="manifesto-eyebrow">— Warum Glanzmanufaktur</p>
      <h2 className="manifesto-title">
        Weil unsere Leidenschaft<br/>
        für <em>Details</em> den<br/>
        Unterschied macht.
      </h2>
      <div className="manifesto-cols">
        <div>
          <h3>Erfahrung & Expertise</h3>
          <p>Modernste Techniken, hochwertigste Produkte. Wir bilden uns laufend weiter und arbeiten mit den Materialien, die auch im Concours-Bereich zum Einsatz kommen.</p>
        </div>
        <div>
          <h3>Individuelle Beratung</h3>
          <p>Kein Auto kommt zweimal vor. Wir gehen auf den Zustand und Ihre Wünsche ein und schlagen genau die Maßnahmen vor, die für Ihr Fahrzeug Sinn ergeben.</p>
        </div>
        <div>
          <h3>Zuverlässigkeit & Qualität</h3>
          <p>Schnell, gründlich, transparent. Vereinbarte Termine, klare Kommunikation, Ergebnisse, die sich bei jedem Lichteinfall messen lassen.</p>
        </div>
      </div>
    </section>
  );
}

// --- Testimonials ----------------------------------------------------------
function Stars({ n = 5 }) {
  return (
    <span className="stars" aria-label={`${n} von 5 Sternen`}>
      {Array(5).fill(0).map((_, i) => (
        <span key={i} className={`star ${i < n ? "is-on" : ""}`}>★</span>
      ))}
    </span>
  );
}

function TestimonialCard({ r, style }) {
  const [expanded, setExpanded] = useState(false);
  const blockRef = useRef(null);
  const [overflow, setOverflow] = useState(false);
  useEffect(() => {
    const el = blockRef.current;
    if (!el) return;
    const check = () => setOverflow(el.scrollHeight > el.clientHeight + 2);
    const ro = new ResizeObserver(check);
    ro.observe(el);
    check();
    return () => ro.disconnect();
  }, [r.text]);
  return (
    <figure className={`testimonial ${expanded ? "is-expanded" : ""}`} style={style}>
      <blockquote ref={blockRef}>{r.text}</blockquote>
      {(overflow || expanded) && (
        <button className="testimonial-toggle" onClick={() => setExpanded(e => !e)}>
          {expanded ? "Weniger anzeigen ↑" : "Mehr lesen ↓"}
        </button>
      )}
      <div className="testimonial-foot">
        <div className="testimonial-head">
          {r.profile_photo_url ? (
            <img className="testimonial-avatar" src={r.profile_photo_url} alt="" loading="lazy" referrerPolicy="no-referrer" />
          ) : (
            <span className="testimonial-avatar testimonial-avatar--initial">{r.author_name[0]}</span>
          )}
          <div className="testimonial-head-meta">
            <span className="testimonial-author">{r.author_name}</span>
            <Stars n={r.rating} />
          </div>
        </div>
        <a className="testimonial-source" href={r.author_url} target="_blank" rel="noopener nofollow">
          Google ↗
        </a>
      </div>
    </figure>
  );
}

const ACCENT_COLORS = [
  "#fbbf24", // amber
  "#60a5fa", // blue
  "#34d399", // emerald
  "#f472b6", // pink
  "#a78bfa", // violet
  "#fb923c", // orange
];

function TestimonialsMasonry({ reviews }) {
  const cols = useColumnCount();

  // Mobile (cols === 1): zwei Marquee-Reihen, oben rechts-laufend, unten links-laufend
  if (cols === 1) {
    const indexed = reviews.map((r, i) => ({ ...r, _idx: i }));
    const top = indexed.filter((_, i) => i % 2 === 0);
    const bot = indexed.filter((_, i) => i % 2 === 1);
    const renderRow = (row, dirClass) => (
      <div className={`testimonials-marquee ${dirClass}`}>
        <div className="testimonials-marquee-track">
          {[...row, ...row].map((r, i) => (
            <TestimonialCard
              key={i}
              r={r}
              style={{ "--accent-glow": ACCENT_COLORS[r._idx % ACCENT_COLORS.length] }}
            />
          ))}
        </div>
      </div>
    );
    return (
      <div className="testimonials-marquee-wrap">
        {renderRow(top, "testimonials-marquee--right")}
        {renderRow(bot, "testimonials-marquee--left")}
      </div>
    );
  }

  // Tablet/Desktop: Masonry wie bisher
  const buckets = Array.from({ length: cols }, () => []);
  reviews.forEach((r, i) => buckets[i % cols].push({ ...r, _idx: i }));
  return (
    <div className="testimonials-masonry" style={{ gridTemplateColumns: `repeat(${cols}, 1fr)` }}>
      {buckets.map((col, ci) => (
        <div key={ci} className="testimonials-col">
          {col.map((r, ri) => {
            const color = ACCENT_COLORS[r._idx % ACCENT_COLORS.length];
            const delay = (r._idx * 0.73) % 4;
            const duration = 6 + (r._idx % 3);
            return (
              <TestimonialCard
                key={ri}
                r={r}
                style={{
                  "--accent-glow": color,
                  animationDelay: `-${delay}s`,
                  animationDuration: `${duration}s`,
                }}
              />
            );
          })}
        </div>
      ))}
    </div>
  );
}

function useColumnCount() {
  const [n, setN] = useState(4);
  useEffect(() => {
    const calc = () => {
      const w = document.documentElement.clientWidth || window.innerWidth;
      if (w < 640) setN(1);
      else if (w < 1100) setN(2);
      else setN(3);
    };
    calc();
    window.addEventListener("resize", calc);
    return () => window.removeEventListener("resize", calc);
  }, []);
  return n;
}

function Testimonials() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch("reviews.json").then(r => r.json()).then(setData).catch(() => {});
  }, []);
  if (!data) return null;
  return (
    <section className="testimonials" id="rezensionen">
      <div className="section-head">
        <span className="section-no">— Rezensionen</span>
        <h2 className="section-title">
          {data.rating?.toString().replace(".", ",")} Sterne aus<br/>
          <em>{data.user_ratings_total} echten Bewertungen.</em>
        </h2>
      </div>

      <div className="testimonials-meta">
        <Stars n={Math.round(data.rating)} />
        <span className="testimonials-meta-text">
          {data.rating?.toString().replace(".", ",")} / 5,0 · {data.user_ratings_total} Google-Bewertungen
        </span>
      </div>

      <TestimonialsMasonry reviews={data.reviews} />

      <div className="testimonials-foot">
        <a href={data.place_url} target="_blank" rel="noopener" className="btn btn--ghost btn--lg">
          Alle {data.user_ratings_total} Bewertungen auf Google →
        </a>
      </div>
    </section>
  );
}

// --- Booking modal ---------------------------------------------------------
const CRM_API = "https://crm.rvertising.com/api/leads";
const CRM_TENANT = "glanzmanufaktur";

function Booking({ open, onClose }) {
  const [data, setData] = useState({ firstName: "", lastName: "", phone: "", email: "" });
  const [consent, setConsent] = useState(false);
  const [sent, setSent] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!open) {
      setTimeout(() => {
        setData({ firstName: "", lastName: "", phone: "", email: "" });
        setConsent(false);
        setSent(false);
        setError(null);
        setSubmitting(false);
      }, 300);
    }
  }, [open]);

  const update = (k, v) => setData({ ...data, [k]: v });

  const canSubmit =
    data.firstName.trim().length >= 2 &&
    data.lastName.trim().length >= 2 &&
    data.phone.trim().length >= 6 &&
    consent;

  const submit = async () => {
    if (!canSubmit || submitting) return;
    setSubmitting(true);
    setError(null);

    // Pull UTMs from current URL — set when ad-traffic landed on the page
    const params = new URLSearchParams(window.location.search);
    const utm = {
      utm_source: params.get("utm_source") || null,
      utm_medium: params.get("utm_medium") || null,
      utm_campaign: params.get("utm_campaign") || null,
    };

    const fullName = `${data.firstName.trim()} ${data.lastName.trim()}`.trim();

    try {
      const res = await fetch(CRM_API, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          tenant: CRM_TENANT,
          name: fullName,
          phone: data.phone.trim(),
          email: data.email.trim() || null,
          ...utm,
        }),
      });
      if (!res.ok) {
        const j = await res.json().catch(() => ({}));
        throw new Error(j.error || `Server-Fehler (${res.status})`);
      }
      setSent(true);
    } catch (err) {
      setError(err && err.message ? err.message : "Senden fehlgeschlagen — bitte später erneut versuchen oder telefonisch melden.");
    } finally {
      setSubmitting(false);
    }
  };

  if (!open) return null;

  return (
    <div className="booking-overlay" onClick={onClose}>
      <div className="booking" onClick={e => e.stopPropagation()}>
        <button className="booking-close" onClick={onClose} aria-label="Schließen">×</button>
        <div className="booking-side">
          <span className="section-no">— Schnellanfrage</span>
          <h3>Wir melden uns innerhalb von 24h.</h3>
          <ul className="booking-meta">
            <li><span>Adresse</span><span>Putzbrunner Str. 89</span></li>
            <li><span>Telefon</span><span>+49 157 34 828 543</span></li>
            <li><span>E-Mail</span><span>info@glanzmanufaktur-muenchen.de</span></li>
          </ul>
        </div>

        <div className="booking-main">
          {sent ? (
            <div className="booking-done">
              <div className="booking-done-mark">✓</div>
              <h4>Anfrage gesendet.</h4>
              <p>Vielen Dank, {data.firstName || "danke"}. Wir melden uns telefonisch unter {data.phone}.</p>
              <button className="btn btn--gold btn--lg" onClick={onClose}>Schließen</button>
            </div>
          ) : (
            <form onSubmit={e => { e.preventDefault(); submit(); }}>
              <span className="config-step">Schnellanfrage</span>
              <h4 className="booking-q">Wen dürfen wir kontaktieren?</h4>

              <div className="booking-name-row">
                <label className="field">
                  <span>Vorname</span>
                  <input
                    value={data.firstName}
                    onChange={e => update("firstName", e.target.value)}
                    placeholder="Max"
                    autoComplete="given-name"
                    required
                  />
                </label>
                <label className="field">
                  <span>Nachname</span>
                  <input
                    value={data.lastName}
                    onChange={e => update("lastName", e.target.value)}
                    placeholder="Mustermann"
                    autoComplete="family-name"
                    required
                  />
                </label>
              </div>

              <label className="field">
                <span>Telefon</span>
                <input
                  type="tel"
                  value={data.phone}
                  onChange={e => update("phone", e.target.value)}
                  placeholder="+49 157 …"
                  autoComplete="tel"
                  required
                />
              </label>

              <label className="field">
                <span>E-Mail <em className="field-optional">— optional</em></span>
                <input
                  type="email"
                  value={data.email}
                  onChange={e => update("email", e.target.value)}
                  placeholder="max@example.com"
                  autoComplete="email"
                />
              </label>

              {error && (
                <div className="booking-error" role="alert">
                  {error}
                </div>
              )}

              <label className="field-consent">
                <input
                  type="checkbox"
                  checked={consent}
                  onChange={e => setConsent(e.target.checked)}
                  required
                />
                <span>
                  Ich akzeptiere die <a href="datenschutz.html" target="_blank" rel="noopener">Datenschutzerklärung</a>. Meine Angaben werden ausschließlich zur Bearbeitung meiner Anfrage verwendet und nicht an Dritte weitergegeben.
                </span>
              </label>

              <div className="booking-actions">
                <button
                  type="submit"
                  className="btn btn--gold"
                  disabled={!canSubmit || submitting}
                >
                  {submitting ? "Sende …" : "Anfrage senden"}
                </button>
              </div>
            </form>
          )}
        </div>
      </div>
    </div>
  );
}

// --- Footer ---------------------------------------------------------------
function Footer({ onBook }) {
  return (
    <footer className="footer" id="kontakt">
      <div className="footer-top">
        <div className="footer-headline-col">
          <h2 className="footer-headline">
            Bereit für<br/><em>Spiegelglanz?</em>
          </h2>
          <p className="footer-cta-text">
            Sagen Sie uns, was Ihr Fahrzeug braucht.<br/>
            Wir melden uns persönlich — meist noch am selben Tag.
          </p>
          <button className="btn btn--gold btn--lg footer-cta-btn" onClick={onBook}>Termin anfragen</button>
        </div>
        <div className="footer-side">
          <figure className="footer-boss">
            <img src="images/Boss.png" alt="Tino Weileder · Glanzmanufaktur München" />
            <figcaption>
              <span className="footer-boss-name">Tino Weileder</span>
            </figcaption>
          </figure>
          <a href="mailto:info@glanzmanufaktur-muenchen.de" className="footer-mail">
            info@glanzmanufaktur-muenchen.de ↗
          </a>
        </div>
      </div>
      <div className="footer-grid footer-grid--5">
        <div>
          <h4>Manufaktur</h4>
          <p>Glanzmanufaktur München<br/>Putzbrunner Straße 89<br/>81739 München</p>
        </div>
        <div>
          <h4>Kontakt</h4>
          <p><a href="tel:+4915734828543">+49 157 34 828 543</a><br/><a href="mailto:info@glanzmanufaktur-muenchen.de">info@glanzmanufaktur-muenchen.de</a></p>
        </div>
        <div>
          <h4>Öffnungszeiten</h4>
          <p>Mo–Fr · 09:00–18:00<br/>Sa · nach Vereinbarung<br/>So · geschlossen</p>
        </div>
        <div>
          <h4>Standorte</h4>
          <ul className="footer-locations">
            {LOCATION_ORDER.map(k => {
              const l = LOCATIONS[k];
              return (
                <li key={k}>
                  <a href={`/${l.slug}`}>{l.name}</a>
                </li>
              );
            })}
          </ul>
        </div>
        <div>
          <h4>Folgen</h4>
          <p><a href="https://www.instagram.com/glanzmanufaktur.weileder/" target="_blank" rel="noopener">Instagram ↗</a><br/>@glanzmanufaktur.weileder</p>
        </div>
      </div>
      <div className="footer-bottom">
        <span>© 2026 Glanzmanufaktur München</span>
        <div className="footer-legal">
          <a href="/impressum">Impressum</a>
          <a href="/datenschutz">Datenschutz</a>
          <a href="#" data-gm-cookies>Cookie-Einstellungen</a>
        </div>
      </div>
      <div className="footer-credit">
        <span>Webdesign &amp; Entwicklung von</span>
        <a href="https://www.rvertising.com" target="_blank" rel="noopener" className="footer-credit-link" aria-label="Webdesign & Entwicklung: Rvertising">
          <img src="images/rvertising-logo.png" alt="" />
          <span>Rvertising</span>
        </a>
      </div>
    </footer>
  );
}

// --- App ------------------------------------------------------------------
function App() {
  const tweaks = TWEAK_DEFAULTS;
  const [bookingOpen, setBookingOpen] = useState(false);

  useEffect(() => {
    const easeInOutCubic = (t) => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3)/2;
    const animateTo = (targetY, duration = 1100) => {
      const startY = window.scrollY;
      const dist = targetY - startY;
      const t0 = performance.now();
      const step = (now) => {
        const p = Math.min(1, (now - t0) / duration);
        window.scrollTo(0, startY + dist * easeInOutCubic(p));
        if (p < 1) requestAnimationFrame(step);
      };
      requestAnimationFrame(step);
    };
    const onClick = (e) => {
      const a = e.target.closest("a[href^='#']");
      if (!a) return;
      const id = a.getAttribute("href").slice(1);
      if (!id) return;
      const el = document.getElementById(id);
      if (!el) return;
      e.preventDefault();
      const offset = 96;
      const target = el.getBoundingClientRect().top + window.scrollY - offset;
      history.replaceState(null, "", "#" + id);
      animateTo(target);
    };
    document.addEventListener("click", onClick);
    return () => document.removeEventListener("click", onClick);
  }, []);

  return (
    <>
      <Nav onBook={() => setBookingOpen(true)} />
      <main>
        <Hero onBook={() => setBookingOpen(true)} variant={tweaks.heroVariant} />
        <LocalAreaSection />
        <Services />
        <Configurator onBook={() => setBookingOpen(true)} />
        <Manifesto />
        <ReelsShowcase />
        <Testimonials />
        <Process />
        <InstagramFeed />
      </main>
      <Footer onBook={() => setBookingOpen(true)} />
      <Booking open={bookingOpen} onClose={() => setBookingOpen(false)} />

    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
