@@ -1,620 +0,0 @@
<!DOCTYPE html>
< html >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< script src = "./support.js" > < / script >
< / head >
< body >
< x-dc >
< helmet >
< link href = "assets/fonts.css" rel = "stylesheet" >
< link href = "https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" rel = "stylesheet" >
< script src = "https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js" > < / script >
< style >
* { margin : 0 ; padding : 0 ; box-sizing : border-box ; }
body { background : #0d0f12 ; font-family : 'Archivo' , system-ui , sans-serif ; -webkit- font-smoothing : antialiased ; }
a { color : #f0f0ec ; text-decoration : none ; }
a : hover { color : var ( - - accent , #F0912F ) ; }
@ keyframes lw-shimmer { 0 % { background-position : -200 % 0 ; } 100 % { background-position : 200 % 0 ; } }
@ keyframes lw-fadein { from { opacity : 0 ; transform : translateY ( 14 px ) ; } to { opacity : 1 ; transform : translateY ( 0 ) ; } }
@ media ( max-width : 1080px ) {
. lw-review { display : none !important ; }
}
@ media ( max-width : 860px ) {
. lw-navlinks { display : none !important ; }
. lw-burger { display : inline-flex !important ; }
. lw-benefits { grid-template-columns : repeat ( 2 , 1 fr ) !important ; row-gap : 26 px !important ; }
}
@ media ( max-width : 520px ) {
. lw-benefits { grid-template-columns : 1 fr !important ; }
}
@ media ( max-width : 980px ) {
. lw-services { grid-template-columns : repeat ( 2 , 1 fr ) !important ; }
}
@ media ( max-width : 620px ) {
. lw-services { grid-template-columns : 1 fr !important ; }
}
@ media ( max-width : 980px ) {
. lw-refs { grid-template-columns : repeat ( 2 , 1 fr ) !important ; grid-auto-rows : 200 px !important ; }
}
@ media ( max-width : 620px ) {
. lw-refs { grid-template-columns : 1 fr !important ; grid-auto-rows : 220 px !important ; }
. lw-ref-wide { grid-column : auto !important ; grid-row : auto !important ; }
}
@ media ( max-width : 860px ) {
. lw-about { grid-template-columns : 1 fr !important ; }
. lw-contact { grid-template-columns : 1 fr !important ; }
. lw-footer-cols { grid-template-columns : repeat ( 2 , 1 fr ) !important ; }
. lw-footer-bottom { flex-direction : column !important ; align-items : flex-start !important ; }
. lw-footer-bottom > div : last-child { text-align : left !important ; }
. lw-footer-bottom > div : last-child > div { justify-content : flex-start !important ; }
}
< / style >
< / helmet >
< div ref = "{{ rootRef }}" style = "width: 100%; --accent: #F0912F; background: #0d0f12;" >
<!-- HERO -->
< div style = "position: relative; min-height: 100vh; overflow: hidden;" >
<!-- BACKGROUND -->
< div style = "position: absolute; inset: 0; z-index: 0; background: radial-gradient(120% 90% at 78% 12%, #1c2530 0%, #12161b 42%, #0a0c0f 100%);" > < / div >
< div style = "position: absolute; inset: 0; z-index: 1; filter: hue-rotate(85deg) saturate(1.15);" >
< img src = "assets/hero-tankklappe.jpg" alt = "" style = "display: block; width: 100%; height: 100%; object-fit: cover;" >
< / div >
<!-- blue tint -->
< div style = "position: absolute; inset: 0; z-index: 2; pointer-events: none; background: rgba(18,60,120,0.32); mix-blend-mode: multiply;" > < / div >
<!-- legibility overlay (click - through) -->
< div style = "position: absolute; inset: 0; z-index: 2; pointer-events: none; background: linear-gradient(180deg, rgba(9,11,14,0.82) 0%, rgba(9,11,14,0.34) 30%, rgba(9,11,14,0.30) 55%, rgba(9,11,14,0.86) 100%);" > < / div >
< div style = "position: absolute; inset: 0; z-index: 2; pointer-events: none; background: radial-gradient(90% 70% at 22% 42%, rgba(9,11,14,0.55) 0%, rgba(9,11,14,0) 60%);" > < / div >
<!-- thin accent glow top -->
< div style = "position: absolute; top: 0; left: 0; right: 0; height: 3px; z-index: 4; background: linear-gradient(90deg, transparent, var(--accent, #F0912F), transparent); opacity: 0.5;" > < / div >
<!-- NAV -->
< nav ref = "{{ navRef }}" style = "position: fixed; top: 0; left: 0; right: 0; z-index: 40; transition: background .35s ease, backdrop-filter .35s ease, border-color .35s ease, padding .35s ease; padding: 22px clamp(20px, 4vw, 56px); border-bottom: 1px solid transparent;" >
< div style = "max-width: 1320px; margin: 0 auto; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 24px;" >
<!-- left links -->
< div class = "lw-navlinks" style = "display: flex; gap: 34px; align-items: center;" >
< a href = "#services" style = "font-size: 15px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase;" > Services< / a >
< a href = "#referenzen" style = "font-size: 15px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase;" > Referenzen< / a >
< / div >
<!-- logo — PLATZHALTER: assets/logo - placeholder.svg durch Ihr Logo ersetzen -->
< a href = "#top" style = "display: flex; align-items: center; justify-self: center;" >
< img src = "assets/logo-placeholder.svg" alt = "Auto Paint Work" style = "height: 26px; width: auto; display: block;" >
< / a >
<!-- right links -->
< div class = "lw-navlinks" style = "display: flex; gap: 34px; align-items: center; justify-content: flex-end;" >
< a href = "#ueber-uns" style = "font-size: 15px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase;" > Über uns< / a >
< a href = "#kontakt" style = "font-size: 15px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase;" > Kontakt< / a >
< / div >
<!-- mobile menu button -->
< button class = "lw-burger" aria-label = "Menü" style = "display: none; grid-column: 3; justify-self: end; background: transparent; border: 1.5px solid rgba(255,255,255,.28); border-radius: 10px; padding: 9px 11px; cursor: pointer; color: #f4f3ef; align-items: center; gap: 8px;" >
< span style = "display: block; width: 18px; height: 2px; background: currentColor; box-shadow: 0 6px 0 currentColor, 0 -6px 0 currentColor;" > < / span >
< / button >
< / div >
< / nav >
<!-- HERO CONTENT -->
< div style = "position: relative; z-index: 10; display: flex; flex-direction: column; min-height: 100vh; padding: clamp(120px, 16vh, 190px) clamp(20px, 4vw, 56px) 0;" >
< div style = "max-width: 1320px; margin: 0 auto; width: 100%; display: flex; flex-direction: column; flex: 1;" >
<!-- spacer -->
< div style = "flex: 1; min-height: 40px;" > < / div >
<!-- headline block -->
< div style = "max-width: 720px; margin: 0 auto; text-align: center; animation: lw-fadein .7s ease both;" >
< h1 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; line-height: 1.08; letter-spacing: -0.005em; color: #f5f4f0; font-size: clamp(36px, 4.8vw, 64px); margin-bottom: 22px;" >
< span style = "display: block;" > Rein mit
< span ref = "{{ wordSlotRef }}" style = "display: inline-block; position: relative; overflow: hidden; vertical-align: bottom; text-align: left;" >
< span ref = "{{ wordRef }}" style = "display: inline-block; color: var(--accent, #F0912F); font-style: italic; will-change: transform, opacity;" > Kratzer< / span >
< / span >
< / span >
< span style = "display: block;" > Raus wie neu.< / span >
< / h1 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.5; color: #c5c9ce; max-width: 560px; margin: 0 auto; font-weight: 500;" >
Wir reparieren alles! Mit Originalteilen und neuester Technik zu fairen Preisen. Als wäre nichts passiert.
< / p >
< / div >
<!-- spacer -->
< div style = "flex: 1; min-height: 40px;" > < / div >
<!-- BENEFIT STRIP -->
< div class = "lw-benefits" style = "display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(18px, 3vw, 48px); padding: 28px 0 46px;" >
< div style = "display: flex; flex-direction: column; justify-content: flex-end; gap: 12px;" >
< p style = "font-size: 15.5px; line-height: 1.4; color: #e7e9ec; font-weight: 600;" > Fahrzeug-Reparatur wie in der Vertragswerkstatt — nur günstiger< / p >
< / div >
< div style = "display: flex; flex-direction: column; justify-content: flex-end; gap: 12px;" >
< img src = "assets/benefit01.svg" alt = "" width = "22" height = "27" style = "opacity: .95;" >
< p style = "font-size: 15.5px; line-height: 1.4; color: #e7e9ec; font-weight: 600;" > Wir nutzen Premium-Lacke< / p >
< / div >
< div style = "display: flex; flex-direction: column; justify-content: flex-end; gap: 12px;" >
< img src = "assets/benefit02.svg" alt = "" width = "30" height = "24" style = "opacity: .95;" >
< p style = "font-size: 15.5px; line-height: 1.4; color: #e7e9ec; font-weight: 600;" > Wir verbauen nur Originalteile< / p >
< / div >
< div style = "display: flex; flex-direction: column; gap: 14px;" >
<!-- Google badge -->
< a href = "{{ googleUrl }}" target = "_blank" rel = "noopener" style = "display: inline-flex; flex-direction: column; gap: 3px; align-self: flex-start; background: #000; border: 1px solid rgba(255,255,255,.12); border-radius: 44px; padding: 11px 20px; transition: transform .3s ease, box-shadow .3s ease;" style-hover = "transform: scale(1.05); box-shadow: 0 6px 20px rgba(0,0,0,.4);" >
< span style = "color: #fff; font-weight: 700; font-size: 15px; letter-spacing: .01em;" > Auto Paint Work< / span >
< span style = "display: inline-flex; align-items: center; gap: 7px;" >
< span style = "color: #DBDED9; font-weight: 600; font-size: 15px;" > {{ ratingLabel }}< / span >
< img src = "assets/review.svg" alt = "Bewertung" width = "66" height = "10" >
< span style = "color: #DBDED9; font-weight: 300; font-size: 15px;" > {{ countLabel }}< / span >
< / span >
< / a >
< img src = "assets/benefit03.svg" alt = "" width = "20" height = "30" style = "opacity: .95;" >
< p style = "font-size: 15.5px; line-height: 1.4; color: #e7e9ec; font-weight: 600;" > Wir arbeiten mit der gleichen Genauigkeit< / p >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- LEISTUNGEN SECTION -->
< section id = "services" data-screen-label = "Leistungen" style = "position: relative; overflow: hidden; background: #0D2A46; padding: clamp(80px, 12vh, 140px) clamp(20px, 4vw, 56px);" >
< svg viewBox = "0 0 1440 900" preserveAspectRatio = "xMidYMid slice" aria-hidden = "true" style = "position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;" >
< path d = "M-100,780 C300,620 500,930 900,760 C1180,640 1350,700 1540,560" fill = "none" stroke = "#164a7d" stroke-width = "150" stroke-linecap = "round" opacity = "0.75" > < / path >
< path d = "M-120,320 C260,480 620,120 980,300 C1240,430 1380,320 1560,400" fill = "none" stroke = "#123a63" stroke-width = "190" stroke-linecap = "round" opacity = "0.8" > < / path >
< path d = "M-80,90 C380,220 760,-40 1120,120 C1330,215 1450,140 1560,190" fill = "none" stroke = "#1d5c99" stroke-width = "110" stroke-linecap = "round" opacity = "0.55" > < / path >
< path d = "M-100,560 C340,420 700,660 1080,500 C1300,410 1420,470 1560,410" fill = "none" stroke = "#0f3154" stroke-width = "230" stroke-linecap = "round" opacity = "0.9" > < / path >
< / svg >
< div style = "position: absolute; inset: 0; background: linear-gradient(180deg, rgba(13,42,70,.45) 0%, rgba(13,42,70,.10) 40%, rgba(13,42,70,.45) 100%); pointer-events: none;" > < / div >
< div style = "position: relative; max-width: 1320px; margin: 0 auto;" >
< div style = "max-width: 760px; margin-bottom: clamp(40px, 6vh, 64px);" >
< span style = "display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: #9FD1F5; margin-bottom: 16px;" > Leistungen< / span >
< h2 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; line-height: 1.1; color: #f5f4f0; font-size: clamp(30px, 3.6vw, 52px); margin-bottom: 18px;" > Alles rund um den Lack – aus einer Hand< / h2 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.55; color: #c9d6e2; font-weight: 500;" > Ob Neuwagen, Alltagsauto oder Klassiker: Wir bringen jede Oberfläche zurück in Bestform. Präzise vorbereitet, sauber lackiert, perfekt finiert.< / p >
< / div >
< div class = "lw-services" style = "display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(18px, 2.4vw, 30px);" >
< div style = "background: #F1F0EB; border-radius: 24px; padding: 34px 30px; display: flex; flex-direction: column; gap: 12px; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #0D2A46;" > Lackierungen aller Art< / h3 >
< p style = "font-size: 15.5px; line-height: 1.55; color: #45535f; font-weight: 500; flex: 1;" > Vom Kotflügel bis zur Komplettlackierung – markenunabhängig, farbtongenau und in dauerhafter Qualität.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #1e7fc2;" > Uni · Metallic · Perleffekt< / span >
< / div >
< div style = "background: #F1F0EB; border-radius: 24px; padding: 34px 30px; display: flex; flex-direction: column; gap: 12px; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #0D2A46;" > Smart Repair< / h3 >
< p style = "font-size: 15.5px; line-height: 1.55; color: #45535f; font-weight: 500; flex: 1;" > Kleine Kratzer, Steinschläge und Parkdellen punktgenau beseitigt – schnell, günstig und ohne sichtbaren Übergang.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #1e7fc2;" > Spot-Repair · Same Day< / span >
< / div >
< div style = "background: #F1F0EB; border-radius: 24px; padding: 34px 30px; display: flex; flex-direction: column; gap: 12px; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #0D2A46;" > Oldtimer & Klassiker< / h3 >
< p style = "font-size: 15.5px; line-height: 1.55; color: #45535f; font-weight: 500; flex: 1;" > Restaurierung mit Fingerspitzengefühl: originalgetreue Farbtöne und ein Finish, das dem Wert Ihres Fahrzeugs gerecht wird.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #1e7fc2;" > Restauration · Karosserie< / span >
< / div >
< div style = "background: #F1F0EB; border-radius: 24px; padding: 34px 30px; display: flex; flex-direction: column; gap: 12px; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #0D2A46;" > Lackaufbereitung & Politur< / h3 >
< p style = "font-size: 15.5px; line-height: 1.55; color: #45535f; font-weight: 500; flex: 1;" > Professionelles Polieren, Versiegeln und Aufbereiten für tiefen Glanz und langanhaltenden Schutz Ihres Lacks.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #1e7fc2;" > Politur · Versiegelung< / span >
< / div >
< div style = "background: #F1F0EB; border-radius: 24px; padding: 34px 30px; display: flex; flex-direction: column; gap: 12px; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #0D2A46;" > Felgen & Motorrad< / h3 >
< p style = "font-size: 15.5px; line-height: 1.55; color: #45535f; font-weight: 500; flex: 1;" > Felgenaufbereitung und Motorradlackierung im gleichen hohen Standard – für den perfekten Auftritt bis ins Detail.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #1e7fc2;" > Felgen · Zweirad< / span >
< / div >
<!-- LEISTUNGS - VIDEO — bewusst beibehalten (assets/leistungen - video.mp4) -->
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 4px 14px rgba(0,0,0,.25);" >
< video ref = "{{ videoRef }}" src = "assets/leistungen-video.mp4" autoplay muted loop playsinline style = "position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;" > < / video >
< div style = "position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8,20,34,0) 45%, rgba(8,20,34,.78) 100%); pointer-events: none;" > < / div >
< div style = "position: absolute; left: 30px; right: 30px; bottom: 26px; display: flex; flex-direction: column; gap: 8px;" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: 23px; color: #f5f4f0;" > Design- & Sonderlackierung< / h3 >
< p style = "font-size: 14.5px; line-height: 1.5; color: #d8e2ec; font-weight: 500;" > Individuelle Farbwünsche, Sondereffekte und Akzente – wir setzen Ihre Ideen sauber und hochwertig um.< / p >
< span style = "font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #9FD1F5;" > Custom · Effektlack< / span >
< / div >
< / div >
< / div >
< / div >
< / section >
<!-- REFERENZEN SECTION — Vorher/Nachher - Vergleich entfernt, durch Foto - Galerie ersetzt -->
< section id = "referenzen" data-screen-label = "Referenzen" style = "position: relative; overflow: hidden; background: #0D2A46; padding: clamp(80px, 12vh, 140px) clamp(20px, 4vw, 56px);" >
< img src = "assets/bg-radkasten.jpg" alt = "" style = "position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: hue-rotate(85deg) saturate(1.2); pointer-events: none;" >
< div style = "position: absolute; inset: 0; background: rgba(18,60,120,0.38); mix-blend-mode: multiply; pointer-events: none;" > < / div >
< div style = "position: absolute; inset: 0; background: linear-gradient(180deg, rgba(10,18,30,.8) 0%, rgba(10,18,30,.45) 45%, rgba(10,18,30,.8) 100%); pointer-events: none;" > < / div >
< div style = "position: relative; max-width: 1320px; margin: 0 auto;" >
< div style = "max-width: 760px; margin-bottom: clamp(40px, 6vh, 64px);" >
< span style = "display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: #9FD1F5; margin-bottom: 16px;" > Referenzen< / span >
< h2 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; line-height: 1.1; color: #f5f4f0; font-size: clamp(30px, 3.6vw, 52px); margin-bottom: 18px;" > Echte Arbeiten aus unserer Werkstatt< / h2 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.55; color: #c9d6e2; font-weight: 500;" > Ein Auszug aus unseren Projekten. Ziehen Sie eigene Fotos (z. B. Vorher/Nachher) einfach per Drag & Drop in die Platzhalter.< / p >
< / div >
<!-- Foto - Galerie — jeder Platzhalter ist per Drag & Drop mit einem eigenen Foto befüllbar -->
< div class = "lw-refs" style = "display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 240px; gap: clamp(14px, 1.8vw, 22px);" >
< div class = "lw-ref-wide" style = "position: relative; grid-column: span 2; grid-row: span 2; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.45); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "ref-1" shape = "rect" fit = "cover" placeholder = "Projektfoto 1 hier ablegen" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.45); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "ref-2" shape = "rect" fit = "cover" placeholder = "Foto 2" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.45); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "ref-3" shape = "rect" fit = "cover" placeholder = "Foto 3" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.45); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "ref-4" shape = "rect" fit = "cover" placeholder = "Foto 4" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.45); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "ref-5" shape = "rect" fit = "cover" placeholder = "Foto 5" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< / div >
< p style = "margin-top: clamp(28px, 4vh, 44px); text-align: center; font-size: 15px; color: #c9d6e2; font-weight: 500;" > Mehr laufende Projekte gibt es auf unserer < a href = "" target = "_blank" rel = "noopener" style = "color: #9FD1F5; font-weight: 700;" > Facebook-Seite< / a > .< / p >
< / div >
< / section >
<!-- ÜBER UNS SECTION -->
< section id = "ueber-uns" data-screen-label = "Über uns" style = "position: relative; overflow: hidden; background: #0D2A46; padding: clamp(80px, 12vh, 140px) clamp(20px, 4vw, 56px);" >
< svg viewBox = "0 0 1440 900" preserveAspectRatio = "xMidYMid slice" aria-hidden = "true" style = "position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;" >
< path d = "M-100,120 C340,260 720,-20 1100,140 C1320,230 1450,160 1560,210" fill = "none" stroke = "#1d5c99" stroke-width = "120" stroke-linecap = "round" opacity = "0.5" > < / path >
< path d = "M-120,480 C280,340 640,620 1020,460 C1280,350 1400,420 1560,360" fill = "none" stroke = "#123a63" stroke-width = "210" stroke-linecap = "round" opacity = "0.8" > < / path >
< path d = "M-100,800 C320,660 560,940 960,780 C1220,670 1380,730 1560,610" fill = "none" stroke = "#164a7d" stroke-width = "150" stroke-linecap = "round" opacity = "0.7" > < / path >
< / svg >
< div style = "position: absolute; inset: 0; background: linear-gradient(180deg, rgba(13,42,70,.45) 0%, rgba(13,42,70,.10) 40%, rgba(13,42,70,.45) 100%); pointer-events: none;" > < / div >
< div style = "position: relative; max-width: 1320px; margin: 0 auto;" >
< div style = "max-width: 900px; margin-bottom: clamp(50px, 8vh, 90px);" >
< span style = "display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: #9FD1F5; margin-bottom: 16px;" > Über uns< / span >
< h2 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; line-height: 1.12; color: #f5f4f0; font-size: clamp(32px, 4vw, 58px);" > Wir kombinieren handwerkliches Wissen und viel Erfahrung mit modernster Technik.< / h2 >
< / div >
<!-- Reihe 1: Vorstellung + Profilbild - Platzhalter -->
< div class = "lw-about" style = "display: grid; grid-template-columns: 1.1fr 0.9fr; gap: clamp(28px, 4vw, 64px); align-items: center; margin-bottom: clamp(50px, 8vh, 90px);" >
< div style = "display: flex; flex-direction: column; gap: 16px;" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: clamp(24px, 2.2vw, 32px); line-height: 1.2; color: #f5f4f0;" > Hallo, ich bin< br > Philipp Mover,< / h3 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: #c9d6e2; font-weight: 500;" > Lack- und Karosserie-Experte und Inhaber der Mover GmbH (Auto Paint Work) in Kaiserslautern-Siegelbach.< / p >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: #c9d6e2; font-weight: 500;" > Mit meinem Werkstatt-Team aus professionellen Fachkräften erfülle ich Ihre individuellen Wünsche zum bestmöglichen Preis — von der Smart-Repair bis zur kompletten Restauration.< / p >
< / div >
< div style = "justify-self: center; width: min(340px, 100%);" >
< div style = "position: relative; width: 100%; aspect-ratio: 4/5; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.4); background: rgba(255,255,255,.05); border: 1.5px dashed rgba(255,255,255,.3);" >
< x-import component-from-global-scope = "image-slot" from = "./assets/image-slot.js" id = "profil-inhaber" shape = "rect" fit = "cover" placeholder = "Profilbild der Inhaberin / des Inhabers hier ablegen" hint-size = "100%,100%" style = "display:block;width:100%;height:100%;" > < / x-import >
< / div >
< / div >
< / div >
<!-- Reihe 2: Kundenstimmen + Philosophie -->
< div class = "lw-about" style = "display: grid; grid-template-columns: 0.9fr 1.1fr; gap: clamp(28px, 4vw, 64px); align-items: center;" >
< div style = "position: relative; border-radius: 24px; padding: 28px; background: rgba(10,20,34,.55); border: 1px solid rgba(255,255,255,.12); backdrop-filter: blur(10px); box-shadow: 2px 6px 24px rgba(0,0,0,.35);" >
< div style = "display: flex; align-items: center; gap: 12px; margin-bottom: 16px;" >
< span style = "display: inline-flex; align-items: center; justify-content: center; width: 42px; height: 42px; border-radius: 50%; background: var(--accent, #2FA4F0); color: #0d0f12; font-weight: 800; font-size: 17px; flex-shrink: 0;" > {{ reviewInitial }}< / span >
< div style = "display: flex; flex-direction: column; gap: 3px; min-width: 0;" >
< span style = "color: #f4f3ef; font-weight: 700; font-size: 15px;" > {{ reviewAuthor }}< / span >
< span style = "color: #F2BD3F; font-size: 14px; letter-spacing: 2px; line-height: 1;" > ★★★★★< / span >
< / div >
< / div >
< p ref = "{{ reviewTextRef }}" style = "color: #cdd6e0; font-size: 15px; line-height: 1.6; font-weight: 500; transition: opacity .4s ease;" > {{ reviewText }}< / p >
< span style = "display: block; margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.1); color: #8b9cae; font-size: 12.5px; font-weight: 600;" > {{ reviewMeta }}< / span >
< / div >
< div style = "display: flex; flex-direction: column; gap: 16px;" >
< h3 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; font-size: clamp(24px, 2.2vw, 32px); line-height: 1.2; color: #f5f4f0;" > Meine Philosophie< / h3 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: #c9d6e2; font-weight: 500;" > Für Sie – meine Kunden – soll meisterhafte Arbeit bezahlbar sein. Unser Service sieht aus wie aus der Vertragswerkstatt, hat die gleiche Qualität, ist aber deutlich günstiger.< / p >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: #c9d6e2; font-weight: 500;" > Es ist für mich oberstes Gesetz, dass Sie nur 1A-Qualität bekommen, statt billiger „Pinselarbeiten". Mit hastig erledigten, minderwertigen Flickarbeiten kann ich nicht dienen.< / p >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: #c9d6e2; font-weight: 500;" > Mein Credo: Glückliche Kunden kommen wieder. Mein Team und ich sind unheimlich stolz auf Ihre Zufriedenheit und begeisterten Bewertungen.< / p >
< / div >
< / div >
< / div >
< / section >
<!-- KONTAKT SECTION -->
< section id = "kontakt" data-screen-label = "Kontakt & Anfahrt" style = "position: relative; overflow: hidden; background: #10151b; padding: clamp(80px, 12vh, 140px) clamp(20px, 4vw, 56px);" >
< img src = "assets/bg-vorderlicht.jpg" alt = "" style = "position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: hue-rotate(85deg) saturate(1.2); pointer-events: none;" >
< div style = "position: absolute; inset: 0; background: rgba(18,60,120,0.38); mix-blend-mode: multiply; pointer-events: none;" > < / div >
< div style = "position: absolute; inset: 0; background: linear-gradient(180deg, rgba(10,18,30,.88) 0%, rgba(10,18,30,.55) 45%, rgba(10,18,30,.88) 100%); pointer-events: none;" > < / div >
< div style = "position: relative; max-width: 1320px; margin: 0 auto;" >
< div style = "max-width: 760px; margin-bottom: clamp(40px, 6vh, 64px);" >
< span style = "display: inline-block; font-size: 13px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: #9FD1F5; margin-bottom: 16px;" > Kontakt & Anfahrt< / span >
< h2 style = "font-family: 'Source Serif 4', Georgia, serif; font-weight: 600; line-height: 1.1; color: #f5f4f0; font-size: clamp(30px, 3.6vw, 52px); margin-bottom: 18px;" > Sprechen wir über Ihr Fahrzeug< / h2 >
< p style = "font-size: clamp(15px, 1.3vw, 18px); line-height: 1.55; color: #c9d6e2; font-weight: 500;" > Rufen Sie an, schreiben Sie uns oder kommen Sie direkt vorbei – wir beraten Sie gern und unverbindlich.< / p >
< / div >
< div class = "lw-contact" style = "display: grid; grid-template-columns: 0.9fr 1.1fr; gap: clamp(24px, 3vw, 40px); align-items: stretch;" >
<!-- Kontakt - Infos -->
< div style = "display: flex; flex-direction: column; gap: 18px; border-radius: 24px; padding: clamp(26px, 3vw, 40px); background: rgba(10,20,34,.6); border: 1px solid rgba(255,255,255,.12); backdrop-filter: blur(10px); box-shadow: 2px 6px 24px rgba(0,0,0,.35);" >
< div style = "display: flex; flex-direction: column; gap: 4px;" >
< span style = "font-size: 12.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #9FD1F5;" > Adresse< / span >
< p style = "font-size: 16px; line-height: 1.5; color: #e7ecf1; font-weight: 600;" > Mover GmbH< br > Sauerwiesen 28, 67661 Kaiserslautern-Siegelbach< / p >
< / div >
< div style = "display: flex; flex-direction: column; gap: 4px;" >
< span style = "font-size: 12.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #9FD1F5;" > Telefon< / span >
< p style = "font-size: 16px; line-height: 1.5; color: #e7ecf1; font-weight: 600;" > < a href = "tel:+496301793248" style = "color: #e7ecf1;" > 06301 / 79 32 48< / a > < br > < span style = "font-weight: 500; color: #b8c4d0;" > Fax: 06301 / 79 32 49< / span > < / p >
< / div >
< div style = "display: flex; flex-direction: column; gap: 4px;" >
< span style = "font-size: 12.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #9FD1F5;" > E-Mail< / span >
< p style = "font-size: 16px; line-height: 1.5; font-weight: 600;" > < a href = "mailto:service@lackexperte-mover.de" style = "color: #e7ecf1;" > service@lackexperte-mover.de< / a > < / p >
< / div >
< div style = "display: flex; flex-direction: column; gap: 4px;" >
< span style = "font-size: 12.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #9FD1F5;" > Öffnungszeiten< / span >
< p style = "font-size: 16px; line-height: 1.6; color: #e7ecf1; font-weight: 600;" > Montag – Donnerstag< br > < span style = "font-weight: 500; color: #b8c4d0;" > 07:30 – 12:00 · 13:00 – 17:00< / span > < br > Freitag< br > < span style = "font-weight: 500; color: #b8c4d0;" > 06:00 – 12:00 · 13:00 – 14:15 · Sa/So geschlossen< / span > < / p >
< / div >
< div style = "display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px;" >
< a href = "tel:+496301793248" style = "display: inline-flex; align-items: center; gap: 8px; font-size: 14.5px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase; color: #0d0f12; background: var(--accent, #2FA4F0); padding: 13px 22px; border-radius: 44px; transition: transform .2s ease, box-shadow .2s ease;" style-hover = "transform: translateY(-2px); box-shadow: 0 10px 26px rgba(47,164,240,.4);" > Jetzt anrufen< / a >
< a href = "mailto:service@lackexperte-mover.de" style = "display: inline-flex; align-items: center; font-size: 14.5px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase; color: #f4f3ef; padding: 13px 22px; border-radius: 44px; border: 1.5px solid rgba(255,255,255,.3); transition: border-color .2s ease, background .2s ease;" style-hover = "border-color: var(--accent, #2FA4F0); background: rgba(255,255,255,.05);" > E-Mail schreiben< / a >
< / div >
< / div >
<!-- Karte -->
< div style = "position: relative; border-radius: 24px; overflow: hidden; box-shadow: 2px 6px 24px rgba(0,0,0,.4); min-height: 420px; background: #0a0e13;" >
< div ref = "{{ mapRef }}" style = "position: absolute; inset: 0;" > < / div >
< / div >
< / div >
< / div >
< / section >
<!-- FOOTER (sticky reveal) -->
< footer data-screen-label = "Footer" style = "position: relative; height: 70vh; clip-path: polygon(0% 0, 100% 0%, 100% 100%, 0 100%);" >
< div style = "position: relative; height: calc(100vh + 70vh); top: -100vh;" >
< div style = "height: 70vh; position: sticky; top: calc(100vh - 70vh);" >
< div style = "height: 100%; width: 100%; display: flex; flex-direction: column; justify-content: space-between; overflow: hidden; position: relative; background: linear-gradient(135deg, #0a1526 0%, #0D2A46 55%, #0a1526 100%); padding: clamp(32px, 6vh, 64px) clamp(20px, 4vw, 56px);" >
<!-- Glow - Elemente -->
< div style = "position: absolute; top: -120px; right: -120px; width: 380px; height: 380px; border-radius: 50%; background: rgba(47,164,240,.09); filter: blur(70px); pointer-events: none;" > < / div >
< div style = "position: absolute; bottom: -140px; left: -100px; width: 380px; height: 380px; border-radius: 50%; background: rgba(29,92,153,.12); filter: blur(70px); pointer-events: none;" > < / div >
<!-- Link - Spalten -->
< div class = "lw-footer-cols" style = "position: relative; z-index: 1; display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(24px, 4vw, 80px); max-width: 1320px; width: 100%; margin: 0 auto;" >
< div style = "display: flex; flex-direction: column; gap: 9px;" >
< h3 style = "font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #7f97ac; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: 7px; margin-bottom: 6px;" > Navigation< / h3 >
< a href = "#top" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Start< / a >
< a href = "#services" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Services< / a >
< a href = "#referenzen" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Referenzen< / a >
< a href = "#ueber-uns" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Über uns< / a >
< a href = "#kontakt" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Kontakt< / a >
< / div >
< div style = "display: flex; flex-direction: column; gap: 9px;" >
< h3 style = "font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #7f97ac; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: 7px; margin-bottom: 6px;" > Leistungen< / h3 >
< a href = "#services" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Lackierungen aller Art< / a >
< a href = "#services" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Smart Repair< / a >
< a href = "#services" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Oldtimer & Klassiker< / a >
< a href = "#services" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Felgen & Motorrad< / a >
< / div >
< div style = "display: flex; flex-direction: column; gap: 9px;" >
< h3 style = "font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #7f97ac; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: 7px; margin-bottom: 6px;" > Kontakt< / h3 >
< span style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Sauerwiesen 28< br > 67661 Kaiserslautern-Siegelbach< / span >
< a href = "tel:+496301793248" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > 06301 / 79 32 48< / a >
< a href = "mailto:service@lackexperte-mover.de" style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > service@lackexperte-mover.de< / a >
< / div >
< div style = "display: flex; flex-direction: column; gap: 9px;" >
< h3 style = "font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #7f97ac; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: 7px; margin-bottom: 6px;" > Öffnungszeiten< / h3 >
< span style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Montag – Donnerstag< br > 07:30 – 12:00 · 13:00 – 17:00< / span >
< span style = "font-size: 14px; color: #c9d6e2; font-weight: 500;" > Freitag · 06:00 – 12:00 · 13:00 – 14:15 · Sa/So geschlossen< / span >
< / div >
< / div >
<!-- Untere Zeile -->
< div class = "lw-footer-bottom" style = "position: relative; z-index: 1; display: flex; justify-content: space-between; align-items: flex-end; gap: 24px; max-width: 1320px; width: 100%; margin: 0 auto;" >
< div style = "min-width: 0;" >
< img src = "assets/logo-placeholder.svg" alt = "Auto Paint Work" style = "width: min(46vw, 360px); height: auto; display: block; opacity: .95;" >
< div style = "display: flex; align-items: center; gap: 14px; margin-top: 16px;" >
< span style = "width: 44px; height: 2px; background: linear-gradient(90deg, #2FA4F0, transparent);" > < / span >
< span style = "font-size: 13.5px; color: #8fa4b8; font-weight: 500;" > Autolackiererei & Karosserie · Kaiserslautern-Siegelbach< / span >
< / div >
< / div >
< div style = "text-align: right; flex-shrink: 0;" >
< p style = "font-size: 13px; color: #8fa4b8; font-weight: 500; margin-bottom: 12px;" > © 2026 Mover GmbH · Alle Rechte vorbehalten< / p >
< div style = "display: flex; justify-content: flex-end; align-items: center; gap: 14px;" >
< a href = "" target = "_blank" rel = "noopener" aria-label = "Facebook" style = "width: 36px; height: 36px; border-radius: 50%; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14); display: inline-flex; align-items: center; justify-content: center; color: #c9d6e2; font-weight: 800; font-size: 15px; transition: background .2s ease, transform .2s ease;" style-hover = "background: var(--accent, #2FA4F0); color: #0a1526; transform: scale(1.1);" > f< / a >
< a href = "#impressum" style = "font-size: 13px; color: #8fa4b8; font-weight: 500;" > Impressum< / a >
< a href = "#datenschutz" style = "font-size: 13px; color: #8fa4b8; font-weight: 500;" > Datenschutz< / a >
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< / footer >
< / div >
< / x-dc >
< script type = "text/x-dc" data-dc-script data-props = "{"$preview":{"width":1440,"height":900},"accentColor":{"editor":"color","default":"#2FA4F0","tsType":"string","section":"Design","options":["#2FA4F0","#F0912F","#E23744","#16462C","#C9A227"]},"showReviewCard":{"editor":"boolean","default":true,"tsType":"boolean","section":"Inhalt"},"rotateWords":{"editor":"boolean","default":true,"tsType":"boolean","section":"Inhalt"}}" >
class Component extends DCLogic {
state = {
loading : true ,
reviews : [ ] ,
idx : 0 ,
// 4,9 / 50 — werden nur als Text angezeigt, dürfen daher Platzhalter bleiben
rating : "4,9" ,
count : "50" ,
} ;
// Demo-/Fallback-Bewertungen (angezeigt, wenn /api/reviews nicht erreichbar ist, z. B. in der Vorschau)
// Durch echte Beispiel-Bewertungen ersetzen ODER über das eigene /api/reviews-Backend live laden.
DEMO = {
rating : "4,9" ,
user _ratings _total : "50" ,
reviews : [
{ author _name : "Sabine M." , rating : 5 , text : "„Kratzer war komplett verschwunden – schnelle Terminvergabe und faires Angebot.“" } ,
{ author _name : "Thomas K." , rating : 5 , text : "„Top Beratung, saubere Arbeit, Auto kam sogar gereinigt zurück.“" } ,
{ author _name : "Petra L." , rating : 5 , text : "„Delle wie neu, Farbton perfekt getroffen – gerne wieder!“" } ,
] ,
} ;
PLACE _ID = "ChIJY3BRmR0NlkcRvKcqsfxcPNE" ;
WORDS = [ "Unfall" , "Delle" , "Beule" , "Kratzer" ] ;
componentDidMount ( ) {
this . applyAccent ( ) ;
this . sizeWordSlot ( ) ;
this . onResize = ( ) => this . sizeWordSlot ( ) ;
window . addEventListener ( "resize" , this . onResize ) ;
if ( document . fonts && document . fonts . ready ) document . fonts . ready . then ( ( ) => this . sizeWordSlot ( ) ) ;
this . loadReviews ( ) ;
if ( this . props . rotateWords !== false ) this . startWordRotator ( ) ;
this . onScroll = ( ) => this . updateNav ( ) ;
window . addEventListener ( "scroll" , this . onScroll , { passive : true } ) ;
this . updateNav ( ) ;
this . initMap ( ) ;
// Immer oben starten (kein Wiederherstellen der Scroll-Position)
if ( "scrollRestoration" in history ) history . scrollRestoration = "manual" ;
if ( ! location . hash ) requestAnimationFrame ( ( ) => window . scrollTo ( 0 , 0 ) ) ;
}
initMap ( ) {
if ( this . _map || ! this . mapEl ) return ;
if ( ! window . maplibregl ) { setTimeout ( ( ) => this . initMap ( ) , 250 ) ; return ; }
// 7.699953 / 49.462768 — Koordinaten des eigenen Standorts eintragen.
// Vorbelegt mit der geografischen Mitte Deutschlands, damit die Karte auch ohne Eintrag funktioniert.
const LNG = 10.4515 , LAT = 51.1657 ;
const map = new maplibregl . Map ( {
container : this . mapEl ,
style : "https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json" ,
center : [ LNG , LAT ] ,
zoom : 14.5 ,
attributionControl : { compact : true } ,
cooperativeGestures : true ,
} ) ;
map . addControl ( new maplibregl . NavigationControl ( { showCompass : false } ) , "bottom-right" ) ;
const el = document . createElement ( "div" ) ;
el . style . cssText = "width:20px;height:20px;border-radius:50%;background:#2FA4F0;border:3px solid #fff;box-shadow:0 0 0 6px rgba(47,164,240,.3),0 4px 12px rgba(0,0,0,.5);cursor:pointer;" ;
const popupHtml =
'<div style="font-family:Archivo,sans-serif;padding:4px 2px;min-width:190px;">' +
'<div style="font-weight:800;font-size:14px;color:#0D2A46;margin-bottom:3px;">Mover GmbH</div>' +
'<div style="font-size:12.5px;color:#45535f;line-height:1.45;margin-bottom:8px;">Sauerwiesen 28<br>67661 Kaiserslautern-Siegelbach</div>' +
'<a href="https://www.google.com/maps/dir/?api=1&destination=Sauerwiesen+28,+67661+Kaiserslautern" target="_blank" rel="noopener" style="display:inline-block;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:#fff;background:#2FA4F0;padding:7px 13px;border-radius:20px;text-decoration:none;">Route planen</a>' +
"</div>" ;
const popup = new maplibregl . Popup ( { offset : 20 , closeButton : false } ) . setHTML ( popupHtml ) ;
new maplibregl . Marker ( { element : el } ) . setLngLat ( [ LNG , LAT ] ) . setPopup ( popup ) . addTo ( map ) ;
popup . setLngLat ( [ LNG , LAT ] ) . addTo ( map ) ;
this . _map = map ;
}
componentDidUpdate ( ) { this . applyAccent ( ) ; }
componentWillUnmount ( ) {
clearInterval ( this . _wordTimer ) ;
clearInterval ( this . _revTimer ) ;
if ( this . _map ) { this . _map . remove ( ) ; this . _map = null ; }
window . removeEventListener ( "scroll" , this . onScroll ) ;
window . removeEventListener ( "resize" , this . onResize ) ;
}
// fix the rotating-word slot to the widest word so "Rein mit" never shifts
sizeWordSlot ( ) {
const slot = this . wordSlot , word = this . word ;
if ( ! slot || ! word ) return ;
const probe = document . createElement ( "span" ) ;
probe . style . cssText = "position:absolute;visibility:hidden;white-space:nowrap;" ;
const cs = getComputedStyle ( word ) ;
probe . style . font = cs . font ;
probe . style . fontStyle = cs . fontStyle ;
probe . style . letterSpacing = cs . letterSpacing ;
document . body . appendChild ( probe ) ;
let max = 0 ;
for ( const w of this . WORDS ) { probe . textContent = w ; max = Math . max ( max , probe . getBoundingClientRect ( ) . width ) ; }
probe . remove ( ) ;
slot . style . width = Math . ceil ( max + 2 ) + "px" ;
}
applyAccent ( ) {
if ( this . root ) this . root . style . setProperty ( "--accent" , this . props . accentColor || "#F0912F" ) ;
}
updateNav ( ) {
if ( ! this . nav ) return ;
const s = window . scrollY > 30 ;
this . nav . style . background = s ? "rgba(11,13,16,0.92)" : "transparent" ;
this . nav . style . backdropFilter = s ? "blur(14px)" : "none" ;
this . nav . style . borderBottomColor = s ? "rgba(255,255,255,0.10)" : "transparent" ;
this . nav . style . paddingTop = s ? "14px" : "22px" ;
this . nav . style . paddingBottom = s ? "14px" : "22px" ;
}
async loadReviews ( ) {
let data = null ;
try {
// In der produktiven Next.js-App trifft das den Server-Route-Handler
// /api/reviews (Place ID + GOOGLE_PLACES_API_KEY bleiben server-seitig).
const res = await fetch ( "/api/reviews" , { cache : "no-store" } ) ;
if ( res . ok ) {
const j = await res . json ( ) ;
if ( j && Array . isArray ( j . reviews ) && j . reviews . length ) data = j ;
}
} catch ( e ) { /* fall through to demo */ }
if ( ! data ) data = this . DEMO ;
const reviews = ( data . reviews || [ ] ) . filter ( r => r && r . text ) . slice ( 0 , 5 ) ;
this . setState ( {
loading : false ,
reviews ,
rating : data . rating || this . DEMO . rating ,
count : data . user _ratings _total || this . DEMO . user _ratings _total ,
} , ( ) => { if ( reviews . length > 1 ) this . startReviewRotator ( ) ; } ) ;
}
startReviewRotator ( ) {
this . _revTimer = setInterval ( ( ) => {
const el = this . reviewText ;
const next = ( this . state . idx + 1 ) % this . state . reviews . length ;
const swap = ( ) => this . setState ( { idx : next } , ( ) => { if ( this . reviewText ) this . reviewText . style . opacity = 1 ; } ) ;
if ( el ) { el . style . opacity = 0 ; setTimeout ( swap , 380 ) ; } else { swap ( ) ; }
} , 5200 ) ;
}
startWordRotator ( ) {
let i = this . WORDS . indexOf ( "Kratzer" ) ;
this . _wordTimer = setInterval ( ( ) => {
const el = this . word ;
if ( ! el ) return ;
i = ( i + 1 ) % this . WORDS . length ;
const next = this . WORDS [ i ] ;
el . animate (
[ { transform : "translateY(0)" , opacity : 1 } , { transform : "translateY(-110%)" , opacity : 0 } ] ,
{ duration : 300 , easing : "cubic-bezier(.6,0,.4,1)" , fill : "forwards" }
) . onfinish = ( ) => {
el . textContent = next ;
el . animate (
[ { transform : "translateY(110%)" , opacity : 0 } , { transform : "translateY(0)" , opacity : 1 } ] ,
{ duration : 340 , easing : "cubic-bezier(.2,.7,.3,1)" , fill : "forwards" }
) ;
} ;
} , 2400 ) ;
}
renderVals ( ) {
const st = this . state ;
const r = st . reviews [ st . idx ] || null ;
const author = r ? r . author _name : "" ;
const initial = author ? author . trim ( ) . charAt ( 0 ) . toUpperCase ( ) : "" ;
const ratingLabel = String ( st . rating ) . replace ( "." , "," ) ;
return {
rootRef : ( el ) => { this . root = el ; } ,
videoRef : ( el ) => { if ( el ) { el . muted = true ; el . volume = 0 ; } } ,
mapRef : ( el ) => { this . mapEl = el ; if ( el ) this . initMap ( ) ; } ,
navRef : ( el ) => { this . nav = el ; } ,
wordRef : ( el ) => { this . word = el ; } ,
wordSlotRef : ( el ) => { this . wordSlot = el ; } ,
reviewTextRef : ( el ) => { this . reviewText = el ; } ,
dot0Ref : ( ) => { } , dot1Ref : ( ) => { } , dot2Ref : ( ) => { } ,
showReviewCard : this . props . showReviewCard !== false ,
loading : st . loading ,
hasReview : ! st . loading && ! ! r ,
reviewAuthor : author ,
reviewInitial : initial ,
reviewText : r ? ( "“" + r . text + "”" ) : "" ,
reviewMeta : "Google Bewertung" ,
ratingLabel : ratingLabel ,
countLabel : "(" + st . count + ")" ,
googleUrl : "https://www.google.com/maps/place/?q=place_id:" + this . PLACE _ID ,
} ;
}
}
< / script >
< / body >
< / html >