Startseite entschlackt: weniger Text, ruhiger, besser am Handy
- Cursor-Effekt im Hero entfernt (Messfadenkreuz, Temperatur-Skala, Maus-Wärmespur); Wärmebild läuft nur noch ruhig im Hintergrund - Preloader, Laufbänder, Intro, Ablauf-Block, Kennzahlen, Footer-CTA, kursive Akzentwörter, Verlaufstext und Nummern-Pillen entfernt - Texte in allen Abschnitten deutlich gekürzt, schlichte Überschriften - Leistungen: auf Handy/Tablet wischbare Kärtchen mit Zähler und Pfeilen - Neue, zusammenhängende Illustrationen für die fünf Leistungs-Kärtchen - Heizungs-Finder ohne Prozent-Balken, kürzere Ergebnistexte - Energie-Haus: Tabs als Wischleiste am Handy, kein Autoplay - GSAP, ScrollTrigger, SplitText, Lenis und Instrument Serif entfernt - Barrierefreiheit: Kontrast, Fokusring, Fokus im Kartenstapel, Telefon-Button mit Namen, Menü sperrt Hintergrund - Cache-Busting (?v=20261004) für CSS und JS, README aktualisiert Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
717a7de501
commit
ad155124b1
20 files changed
+1182
-2147
No files matched your search
@@ -8,13 +8,13 @@
|
||||
<meta name="theme-color" content="#0B151E">
|
||||
<!-- Absolute Pfade, damit die Seite auch bei verschachtelten URLs korrekt lädt -->
|
||||
<link rel="icon" href="/assets/img/favicon.svg" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
<link rel="stylesheet" href="/assets/css/main.css?v=20261004">
|
||||
<style>
|
||||
.e404 { min-height: 100vh; min-height: 100svh; display: grid; grid-template-rows: auto 1fr auto; background: var(--ink); color: var(--paper); position: relative; overflow: hidden; isolation: isolate; }
|
||||
.e404::before { content: ""; position: absolute; inset: 0; z-index: -1; background: radial-gradient(45% 60% at 80% 30%, rgba(255,106,43,.35), rgba(138,35,80,.16) 50%, transparent 75%), radial-gradient(40% 55% at 10% 90%, rgba(46,155,239,.22), transparent 70%); }
|
||||
.e404__top { display: flex; justify-content: space-between; align-items: center; padding: 22px var(--gutter); }
|
||||
.e404__main { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(24px, 5vw, 80px); align-items: center; padding: 24px var(--gutter) 48px; max-width: calc(var(--container) + var(--gutter) * 2); margin: 0 auto; width: 100%; }
|
||||
.e404__code { font-family: var(--font-mono); font-size: 14px; color: var(--text-dark-2); margin: 0 0 20px; }
|
||||
.e404__code { font-size: 14px; color: var(--text-dark-2); margin: 0 0 20px; }
|
||||
.e404 .h1 { margin-bottom: 24px; }
|
||||
.e404 .lead { color: var(--text-dark-2); margin-bottom: 32px; }
|
||||
.e404__ctas { display: flex; flex-wrap: wrap; gap: 12px; }
|
||||
@@ -46,7 +46,7 @@
|
||||
<main class="e404__main" id="main">
|
||||
<div>
|
||||
<p class="e404__code">Fehler 404</p>
|
||||
<h1 class="h1">Kein Anschluss <em class="accent" style="color:var(--amber)">unter dieser Adresse.</em></h1>
|
||||
<h1 class="h1">Kein Anschluss unter dieser Adresse.</h1>
|
||||
<p class="lead">Diese Seite gibt es nicht – oder nicht mehr. Kein Grund zur Sorge: Wir bringen Sie zurück auf den richtigen Weg.</p>
|
||||
<div class="e404__ctas">
|
||||
<a class="btn btn--light" href="/">
|
||||
@@ -78,8 +78,6 @@
|
||||
<g transform="translate(398 112) rotate(14)">
|
||||
<path d="M-8 -26h86v52H-8l10-8-12-8 11-8-11-8 12-7z" fill="url(#g-pipe2)" opacity=".9"/>
|
||||
</g>
|
||||
<text x="300" y="58" fill="rgba(255,255,255,.55)" font-family="ui-monospace, Menlo, Consolas, monospace" font-size="13" letter-spacing="1.5">ANSCHLUSS FEHLT</text>
|
||||
<path d="M296 64v14" stroke="rgba(255,255,255,.35)" stroke-width="1.5"/>
|
||||
<g>
|
||||
<path class="drip" d="M305 140c0 0 9 11 9 17a9 9 0 0 1-18 0c0-6 9-17 9-17z" fill="#8ED9FA"/>
|
||||
<path class="drip drip-2" d="M305 140c0 0 9 11 9 17a9 9 0 0 1-18 0c0-6 9-17 9-17z" fill="#8ED9FA"/>
|
||||
|
||||
@@ -2,30 +2,28 @@
|
||||
|
||||
Maßgeschneiderter Neuauftritt für die **Olzinger GmbH** (Heizung · Sanitär · Lüftung · Regenerative Energien, Ergolding bei Landshut, seit 1985).
|
||||
|
||||
Statische Website ohne Build-Schritt: HTML, CSS und JavaScript, alle Bibliotheken und Schriften lokal eingebunden (kein CDN, keine Cookies, kein Tracking).
|
||||
Statische Website ohne Build-Schritt: HTML, CSS und JavaScript ohne Bibliotheken, Schrift lokal eingebunden (kein CDN, keine Cookies, kein Tracking).
|
||||
|
||||
## Gestaltungsidee: „Wärmebild“
|
||||
|
||||
Haustechnik sieht man nicht – man spürt sie. Deshalb zeigt der Einstieg ein **live berechnetes Wärmebild** (WebGL) mit Isothermen wie bei einer Thermografie-Kamera. Die Maus wirkt als Wärmequelle, hinterlässt eine Wärmespur, und ein Messfadenkreuz zeigt die „Temperatur“ an. Durch die ganze Seite zieht sich das Motiv **warm trifft kalt** (Vorlauf/Rücklauf): Farbverlauf Blau → Magenta → Orange, das Logo als Thermostat-Skala, Bänder in Rücklauf-Blau und Vorlauf-Orange.
|
||||
Haustechnik sieht man nicht – man spürt sie. Deshalb zeigt der Einstieg ein ruhiges, live berechnetes **Wärmebild** (WebGL) mit Isothermen wie bei einer Thermografie-Kamera. Eine langsam wandernde Wärmequelle hält das Bild in Bewegung – ohne Maus- oder Touch-Effekte. Durch die Seite zieht sich das Motiv **warm trifft kalt** (Vorlauf/Rücklauf): Farbverlauf Blau → Magenta → Orange, das Logo als Thermostat-Skala.
|
||||
|
||||
Grundsatz: wenig Text, wenig Bewegung, gut bedienbar am Handy.
|
||||
|
||||
## Bereiche der Startseite
|
||||
|
||||
| Bereich | Besonderheit |
|
||||
|---|---|
|
||||
| Preloader | Thermostat-Bogen „dreht hoch“, Jahreszahl zählt von 1985 bis heute (nur beim ersten Aufruf) |
|
||||
| Hero | WebGL-Wärmebild, Live-Öffnungsstatus (inkl. bayerischer Feiertage) |
|
||||
| Bänder | Endlos-Laufschrift, reagiert auf Scroll-Tempo und -Richtung |
|
||||
| Intro | Text „heizt sich“ Wort für Wort beim Scrollen auf |
|
||||
| Leistungen | 5 gestapelte Karten (Sticky-Stack) mit animierten Illustrationen: Wärmepumpe, Heizkörper, Waschtisch, Wärmetauscher, Manometer |
|
||||
| Energie-Haus | Interaktiver Hausschnitt: Wärmepumpe, Solarthermie, Speicher, Fußbodenheizung, Bad, Lüftung – mit fließenden Medien in den Leitungen |
|
||||
| Heizungs-Finder | 4 Fragen → Empfehlung (Wärmepumpe / Pellets / Hybrid) mit Passungsbalken; Ergebnis lässt sich ins Kontaktformular übernehmen |
|
||||
| Ablauf | Rohrleitung füllt sich beim Scrollen |
|
||||
| Über uns | Zähler, Zeitleiste mit Zählwerk-Jahreszahl (1985 → heute), Geschäftsführung |
|
||||
| Karriere | Ausbildung Anlagenmechaniker/in SHK, Initiativbewerbung |
|
||||
| Hero | Ruhiges WebGL-Wärmebild, Live-Öffnungsstatus (inkl. bayerischer Feiertage), Anfrage- und Telefon-Button |
|
||||
| Leistungen | 5 Karten mit Illustrationen; am Desktop als Kartenstapel, unter 900 px wischbare Karten mit Zähler (1 / 5) und Pfeilen |
|
||||
| Energie-Haus | Hausschnitt: Wärmepumpe, Solarthermie, Speicher, Fußbodenheizung, Bad, Lüftung – der gewählte Kreislauf wird hervorgehoben |
|
||||
| Heizungs-Finder | 4 Fragen → Empfehlung (Wärmepumpe / Pellets / Hybrid); Ergebnis lässt sich ins Kontaktformular übernehmen |
|
||||
| Über uns | Kurze Chronik 1985 → heute |
|
||||
| Karriere | Ausbildung Anlagenmechaniker SHK, Initiativbewerbung |
|
||||
| Kontakt | Kontaktkarten, Öffnungszeiten mit „Heute“-Markierung, Formular |
|
||||
| Footer | Schriftzug „heizt auf“ beim Scrollen |
|
||||
| Footer | Adresse, Kontakt, Öffnungszeiten, Links |
|
||||
|
||||
Außerdem: `impressum.html`, `datenschutz.html`, `404.html` („Kein Anschluss unter dieser Adresse“), weicher Seitenwechsel, SEO-Daten (Schema.org `HVACBusiness`, Open-Graph-Bild, Sitemap).
|
||||
Außerdem: `impressum.html`, `datenschutz.html`, `404.html` („Kein Anschluss unter dieser Adresse“), SEO-Daten (Schema.org `HVACBusiness`, Open-Graph-Bild, Sitemap).
|
||||
|
||||
## Lokal ansehen
|
||||
|
||||
@@ -44,6 +42,7 @@ Den kompletten Ordnerinhalt auf den Webspace laden (z. B. per FTP). Voraussetzun
|
||||
- Ohne PHP funktioniert das Formular trotzdem: Es öffnet dann das E-Mail-Programm des Besuchers mit vorbereiteter Nachricht.
|
||||
- `.htaccess` (Apache) setzt die 404-Seite, Caching und Sicherheits-Header. Bei anderen Servern einfach ignorieren.
|
||||
- `404.html` nutzt absolute Pfade (`/assets/...`) – liegt die Seite in einem Unterordner, Pfade dort anpassen.
|
||||
- CSS und JS werden eine Woche gecacht. Nach Änderungen an `main.css` oder den Skripten die Versionsnummer `?v=…` in allen HTML-Dateien hochzählen, damit Besucher nicht alte Dateien sehen.
|
||||
|
||||
## Vor dem Livegang prüfen (TODO)
|
||||
|
||||
@@ -65,12 +64,11 @@ datenschutz.html Datenschutzerklärung
|
||||
kontakt.php Formularversand (optional, PHP)
|
||||
assets/css/main.css Design-System und alle Styles
|
||||
assets/js/thermal.js WebGL-Wärmebild
|
||||
assets/js/main.js Preloader, Smooth-Scroll, Navigation, Scroll-Animationen
|
||||
assets/js/house.js Energie-Haus (Tabs, Hotspots, Autoplay)
|
||||
assets/js/main.js Navigation, mobiles Menü, Einblendungen, Leistungs-Karussell/Kartenstapel
|
||||
assets/js/house.js Energie-Haus (Tabs, Hotspots)
|
||||
assets/js/finder.js Heizungs-Finder (Bewertungslogik + Ergebnis)
|
||||
assets/js/contact.js Öffnungszeiten live, Feiertage, Kontaktformular
|
||||
assets/vendor/ GSAP, ScrollTrigger, SplitText, Lenis (lokal)
|
||||
assets/fonts/ Instrument Sans & Instrument Serif (OFL, lokal)
|
||||
assets/fonts/ Instrument Sans (OFL, lokal)
|
||||
assets/img/ Favicon, Logo, Open-Graph-Bild
|
||||
tools/og-template.html Vorlage zum Neu-Rendern des Open-Graph-Bilds
|
||||
```
|
||||
@@ -84,10 +82,10 @@ tools/og-template.html Vorlage zum Neu-Rendern des Open-Graph-Bilds
|
||||
|
||||
## Qualität
|
||||
|
||||
- Barrierefreiheit: geprüft mit axe-core (WCAG 2.1 AA) – keine Befunde; Tastaturbedienung für Menü, Tabs und Finder; `prefers-reduced-motion` wird respektiert (keine Smooth-Scroll-, Preloader- oder Scroll-Animationen).
|
||||
- Ohne JavaScript bleiben alle Inhalte sichtbar und nutzbar.
|
||||
- Getestet in Chromium: Desktop (1366, 1440, 1920 px), Tablet (834 px) und Smartphone (390 px).
|
||||
- Barrierefreiheit: Tastaturbedienung für Menü, Tabs, Karussell und Finder; `prefers-reduced-motion` wird respektiert (keine Einblendungen, Wärmebild als Standbild).
|
||||
- Ohne JavaScript bleiben alle Texte sichtbar; Energie-Haus und Heizungs-Finder brauchen JavaScript für die Bedienung.
|
||||
- Getestet in Chromium: Desktop (1024–1920 px), Tablet (768/834 px, hoch und quer) und Smartphone (360–414 px).
|
||||
|
||||
## Lizenzen
|
||||
|
||||
GSAP 3.15 (Standard „No Charge“ License), Lenis (MIT), Instrument Sans/Serif (SIL OFL 1.1) – Details in `assets/vendor/LICENSES.md` und `assets/fonts/`.
|
||||
Instrument Sans (SIL OFL 1.1) – Lizenztext in `assets/fonts/OFL-InstrumentSans.txt`. Keine weiteren Bibliotheken.
|
||||
+271
-671
File diff suppressed because it is too large.
Load diff
@@ -1,93 +0,0 @@
|
||||
Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif) InstrumentSerif-Italic.ttf: Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
@@ -76,14 +76,14 @@
|
||||
var i;
|
||||
for (i = 0; i < slots.length; i++) {
|
||||
if (n.mins >= slots[i][0] && n.mins < slots[i][1]) {
|
||||
return { state: 'open', text: 'Jetzt geöffnet · bis ' + fmt(slots[i][1]) + ' Uhr', wd: n.wd };
|
||||
return { state: 'open', text: 'Geöffnet bis ' + fmt(slots[i][1]) + '\u00a0Uhr', wd: n.wd };
|
||||
}
|
||||
}
|
||||
if (slots.length > 1 && n.mins >= slots[0][1] && n.mins < slots[1][0]) {
|
||||
return { state: 'break', text: 'Mittagspause · ab ' + fmt(slots[1][0]) + ' Uhr wieder erreichbar', wd: n.wd };
|
||||
return { state: 'break', text: 'Mittagspause bis ' + fmt(slots[1][0]) + '\u00a0Uhr', wd: n.wd };
|
||||
}
|
||||
if (slots.length && n.mins < slots[0][0]) {
|
||||
return { state: 'closed', text: 'Geschlossen · öffnet heute um ' + fmt(slots[0][0]) + ' Uhr', wd: n.wd };
|
||||
return { state: 'closed', text: 'Öffnet heute um ' + fmt(slots[0][0]) + '\u00a0Uhr', wd: n.wd };
|
||||
}
|
||||
var base = Date.UTC(n.y, n.m - 1, n.d);
|
||||
for (i = 1; i <= 14; i++) {
|
||||
@@ -91,9 +91,8 @@
|
||||
var wd = dt.getUTCDay();
|
||||
var s = SCHEDULE[wd];
|
||||
if (!s.length || holidayName(dt.getUTCFullYear(), dt.getUTCMonth() + 1, dt.getUTCDate())) continue;
|
||||
var when = i === 1 ? 'morgen' : (i < 7 ? 'am ' + DAYS[wd] : 'am ' + dt.getUTCDate() + '.' + (dt.getUTCMonth() + 1) + '.');
|
||||
var prefix = hol ? 'Heute Feiertag (' + hol + ')' : 'Geschlossen';
|
||||
return { state: 'closed', text: prefix + ' · öffnet ' + when + ' um ' + fmt(s[0][0]) + ' Uhr', wd: n.wd };
|
||||
var when = i === 1 ? 'morgen' : (i < 7 ? DAYS[wd] : 'am ' + dt.getUTCDate() + '.' + (dt.getUTCMonth() + 1) + '.');
|
||||
return { state: 'closed', text: (hol ? 'Feiertag · öffnet ' : 'Öffnet ') + when + ' um ' + fmt(s[0][0]) + '\u00a0Uhr', wd: n.wd };
|
||||
}
|
||||
return { state: 'closed', text: 'Geschlossen', wd: n.wd };
|
||||
}
|
||||
@@ -136,7 +135,6 @@
|
||||
Object.keys(fields).forEach(function (k) {
|
||||
var el = fields[k];
|
||||
if (!el) return;
|
||||
el.setAttribute('aria-describedby', 'e-' + k);
|
||||
el.addEventListener(el.type === 'checkbox' ? 'change' : 'input', function () {
|
||||
if (el.closest('.field').classList.contains('is-invalid')) validateField(k);
|
||||
});
|
||||
@@ -150,6 +148,8 @@
|
||||
else ok = el.value.trim().length > 0;
|
||||
el.closest('.field').classList.toggle('is-invalid', !ok);
|
||||
el.setAttribute('aria-invalid', ok ? 'false' : 'true');
|
||||
// Fehlertext nur verknüpfen, solange er sichtbar ist
|
||||
if (ok) el.removeAttribute('aria-describedby'); else el.setAttribute('aria-describedby', 'e-' + k);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
selectTopic(det.topic);
|
||||
if (det.message && fields.message) {
|
||||
fields.message.value = det.message;
|
||||
fields.message.closest('.field').classList.remove('is-invalid');
|
||||
if (fields.message.closest('.field').classList.contains('is-invalid')) validateField('message');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+15
-44
@@ -16,7 +16,6 @@
|
||||
var barEl = app.querySelector('[data-finder-bar]');
|
||||
var backBtn = app.querySelector('[data-finder-back]');
|
||||
var nextBtn = app.querySelector('[data-finder-next]');
|
||||
var reduce = w.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
var total = steps.length;
|
||||
var index = 0;
|
||||
var answers = {};
|
||||
@@ -31,19 +30,19 @@
|
||||
};
|
||||
|
||||
var SYSTEMS = {
|
||||
wp: { name: 'Wärmepumpe', title: 'Luft-Wasser-Wärmepumpe', topic: 'Wärmepumpe & Regenerativ' },
|
||||
pellet: { name: 'Pelletheizung', title: 'Pelletheizung', topic: 'Wärmepumpe & Regenerativ' },
|
||||
hybrid: { name: 'Hybridheizung', title: 'Hybridheizung', topic: 'Heizung' }
|
||||
wp: { name: 'Wärmepumpe', title: 'Luft-Wasser-Wärmepumpe', topic: 'Heizung & Wärmepumpe' },
|
||||
pellet: { name: 'Pelletheizung', title: 'Pelletheizung', topic: 'Heizung & Wärmepumpe' },
|
||||
hybrid: { name: 'Hybridheizung', title: 'Hybridheizung', topic: 'Heizung & Wärmepumpe' }
|
||||
};
|
||||
|
||||
var TEXTS = {
|
||||
wp: {
|
||||
neubau: 'Für Ihren Neubau ist die Wärmepumpe die naheliegende Wahl: Sie heizt mit Umweltwärme, arbeitet mit niedrigen Vorlauftemperaturen besonders effizient und harmoniert perfekt mit einer Fußbodenheizung.',
|
||||
gedaemmt: 'Ihr Haus bringt gute Voraussetzungen mit: Dank Dämmung reichen niedrige Vorlauftemperaturen – genau dort arbeitet eine Wärmepumpe besonders effizient. Ob einzelne Heizkörper getauscht werden sollten, prüfen wir vor Ort.',
|
||||
neubau: 'Im Neubau die naheliegende Wahl: Die Wärmepumpe heizt mit Umweltwärme und arbeitet bei niedrigen Vorlauftemperaturen besonders effizient.',
|
||||
gedaemmt: 'Dank guter Dämmung reichen niedrige Vorlauftemperaturen – ideal für eine Wärmepumpe. Ob einzelne Heizkörper getauscht werden sollten, prüfen wir vor Ort.',
|
||||
unsaniert: 'Mit Flächenheizung kann eine Wärmepumpe auch im älteren Haus gut funktionieren. Eine Heizlastberechnung zeigt, ob vorher kleinere Maßnahmen sinnvoll sind.'
|
||||
},
|
||||
pellet: 'Holzpellets heizen nahezu CO<sub>2</sub>-neutral und liefern auch die hohen Vorlauftemperaturen, die klassische Heizkörper in älteren Häusern brauchen. Den nötigen Lagerraum haben Sie bereits.',
|
||||
hybrid: 'Wärmepumpe und Brennwertkessel im Team: Die Wärmepumpe übernimmt den Großteil des Jahres, der Kessel springt nur an sehr kalten Tagen ein. Oft ist auch eine Hochtemperatur-Wärmepumpe möglich – das klären wir mit einer Heizlastberechnung.'
|
||||
pellet: 'Holzpellets heizen nahezu CO<sub>2</sub>-neutral und schaffen die hohen Vorlauftemperaturen, die ältere Heizkörper brauchen. Den Lagerraum haben Sie bereits.',
|
||||
hybrid: 'Die Wärmepumpe übernimmt den Großteil des Jahres, ein Brennwertkessel springt nur an sehr kalten Tagen ein. Oft reicht auch eine Hochtemperatur-Wärmepumpe.'
|
||||
};
|
||||
|
||||
/* Bewertung – bewusst einfach und nachvollziehbar gehalten */
|
||||
@@ -72,8 +71,8 @@
|
||||
function addons(a, top) {
|
||||
var list = [];
|
||||
if (a.dach === 'ja') list.push(['i-sun', 'Solarthermie für Warmwasser']);
|
||||
if (a.gebaeude === 'neubau') list.push(['i-wind', 'Wohnraumlüftung mit Wärmerückgewinnung']);
|
||||
if (top === 'wp' && a.verteilung !== 'fbh') list.push(['i-gauge', 'Heizkörper-Check & hydraulischer Abgleich']);
|
||||
if (a.gebaeude === 'neubau') list.push(['i-wind', 'Wohnraumlüftung']);
|
||||
if (top === 'wp' && a.verteilung !== 'fbh') list.push(['i-gauge', 'Hydraulischer Abgleich']);
|
||||
if (top === 'pellet') list.push(['i-tank', 'Pufferspeicher für mehr Effizienz']);
|
||||
if (top === 'hybrid') list.push(['i-gauge', 'Heizlastberechnung vor Ort']);
|
||||
list.push(['i-check', 'Beratung zu Fördermöglichkeiten']);
|
||||
@@ -104,7 +103,6 @@
|
||||
if (to === index) return;
|
||||
var from = panel(index);
|
||||
var target = panel(to);
|
||||
var dir = to > index ? 1 : -1;
|
||||
index = to;
|
||||
if (to >= total) renderResult();
|
||||
updateChrome();
|
||||
@@ -114,25 +112,13 @@
|
||||
from.classList.remove('is-active');
|
||||
target.hidden = false;
|
||||
target.classList.add('is-active');
|
||||
if (to >= total) animateBars();
|
||||
if (focus) {
|
||||
var f = to >= total ? result : target.querySelector('input:checked') || target.querySelector('input');
|
||||
if (f) f.focus({ preventScroll: true });
|
||||
}
|
||||
};
|
||||
|
||||
if (w.gsap && !reduce) {
|
||||
w.gsap.to(from, {
|
||||
x: -28 * dir, opacity: 0, duration: 0.28, ease: 'power2.in',
|
||||
onComplete: function () {
|
||||
w.gsap.set(from, { clearProps: 'transform,opacity' });
|
||||
reveal();
|
||||
w.gsap.fromTo(target, { x: 36 * dir, opacity: 0 }, { x: 0, opacity: 1, duration: 0.7, ease: 'expo.out', clearProps: 'transform,opacity' });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reveal();
|
||||
}
|
||||
reveal();
|
||||
}
|
||||
|
||||
function renderResult() {
|
||||
@@ -142,12 +128,10 @@
|
||||
var text = r.top === 'wp' ? TEXTS.wp[answers.gebaeude] || TEXTS.wp.gedaemmt : TEXTS[r.top];
|
||||
result.querySelector('[data-result-text]').innerHTML = text;
|
||||
|
||||
var bars = result.querySelector('[data-result-bars]');
|
||||
bars.innerHTML = r.order.map(function (k, i) {
|
||||
return '<li class="' + (i === 0 ? 'is-top' : '') + '"><span>' + SYSTEMS[k].name + '</span>' +
|
||||
'<span class="bar" role="img" aria-label="' + SYSTEMS[k].name + ': ' + r.pct[k] + ' Prozent Übereinstimmung"><i data-v="' + (r.pct[k] / 100) + '"></i></span>' +
|
||||
'<b>' + r.pct[k] + ' %</b></li>';
|
||||
}).join('');
|
||||
// Keine Prozentwerte – nur sinnvolle Alternativen nennen
|
||||
var alt = r.order.slice(1).filter(function (k) { return r.pct[k] >= 50; })
|
||||
.map(function (k) { return SYSTEMS[k].name; });
|
||||
result.querySelector('[data-result-bars]').innerHTML = alt.length ? '<li>Ebenfalls denkbar: ' + alt.join(' oder ') + '</li>' : '';
|
||||
|
||||
result.querySelector('[data-result-addons]').innerHTML = addons(answers, r.top).map(function (a) {
|
||||
return '<li>' + icon(a[0]) + a[1] + '</li>';
|
||||
@@ -163,19 +147,6 @@
|
||||
apply.setAttribute('data-message', summary);
|
||||
}
|
||||
|
||||
function animateBars() {
|
||||
var bars = result.querySelectorAll('.bar i');
|
||||
// Zwei Frames warten, damit die Transition sicher greift
|
||||
w.requestAnimationFrame(function () {
|
||||
w.requestAnimationFrame(function () {
|
||||
Array.prototype.forEach.call(bars, function (b, i) {
|
||||
b.style.transitionDelay = (0.12 * i) + 's';
|
||||
b.style.transform = 'scaleX(' + b.getAttribute('data-v') + ')';
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function restart() {
|
||||
answers = {};
|
||||
Array.prototype.forEach.call(form.querySelectorAll('input[type="radio"]'), function (r) { r.checked = false; });
|
||||
@@ -202,7 +173,7 @@
|
||||
updateChrome();
|
||||
if (pointerSelect) {
|
||||
w.clearTimeout(advanceTimer);
|
||||
advanceTimer = w.setTimeout(function () { go(index + 1, false); }, 320);
|
||||
advanceTimer = w.setTimeout(function () { go(index + 1, true); }, 320);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+10
-53
@@ -1,7 +1,6 @@
|
||||
/* ==========================================================================
|
||||
Olzinger – Energie-Haus
|
||||
Tabs + Hotspots steuern, welches System im Hausschnitt hervorgehoben wird.
|
||||
Solange niemand klickt, wechseln die Komponenten automatisch.
|
||||
========================================================================== */
|
||||
(function (w, d) {
|
||||
'use strict';
|
||||
@@ -13,16 +12,9 @@
|
||||
var parts = Array.prototype.slice.call(root.querySelectorAll('.part'));
|
||||
var hotspots = Array.prototype.slice.call(root.querySelectorAll('.hotspot'));
|
||||
var systems = Array.prototype.slice.call(root.querySelectorAll('.sys'));
|
||||
var progress = root.querySelector('[data-house-progress]');
|
||||
var order = tabs.map(function (t) { return t.getAttribute('data-part'); });
|
||||
var reduce = w.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
var DURATION = 6500;
|
||||
var current = null;
|
||||
var autoplay = !reduce;
|
||||
var inView = false;
|
||||
var hovering = false;
|
||||
var elapsed = 0, last = 0, raf = 0;
|
||||
|
||||
hotspots.forEach(function (h) {
|
||||
h.setAttribute('aria-controls', 'part-' + h.getAttribute('data-part'));
|
||||
@@ -37,6 +29,11 @@
|
||||
t.setAttribute('aria-selected', on ? 'true' : 'false');
|
||||
t.tabIndex = on ? 0 : -1;
|
||||
if (on && focusTab) t.focus();
|
||||
// Auf dem Handy liegen die Tabs in einer Wischleiste: aktiven Tab sichtbar halten
|
||||
if (on && t.parentNode.scrollWidth > t.parentNode.clientWidth) {
|
||||
var bar = t.parentNode;
|
||||
bar.scrollTo({ left: t.offsetLeft - (bar.clientWidth - t.offsetWidth) / 2, behavior: reduce ? 'auto' : 'smooth' });
|
||||
}
|
||||
});
|
||||
parts.forEach(function (p) {
|
||||
var on = p.getAttribute('data-part') === id;
|
||||
@@ -49,41 +46,13 @@
|
||||
h.setAttribute('aria-pressed', on ? 'true' : 'false');
|
||||
});
|
||||
systems.forEach(function (s) { s.classList.toggle('is-on', s.getAttribute('data-sys') === id); });
|
||||
elapsed = 0;
|
||||
}
|
||||
|
||||
function stopAutoplay() {
|
||||
autoplay = false;
|
||||
if (progress) progress.style.transform = 'scaleX(0)';
|
||||
if (raf) w.cancelAnimationFrame(raf);
|
||||
raf = 0;
|
||||
}
|
||||
|
||||
function tick(now) {
|
||||
raf = 0;
|
||||
if (!autoplay || !inView || hovering) return;
|
||||
var dt = last ? now - last : 16;
|
||||
last = now;
|
||||
elapsed += Math.min(dt, 100);
|
||||
if (progress) progress.style.transform = 'scaleX(' + Math.min(elapsed / DURATION, 1) + ')';
|
||||
if (elapsed >= DURATION) {
|
||||
var next = order[(order.indexOf(current) + 1) % order.length];
|
||||
activate(next, false);
|
||||
}
|
||||
raf = w.requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
function run() {
|
||||
if (!autoplay || !inView || hovering || raf) return;
|
||||
last = 0;
|
||||
raf = w.requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
tabs.forEach(function (t) {
|
||||
t.addEventListener('click', function () { stopAutoplay(); activate(t.getAttribute('data-part'), false); });
|
||||
t.addEventListener('click', function () { activate(t.getAttribute('data-part'), false); });
|
||||
});
|
||||
hotspots.forEach(function (h) {
|
||||
h.addEventListener('click', function () { stopAutoplay(); activate(h.getAttribute('data-part'), false); });
|
||||
h.addEventListener('click', function () { activate(h.getAttribute('data-part'), false); });
|
||||
});
|
||||
|
||||
// Tastatursteuerung nach WAI-ARIA Tabs-Pattern
|
||||
@@ -97,30 +66,18 @@
|
||||
else if (e.key === 'End') to = n - 1;
|
||||
if (to < 0) return;
|
||||
e.preventDefault();
|
||||
stopAutoplay();
|
||||
activate(order[to], true);
|
||||
});
|
||||
}
|
||||
|
||||
// Pausieren, solange der Nutzer mit der Maus im Bereich ist
|
||||
root.addEventListener('pointerenter', function (e) {
|
||||
if (e.pointerType === 'mouse') { hovering = true; if (raf) { w.cancelAnimationFrame(raf); raf = 0; } }
|
||||
});
|
||||
root.addEventListener('pointerleave', function (e) {
|
||||
if (e.pointerType === 'mouse') { hovering = false; run(); }
|
||||
});
|
||||
|
||||
// SVG-Animationen (Strömungen, Lüfter) nur laufen lassen, solange das Haus sichtbar ist
|
||||
if ('IntersectionObserver' in w) {
|
||||
new IntersectionObserver(function (entries) {
|
||||
inView = entries[0].isIntersecting;
|
||||
if (inView) run();
|
||||
}, { threshold: 0.35 }).observe(root);
|
||||
// SVG-Animationen (Strömungen, Lüfter) nur laufen lassen, solange das Haus sichtbar ist
|
||||
new IntersectionObserver(function (entries) {
|
||||
root.classList.toggle('is-inview', entries[0].isIntersecting);
|
||||
}, { threshold: 0 }).observe(root);
|
||||
} else {
|
||||
root.classList.add('is-inview');
|
||||
}
|
||||
|
||||
if (!('IntersectionObserver' in w)) root.classList.add('is-inview');
|
||||
activate(order[0], false);
|
||||
})(window, document);
|
||||
+152
-456
@@ -1,6 +1,7 @@
|
||||
/* ==========================================================================
|
||||
Olzinger – Hauptskript
|
||||
Preloader, Hero-Intro, Smooth-Scroll, Navigation und Scroll-Animationen.
|
||||
Navigation, mobiles Menü, dezente Einblendungen, Leistungs-Karussell (mobil)
|
||||
und Kartenstapel (Desktop). Bewusst ohne Bibliotheken.
|
||||
========================================================================== */
|
||||
(function (w, d) {
|
||||
'use strict';
|
||||
@@ -10,497 +11,192 @@
|
||||
var $$ = function (s, c) { return Array.prototype.slice.call((c || d).querySelectorAll(s)); };
|
||||
|
||||
var reduce = w.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
var finePointer = w.matchMedia('(hover: hover) and (pointer: fine)').matches;
|
||||
var fallback = !!w.__olzFallback;
|
||||
var hasIO = 'IntersectionObserver' in w;
|
||||
w.__olzReady = true;
|
||||
|
||||
/* ---------- Dynamische Jahreszahlen ---------- */
|
||||
var nowYear = new Date().getFullYear();
|
||||
$$('[data-current-year]').forEach(function (el) { el.textContent = nowYear; });
|
||||
$$('[data-years]').forEach(function (el) {
|
||||
el.textContent = nowYear - 1985;
|
||||
el.setAttribute('data-count', nowYear - 1985);
|
||||
});
|
||||
$$('[data-years]').forEach(function (el) { el.textContent = nowYear - 1985; });
|
||||
|
||||
/* ---------- Wärmebild ---------- */
|
||||
var thermal = null;
|
||||
/* ---------- Wärmebild (ruhiger Hintergrund im Hero) ---------- */
|
||||
var canvas = $('[data-thermal]');
|
||||
if (canvas && w.OlzThermal) {
|
||||
thermal = w.OlzThermal.init(canvas, {
|
||||
var thermal = w.OlzThermal.init(canvas, {
|
||||
hero: $('[data-hero]'),
|
||||
probe: $('[data-probe]'),
|
||||
probeVal: $('[data-probe-val]'),
|
||||
legendMarker: $('[data-legend-marker]'),
|
||||
calm: $('.hero__aside'),
|
||||
reduceMotion: reduce
|
||||
});
|
||||
} else {
|
||||
if (thermal) thermal.heatUp();
|
||||
} else if (canvas) {
|
||||
html.classList.add('no-webgl');
|
||||
}
|
||||
|
||||
/* ---------- Mobiles Menü (auch ohne GSAP funktionsfähig) ---------- */
|
||||
/* ---------- Mobiles Menü ---------- */
|
||||
var menu = $('[data-menu]');
|
||||
var toggle = $('[data-menu-toggle]');
|
||||
var menuOpen = false;
|
||||
var lenis = null;
|
||||
|
||||
if (menu) { menu.hidden = false; menu.setAttribute('inert', ''); menu.setAttribute('aria-hidden', 'true'); }
|
||||
|
||||
function openMenu() {
|
||||
if (!menu || menuOpen) return;
|
||||
menuOpen = true;
|
||||
menu.classList.add('is-open');
|
||||
menu.removeAttribute('inert');
|
||||
menu.setAttribute('aria-hidden', 'false');
|
||||
toggle.setAttribute('aria-expanded', 'true');
|
||||
toggle.querySelector('.sr-only').textContent = 'Menü schließen';
|
||||
html.classList.add('menu-open');
|
||||
if (lenis) lenis.stop();
|
||||
if (w.gsap && !reduce) {
|
||||
w.gsap.fromTo($$('.menu__links a, .menu__foot > *'), { y: 40, opacity: 0 },
|
||||
{ y: 0, opacity: 1, duration: 0.9, ease: 'expo.out', stagger: 0.045, delay: 0.25 });
|
||||
}
|
||||
}
|
||||
function closeMenu() {
|
||||
if (!menu || !menuOpen) return;
|
||||
menuOpen = false;
|
||||
menu.classList.remove('is-open');
|
||||
menu.setAttribute('inert', '');
|
||||
menu.setAttribute('aria-hidden', 'true');
|
||||
toggle.setAttribute('aria-expanded', 'false');
|
||||
toggle.querySelector('.sr-only').textContent = 'Menü öffnen';
|
||||
html.classList.remove('menu-open');
|
||||
if (lenis) lenis.start();
|
||||
}
|
||||
if (toggle) toggle.addEventListener('click', function () { if (menuOpen) closeMenu(); else openMenu(); });
|
||||
d.addEventListener('keydown', function (e) { if (e.key === 'Escape' && menuOpen) { closeMenu(); toggle.focus(); } });
|
||||
|
||||
/* ---------- Ohne GSAP: alles statisch sichtbar lassen ---------- */
|
||||
if (!w.gsap || !w.ScrollTrigger) {
|
||||
html.classList.remove('has-loader', 'js');
|
||||
html.classList.add('no-js');
|
||||
if (thermal) thermal.heatUp();
|
||||
d.addEventListener('click', function (e) {
|
||||
var a = e.target.closest('a[href^="#"]');
|
||||
if (a && a.dataset.topic) w.dispatchEvent(new CustomEvent('olz:topic', { detail: a.dataset.topic }));
|
||||
if (a) closeMenu();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var gsap = w.gsap, ScrollTrigger = w.ScrollTrigger;
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
if (w.SplitText) gsap.registerPlugin(w.SplitText);
|
||||
gsap.config({ nullTargetWarn: false });
|
||||
|
||||
/* ---------- Smooth Scroll ---------- */
|
||||
if (!reduce && typeof w.Lenis === 'function') {
|
||||
lenis = new w.Lenis({ lerp: 0.105, smoothWheel: true, wheelMultiplier: 1, touchMultiplier: 1.4 });
|
||||
lenis.on('scroll', ScrollTrigger.update);
|
||||
gsap.ticker.add(function (t) { lenis.raf(t * 1000); });
|
||||
gsap.ticker.lagSmoothing(0);
|
||||
if (html.classList.contains('has-loader')) lenis.stop();
|
||||
}
|
||||
w.olzLenis = lenis;
|
||||
|
||||
function navHeight() {
|
||||
var bar = $('.nav__bar');
|
||||
return bar ? bar.getBoundingClientRect().height + 12 : 76;
|
||||
}
|
||||
|
||||
// Natürliche Position berechnen (auch für „sticky“-Elemente)
|
||||
function targetY(el) {
|
||||
if (el.id === 'top') return 0;
|
||||
var stack = el.closest('[data-stack]');
|
||||
if (stack && el.classList.contains('svc')) {
|
||||
var cards = $$('.svc', stack);
|
||||
var gap = parseFloat(getComputedStyle(stack).rowGap) || 0;
|
||||
var y = stack.getBoundingClientRect().top + w.scrollY;
|
||||
for (var i = 0; i < cards.length && cards[i] !== el; i++) y += cards[i].offsetHeight + gap;
|
||||
var stickyTop = parseFloat(getComputedStyle(el).top);
|
||||
return y - (isNaN(stickyTop) ? navHeight() : stickyTop);
|
||||
}
|
||||
return el.getBoundingClientRect().top + w.scrollY;
|
||||
}
|
||||
|
||||
function scrollToEl(el) {
|
||||
var y = targetY(el);
|
||||
if (lenis) lenis.scrollTo(y, { duration: 1.4, easing: function (t) { return t === 1 ? 1 : 1 - Math.pow(2, -10 * t); } });
|
||||
else w.scrollTo({ top: y, behavior: reduce ? 'auto' : 'smooth' });
|
||||
function setMenu(open) {
|
||||
if (!menu || !toggle || open === menuOpen) return;
|
||||
menuOpen = open;
|
||||
menu.classList.toggle('is-open', open);
|
||||
if (open) menu.removeAttribute('inert'); else menu.setAttribute('inert', '');
|
||||
menu.setAttribute('aria-hidden', open ? 'false' : 'true');
|
||||
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
toggle.querySelector('.sr-only').textContent = open ? 'Menü schließen' : 'Menü öffnen';
|
||||
html.classList.toggle('menu-open', open);
|
||||
// Inhalt hinter dem Menü für Tastatur und Screenreader sperren
|
||||
[d.getElementById('main'), $('.footer')].forEach(function (el) { if (el) el.inert = open; });
|
||||
}
|
||||
if (toggle) toggle.addEventListener('click', function () { setMenu(!menuOpen); });
|
||||
d.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && menuOpen) { setMenu(false); toggle.focus(); }
|
||||
});
|
||||
// Wird das Fenster breit genug für die Desktop-Navigation (z. B. Tablet drehen), Menü schließen
|
||||
var deskMQ = w.matchMedia('(min-width: 1181px)');
|
||||
var onDesk = function (e) { if (e.matches) setMenu(false); };
|
||||
if (deskMQ.addEventListener) deskMQ.addEventListener('change', onDesk); else if (deskMQ.addListener) deskMQ.addListener(onDesk);
|
||||
|
||||
/* ---------- Anker-Links: Menü schließen, Thema ans Formular geben ---------- */
|
||||
d.addEventListener('click', function (e) {
|
||||
var a = e.target.closest('a[href^="#"]');
|
||||
var a = e.target.closest('a[href*="#"]');
|
||||
if (!a) return;
|
||||
var id = a.getAttribute('href');
|
||||
if (id.length < 2) return;
|
||||
var target = d.getElementById(id.slice(1));
|
||||
if (!target) return;
|
||||
e.preventDefault();
|
||||
if (a.dataset.topic) w.dispatchEvent(new CustomEvent('olz:topic', { detail: a.dataset.topic }));
|
||||
var wasOpen = menuOpen;
|
||||
closeMenu();
|
||||
setTimeout(function () { scrollToEl(target); }, wasOpen ? 380 : 0);
|
||||
try { history.replaceState(null, '', id === '#top' ? location.pathname : id); } catch (err) { /* z. B. in Sandbox-Vorschauen */ }
|
||||
if (menuOpen) setMenu(false);
|
||||
});
|
||||
|
||||
/* ---------- Seitenwechsel mit Vorhang ---------- */
|
||||
var curtain = d.createElement('div');
|
||||
curtain.className = 'curtain';
|
||||
curtain.setAttribute('aria-hidden', 'true');
|
||||
d.body.appendChild(curtain);
|
||||
d.addEventListener('click', function (e) {
|
||||
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
||||
var a = e.target.closest('a[href]');
|
||||
if (!a || a.target === '_blank' || a.hasAttribute('download')) return;
|
||||
var url;
|
||||
try { url = new URL(a.getAttribute('href'), location.href); } catch (err) { return; }
|
||||
if (!/^https?:$/.test(url.protocol) || url.origin !== location.origin) return;
|
||||
if (url.pathname === location.pathname) return;
|
||||
e.preventDefault();
|
||||
closeMenu();
|
||||
html.classList.add('is-leaving');
|
||||
w.setTimeout(function () { location.href = url.href; }, reduce ? 0 : 560);
|
||||
});
|
||||
w.addEventListener('pageshow', function () { html.classList.remove('is-leaving'); });
|
||||
|
||||
/* ---------- Preloader & Hero-Intro ---------- */
|
||||
function heroIn() {
|
||||
var tl = gsap.timeline({ defaults: { ease: 'expo.out' } });
|
||||
tl.fromTo('.hero__word', { y: 0, yPercent: 112 }, { yPercent: 0, duration: 1.5, stagger: 0.1 }, 0)
|
||||
.fromTo('.hero__dot', { scale: 0 }, { scale: 1, duration: 1.1, ease: 'back.out(3)', stagger: 0.1 }, 0.55)
|
||||
.fromTo('.hero__tag', { opacity: 0, x: -12 }, { opacity: 1, x: 0, duration: 1.2, stagger: 0.1 }, 0.7)
|
||||
.fromTo('[data-hero-fade]', { opacity: 0, y: 24 }, { opacity: 1, y: 0, duration: 1.3, stagger: 0.12 }, 0.35)
|
||||
.fromTo('.nav__bar', { opacity: 0, y: -16 }, { opacity: 1, y: 0, duration: 1.2 }, 0.2)
|
||||
.add(function () { if (thermal) thermal.measure(); });
|
||||
return tl;
|
||||
}
|
||||
|
||||
function intro() {
|
||||
if (fallback) { gsap.set('.nav__bar', { opacity: 1 }); if (thermal) thermal.heatUp(); return; }
|
||||
var loader = $('.loader');
|
||||
var useLoader = html.classList.contains('has-loader') && loader && !reduce;
|
||||
|
||||
if (reduce) {
|
||||
html.classList.remove('has-loader');
|
||||
gsap.set('.hero__word', { yPercent: 0, y: 0 });
|
||||
gsap.set('.hero__dot', { scale: 1 });
|
||||
gsap.set('.hero__tag, [data-hero-fade], .nav__bar', { opacity: 1 });
|
||||
if (thermal) thermal.heatUp();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!useLoader) {
|
||||
html.classList.remove('has-loader');
|
||||
if (loader) loader.remove();
|
||||
if (thermal) thermal.heatUp();
|
||||
heroIn();
|
||||
return;
|
||||
}
|
||||
|
||||
var arc = $('.loader__arc', loader);
|
||||
var yearEl = $('[data-loader-year]', loader);
|
||||
var len = arc.getTotalLength();
|
||||
var counter = { v: 1985 };
|
||||
gsap.set(arc, { strokeDasharray: len, strokeDashoffset: len });
|
||||
|
||||
var tl = gsap.timeline();
|
||||
tl.from('.loader__center', { opacity: 0, scale: 0.92, duration: 0.6, ease: 'power2.out' }, 0)
|
||||
.from('.loader__caption', { opacity: 0, y: 10, duration: 0.6, stagger: 0.08 }, 0.1)
|
||||
.to(arc, { strokeDashoffset: 0, duration: 1.35, ease: 'power2.inOut' }, 0.15)
|
||||
.to(counter, {
|
||||
v: nowYear, duration: 1.35, ease: 'power2.inOut',
|
||||
onUpdate: function () { yearEl.textContent = Math.round(counter.v); }
|
||||
}, 0.15)
|
||||
.to('.loader__center, .loader__caption', { opacity: 0, y: -20, duration: 0.5, ease: 'power2.in', stagger: 0.03 }, 1.65)
|
||||
.add(function () { if (thermal) thermal.heatUp(); }, 1.7)
|
||||
.to(loader, { clipPath: 'inset(0% 0% 100% 0%)', duration: 1.05, ease: 'expo.inOut' }, 1.85)
|
||||
.add(heroIn(), 2.25)
|
||||
.add(function () {
|
||||
html.classList.remove('has-loader');
|
||||
loader.remove();
|
||||
if (lenis) lenis.start();
|
||||
ScrollTrigger.refresh();
|
||||
}, 2.95);
|
||||
}
|
||||
|
||||
/* ---------- Navigation ---------- */
|
||||
/* ---------- Navigation: Hintergrund ab Scroll, beim Runterscrollen ausblenden ---------- */
|
||||
var nav = $('[data-nav]');
|
||||
ScrollTrigger.create({
|
||||
start: 0, end: 'max',
|
||||
onUpdate: function (self) {
|
||||
var y = self.scroll();
|
||||
if (nav) {
|
||||
var lastY = w.scrollY, ticking = false;
|
||||
var onScroll = function () {
|
||||
ticking = false;
|
||||
var y = w.scrollY;
|
||||
nav.classList.toggle('is-scrolled', y > 40);
|
||||
if (menuOpen) return;
|
||||
if (y > 420 && self.direction === 1) nav.classList.add('is-hidden');
|
||||
else if (self.direction === -1 || y <= 420) nav.classList.remove('is-hidden');
|
||||
}
|
||||
});
|
||||
nav.addEventListener('focusin', function () { nav.classList.remove('is-hidden'); });
|
||||
if (!menuOpen) {
|
||||
if (y > 420 && y > lastY + 4) nav.classList.add('is-hidden');
|
||||
else if (y < lastY - 4 || y <= 420) nav.classList.remove('is-hidden');
|
||||
}
|
||||
lastY = y;
|
||||
};
|
||||
w.addEventListener('scroll', function () {
|
||||
if (!ticking) { ticking = true; w.requestAnimationFrame(onScroll); }
|
||||
}, { passive: true });
|
||||
nav.addEventListener('focusin', function () { nav.classList.remove('is-hidden'); });
|
||||
onScroll();
|
||||
}
|
||||
|
||||
// Aktiven Menüpunkt markieren
|
||||
var navLinks = $$('[data-nav-link]');
|
||||
navLinks.forEach(function (link) {
|
||||
var sec = d.getElementById(link.getAttribute('href').slice(1));
|
||||
if (!sec) return;
|
||||
ScrollTrigger.create({
|
||||
trigger: sec, start: 'top 45%', end: 'bottom 45%',
|
||||
onToggle: function (self) {
|
||||
if (self.isActive) navLinks.forEach(function (l) { l.classList.toggle('is-active', l === link); });
|
||||
else link.classList.remove('is-active');
|
||||
if (hasIO && navLinks.length) {
|
||||
var linkFor = {};
|
||||
navLinks.forEach(function (l) { linkFor[l.getAttribute('href').slice(1)] = l; });
|
||||
var activeIO = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) {
|
||||
var link = linkFor[en.target.id];
|
||||
if (link) link.classList.toggle('is-active', en.isIntersecting);
|
||||
});
|
||||
}, { rootMargin: '-45% 0px -54% 0px' });
|
||||
Object.keys(linkFor).forEach(function (id) {
|
||||
var sec = d.getElementById(id);
|
||||
if (sec) activeIO.observe(sec);
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Dezente Einblendungen ---------- */
|
||||
var reveals = $$('[data-reveal]');
|
||||
if (hasIO && !reduce) {
|
||||
var revealIO = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) {
|
||||
if (!en.isIntersecting) return;
|
||||
en.target.classList.add('is-in');
|
||||
revealIO.unobserve(en.target);
|
||||
});
|
||||
}, { rootMargin: '0px 0px -8% 0px' });
|
||||
reveals.forEach(function (el) { revealIO.observe(el); });
|
||||
} else {
|
||||
reveals.forEach(function (el) { el.classList.add('is-in'); });
|
||||
}
|
||||
|
||||
/* ---------- Leistungen: Karussell (mobil) bzw. Kartenstapel (Desktop) ---------- */
|
||||
var stack = $('[data-stack]');
|
||||
var cards = $$('.svc');
|
||||
var stackNav = $('[data-stack-nav]');
|
||||
var stackMQ = w.matchMedia('(min-width: 900px) and (min-height: 660px)');
|
||||
|
||||
if (stack && stackNav) {
|
||||
var prevBtn = $('[data-stack-prev]', stackNav);
|
||||
var nextBtn = $('[data-stack-next]', stackNav);
|
||||
var currentEl = $('[data-stack-current]', stackNav);
|
||||
var current = 0;
|
||||
|
||||
var isCarousel = function () { return stack.scrollWidth > stack.clientWidth + 2; };
|
||||
var cardLeft = function (c) { return c.offsetLeft - cards[0].offsetLeft; };
|
||||
var cardIndex = function () {
|
||||
var x = stack.scrollLeft, best = 0, bestDist = Infinity;
|
||||
if (x >= stack.scrollWidth - stack.clientWidth - 2) return cards.length - 1;
|
||||
cards.forEach(function (c, i) {
|
||||
var dist = Math.abs(cardLeft(c) - x);
|
||||
if (dist < bestDist) { bestDist = dist; best = i; }
|
||||
});
|
||||
return best;
|
||||
};
|
||||
var updateNav = function () {
|
||||
stackNav.hidden = !isCarousel();
|
||||
current = cardIndex();
|
||||
currentEl.textContent = current + 1;
|
||||
prevBtn.disabled = current === 0;
|
||||
nextBtn.disabled = current === cards.length - 1;
|
||||
};
|
||||
// Schritt relativ zur echten Scrollposition (bei zwei Karten nebeneinander
|
||||
// liegen die letzten Karten jenseits der maximalen Scrollweite)
|
||||
var step = function (dir) {
|
||||
var x = stack.scrollLeft, i;
|
||||
if (dir > 0) { for (i = 0; i < cards.length - 1; i++) if (cardLeft(cards[i]) > x + 2) break; }
|
||||
else { for (i = cards.length - 1; i > 0; i--) if (cardLeft(cards[i]) < x - 2) break; }
|
||||
stack.scrollTo({ left: Math.min(cardLeft(cards[i]), stack.scrollWidth - stack.clientWidth), behavior: reduce ? 'auto' : 'smooth' });
|
||||
};
|
||||
|
||||
var navTick = false;
|
||||
stack.addEventListener('scroll', function () {
|
||||
if (!navTick) { navTick = true; w.requestAnimationFrame(function () { navTick = false; updateNav(); }); }
|
||||
}, { passive: true });
|
||||
prevBtn.addEventListener('click', function () { step(-1); });
|
||||
nextBtn.addEventListener('click', function () { step(1); });
|
||||
w.addEventListener('resize', updateNav);
|
||||
updateNav();
|
||||
}
|
||||
|
||||
// Desktop: hintere Karten treten beim Überdecken leicht zurück
|
||||
if (stack && !reduce) {
|
||||
var inners = cards.map(function (c) { return $('.svc__inner', c); });
|
||||
var stackTick = false;
|
||||
var paintStack = function () {
|
||||
stackTick = false;
|
||||
if (!stackMQ.matches) {
|
||||
inners.forEach(function (el) { el.style.transform = ''; el.style.removeProperty('--shade'); });
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* ---------- Animationen, die Bewegung voraussetzen ---------- */
|
||||
function motion() {
|
||||
// Hero: Inhalte beim Scrollen leicht anheben und ausblenden
|
||||
gsap.to('.hero__inner', {
|
||||
yPercent: -10, opacity: 0.15, ease: 'none',
|
||||
scrollTrigger: { trigger: '.hero', start: 'top top', end: 'bottom top', scrub: true }
|
||||
});
|
||||
|
||||
// Überschriften: Zeilen-Reveal
|
||||
if (w.SplitText) {
|
||||
$$('[data-split]').forEach(function (el) {
|
||||
w.SplitText.create(el, {
|
||||
type: 'lines', mask: 'lines', linesClass: 'split-line', autoSplit: true,
|
||||
onSplit: function (self) {
|
||||
return gsap.from(self.lines, {
|
||||
yPercent: 110, duration: 1.25, ease: 'expo.out', stagger: 0.09,
|
||||
scrollTrigger: { trigger: el, start: 'top 86%', once: true }
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Intro-Text: Wort für Wort „aufheizen“
|
||||
var introText = $('[data-scrub-words]');
|
||||
if (introText) {
|
||||
var split = w.SplitText.create(introText, { type: 'words', wordsClass: 'scrub-word' });
|
||||
gsap.fromTo(split.words, { opacity: 0.13 }, {
|
||||
opacity: 1, ease: 'none', stagger: 0.1,
|
||||
scrollTrigger: { trigger: introText, start: 'top 82%', end: 'bottom 50%', scrub: true }
|
||||
});
|
||||
gsap.from('.ichip', {
|
||||
scale: 0, rotate: -25, duration: 0.9, ease: 'back.out(2.4)', stagger: 0.18,
|
||||
scrollTrigger: { trigger: introText, start: 'top 70%', once: true }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Allgemeine Reveals
|
||||
$$('[data-reveal]').forEach(function (el) {
|
||||
gsap.from(el, {
|
||||
y: 44, opacity: 0, duration: 1.25, ease: 'expo.out',
|
||||
scrollTrigger: { trigger: el, start: 'top 90%', once: true }
|
||||
});
|
||||
});
|
||||
$$('[data-reveal-stagger]').forEach(function (el) {
|
||||
gsap.from(el.children, {
|
||||
y: 40, opacity: 0, duration: 1.15, ease: 'expo.out', stagger: 0.09,
|
||||
scrollTrigger: { trigger: el, start: 'top 90%', once: true }
|
||||
});
|
||||
});
|
||||
|
||||
// Vorlauf/Rücklauf-Bänder
|
||||
$$('.tape').forEach(function (tape) {
|
||||
var track = $('.tape__track', tape);
|
||||
var unit = track.innerHTML;
|
||||
var unitWidth = track.scrollWidth;
|
||||
if (!unitWidth) return;
|
||||
var copies = Math.max(2, Math.ceil((tape.offsetWidth * 1.2) / unitWidth) + 1);
|
||||
track.innerHTML = new Array(copies + 1).join(unit);
|
||||
var dir = parseFloat(tape.getAttribute('data-tape')) || 1;
|
||||
var tween = gsap.fromTo(track,
|
||||
{ x: dir > 0 ? 0 : -unitWidth },
|
||||
{ x: dir > 0 ? -unitWidth : 0, duration: unitWidth / 70, ease: 'none', repeat: -1 });
|
||||
tween.totalTime(tween.duration() * 500);
|
||||
var boost = { v: 1 };
|
||||
var tapeST = ScrollTrigger.create({
|
||||
trigger: tape, start: 'top bottom', end: 'bottom top',
|
||||
onToggle: function (self) { if (self.isActive) tween.resume(); else tween.pause(); },
|
||||
onUpdate: function (self) {
|
||||
var v = Math.min(Math.abs(self.getVelocity()) / 250, 7);
|
||||
var sign = self.direction === 1 ? 1 : -1;
|
||||
gsap.to(boost, {
|
||||
v: (1 + v) * sign, duration: 0.25, overwrite: true,
|
||||
onUpdate: function () { tween.timeScale(boost.v); }
|
||||
});
|
||||
gsap.to(boost, {
|
||||
v: sign, duration: 1.2, delay: 0.25, ease: 'power2.out',
|
||||
onUpdate: function () { tween.timeScale(boost.v); }
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!tapeST.isActive) tween.pause();
|
||||
gsap.fromTo(tape, { xPercent: dir * -4 }, {
|
||||
xPercent: dir * 4, ease: 'none',
|
||||
scrollTrigger: { trigger: '.tapes', start: 'top bottom', end: 'bottom top', scrub: true }
|
||||
});
|
||||
});
|
||||
|
||||
// Leistungskarten: gestapelt, die hinteren treten zurück
|
||||
var cards = $$('.svc');
|
||||
var mm = gsap.matchMedia();
|
||||
mm.add('(min-width: 900px) and (min-height: 660px)', function () {
|
||||
var vh = w.innerHeight;
|
||||
cards.forEach(function (card, i) {
|
||||
var next = cards[i + 1];
|
||||
if (!next) return;
|
||||
var inner = $('.svc__inner', card);
|
||||
gsap.to(inner, {
|
||||
scale: 0.9 + i * 0.012, '--shade': 0.55, ease: 'none',
|
||||
scrollTrigger: {
|
||||
trigger: next, start: 'top 82%',
|
||||
end: function () { return 'top ' + (parseFloat(getComputedStyle(next).top) || 100) + 'px'; },
|
||||
scrub: true, invalidateOnRefresh: true
|
||||
}
|
||||
});
|
||||
var stickyTop = parseFloat(getComputedStyle(next).top) || 100;
|
||||
var start = vh * 0.85;
|
||||
var p = (start - next.getBoundingClientRect().top) / (start - stickyTop);
|
||||
p = Math.max(0, Math.min(1, p));
|
||||
inners[i].style.transform = p ? 'scale(' + (1 - p * (0.1 - i * 0.012)) + ')' : '';
|
||||
inners[i].style.setProperty('--shade', (p * 0.5).toFixed(3));
|
||||
});
|
||||
});
|
||||
|
||||
// Illustrationen: leichter Parallax
|
||||
$$('.svc__art .art').forEach(function (art) {
|
||||
gsap.fromTo(art, { yPercent: 4 }, {
|
||||
yPercent: -4, ease: 'none',
|
||||
scrollTrigger: { trigger: art.closest('.svc'), start: 'top bottom', end: 'bottom top', scrub: true }
|
||||
});
|
||||
});
|
||||
|
||||
// Ablauf: Rohr füllt sich
|
||||
var steps = $('[data-steps]');
|
||||
if (steps) {
|
||||
var fill = $('[data-steps-fill]', steps);
|
||||
var pipe = $('.steps__pipe', steps);
|
||||
var items = $$('[data-step-item]', steps);
|
||||
var mmSteps = gsap.matchMedia();
|
||||
mmSteps.add({ desktop: '(min-width: 901px)', mobile: '(max-width: 900px)' }, function (ctx) {
|
||||
var desktop = ctx.conditions.desktop;
|
||||
if (!desktop) {
|
||||
var nodes = items.map(function (it) { return $('.step__node', it); });
|
||||
var placePipe = function () {
|
||||
var top0 = steps.getBoundingClientRect().top;
|
||||
var a = nodes[0].getBoundingClientRect(), b = nodes[nodes.length - 1].getBoundingClientRect();
|
||||
pipe.style.top = (a.top - top0 + a.height / 2) + 'px';
|
||||
pipe.style.height = (b.top - a.top) + 'px';
|
||||
pipe.style.bottom = 'auto';
|
||||
};
|
||||
placePipe();
|
||||
ScrollTrigger.addEventListener('refreshInit', placePipe);
|
||||
}
|
||||
gsap.fromTo(fill, desktop ? { scaleX: 0, scaleY: 1 } : { scaleY: 0, scaleX: 1 }, {
|
||||
scaleX: 1, scaleY: 1, ease: 'none',
|
||||
scrollTrigger: {
|
||||
trigger: steps, start: 'top 72%', end: desktop ? 'bottom 62%' : 'bottom 68%', scrub: 0.6,
|
||||
onUpdate: function (self) {
|
||||
var p = self.progress, n = items.length;
|
||||
items.forEach(function (it, i) { it.classList.toggle('is-on', p > (i / (n - 1)) * 0.97 + 0.01); });
|
||||
}
|
||||
}
|
||||
});
|
||||
return function () {
|
||||
pipe.style.top = pipe.style.height = pipe.style.bottom = '';
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Footer-Schriftzug „heizt auf“
|
||||
var word = $('[data-footer-word] span');
|
||||
if (word) {
|
||||
gsap.fromTo(word, { '--heat': '0%' }, {
|
||||
'--heat': '100%', ease: 'none',
|
||||
scrollTrigger: { trigger: '.footer', start: 'top 80%', end: 'bottom bottom', scrub: true }
|
||||
});
|
||||
gsap.from(word, {
|
||||
yPercent: 60, opacity: 0, duration: 1.4, ease: 'expo.out',
|
||||
scrollTrigger: { trigger: '[data-footer-word]', start: 'top 95%', once: true }
|
||||
});
|
||||
}
|
||||
|
||||
// Magnetische Buttons
|
||||
if (finePointer) {
|
||||
$$('[data-magnetic]').forEach(function (btn) {
|
||||
var xTo = gsap.quickTo(btn, 'x', { duration: 0.6, ease: 'power3' });
|
||||
var yTo = gsap.quickTo(btn, 'y', { duration: 0.6, ease: 'power3' });
|
||||
btn.addEventListener('pointermove', function (e) {
|
||||
var r = btn.getBoundingClientRect();
|
||||
xTo((e.clientX - r.left - r.width / 2) * 0.22);
|
||||
yTo((e.clientY - r.top - r.height / 2) * 0.32);
|
||||
});
|
||||
btn.addEventListener('pointerleave', function () { xTo(0); yTo(0); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Funktionen, die immer laufen (auch bei reduzierter Bewegung) ---------- */
|
||||
function essentials() {
|
||||
// Illustrationen nur animieren, wenn sichtbar
|
||||
if ('IntersectionObserver' in w) {
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) { en.target.classList.toggle('is-inview', en.isIntersecting); });
|
||||
}, { threshold: 0.15 });
|
||||
$$('.svc').forEach(function (c) { io.observe(c); });
|
||||
} else {
|
||||
$$('.svc').forEach(function (c) { c.classList.add('is-inview'); });
|
||||
}
|
||||
|
||||
// Zähler
|
||||
$$('[data-count]').forEach(function (el) {
|
||||
var target = parseInt(el.getAttribute('data-count') || el.textContent, 10);
|
||||
if (isNaN(target)) return;
|
||||
if (reduce) { el.textContent = target; return; }
|
||||
var from = parseInt(el.getAttribute('data-count-from') || '0', 10);
|
||||
var obj = { v: from };
|
||||
el.textContent = from;
|
||||
gsap.to(obj, {
|
||||
v: target, duration: 2.2, ease: 'power3.out',
|
||||
scrollTrigger: { trigger: el, start: 'top 92%', once: true },
|
||||
onUpdate: function () { el.textContent = Math.round(obj.v); }
|
||||
});
|
||||
});
|
||||
|
||||
// Historie: Zählwerk mit Jahreszahl
|
||||
var hist = $('[data-history]');
|
||||
if (hist) {
|
||||
var strips = $$('[data-odo-digit] .odo__strip', hist);
|
||||
var caption = $('[data-odo-caption]', hist);
|
||||
var entries = $$('.tl', hist);
|
||||
var setYear = function (year, title) {
|
||||
String(year).split('').forEach(function (ch, i) {
|
||||
if (!strips[i]) return;
|
||||
gsap.to(strips[i], { yPercent: -10 * parseInt(ch, 10), duration: reduce ? 0 : 1.1, ease: 'expo.inOut', delay: reduce ? 0 : i * 0.06, overwrite: true });
|
||||
});
|
||||
if (caption) caption.textContent = title;
|
||||
};
|
||||
entries.forEach(function (entry) {
|
||||
var y = entry.getAttribute('data-year');
|
||||
var year = y === 'now' ? nowYear : parseInt(y, 10);
|
||||
var title = ($('.tl__title', entry) || {}).textContent || '';
|
||||
ScrollTrigger.create({
|
||||
trigger: entry, start: 'top 62%', end: 'bottom 62%',
|
||||
onToggle: function (self) {
|
||||
if (!self.isActive) return;
|
||||
entries.forEach(function (e2) { e2.classList.toggle('is-active', e2 === entry); });
|
||||
setYear(year, title);
|
||||
}
|
||||
});
|
||||
});
|
||||
entries[0].classList.add('is-active');
|
||||
setYear(1985, ($('.tl__title', entries[0]) || {}).textContent || '');
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Start ---------- */
|
||||
essentials();
|
||||
if (!reduce) motion();
|
||||
intro();
|
||||
|
||||
// Nach dem Laden der Schriften Positionen neu berechnen
|
||||
if (d.fonts && d.fonts.ready) d.fonts.ready.then(function () { ScrollTrigger.refresh(); });
|
||||
w.addEventListener('load', function () { ScrollTrigger.refresh(); });
|
||||
|
||||
// Deep-Link (#kontakt etc.) nach dem Aufbau korrekt anspringen
|
||||
if (location.hash && location.hash.length > 1) {
|
||||
var deep = d.getElementById(location.hash.slice(1));
|
||||
if (deep) w.setTimeout(function () { w.scrollTo(0, targetY(deep)); }, 60);
|
||||
};
|
||||
var requestPaint = function () {
|
||||
if (!stackTick) { stackTick = true; w.requestAnimationFrame(paintStack); }
|
||||
};
|
||||
w.addEventListener('scroll', requestPaint, { passive: true });
|
||||
w.addEventListener('resize', requestPaint);
|
||||
paintStack();
|
||||
}
|
||||
})(window, document);
|
||||
+16
-131
@@ -1,15 +1,13 @@
|
||||
/* ==========================================================================
|
||||
Olzinger – „Wärmebild“ (WebGL)
|
||||
Ein thermografisches Feld mit Isothermen. Die Maus wirkt als Wärmequelle
|
||||
und hinterlässt eine Wärmespur, die langsam abkühlt und sich ausbreitet.
|
||||
Hinter Fließtext wird die Wärme gedämpft, damit alles lesbar bleibt.
|
||||
Ein ruhiges thermografisches Feld mit Isothermen als Hero-Hintergrund.
|
||||
Eine langsam wandernde Wärmequelle hält das Bild lebendig – ganz ohne
|
||||
Maus- oder Touch-Interaktion. Hinter Fließtext wird die Wärme gedämpft.
|
||||
========================================================================== */
|
||||
(function (w) {
|
||||
'use strict';
|
||||
|
||||
var MAX_HEAT = 14;
|
||||
var T_MIN = 14; // °C bei Feldwert 0
|
||||
var T_MAX = 28; // °C bei Feldwert 1
|
||||
|
||||
var VERT = 'attribute vec2 aPos; void main(){ gl_Position = vec4(aPos, 0.0, 1.0); }';
|
||||
|
||||
@@ -87,32 +85,6 @@
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
/* ---------- CPU-Gegenstück des Feldes (für die Temperaturanzeige) ---------- */
|
||||
function fract(x) { return x - Math.floor(x); }
|
||||
function smoothstep(a, b, x) { var t = Math.min(1, Math.max(0, (x - a) / (b - a))); return t * t * (3 - 2 * t); }
|
||||
function hash(x, y) {
|
||||
x = fract(x * 123.34); y = fract(y * 456.21);
|
||||
var dd = x * (x + 45.32) + y * (y + 45.32);
|
||||
x += dd; y += dd;
|
||||
return fract(x * y);
|
||||
}
|
||||
function noise(x, y) {
|
||||
var ix = Math.floor(x), iy = Math.floor(y), fx = x - ix, fy = y - iy;
|
||||
var ux = fx * fx * (3 - 2 * fx), uy = fy * fy * (3 - 2 * fy);
|
||||
var a = hash(ix, iy), b = hash(ix + 1, iy), c = hash(ix, iy + 1), d = hash(ix + 1, iy + 1);
|
||||
var ab = a + (b - a) * ux, cd = c + (d - c) * ux;
|
||||
return ab + (cd - ab) * uy;
|
||||
}
|
||||
function fbm(x, y) {
|
||||
var v = 0, a = 0.5;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
v += a * noise(x, y);
|
||||
var nx = 1.6 * x - 1.2 * y, ny = 1.2 * x + 1.6 * y;
|
||||
x = nx; y = ny; a *= 0.5;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function compile(gl, type, src) {
|
||||
var sh = gl.createShader(type);
|
||||
gl.shaderSource(sh, src);
|
||||
@@ -161,8 +133,6 @@
|
||||
var uHeat = gl.getUniformLocation(prog, 'uHeat');
|
||||
|
||||
var reduce = !!opts.reduceMotion;
|
||||
var finePointer = w.matchMedia('(hover: hover) and (pointer: fine)').matches;
|
||||
var probe = opts.probe, probeVal = opts.probeVal, marker = opts.legendMarker;
|
||||
var calmEl = opts.calm || null;
|
||||
|
||||
var heatData = new Float32Array(MAX_HEAT * 4);
|
||||
@@ -171,12 +141,11 @@
|
||||
var quality = (w.devicePixelRatio || 1) > 1.5 ? 0.62 : 0.85;
|
||||
var width = 0, height = 0, rect = null;
|
||||
var running = false, visible = true, raf = 0;
|
||||
var time = Math.random() * 40, last = 0, frame = 0;
|
||||
var time = Math.random() * 40, last = 0;
|
||||
var intro = reduce ? 1 : 0, introTarget = reduce ? 1 : 0;
|
||||
var frameTimes = [], tuned = false;
|
||||
|
||||
// Cursor in Feldkoordinaten: x ∈ [0, Seitenverhältnis], y ∈ [0, 1] (y nach oben)
|
||||
var cur = { x: 0, y: 0, tx: 0, ty: 0, inside: false, lastMove: -1e9, heat: 0, clientX: 0, clientY: 0 };
|
||||
// Feldkoordinaten: x ∈ [0, Seitenverhältnis], y ∈ [0, 1] (y nach oben)
|
||||
var auto = { heat: 0 };
|
||||
var lastEmit = 0;
|
||||
|
||||
@@ -204,10 +173,7 @@
|
||||
gl.viewport(0, 0, width, height);
|
||||
}
|
||||
measureCalm();
|
||||
}
|
||||
|
||||
function toField(clientX, clientY) {
|
||||
return { x: (clientX - rect.left) / rect.height, y: 1 - (clientY - rect.top) / rect.height };
|
||||
if (reduce) start(); // Größenänderung leert die Leinwand – Standbild neu zeichnen
|
||||
}
|
||||
|
||||
function emit(x, y, i, r) {
|
||||
@@ -215,75 +181,6 @@
|
||||
if (trail.length > MAX_HEAT - 2) trail.pop();
|
||||
}
|
||||
|
||||
function onMove(e) {
|
||||
if (e.pointerType && e.pointerType !== 'mouse') return;
|
||||
rect = canvas.getBoundingClientRect();
|
||||
var f = toField(e.clientX, e.clientY);
|
||||
cur.tx = f.x; cur.ty = f.y;
|
||||
cur.clientX = e.clientX - rect.left;
|
||||
cur.clientY = e.clientY - rect.top;
|
||||
if (!cur.inside) { cur.x = f.x; cur.y = f.y; }
|
||||
cur.inside = true;
|
||||
cur.lastMove = performance.now();
|
||||
if (probe) probe.classList.add('is-on');
|
||||
}
|
||||
function onLeave() {
|
||||
cur.inside = false;
|
||||
if (probe) probe.classList.remove('is-on');
|
||||
}
|
||||
function onTouch(e) {
|
||||
var t = e.touches && e.touches[0];
|
||||
if (!t) return;
|
||||
rect = canvas.getBoundingClientRect();
|
||||
var f = toField(t.clientX, t.clientY);
|
||||
emit(f.x, f.y, 0.85, 0.09);
|
||||
}
|
||||
|
||||
if (finePointer) {
|
||||
hero.addEventListener('pointermove', onMove, { passive: true });
|
||||
hero.addEventListener('pointerleave', onLeave, { passive: true });
|
||||
}
|
||||
hero.addEventListener('touchstart', onTouch, { passive: true });
|
||||
|
||||
function calmAt(px, py) {
|
||||
var a = smoothstep(calmRect[0] - 0.08, calmRect[0] + 0.02, px) * smoothstep(calmRect[1] - 0.08, calmRect[1] + 0.02, py);
|
||||
var b = (1 - smoothstep(calmRect[2] - 0.02, calmRect[2] + 0.08, px)) * (1 - smoothstep(calmRect[3] - 0.02, calmRect[3] + 0.08, py));
|
||||
return a * b;
|
||||
}
|
||||
|
||||
// Exakt dieselbe Rechnung wie im Shader – nur für einen Punkt
|
||||
function fieldAt(px, py) {
|
||||
var asp = width / height, t = time;
|
||||
var sx = px * 1.35, sy = py * 1.35;
|
||||
var qx = fbm(sx, sy + t * 0.05), qy = fbm(sx + 5.2, sy - t * 0.04);
|
||||
var rx = fbm(sx + 2.4 * qx + 1.7 + t * 0.06, sy + 2.4 * qy + 9.2 + t * 0.06);
|
||||
var ry = fbm(sx + 2.4 * qx + 8.3 - t * 0.05, sy + 2.4 * qy + 2.8 - t * 0.05);
|
||||
var f = fbm(sx + 2.2 * rx, sy + 2.2 * ry);
|
||||
var base = smoothstep(0.18, 0.86, f) * 0.46;
|
||||
var zx = px - asp * 0.8, zy = py - 0.76;
|
||||
base += 0.36 * Math.exp(-(zx * zx + zy * zy) / 0.11) * (0.8 + 0.2 * Math.sin(t * 0.45));
|
||||
var cx = px - asp * 0.16, cy = py - 0.12;
|
||||
base -= 0.12 * Math.exp(-(cx * cx + cy * cy) / 0.2);
|
||||
base *= 0.5 + 0.5 * smoothstep(0, 0.95, (px / asp) * 0.85 + py * 0.55);
|
||||
var heat = 0;
|
||||
for (var i = 0; i < MAX_HEAT; i++) {
|
||||
var o = i * 4, dx = px - heatData[o], dy = py - heatData[o + 1];
|
||||
heat += heatData[o + 2] * Math.exp(-(dx * dx + dy * dy) / Math.max(heatData[o + 3] * heatData[o + 3], 0.0001));
|
||||
}
|
||||
heat *= 1 - 0.62 * calmAt(px, py);
|
||||
var v = Math.max(base, 0) + heat * 0.66;
|
||||
v = 1 - Math.exp(-v * 1.35);
|
||||
return v * intro;
|
||||
}
|
||||
|
||||
function readTemperature() {
|
||||
if (!probeVal || !cur.inside) return;
|
||||
var v = Math.max(0, Math.min(1, fieldAt(cur.tx, cur.ty)));
|
||||
var temp = T_MIN + v * (T_MAX - T_MIN);
|
||||
probeVal.textContent = temp.toFixed(1).replace('.', ',') + ' °C';
|
||||
if (marker) marker.style.left = (v * 100).toFixed(1) + '%';
|
||||
}
|
||||
|
||||
function step(now) {
|
||||
raf = 0;
|
||||
if (!running) return;
|
||||
@@ -304,22 +201,14 @@
|
||||
intro += (introTarget - intro) * (1 - Math.exp(-dt * 1.8));
|
||||
var asp = width / height;
|
||||
|
||||
// Maus
|
||||
var k = 1 - Math.exp(-dt * 9);
|
||||
cur.x += (cur.tx - cur.x) * k;
|
||||
cur.y += (cur.ty - cur.y) * k;
|
||||
var idle = (now - cur.lastMove) > 3200;
|
||||
cur.heat += ((cur.inside ? 1 : 0) - cur.heat) * (1 - Math.exp(-dt * 3));
|
||||
if (cur.inside && !idle && now - lastEmit > 55) {
|
||||
emit(cur.x, cur.y, 0.42, 0.075);
|
||||
lastEmit = now;
|
||||
}
|
||||
|
||||
// Autopilot: wandernde Wärmequelle oben rechts, solange niemand interagiert
|
||||
var autoOn = (!cur.inside || idle) && !reduce;
|
||||
// Langsam wandernde Wärmequelle oben rechts
|
||||
var autoOn = !reduce;
|
||||
auto.heat += ((autoOn ? 0.8 : 0) - auto.heat) * (1 - Math.exp(-dt * 1.2));
|
||||
var ax = asp * (0.7 + 0.16 * Math.sin(time * 0.21)) + 0.05 * Math.sin(time * 0.53);
|
||||
var ay = 0.66 + 0.16 * Math.sin(time * 0.29 + 1.3);
|
||||
// Hochkant (Handy): Wärme am rechten Rand halten, damit die Überschrift lesbar bleibt
|
||||
var portrait = asp < 1;
|
||||
var ax = portrait ? asp * (1.0 + 0.04 * Math.sin(time * 0.21)) + 0.03 * Math.sin(time * 0.53)
|
||||
: asp * (0.7 + 0.16 * Math.sin(time * 0.21)) + 0.05 * Math.sin(time * 0.53);
|
||||
var ay = portrait ? 0.62 + 0.12 * Math.sin(time * 0.29 + 1.3) : 0.66 + 0.16 * Math.sin(time * 0.29 + 1.3);
|
||||
if (autoOn && now - lastEmit > 140) {
|
||||
emit(ax, ay, 0.3 * auto.heat, 0.09);
|
||||
lastEmit = now;
|
||||
@@ -331,7 +220,7 @@
|
||||
trail[i].r += dt * 0.032;
|
||||
}
|
||||
|
||||
heatData[0] = cur.x; heatData[1] = cur.y; heatData[2] = cur.heat * 0.8; heatData[3] = 0.095;
|
||||
heatData[0] = 0; heatData[1] = 0; heatData[2] = 0; heatData[3] = 0.1;
|
||||
heatData[4] = ax; heatData[5] = ay; heatData[6] = auto.heat * 0.7; heatData[7] = 0.13;
|
||||
for (var j = 0; j < MAX_HEAT - 2; j++) {
|
||||
var o = (j + 2) * 4, tp = trail[j];
|
||||
@@ -346,12 +235,8 @@
|
||||
gl.uniform4fv(uHeat, heatData);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||
|
||||
frame++;
|
||||
if (frame % 4 === 0) readTemperature();
|
||||
if (probe && cur.inside) {
|
||||
probe.style.transform = 'translate3d(' + cur.clientX.toFixed(1) + 'px,' + cur.clientY.toFixed(1) + 'px,0)';
|
||||
}
|
||||
|
||||
// Reduzierte Bewegung: Standbild – einmal zeichnen, dann ruhen
|
||||
if (reduce) { running = false; return; }
|
||||
raf = w.requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2024 darkroom.engineering
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
# Eingebundene Bibliotheken
|
||||
|
||||
Alle Bibliotheken werden lokal ausgeliefert (kein CDN, keine Datenübertragung an Dritte).
|
||||
|
||||
| Datei | Bibliothek | Version | Lizenz |
|
||||
|---|---|---|---|
|
||||
| `gsap.min.js`, `ScrollTrigger.min.js`, `SplitText.min.js` | [GSAP](https://gsap.com) | 3.15.0 | GSAP Standard „No Charge“ License – kostenlos, auch kommerziell (https://gsap.com/standard-license) |
|
||||
| `lenis.min.js` | [Lenis](https://github.com/darkroomengineering/lenis) | 1.3.26 | MIT (siehe `LICENSE-lenis.txt`) |
|
||||
|
||||
Schriften in `assets/fonts/`: Instrument Sans & Instrument Serif – SIL Open Font License 1.1 (siehe `OFL-*.txt`).
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
/*!
|
||||
* ScrollTrigger 3.15.0
|
||||
* https://gsap.com
|
||||
*
|
||||
* @license Copyright 2026, GreenSock. All rights reserved.
|
||||
* Subject to the terms at https://gsap.com/standard-license.
|
||||
* @author: Jack Doyle, jack@greensock.com
|
||||
*/
|
||||
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function _defineProperties(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function r(){return Se||"undefined"!=typeof window&&(Se=window.gsap)&&Se.registerPlugin&&Se}function z(e,t){return~Le.indexOf(e)&&Le[Le.indexOf(e)+1][t]}function A(e){return!!~t.indexOf(e)}function B(e,t,r,n,i){return e.addEventListener(t,r,{passive:!1!==n,capture:!!i})}function C(e,t,r,n){return e.removeEventListener(t,r,!!n)}function F(){return Re&&Re.isPressed||Ie.cache++}function G(r,n){function fd(e){if(e||0===e){i&&(Ce.history.scrollRestoration="manual");var t=Re&&Re.isPressed;e=fd.v=Math.round(e)||(Re&&Re.iOS?1:0),r(e),fd.cacheID=Ie.cache,t&&o("ss",e)}else(n||Ie.cache!==fd.cacheID||o("ref"))&&(fd.cacheID=Ie.cache,fd.v=r());return fd.v+fd.offset}return fd.offset=0,r&&fd}function J(e,t){return(t&&t._ctx&&t._ctx.selector||Se.utils.toArray)(e)[0]||("string"==typeof e&&!1!==Se.config().nullTargetWarn?console.warn("Element not found:",e):null)}function L(t,e){var r=e.s,n=e.sc;A(t)&&(t=Me.scrollingElement||ke);var i=Ie.indexOf(t),o=n===Xe.sc?1:2;~i||(i=Ie.push(t)-1),Ie[i+o]||B(t,"scroll",F);var a=Ie[i+o],s=a||(Ie[i+o]=G(z(t,r),!0)||(A(t)?n:G(function(e){return arguments.length?t[r]=e:t[r]})));return s.target=t,a||(s.smooth="smooth"===Se.getProperty(t,"scrollBehavior")),s}function M(e,t,i){function Hd(e,t){var r=Ye();t||n<r-s?(a=o,o=e,l=s,s=r):i?o+=e:o=a+(e-a)/(r-l)*(s-l)}var o=e,a=e,s=Ye(),l=s,n=t||50,c=Math.max(500,3*n);return{update:Hd,reset:function reset(){a=o=i?0:o,l=s=0},getVelocity:function getVelocity(e){var t=l,r=a,n=Ye();return!e&&0!==e||e===o||Hd(e),s===l||c<n-l?0:(o+(i?r:-r))/((i?n:s)-t)*1e3}}}function N(e,t){return t&&!e._gsapAllow&&!1!==e.cancelable&&e.preventDefault(),e.changedTouches?e.changedTouches[0]:e}function O(e){var t=Math.max.apply(Math,e),r=Math.min.apply(Math,e);return Math.abs(t)>=Math.abs(r)?t:r}function P(){(Ae=Se.core.globals().ScrollTrigger)&&Ae.core&&function _integrate(){var e=Ae.core,r=e.bridge||{},t=e._scrollers,n=e._proxies;t.push.apply(t,Ie),n.push.apply(n,Le),Ie=t,Le=n,o=function _bridge(e,t){return r[e](t)}}()}function Q(e){return Se=e||r(),!Te&&Se&&"undefined"!=typeof document&&document.body&&(Ce=window,ke=(Me=document).documentElement,Ee=Me.body,t=[Ce,Me,ke,Ee],Se.utils.clamp,Be=Se.core.context||function(){},Oe="onpointerenter"in Ee?"pointer":"mouse",Pe=k.isTouch=Ce.matchMedia&&Ce.matchMedia("(hover: none), (pointer: coarse)").matches?1:"ontouchstart"in Ce||0<navigator.maxTouchPoints||0<navigator.msMaxTouchPoints?2:0,De=k.eventTypes=("ontouchstart"in ke?"touchstart,touchmove,touchcancel,touchend":"onpointerdown"in ke?"pointerdown,pointermove,pointercancel,pointerup":"mousedown,mousemove,mouseup,mouseup").split(","),setTimeout(function(){return i=0},500),Te=1),Ae||P(),Te}var Se,Te,Ce,Me,ke,Ee,Pe,Oe,Ae,t,Re,De,Be,i=1,ze=[],Ie=[],Le=[],Ye=Date.now,o=function _bridge(e,t){return t},n="scrollLeft",a="scrollTop",Ne={s:n,p:"left",p2:"Left",os:"right",os2:"Right",d:"width",d2:"Width",a:"x",sc:G(function(e){return arguments.length?Ce.scrollTo(e,Xe.sc()):Ce.pageXOffset||Me[n]||ke[n]||Ee[n]||0})},Xe={s:a,p:"top",p2:"Top",os:"bottom",os2:"Bottom",d:"height",d2:"Height",a:"y",op:Ne,sc:G(function(e){return arguments.length?Ce.scrollTo(Ne.sc(),e):Ce.pageYOffset||Me[a]||ke[a]||Ee[a]||0})};Ne.op=Xe,Ie.cache=0;var k=(Observer.prototype.init=function init(e){Te||Q(Se)||console.warn("Please gsap.registerPlugin(Observer)"),Ae||P();var i=e.tolerance,a=e.dragMinimum,t=e.type,o=e.target,r=e.lineHeight,n=e.debounce,s=e.preventDefault,l=e.onStop,c=e.onStopDelay,u=e.ignore,f=e.wheelSpeed,d=e.event,p=e.onDragStart,g=e.onDragEnd,h=e.onDrag,v=e.onPress,b=e.onRelease,m=e.onRight,y=e.onLeft,x=e.onUp,_=e.onDown,w=e.onChangeX,S=e.onChangeY,T=e.onChange,k=e.onToggleX,E=e.onToggleY,R=e.onHover,D=e.onHoverEnd,z=e.onMove,I=e.ignoreCheck,Y=e.isNormalizer,X=e.onGestureStart,U=e.onGestureEnd,q=e.onWheel,H=e.onEnable,W=e.onDisable,V=e.onClick,G=e.scrollSpeed,j=e.capture,K=e.allowClicks,$=e.lockAxis,Z=e.onLockAxis;function hf(){return xe=Ye()}function jf(e,t){return(se.event=e)&&u&&function _isWithin(e,t){for(var r=t.length;r--;)if(t[r]===e||t[r].contains(e))return!0;return!1}(e.target,u)||t&&he&&"touch"!==e.pointerType||I&&I(e,t)}function lf(){var e=se.deltaX=O(me),t=se.deltaY=O(ye),r=Math.abs(e)>=i,n=Math.abs(t)>=i;T&&(r||n)&&T(se,e,t,me,ye),r&&(m&&0<se.deltaX&&m(se),y&&se.deltaX<0&&y(se),w&&w(se),k&&se.deltaX<0!=le<0&&k(se),le=se.deltaX,me[0]=me[1]=me[2]=0),n&&(_&&0<se.deltaY&&_(se),x&&se.deltaY<0&&x(se),S&&S(se),E&&se.deltaY<0!=ce<0&&E(se),ce=se.deltaY,ye[0]=ye[1]=ye[2]=0),(ne||re)&&(z&&z(se),re&&(p&&1===re&&p(se),h&&h(se),re=0),ne=!1),oe&&!(oe=!1)&&Z&&Z(se),ie&&(q(se),ie=!1),ee=0}function mf(e,t,r){me[r]+=e,ye[r]+=t,se._vx.Line truncated
|
||||
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
/*!
|
||||
* SplitText 3.15.0
|
||||
* https://gsap.com
|
||||
*
|
||||
* @license Copyright 2026, GreenSock. All rights reserved. Subject to the terms at https://gsap.com/standard-license.
|
||||
* @author: Jack Doyle
|
||||
*/
|
||||
|
||||
(function(k,A){typeof exports=="object"&&typeof module!="undefined"?A(exports):typeof define=="function"&&define.amd?define(["exports"],A):(k=typeof globalThis!="undefined"?globalThis:k||self,A(k.window=k.window||{}))})(this,function(k){"use strict";let A,D,Z=typeof Symbol=="function"?Symbol():"_split",K,he=()=>K||Y.register(window.gsap),$=typeof Intl!="undefined"&&"Segmenter"in Intl?new Intl.Segmenter:0,I=e=>e?typeof e=="string"?I(document.querySelectorAll(e)):"length"in e?Array.from(e).reduce((t,i)=>(typeof i=="string"?t.push(...I(i)):t.push(i),t),[]):[e]:[],ee=e=>I(e).filter(t=>t&&t.nodeType===1),Q=[],U=function(){},de={add:e=>e()},pe=/\s+/g,te=new RegExp("\\p{RI}\\p{RI}|\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?(\\u{200D}\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?)*|.","gu"),J={left:0,top:0,width:0,height:0},ue=(e,t)=>{for(;++t<e.length&&e[t]===J;);return e[t]||J},ie=({element:e,html:t,ariaL:i,ariaH:n})=>{e.innerHTML=t,i?e.setAttribute("aria-label",i):e.removeAttribute("aria-label"),n?e.setAttribute("aria-hidden",n):e.removeAttribute("aria-hidden")},ne=(e,t)=>{if(t){let i=new Set(e.join("").match(t)||Q),n=e.length,a,c,l,o;if(i.size)for(;--n>-1;){c=e[n];for(l of i)if(l.startsWith(c)&&l.length>c.length){for(a=0,o=c;l.startsWith(o+=e[n+ ++a])&&o.length<l.length;);if(a&&o.length===l.length){e[n]=l,e.splice(n+1,a);break}}}}return e},le=e=>window.getComputedStyle(e).display==="inline"&&(e.style.display="inline-block"),M=(e,t,i)=>t.insertBefore(typeof e=="string"?document.createTextNode(e):e,i),X=(e,t,i)=>{let n=t[e+"sClass"]||"",{tag:a="div",aria:c="auto",propIndex:l=!1}=t,o=e==="line"?"block":"inline-block",d=n.indexOf("++")>-1,_=y=>{let m=document.createElement(a),E=i.length+1;return n&&(m.className=n+(d?" "+n+E:"")),l&&m.style.setProperty("--"+e,E+""),c!=="none"&&m.setAttribute("aria-hidden","true"),a!=="span"&&(m.style.position="relative",m.style.display=o),m.textContent=y,i.push(m),m};return d&&(n=n.replace("++","")),_.collection=i,_},fe=(e,t,i,n)=>{let a=X("line",i,n),c=window.getComputedStyle(e).textAlign||"left";return(l,o)=>{let d=a("");for(d.style.textAlign=c,e.insertBefore(d,t[l]);l<o;l++)d.appendChild(t[l]);d.normalize()}},se=(e,t,i,n,a,c,l,o,d,_)=>{var y;let m=Array.from(e.childNodes),E=0,{wordDelimiter:R,reduceWhiteSpace:B=!0,prepareText:V}=t,G=e.getBoundingClientRect(),j=G,q=!B&&window.getComputedStyle(e).whiteSpace.substring(0,3)==="pre",C=0,b=i.collection,r,f,O,s,g,x,z,h,p,W,S,T,N,F,w,u,L,v;for(typeof R=="object"?(O=R.delimiter||R,f=R.replaceWith||""):f=R===""?"":R||" ",r=f!==" ";E<m.length;E++)if(s=m[E],s.nodeType===3){for(w=s.textContent||"",B?w=w.replace(pe," "):q&&(w=w.replace(/\n/g,f+`
|
||||
`)),V&&(w=V(w,e)),s.textContent=w,g=f||O?w.split(O||f):w.match(o)||Q,L=g[g.length-1],h=r?L.slice(-1)===" ":!L,L||g.pop(),j=G,z=r?g[0].charAt(0)===" ":!g[0],z&&M(" ",e,s),g[0]||g.shift(),ne(g,d),c&&_||(s.textContent=""),p=1;p<=g.length;p++)if(u=g[p-1],!B&&q&&u.charAt(0)===`
|
||||
`&&((y=s.previousSibling)==null||y.remove(),M(document.createElement("br"),e,s),u=u.slice(1)),!B&&u==="")M(f,e,s);else if(u===" ")e.insertBefore(document.createTextNode(" "),s);else{if(r&&u.charAt(0)===" "&&M(" ",e,s),C&&p===1&&!z&&b.indexOf(C.parentNode)>-1?(x=b[b.length-1],x.appendChild(document.createTextNode(n?"":u))):(x=i(n?"":u),M(x,e,s),C&&p===1&&!z&&x.insertBefore(C,x.firstChild)),n)for(S=$?ne([...$.segment(u)].map(H=>H.segment),d):u.match(o)||Q,v=0;v<S.length;v++)x.appendChild(S[v]===" "?document.createTextNode(" "):n(S[v]));if(c&&_){if(w=s.textContent=w.substring(u.length+1,w.length),W=x.getBoundingClientRect(),W.top>j.top&&W.left<=j.left){for(T=e.cloneNode(),N=e.childNodes[0];N&&N!==x;)F=N,N=N.nextSibling,T.appendChild(F);e.parentNode.insertBefore(T,e),a&&le(T)}j=W}(p<g.length||h)&&M(p>=g.length?" ":r&&u.slice(-1)===" "?" "+f:f,e,s)}e.removeChild(s),C=0}else s.nodeType===1&&(l&&l.indexOf(s)>-1?(b.indexOf(s.previousSibling)>-1&&b[b.length-1].appendChild(s),C=s):(se(s,t,i,n,a,c,l,o,d,!0),C=0),a&&le(s))};const re=class ae{constructor(t,i){this.isSplit=!1,he(),this.elements=ee(t),this.chars=[],this.words=[],this.lines=[],this.masks=[],this.vars=i,this.elements.forEach(l=>{var o;i.overwrite!==!1&&((o=l[Z])==null||o._data.orig.filter(({element:d})=>d===l).forEach(ie)),l[Z]=this}),this._split=()=>this.isSplit&&this.split(this.vars);let n=[],a,c=()=>{let l=n.length,o;for(;l--;){o=n[l];let d=o.element.offsetWidth;if(d!==o.width){o.width=d,this._split();return}}};this._data={orig:n,obs:typeof ResizeObserver!="undefined"&&new ResizeObserver(()=>{clearTimeout(a),a=setTimeout(c,200)})},U(this),this.split(i)}split(t){return(this._ctx||de).add(()=>{this.isSplit&&this.revert(),this.vars=t=t||this.vars||{};let{type:i="chars,words,lines",aria:n="auto",deepSlice:a=!0,smartWrap:c,onSplit:l,autoSplit:o=!1,specialChars:d,mask:_}=this.vars,y=i.indexOf("lines")>-1,m=i.indexOf("chars")>-1,E=i.indexOf("words")>-1,R=m&&!E&&!y,B=d&&("push"in d?new RegExp("(?:"+d.join("|")+")","gu"):d),V=B?new RegExp(B.source+"|"+te.source,"gu"):te,G=!!t.ignore&&ee(t.ignore),{orig:j,animTime:q,obs:C}=this._data,b;(m||E||y)&&(this.elements.forEach((r,f)=>{j[f]={element:r,html:r.innerHTML,ariaL:r.getAttribute("aria-label"),ariaH:r.getAttribute("aria-hidden")},n==="auto"?r.setAttribute("aria-label",(r.textContent||"").trim()):n==="hidden"&&r.setAttribute("aria-hidden","true");let O=[],s=[],g=[],x=m?X("char",t,O):null,z=X("word",t,s),h,p,W,S;if(se(r,t,z,x,R,a&&(y||R),G,V,B,!1),y){let T=I(r.childNodes),N=fe(r,T,t,g),F,w=[],u=0,L=T.map(P=>P.nodeType===1?P.getBoundingClientRect():J),v=J,H;for(h=0;h<T.length;h++)F=T[h],F.nodeType===1&&(F.nodeName==="BR"?((!h||T[h-1].nodeName!=="BR")&&(w.push(F),N(u,h+1)),u=h+1,v=ue(L,h)):(H=L[h],h&&H.top>v.top&&H.left<v.left+v.width-1&&(N(u,h),u=h),v=H));u<h&&N(u,h),w.forEach(P=>{var oe;return(oe=P.parentNode)==null?void 0:oe.removeChild(P)})}if(!E){for(h=0;h<s.length;h++)if(p=s[h],m||!p.nextSibling||p.nextSibling.nodeType!==3)if(c&&!y){for(W=document.createElement("span"),W.style.whiteSpace="nowrap";p.firstChild;)W.appendChild(p.firstChild);p.replaceWith(W)}else p.replaceWith(...p.childNodes);else S=p.nextSibling,S&&S.nodeType===3&&(S.textContent=(p.textContent||"")+(S.textContent||""),p.remove());s.length=0,r.normalize()}this.lines.push(...g),this.words.push(...s),this.chars.push(...O)}),_&&this[_]&&this.masks.push(...this[_].map(r=>{let f=r.cloneNode();return r.replaceWith(f),f.appendChild(r),r.className&&(f.className=r.className.trim().split(" ").map(O=>O+"-mask").join(" ")),f.style.overflow="clip",f}))),this.isSplit=!0,D&&y&&o&&D.addEventListener("loadingdone",this._split),(b=l&&l(this))&&b.totalTime&&(this._data.anim=q?b.totalTime(q):b),y&&o&&this.elements.forEach((r,f)=>{j[f].width=r.offsetWidth,C&&C.observe(r)})}),this}kill(){let{obs:t}=this._data;t&&t.disconnect(),D==null||D.removeEventListener("loadingdone",this._split)}revert(){var t,i;if(this.isSplit){let{orig:n,anim:a}=this._data;this.kill(),n.forEach(ie),this.chars.length=this.words.length=this.lines.length=n.length=this.masks.length=0,this.isSplit=!1,a&&(this._data.animTime=a.totalTime(),a.revert()),(i=(t=this.vars).onRevert)==null||i.call(t,this)}return this}static create(t,i){return new ae(t,i)}static register(t){A=A||t||window.gsap,A&&(I=A.utils.toArray,U=A.core.context||U),!K&&window.innerWidth>0&&(D=document.fonts,K=!0)}};re.version="3.15.0";let Y=re;k.SplitText=Y,k.default=Y;if (typeof(window)==="undefined"||window!==k){Object.defineProperty(k,"__esModule",{value:!0})} else {delete k.default}});
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
/*!
|
||||
* GSAP 3.15.0
|
||||
* https://gsap.com
|
||||
*
|
||||
* @license Copyright 2026, GreenSock. All rights reserved.
|
||||
* Subject to the terms at https://gsap.com/standard-license.
|
||||
* @author: Jack Doyle, jack@greensock.com
|
||||
*/
|
||||
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function R(t){return(i=bt(t,ht))&&Fe}function S(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function T(t,e){return!e&&console.warn(t)}function U(t,e){return t&&(ht[t]=e)&&i&&(i[t]=e)||ht}function V(){return 0}function ga(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=yt.length;r--&&!yt[r].targetTest(i););e=yt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new Xt(t[r],e)))||t.splice(r,1);return t}function ha(t){return t._gsap||ga(Pt(t))[0]._gsap}function ia(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ja(t,e){return(t=t.split(",")).forEach(e)||t}function ka(t){return Math.round(1e5*t)/1e5||0}function la(t){return Math.round(1e7*t)/1e7||0}function ma(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function na(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++i<r;);return i<r}function oa(){var t,e,r=pt.length,i=pt.slice(0);for(_t={},t=pt.length=0;t<r;t++)(e=i[t])&&e._lazy&&(e.render(e._lazy[0],e._lazy[1],!0)._lazy=0)}function pa(t){return!!(t._initted||t._startAt||t.add)}function qa(t,e,r,i){pt.length&&!I&&oa(),t.render(e,r,i||!!(I&&e<0&&pa(t))),pt.length&&!I&&oa()}function ra(t){var e=parseFloat(t);return(e||0===e)&&(t+"").match(ot).length<2?e:r(t)?t.trim():t}function sa(t){return t}function ta(t,e){for(var r in e)r in t||(t[r]=e[r]);return t}function wa(t,e){for(var r in e)"__proto__"!==r&&"constructor"!==r&&"prototype"!==r&&(t[r]=v(e[r])?wa(t[r]||(t[r]={}),e[r]):e[r]);return t}function xa(t,e){var r,i={};for(r in t)r in e||(i[r]=t[r]);return i}function ya(t){var e=t.parent||L,r=t.keyframes?function _setKeyframeDefaults(i){return function(t,e){for(var r in e)r in t||"duration"===r&&i||"ease"===r||(t[r]=e[r])}}(K(t.keyframes)):ta;if(w(t.inherit))for(;e;)r(t,e.vars.defaults),e=e.parent||e._dp;return t}function Aa(t,e,r,i,n){void 0===r&&(r="_first"),void 0===i&&(i="_last");var a,s=t[i];if(n)for(a=e[n];s&&s[n]>a;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function Ba(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function Ca(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Da(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Fa(t,e,r,i){return t._startAt&&(I?t._startAt.revert(ft):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ha(t){return t._repeat?wt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ja(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ka(t){return t._end=la(t._start+(t._tDur/Math.abs(t._ts||t._rts||q)||0))}function La(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=la(r._time-(0<t._ts?e/t._ts:((t._dirty?t.totalDuration():t._tDur)-e)/-t._ts)),Ka(t),r._dirty||Da(r,t)),t}function Ma(t,e){var r;if((e._time||!e._dur&&e._initted||e._start<t._time&&(e._dur||!e.add))&&(r=Ja(t.rawTime(),e),(!e._dur||Mt(0,e.totalDuration(),r)-e._tTime>q)&&e.render(r,!0)),Da(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur<t.duration())for(r=t;r._dp;)0<=r.rawTime()&&r.totalTime(r._tTime),r=r._dp;t._zTime=-q}}function Na(e,r,i,n){return r.parent&&Ca(r),r._start=la((t(i)?i:i||e!==L?Ot(e,i,r):e._time)+r._delay),r._end=la(r._start+(r.totalDuration()/Math.abs(r.timeScale())||0)),Aa(e,r,"_first","_last",e._sort?"_start":0),xt(r)||(e._recent=r),n||Ma(e,r),e._ts<0&&La(e,e._tTime),e}function Oa(t,e){return(ht.ScrollTrigger||S("scrollTrigger",e))&&ht.ScrollTrigger.create(e,t)}function Pa(t,e,r,i,n){return Ht(t,e,n),t._initted?!r&&t._pt&&!I&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&f!==It.frame?(pt.push(t),t._lazy=[n,i],1):void 0:1}function Ua(t,e,r,i){var n=t._repeat,a=la(e)||0,s=t._tTime/t._tDur;return s&&!i&&(t._time*=a/t._dur),t._dur=a,t._tDur=n?n<0?1e10:la(a*(n+1)+t._rDelay*n):a,0<s&&!i&&La(t,t._tTime=t._tDur*s),t.parent&&Ka(t),r||Da(t.parent,t),t}function Va(t){return t instanceof Gt?Da(t)Line truncated
|
||||
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
(function(){var e=`1.3.26`;function t(e,t,n){return Math.max(e,Math.min(t,n))}function n(e,t,n){return(1-n)*e+n*t}function r(e,t,r,i){return n(e,t,1-Math.exp(-r*i))}function i(e,t){return(e%t+t)%t}var a=class{isRunning=!1;value=0;from=0;to=0;currentTime=0;lerp;duration;easing;onUpdate;advance(e){if(!this.isRunning)return;let n=!1;if(this.duration&&this.easing){this.currentTime+=e;let r=t(0,this.currentTime/this.duration,1);n=r>=1;let i=n?1:this.easing(r);this.value=this.from+(this.to-this.from)*i}else this.lerp?(this.value=r(this.value,this.to,this.lerp*60,e),Math.round(this.value)===Math.round(this.to)&&(this.value=this.to,n=!0)):(this.value=this.to,n=!0);n&&this.stop(),this.onUpdate?.(this.value,n)}stop(){this.isRunning=!1}fromTo(e,t,{lerp:n,duration:r,easing:i,onStart:a,onUpdate:o}){this.from=this.value=e,this.to=t,this.lerp=n,this.duration=r,this.easing=i,this.currentTime=0,this.isRunning=!0,a?.(),this.onUpdate=o}};function o(e,t){let n;return function(...r){clearTimeout(n),n=setTimeout(()=>{n=void 0,e.apply(this,r)},t)}}var s=class{width=0;height=0;scrollHeight=0;scrollWidth=0;debouncedResize;wrapperResizeObserver;contentResizeObserver;constructor(e,t,{autoResize:n=!0,debounce:r=250}={}){this.wrapper=e,this.content=t,n&&(this.debouncedResize=o(this.resize,r),this.wrapper instanceof Window?window.addEventListener(`resize`,this.debouncedResize):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){this.wrapperResizeObserver?.disconnect(),this.contentResizeObserver?.disconnect(),this.wrapper===window&&this.debouncedResize&&window.removeEventListener(`resize`,this.debouncedResize)}resize=()=>{this.onWrapperResize(),this.onContentResize()};onWrapperResize=()=>{this.wrapper instanceof Window?(this.width=window.innerWidth,this.height=window.innerHeight):(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)};onContentResize=()=>{this.wrapper instanceof Window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)};get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}},c=class{events={};emit(e,...t){let n=this.events[e]||[];for(let e=0,r=n.length;e<r;e++)n[e]?.(...t)}on(e,t){return this.events[e]?this.events[e].push(t):this.events[e]=[t],()=>{this.events[e]=this.events[e]?.filter(e=>t!==e)}}off(e,t){this.events[e]=this.events[e]?.filter(e=>t!==e)}destroy(){this.events={}}};let l={passive:!1};function u(e,t){return e===1?16.666666666666668:e===2?t:1}var d=class{touchStart={x:0,y:0};lastDelta={x:0,y:0};window={width:0,height:0};emitter=new c;constructor(e,t={wheelMultiplier:1,touchMultiplier:1}){this.element=e,this.options=t,window.addEventListener(`resize`,this.onWindowResize),this.onWindowResize(),this.element.addEventListener(`wheel`,this.onWheel,l),this.element.addEventListener(`touchstart`,this.onTouchStart,l),this.element.addEventListener(`touchmove`,this.onTouchMove,l),this.element.addEventListener(`touchend`,this.onTouchEnd,l)}on(e,t){return this.emitter.on(e,t)}destroy(){this.emitter.destroy(),window.removeEventListener(`resize`,this.onWindowResize),this.element.removeEventListener(`wheel`,this.onWheel,l),this.element.removeEventListener(`touchstart`,this.onTouchStart,l),this.element.removeEventListener(`touchmove`,this.onTouchMove,l),this.element.removeEventListener(`touchend`,this.onTouchEnd,l)}onTouchStart=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:0,y:0},this.emitter.emit(`scroll`,{deltaX:0,deltaY:0,event:e})};onTouchMove=e=>{let{clientX:t,clientY:n}=e.targetTouches?e.targetTouches[0]:e,r=-(t-this.touchStart.x)*this.options.touchMultiplier,i=-(n-this.touchStart.y)*this.options.touchMultiplier;this.touchStart.x=t,this.touchStart.y=n,this.lastDelta={x:r,y:i},this.emitter.emit(`scroll`,{deltaX:r,deltaY:i,event:e})};onTouchEnd=e=>{this.emitter.emit(`scroll`,{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:e})};onWheel=e=>{let{deltaX:t,deltaY:n,deltaMode:r}=e,i=u(r,this.window.width),a=u(r,this.window.height);t*=i,n*=a,t*=this.options.wheelMultiplier,n*=this.options.wheelMultiplier,this.emitter.emit(`scroll`,{deltaX:t,deltaY:n,event:e})};onWindowResize=()=>{this.window={width:window.innerWidth,height:window.innerHeight}}};let f=e=>Math.min(1,1.001-2**(-10*e));var p=class{_isScrolling=!1;_isStopped=!1;_isLocked=!1;_preventNextNativeScrollEvent=!1;_resetVelocityTimeout=null;_rafId=null;_isDraggingSelection=!1;reducedMotionMediaQuery=window.matchMedia(`(prefers-reduced-motion: reduce)`);isTouching;isIos;time=0;userData={};lastVelocity=0;velocity=0;direction=0;options;targetScroll;animatedScroll;animate=new a;emitter=new c;dimensions;virtualScroll;conLine truncated
|
||||
//# sourceMappingURL=lenis.min.js.map
|
||||
+15
-22
@@ -13,7 +13,7 @@
|
||||
<link rel="icon" href="assets/img/favicon-32.png" sizes="32x32" type="image/png">
|
||||
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="preload" href="assets/fonts/instrument-sans-var.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="assets/css/main.css">
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20261004">
|
||||
<script>
|
||||
(function (d, w) {
|
||||
var h = d.documentElement;
|
||||
@@ -21,12 +21,8 @@
|
||||
w.setTimeout(function () { if (!w.__olzReady) { h.classList.remove('js'); h.classList.add('no-js'); } }, 4500);
|
||||
})(document, window);
|
||||
</script>
|
||||
<script src="assets/vendor/gsap.min.js" defer></script>
|
||||
<script src="assets/vendor/ScrollTrigger.min.js" defer></script>
|
||||
<script src="assets/vendor/SplitText.min.js" defer></script>
|
||||
<script src="assets/vendor/lenis.min.js" defer></script>
|
||||
<script src="assets/js/contact.js" defer></script>
|
||||
<script src="assets/js/main.js" defer></script>
|
||||
<script src="assets/js/contact.js?v=20261004" defer></script>
|
||||
<script src="assets/js/main.js?v=20261004" defer></script>
|
||||
</head>
|
||||
<body class="page">
|
||||
<!--
|
||||
@@ -63,8 +59,8 @@
|
||||
<a href="index.html#karriere">Karriere</a>
|
||||
</nav>
|
||||
<div class="nav__actions">
|
||||
<a class="nav__phone" href="tel:+4987172617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg><span>0871 72617</span></a>
|
||||
<a class="btn btn--sm btn--warm" href="index.html#kontakt"><span class="btn__label">Anfrage starten</span></a>
|
||||
<a class="nav__phone" href="tel:+4987172617" aria-label="Anrufen: 0871 72617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg><span>0871 72617</span></a>
|
||||
<a class="btn btn--sm btn--warm" href="index.html#kontakt"><span class="btn__label">Anfrage stellen</span></a>
|
||||
<button class="nav__toggle" type="button" aria-expanded="false" aria-controls="menu" data-menu-toggle>
|
||||
<span class="nav__toggle-lines" aria-hidden="true"><i></i><i></i></span>
|
||||
<span class="sr-only">Menü öffnen</span>
|
||||
@@ -74,15 +70,13 @@
|
||||
</header>
|
||||
|
||||
<div class="menu" id="menu" data-menu hidden>
|
||||
<div class="menu__glow" aria-hidden="true"></div>
|
||||
<nav class="menu__links" aria-label="Mobile Navigation">
|
||||
<a href="index.html#leistungen"><span>01</span>Leistungen</a>
|
||||
<a href="index.html#energie-haus"><span>02</span>Energie-Haus</a>
|
||||
<a href="index.html#heizungs-finder"><span>03</span>Heizungs-Finder</a>
|
||||
<a href="index.html#ablauf"><span>04</span>Ablauf</a>
|
||||
<a href="index.html#ueber-uns"><span>05</span>Über uns</a>
|
||||
<a href="index.html#karriere"><span>06</span>Karriere</a>
|
||||
<a href="index.html#kontakt"><span>07</span>Kontakt</a>
|
||||
<a href="index.html#leistungen">Leistungen</a>
|
||||
<a href="index.html#energie-haus">Energie-Haus</a>
|
||||
<a href="index.html#heizungs-finder">Heizungs-Finder</a>
|
||||
<a href="index.html#ueber-uns">Über uns</a>
|
||||
<a href="index.html#karriere">Karriere</a>
|
||||
<a href="index.html#kontakt">Kontakt</a>
|
||||
</nav>
|
||||
<div class="menu__foot">
|
||||
<a href="tel:+4987172617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg>0871 72617</a>
|
||||
@@ -95,7 +89,7 @@
|
||||
<section class="page-hero" aria-labelledby="page-title">
|
||||
<div class="container">
|
||||
<p class="label"><a href="index.html">Startseite</a> / Rechtliches</p>
|
||||
<h1 class="h1" id="page-title" data-split>Datenschutz­erklärung</h1>
|
||||
<h1 class="h1" id="page-title">Datenschutz­erklärung</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -182,11 +176,11 @@
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h2 class="footer__h">Kontakt</h2>
|
||||
<p><a href="tel:+4987172617">Tel. 0871 72617</a><br>Fax 0871 74963<br><a href="mailto:kontakt@olzinger.de">kontakt@olzinger.de</a></p>
|
||||
<p><a href="tel:+4987172617">Tel. 0871 72617</a><br>Fax 0871 74963<br><a href="mailto:kontakt@olzinger.de">kontakt@olzinger.de</a></p>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h2 class="footer__h">Öffnungszeiten</h2>
|
||||
<p>Mo – Do 7:00 – 12:00<br>und 13:00 – 16:30<br>Fr 7:00 – 11:30</p>
|
||||
<p>Mo–Do 7:00–12:00<br>und 13:00–16:30<br>Fr 7:00–11:30</p>
|
||||
</div>
|
||||
<nav class="footer__col" aria-label="Footer-Navigation">
|
||||
<h2 class="footer__h">Seite</h2>
|
||||
@@ -199,9 +193,8 @@
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="footer__word" aria-hidden="true" data-footer-word><span>Olzinger</span></div>
|
||||
<div class="footer__bottom">
|
||||
<p>© <span data-current-year>2026</span> Olzinger GmbH · Heizung · Sanitär · Lüftung · Regenerative Energien</p>
|
||||
<p>© <span data-current-year>2026</span> Olzinger GmbH</p>
|
||||
<ul>
|
||||
<li><a href="impressum.html">Impressum</a></li>
|
||||
<li><a href="datenschutz.html" aria-current="page">Datenschutz</a></li>
|
||||
|
||||
+16
-23
@@ -13,7 +13,7 @@
|
||||
<link rel="icon" href="assets/img/favicon-32.png" sizes="32x32" type="image/png">
|
||||
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon.png">
|
||||
<link rel="preload" href="assets/fonts/instrument-sans-var.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="assets/css/main.css">
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=20261004">
|
||||
<script>
|
||||
(function (d, w) {
|
||||
var h = d.documentElement;
|
||||
@@ -21,12 +21,8 @@
|
||||
w.setTimeout(function () { if (!w.__olzReady) { h.classList.remove('js'); h.classList.add('no-js'); } }, 4500);
|
||||
})(document, window);
|
||||
</script>
|
||||
<script src="assets/vendor/gsap.min.js" defer></script>
|
||||
<script src="assets/vendor/ScrollTrigger.min.js" defer></script>
|
||||
<script src="assets/vendor/SplitText.min.js" defer></script>
|
||||
<script src="assets/vendor/lenis.min.js" defer></script>
|
||||
<script src="assets/js/contact.js" defer></script>
|
||||
<script src="assets/js/main.js" defer></script>
|
||||
<script src="assets/js/contact.js?v=20261004" defer></script>
|
||||
<script src="assets/js/main.js?v=20261004" defer></script>
|
||||
</head>
|
||||
<body class="page">
|
||||
<!--
|
||||
@@ -63,8 +59,8 @@
|
||||
<a href="index.html#karriere">Karriere</a>
|
||||
</nav>
|
||||
<div class="nav__actions">
|
||||
<a class="nav__phone" href="tel:+4987172617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg><span>0871 72617</span></a>
|
||||
<a class="btn btn--sm btn--warm" href="index.html#kontakt"><span class="btn__label">Anfrage starten</span></a>
|
||||
<a class="nav__phone" href="tel:+4987172617" aria-label="Anrufen: 0871 72617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg><span>0871 72617</span></a>
|
||||
<a class="btn btn--sm btn--warm" href="index.html#kontakt"><span class="btn__label">Anfrage stellen</span></a>
|
||||
<button class="nav__toggle" type="button" aria-expanded="false" aria-controls="menu" data-menu-toggle>
|
||||
<span class="nav__toggle-lines" aria-hidden="true"><i></i><i></i></span>
|
||||
<span class="sr-only">Menü öffnen</span>
|
||||
@@ -74,15 +70,13 @@
|
||||
</header>
|
||||
|
||||
<div class="menu" id="menu" data-menu hidden>
|
||||
<div class="menu__glow" aria-hidden="true"></div>
|
||||
<nav class="menu__links" aria-label="Mobile Navigation">
|
||||
<a href="index.html#leistungen"><span>01</span>Leistungen</a>
|
||||
<a href="index.html#energie-haus"><span>02</span>Energie-Haus</a>
|
||||
<a href="index.html#heizungs-finder"><span>03</span>Heizungs-Finder</a>
|
||||
<a href="index.html#ablauf"><span>04</span>Ablauf</a>
|
||||
<a href="index.html#ueber-uns"><span>05</span>Über uns</a>
|
||||
<a href="index.html#karriere"><span>06</span>Karriere</a>
|
||||
<a href="index.html#kontakt"><span>07</span>Kontakt</a>
|
||||
<a href="index.html#leistungen">Leistungen</a>
|
||||
<a href="index.html#energie-haus">Energie-Haus</a>
|
||||
<a href="index.html#heizungs-finder">Heizungs-Finder</a>
|
||||
<a href="index.html#ueber-uns">Über uns</a>
|
||||
<a href="index.html#karriere">Karriere</a>
|
||||
<a href="index.html#kontakt">Kontakt</a>
|
||||
</nav>
|
||||
<div class="menu__foot">
|
||||
<a href="tel:+4987172617"><svg class="icon" aria-hidden="true"><use href="#i-phone"/></svg>0871 72617</a>
|
||||
@@ -95,7 +89,7 @@
|
||||
<section class="page-hero" aria-labelledby="page-title">
|
||||
<div class="container">
|
||||
<p class="label"><a href="index.html">Startseite</a> / Rechtliches</p>
|
||||
<h1 class="h1" id="page-title" data-split>Impressum</h1>
|
||||
<h1 class="h1" id="page-title">Impressum</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -152,7 +146,7 @@
|
||||
|
||||
<h2 id="urheberrecht">Urheberrecht</h2>
|
||||
<p>Die durch uns erstellten Inhalte und Werke auf dieser Website unterliegen dem deutschen Urheberrecht. Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechts bedürfen der schriftlichen Zustimmung der Olzinger GmbH.</p>
|
||||
<p>Illustrationen und Grafiken: individuell für diese Website gestaltet. Schriften: Instrument Sans und Instrument Serif (SIL Open Font License), lokal eingebunden.</p>
|
||||
<p>Illustrationen und Grafiken: individuell für diese Website gestaltet. Schrift: Instrument Sans (SIL Open Font License), lokal eingebunden.</p>
|
||||
|
||||
<p><a class="link-arrow" href="index.html"><svg class="icon" aria-hidden="true"><use href="#i-arrow-left"/></svg> Zurück zur Startseite</a></p>
|
||||
</div>
|
||||
@@ -169,11 +163,11 @@
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h2 class="footer__h">Kontakt</h2>
|
||||
<p><a href="tel:+4987172617">Tel. 0871 72617</a><br>Fax 0871 74963<br><a href="mailto:kontakt@olzinger.de">kontakt@olzinger.de</a></p>
|
||||
<p><a href="tel:+4987172617">Tel. 0871 72617</a><br>Fax 0871 74963<br><a href="mailto:kontakt@olzinger.de">kontakt@olzinger.de</a></p>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h2 class="footer__h">Öffnungszeiten</h2>
|
||||
<p>Mo – Do 7:00 – 12:00<br>und 13:00 – 16:30<br>Fr 7:00 – 11:30</p>
|
||||
<p>Mo–Do 7:00–12:00<br>und 13:00–16:30<br>Fr 7:00–11:30</p>
|
||||
</div>
|
||||
<nav class="footer__col" aria-label="Footer-Navigation">
|
||||
<h2 class="footer__h">Seite</h2>
|
||||
@@ -186,9 +180,8 @@
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="footer__word" aria-hidden="true" data-footer-word><span>Olzinger</span></div>
|
||||
<div class="footer__bottom">
|
||||
<p>© <span data-current-year>2026</span> Olzinger GmbH · Heizung · Sanitär · Lüftung · Regenerative Energien</p>
|
||||
<p>© <span data-current-year>2026</span> Olzinger GmbH</p>
|
||||
<ul>
|
||||
<li><a href="impressum.html" aria-current="page">Impressum</a></li>
|
||||
<li><a href="datenschutz.html">Datenschutz</a></li>
|
||||
|
||||
+656
-565
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user