commit bec24e5bbc8b49c8f83dc460034a756e6fa943ac Author: NotPika69 <86320278+NotPika69@users.noreply.github.com> Date: Sun Oct 4 22:52:10 2026 +0200 Website-Entwurf für HEB Bauelemente (normtor.de) Akquise-Demo mit Startseite (Tor-Intro, 24-h-Renovierung mit Uhr, Über uns, Fachbetrieb statt Discount), Galerie mit Filter und Großansicht, Kontaktseite mit Anfrageformular sowie Impressum- und Datenschutz-Entwürfen. Statisches HTML/CSS/JS, Seiten und Bilder werden über tools/ erzeugt. Co-Authored-By: Claude Opus 5.5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c12ea99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# macOS +.DS_Store +._* + +# Editor / lokale Werkzeuge +.vscode/ +.idea/ +.claude/ +__pycache__/ +*.log +node_modules/ diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..f6197be --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,37 @@ +# Design — HEB Bauelemente + +## Idee: „Meisterbetrieb mit Gesicht“ + +Ein Ein-Mann-Fachbetrieb vom Land konkurriert mit Online-Discountern. Sein Argument ist die Person: +Beratung, Aufmaß, Montage und Hilfe im Ernstfall von Wolfgang Haas selbst. Die Seite ist deshalb hell, +warm und direkt, mit dem Porträt prominent, Telefonnummer überall und Texten in der Ich-/Wir-Form. + +**Signatur:** Beim ersten Besuch liegt ein Sektionaltor aus sechs Lamellen mit HEB-Schild über dem Hero +und fährt nach oben weg, mit Lichtkante darunter. Das Lamellenmotiv kehrt als feine Linien in den +Seitenköpfen, der 24-h-Sektion und dem CTA-Band wieder. + +## Tokens + +| Token | Wert | Verwendung | +|---|---|---| +| `--blue` | `#00569D` | exakt aus dem HEB-Logo; Buttons, Akzente, Vertrauensleiste, CTA-Band | +| `--sky` | `#7CBCF0` | Blau auf dunklem Grund | +| `--amber` | `#F2AD33` | nur die 24-h-Uhr und die erledigten Schritte | +| `--ink` / `--text` / `--muted` | `#14202C` / `#2D3843` / `#5D6A77` | Text | +| `--paper` | `#F4F2ED` | warme Flächen (Über uns, Partner, Seitenköpfe) | +| `--steel` | `#1A232C` | 24-h-Sektion, Footer | + +Schrift: **Barlow Condensed** 600/700 für Überschriften (technisch, Torbau), **Barlow** 400–600 für Text. +Lokal eingebunden, nur Latin-Subset. Radius 4 px. Breite max. 1200 px, Seitenrand `clamp(16px, 4vw, 40px)`. + +## Bewegung + +- Tor-Intro: einmal pro Sitzung (`sessionStorage`), nur ohne `prefers-reduced-motion`, Klick öffnet sofort, + Notbremse im Head-Script nach 3,5 s, falls `site.js` nicht lädt. +- 24-h-Uhr: Ring und Zähler füllen sich, während man an den vier Schritten vorbeiscrollt (6/12/19/24 h). +- Einblendungen beim Scrollen (`.reveal`), Bild-Zoom bei Hover. Alles ohne Bibliotheken. + +## Mobil + +Anrufleiste unten (Anrufen/Anfrage), Menü als Vollbreite-Liste, Hero-Ausschnitt auf das Tor verschoben, +Galerie zweispaltig, Großansicht mit Wischen. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5fe9e8 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# HEB Bauelemente (normtor.de) — Website-Entwurf + +Akquise-Demo für **HEB Bauelemente, Wolfgang Haas** (Essenbach-Mirskofen): Normstahl-Garagentore, +Sommer-Antriebe, Reparatur und Garagen-Renovierung in 24 Stunden. Die alte Seite `normtor.de` läuft auf +WebsiteBaker (Stand 2022). Statisches HTML/CSS/JS, keine Bibliotheken, kein Cookie-Banner, keine Fremdanfragen. + +| Datei | Seite | +|---|---| +| `index.html` | Start (Tor-Intro, Vertrauensleiste, Leistungen, 24-h-Renovierung mit Uhr, Projekt-Streifen, Über uns, Fachbetrieb statt Discount, Partner) | +| `projekte.html` | Galerie „Tore & Projekte“ mit Filter und Großansicht (27 Fotos) | +| `kontakt.html` | Telefon, Adresse, Anfrageformular (im Entwurf ohne Versand) | +| `impressum.html`, `datenschutz.html` | Rechtstexte (Entwürfe, offene Punkte rot markiert) | +| `assets/site.css` | Tokens und alle Komponenten | +| `assets/site.js` | Intro, Navigation, Einblendungen, 24-h-Uhr, Galerie, Formular | +| `tools/build_pages.py` | **Quelle der Seiten** — Inhalte hier ändern, dann neu erzeugen | +| `tools/build_images.py` | Fotos aus `tools/orig/` → `assets/img/` (AVIF + WebP, 800/1200 px) | + +Design-Entscheidungen und Tokens: [`DESIGN.md`](DESIGN.md). + +## Lokal ansehen + +```bash +python3 -m http.server 4175 +``` + +Dann http://localhost:4175 öffnen. Das Tor-Intro läuft nur einmal pro Browser-Sitzung +(zum erneuten Ansehen: Tab schließen oder `sessionStorage.clear()` in der Konsole). + +## Seiten ändern + +```bash +python3 tools/build_pages.py +``` + +Neue Fotos: in `tools/orig/` ablegen, in `tools/build_images.py` unter `IMAGES` eintragen, +`python3 tools/build_images.py` laufen lassen und das Bild in `GALLERY` in `build_pages.py` aufnehmen. + +## Entwurfsmodus + +`DEMO = True` in `build_pages.py` setzt `noindex` und einen Hinweis ins Formular; `robots.txt` sperrt alles. +Vor dem Launch: `DEMO = False`, `robots.txt` öffnen, Sitemap ergänzen. + +## Was für den Launch noch fehlt + +1. **Formularversand.** Das Formular verschickt nichts. Beim Hoster einen PHP-Mailer anschließen + (Muster: `Olzinger/kontakt.php`). +2. **E-Mail-Adresse im Impressum.** Die alte Seite nennt nur das Kontaktformular — § 5 DDG verlangt + eine direkte E-Mail-Adresse. +3. **Datenschutz:** Hoster, AV-Vertrag, Logfile-Speicherdauer, Formularversand eintragen (rot markiert). + Beide Rechtstexte sind Entwürfe — juristisch prüfen lassen. +4. **Logo als Vektor.** Das HEB-Zeichen ist aus dem 200-px-Rasterlogo nachgezeichnet, ohne die kleinen + Schriftzüge „Haas“/„Essenbach“. Original anfragen. + +### Vom Kunden zu klären + +- **Fotos:** Die Header-Bilder der alten Seite haben nur 640×300 px und werden nicht verwendet. Der Hero + nutzt ein Normstahl-Modellbild. Bessere eigene Fotos (Tore im Einsatz, Team, Fahrzeug) würden stärker wirken. +- **Herstellerbilder** (Smart, Superior, Seitensektionaltore): Nutzungsrecht als Fachhändler bestätigen. +- **Partnerlogos** von Normstahl/Sommer: derzeit nur als Text, Logos nach Freigabe einbauen. +- **Sommer-Zertifikat** von der alten Seite einbinden? (Vertrauensleiste nennt „Sommer-Servicepartner“.) +- **Öffnungszeiten** gibt es auf der alten Seite nicht — würden Kontaktseite und Google-Eintrag stärken. +- **Aktion/Flyer** (Smart-Tor zum Aktionspreis, Stand 2022) ist veraltet und wurde nicht übernommen. diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 0000000..98a46d5 --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1 @@ + diff --git a/assets/fonts/OFL.txt b/assets/fonts/OFL.txt new file mode 100644 index 0000000..2f22ba6 --- /dev/null +++ b/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2017 The Barlow Project Authors (https://github.com/jpt/barlow) + +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. diff --git a/assets/fonts/barlow-condensed-latin-600.woff2 b/assets/fonts/barlow-condensed-latin-600.woff2 new file mode 100644 index 0000000..da60c9a Binary files /dev/null and b/assets/fonts/barlow-condensed-latin-600.woff2 differ diff --git a/assets/fonts/barlow-condensed-latin-700.woff2 b/assets/fonts/barlow-condensed-latin-700.woff2 new file mode 100644 index 0000000..3726e9d Binary files /dev/null and b/assets/fonts/barlow-condensed-latin-700.woff2 differ diff --git a/assets/fonts/barlow-latin-400.woff2 b/assets/fonts/barlow-latin-400.woff2 new file mode 100644 index 0000000..3914af4 Binary files /dev/null and b/assets/fonts/barlow-latin-400.woff2 differ diff --git a/assets/fonts/barlow-latin-500.woff2 b/assets/fonts/barlow-latin-500.woff2 new file mode 100644 index 0000000..b1d06c6 Binary files /dev/null and b/assets/fonts/barlow-latin-500.woff2 differ diff --git a/assets/fonts/barlow-latin-600.woff2 b/assets/fonts/barlow-latin-600.woff2 new file mode 100644 index 0000000..b1a019f Binary files /dev/null and b/assets/fonts/barlow-latin-600.woff2 differ diff --git a/assets/img/anthrazit-iso-1200.avif b/assets/img/anthrazit-iso-1200.avif new file mode 100644 index 0000000..507b51a Binary files /dev/null and b/assets/img/anthrazit-iso-1200.avif differ diff --git a/assets/img/anthrazit-iso-1200.webp b/assets/img/anthrazit-iso-1200.webp new file mode 100644 index 0000000..a5d4468 Binary files /dev/null and b/assets/img/anthrazit-iso-1200.webp differ diff --git a/assets/img/anthrazit-iso-800.avif b/assets/img/anthrazit-iso-800.avif new file mode 100644 index 0000000..c24e502 Binary files /dev/null and b/assets/img/anthrazit-iso-800.avif differ diff --git a/assets/img/anthrazit-iso-800.webp b/assets/img/anthrazit-iso-800.webp new file mode 100644 index 0000000..edd9582 Binary files /dev/null and b/assets/img/anthrazit-iso-800.webp differ diff --git a/assets/img/anthrazit-solo-1200.avif b/assets/img/anthrazit-solo-1200.avif new file mode 100644 index 0000000..c39d69f Binary files /dev/null and b/assets/img/anthrazit-solo-1200.avif differ diff --git a/assets/img/anthrazit-solo-1200.webp b/assets/img/anthrazit-solo-1200.webp new file mode 100644 index 0000000..b110946 Binary files /dev/null and b/assets/img/anthrazit-solo-1200.webp differ diff --git a/assets/img/anthrazit-solo-800.avif b/assets/img/anthrazit-solo-800.avif new file mode 100644 index 0000000..fe2c61f Binary files /dev/null and b/assets/img/anthrazit-solo-800.avif differ diff --git a/assets/img/anthrazit-solo-800.webp b/assets/img/anthrazit-solo-800.webp new file mode 100644 index 0000000..5668ae5 Binary files /dev/null and b/assets/img/anthrazit-solo-800.webp differ diff --git a/assets/img/condor-granit-1200.avif b/assets/img/condor-granit-1200.avif new file mode 100644 index 0000000..fe60633 Binary files /dev/null and b/assets/img/condor-granit-1200.avif differ diff --git a/assets/img/condor-granit-1200.webp b/assets/img/condor-granit-1200.webp new file mode 100644 index 0000000..aeb90b9 Binary files /dev/null and b/assets/img/condor-granit-1200.webp differ diff --git a/assets/img/condor-granit-800.avif b/assets/img/condor-granit-800.avif new file mode 100644 index 0000000..b3ffb68 Binary files /dev/null and b/assets/img/condor-granit-800.avif differ diff --git a/assets/img/condor-granit-800.webp b/assets/img/condor-granit-800.webp new file mode 100644 index 0000000..06af238 Binary files /dev/null and b/assets/img/condor-granit-800.webp differ diff --git a/assets/img/condor-ligna-1200.avif b/assets/img/condor-ligna-1200.avif new file mode 100644 index 0000000..61a3ff8 Binary files /dev/null and b/assets/img/condor-ligna-1200.avif differ diff --git a/assets/img/condor-ligna-1200.webp b/assets/img/condor-ligna-1200.webp new file mode 100644 index 0000000..536681e Binary files /dev/null and b/assets/img/condor-ligna-1200.webp differ diff --git a/assets/img/condor-ligna-800.avif b/assets/img/condor-ligna-800.avif new file mode 100644 index 0000000..1008124 Binary files /dev/null and b/assets/img/condor-ligna-800.avif differ diff --git a/assets/img/condor-ligna-800.webp b/assets/img/condor-ligna-800.webp new file mode 100644 index 0000000..5c8bd1f Binary files /dev/null and b/assets/img/condor-ligna-800.webp differ diff --git a/assets/img/condor-schlupftuer-1200.avif b/assets/img/condor-schlupftuer-1200.avif new file mode 100644 index 0000000..667dc70 Binary files /dev/null and b/assets/img/condor-schlupftuer-1200.avif differ diff --git a/assets/img/condor-schlupftuer-1200.webp b/assets/img/condor-schlupftuer-1200.webp new file mode 100644 index 0000000..42d1128 Binary files /dev/null and b/assets/img/condor-schlupftuer-1200.webp differ diff --git a/assets/img/condor-schlupftuer-800.avif b/assets/img/condor-schlupftuer-800.avif new file mode 100644 index 0000000..fb67f63 Binary files /dev/null and b/assets/img/condor-schlupftuer-800.avif differ diff --git a/assets/img/condor-schlupftuer-800.webp b/assets/img/condor-schlupftuer-800.webp new file mode 100644 index 0000000..f2b84bb Binary files /dev/null and b/assets/img/condor-schlupftuer-800.webp differ diff --git a/assets/img/condor-schraegschnitt-1200.avif b/assets/img/condor-schraegschnitt-1200.avif new file mode 100644 index 0000000..d5bb759 Binary files /dev/null and b/assets/img/condor-schraegschnitt-1200.avif differ diff --git a/assets/img/condor-schraegschnitt-1200.webp b/assets/img/condor-schraegschnitt-1200.webp new file mode 100644 index 0000000..0643951 Binary files /dev/null and b/assets/img/condor-schraegschnitt-1200.webp differ diff --git a/assets/img/condor-schraegschnitt-800.avif b/assets/img/condor-schraegschnitt-800.avif new file mode 100644 index 0000000..34e2244 Binary files /dev/null and b/assets/img/condor-schraegschnitt-800.avif differ diff --git a/assets/img/condor-schraegschnitt-800.webp b/assets/img/condor-schraegschnitt-800.webp new file mode 100644 index 0000000..cb7067c Binary files /dev/null and b/assets/img/condor-schraegschnitt-800.webp differ diff --git a/assets/img/doppelgarage-beige-1200.avif b/assets/img/doppelgarage-beige-1200.avif new file mode 100644 index 0000000..72eca82 Binary files /dev/null and b/assets/img/doppelgarage-beige-1200.avif differ diff --git a/assets/img/doppelgarage-beige-1200.webp b/assets/img/doppelgarage-beige-1200.webp new file mode 100644 index 0000000..f896d72 Binary files /dev/null and b/assets/img/doppelgarage-beige-1200.webp differ diff --git a/assets/img/doppelgarage-beige-800.avif b/assets/img/doppelgarage-beige-800.avif new file mode 100644 index 0000000..cb0b419 Binary files /dev/null and b/assets/img/doppelgarage-beige-800.avif differ diff --git a/assets/img/doppelgarage-beige-800.webp b/assets/img/doppelgarage-beige-800.webp new file mode 100644 index 0000000..cc90e17 Binary files /dev/null and b/assets/img/doppelgarage-beige-800.webp differ diff --git a/assets/img/doppelgarage-hell-1200.avif b/assets/img/doppelgarage-hell-1200.avif new file mode 100644 index 0000000..df50c50 Binary files /dev/null and b/assets/img/doppelgarage-hell-1200.avif differ diff --git a/assets/img/doppelgarage-hell-1200.webp b/assets/img/doppelgarage-hell-1200.webp new file mode 100644 index 0000000..be6e9f2 Binary files /dev/null and b/assets/img/doppelgarage-hell-1200.webp differ diff --git a/assets/img/doppelgarage-hell-800.avif b/assets/img/doppelgarage-hell-800.avif new file mode 100644 index 0000000..c8c76b9 Binary files /dev/null and b/assets/img/doppelgarage-hell-800.avif differ diff --git a/assets/img/doppelgarage-hell-800.webp b/assets/img/doppelgarage-hell-800.webp new file mode 100644 index 0000000..44966e9 Binary files /dev/null and b/assets/img/doppelgarage-hell-800.webp differ diff --git a/assets/img/elegant-800.avif b/assets/img/elegant-800.avif new file mode 100644 index 0000000..dc7661f Binary files /dev/null and b/assets/img/elegant-800.avif differ diff --git a/assets/img/elegant-800.webp b/assets/img/elegant-800.webp new file mode 100644 index 0000000..2273c65 Binary files /dev/null and b/assets/img/elegant-800.webp differ diff --git a/assets/img/essenbach-ohu-800.avif b/assets/img/essenbach-ohu-800.avif new file mode 100644 index 0000000..4b3ac4d Binary files /dev/null and b/assets/img/essenbach-ohu-800.avif differ diff --git a/assets/img/essenbach-ohu-800.webp b/assets/img/essenbach-ohu-800.webp new file mode 100644 index 0000000..a41904c Binary files /dev/null and b/assets/img/essenbach-ohu-800.webp differ diff --git a/assets/img/eurostyle-iso-800.avif b/assets/img/eurostyle-iso-800.avif new file mode 100644 index 0000000..2bbd55c Binary files /dev/null and b/assets/img/eurostyle-iso-800.avif differ diff --git a/assets/img/eurostyle-iso-800.webp b/assets/img/eurostyle-iso-800.webp new file mode 100644 index 0000000..cd3a3d0 Binary files /dev/null and b/assets/img/eurostyle-iso-800.webp differ diff --git a/assets/img/flair-1-1200.avif b/assets/img/flair-1-1200.avif new file mode 100644 index 0000000..dcb0c63 Binary files /dev/null and b/assets/img/flair-1-1200.avif differ diff --git a/assets/img/flair-1-1200.webp b/assets/img/flair-1-1200.webp new file mode 100644 index 0000000..24f4e99 Binary files /dev/null and b/assets/img/flair-1-1200.webp differ diff --git a/assets/img/flair-1-800.avif b/assets/img/flair-1-800.avif new file mode 100644 index 0000000..eb4fbd3 Binary files /dev/null and b/assets/img/flair-1-800.avif differ diff --git a/assets/img/flair-1-800.webp b/assets/img/flair-1-800.webp new file mode 100644 index 0000000..4109657 Binary files /dev/null and b/assets/img/flair-1-800.webp differ diff --git a/assets/img/flair-2-1200.avif b/assets/img/flair-2-1200.avif new file mode 100644 index 0000000..fadc44d Binary files /dev/null and b/assets/img/flair-2-1200.avif differ diff --git a/assets/img/flair-2-1200.webp b/assets/img/flair-2-1200.webp new file mode 100644 index 0000000..46d0db4 Binary files /dev/null and b/assets/img/flair-2-1200.webp differ diff --git a/assets/img/flair-2-800.avif b/assets/img/flair-2-800.avif new file mode 100644 index 0000000..478e8ad Binary files /dev/null and b/assets/img/flair-2-800.avif differ diff --git a/assets/img/flair-2-800.webp b/assets/img/flair-2-800.webp new file mode 100644 index 0000000..cfe98da Binary files /dev/null and b/assets/img/flair-2-800.webp differ diff --git a/assets/img/garagenhof-1200.avif b/assets/img/garagenhof-1200.avif new file mode 100644 index 0000000..7c3a022 Binary files /dev/null and b/assets/img/garagenhof-1200.avif differ diff --git a/assets/img/garagenhof-1200.webp b/assets/img/garagenhof-1200.webp new file mode 100644 index 0000000..fcbbbc5 Binary files /dev/null and b/assets/img/garagenhof-1200.webp differ diff --git a/assets/img/garagenhof-800.avif b/assets/img/garagenhof-800.avif new file mode 100644 index 0000000..2db3c4f Binary files /dev/null and b/assets/img/garagenhof-800.avif differ diff --git a/assets/img/garagenhof-800.webp b/assets/img/garagenhof-800.webp new file mode 100644 index 0000000..1b9988f Binary files /dev/null and b/assets/img/garagenhof-800.webp differ diff --git a/assets/img/haas-800.avif b/assets/img/haas-800.avif new file mode 100644 index 0000000..6b3eb24 Binary files /dev/null and b/assets/img/haas-800.avif differ diff --git a/assets/img/haas-800.webp b/assets/img/haas-800.webp new file mode 100644 index 0000000..bc51fca Binary files /dev/null and b/assets/img/haas-800.webp differ diff --git a/assets/img/hero-1200.avif b/assets/img/hero-1200.avif new file mode 100644 index 0000000..df8b855 Binary files /dev/null and b/assets/img/hero-1200.avif differ diff --git a/assets/img/hero-1200.webp b/assets/img/hero-1200.webp new file mode 100644 index 0000000..fc33a38 Binary files /dev/null and b/assets/img/hero-1200.webp differ diff --git a/assets/img/hero-800.avif b/assets/img/hero-800.avif new file mode 100644 index 0000000..c978492 Binary files /dev/null and b/assets/img/hero-800.avif differ diff --git a/assets/img/hero-800.webp b/assets/img/hero-800.webp new file mode 100644 index 0000000..7997474 Binary files /dev/null and b/assets/img/hero-800.webp differ diff --git a/assets/img/mueller-muenster-1200.avif b/assets/img/mueller-muenster-1200.avif new file mode 100644 index 0000000..39f673a Binary files /dev/null and b/assets/img/mueller-muenster-1200.avif differ diff --git a/assets/img/mueller-muenster-1200.webp b/assets/img/mueller-muenster-1200.webp new file mode 100644 index 0000000..0924b61 Binary files /dev/null and b/assets/img/mueller-muenster-1200.webp differ diff --git a/assets/img/mueller-muenster-800.avif b/assets/img/mueller-muenster-800.avif new file mode 100644 index 0000000..ce844cd Binary files /dev/null and b/assets/img/mueller-muenster-800.avif differ diff --git a/assets/img/mueller-muenster-800.webp b/assets/img/mueller-muenster-800.webp new file mode 100644 index 0000000..3316906 Binary files /dev/null and b/assets/img/mueller-muenster-800.webp differ diff --git a/assets/img/og.jpg b/assets/img/og.jpg new file mode 100644 index 0000000..2545035 Binary files /dev/null and b/assets/img/og.jpg differ diff --git a/assets/img/prominent-rottenburg-800.avif b/assets/img/prominent-rottenburg-800.avif new file mode 100644 index 0000000..95c8ed2 Binary files /dev/null and b/assets/img/prominent-rottenburg-800.avif differ diff --git a/assets/img/prominent-rottenburg-800.webp b/assets/img/prominent-rottenburg-800.webp new file mode 100644 index 0000000..d531516 Binary files /dev/null and b/assets/img/prominent-rottenburg-800.webp differ diff --git a/assets/img/rustiko-800.avif b/assets/img/rustiko-800.avif new file mode 100644 index 0000000..ef71556 Binary files /dev/null and b/assets/img/rustiko-800.avif differ diff --git a/assets/img/rustiko-800.webp b/assets/img/rustiko-800.webp new file mode 100644 index 0000000..9b74254 Binary files /dev/null and b/assets/img/rustiko-800.webp differ diff --git a/assets/img/smart-style-1200.avif b/assets/img/smart-style-1200.avif new file mode 100644 index 0000000..4b5bf55 Binary files /dev/null and b/assets/img/smart-style-1200.avif differ diff --git a/assets/img/smart-style-1200.webp b/assets/img/smart-style-1200.webp new file mode 100644 index 0000000..6b25273 Binary files /dev/null and b/assets/img/smart-style-1200.webp differ diff --git a/assets/img/smart-style-800.avif b/assets/img/smart-style-800.avif new file mode 100644 index 0000000..5b31288 Binary files /dev/null and b/assets/img/smart-style-800.avif differ diff --git a/assets/img/smart-style-800.webp b/assets/img/smart-style-800.webp new file mode 100644 index 0000000..9989538 Binary files /dev/null and b/assets/img/smart-style-800.webp differ diff --git a/assets/img/smart-trend-1200.avif b/assets/img/smart-trend-1200.avif new file mode 100644 index 0000000..7f1510a Binary files /dev/null and b/assets/img/smart-trend-1200.avif differ diff --git a/assets/img/smart-trend-1200.webp b/assets/img/smart-trend-1200.webp new file mode 100644 index 0000000..c6e8f77 Binary files /dev/null and b/assets/img/smart-trend-1200.webp differ diff --git a/assets/img/smart-trend-800.avif b/assets/img/smart-trend-800.avif new file mode 100644 index 0000000..b0bb69d Binary files /dev/null and b/assets/img/smart-trend-800.avif differ diff --git a/assets/img/smart-trend-800.webp b/assets/img/smart-trend-800.webp new file mode 100644 index 0000000..2634131 Binary files /dev/null and b/assets/img/smart-trend-800.webp differ diff --git a/assets/img/sst-design-800.avif b/assets/img/sst-design-800.avif new file mode 100644 index 0000000..8350870 Binary files /dev/null and b/assets/img/sst-design-800.avif differ diff --git a/assets/img/sst-design-800.webp b/assets/img/sst-design-800.webp new file mode 100644 index 0000000..8c62cf4 Binary files /dev/null and b/assets/img/sst-design-800.webp differ diff --git a/assets/img/sst-style-1200.avif b/assets/img/sst-style-1200.avif new file mode 100644 index 0000000..8a67c11 Binary files /dev/null and b/assets/img/sst-style-1200.avif differ diff --git a/assets/img/sst-style-1200.webp b/assets/img/sst-style-1200.webp new file mode 100644 index 0000000..e92afcc Binary files /dev/null and b/assets/img/sst-style-1200.webp differ diff --git a/assets/img/sst-style-800.avif b/assets/img/sst-style-800.avif new file mode 100644 index 0000000..0fbfa29 Binary files /dev/null and b/assets/img/sst-style-800.avif differ diff --git a/assets/img/sst-style-800.webp b/assets/img/sst-style-800.webp new file mode 100644 index 0000000..cdcf71f Binary files /dev/null and b/assets/img/sst-style-800.webp differ diff --git a/assets/img/sst-trend-1200.avif b/assets/img/sst-trend-1200.avif new file mode 100644 index 0000000..de69b8f Binary files /dev/null and b/assets/img/sst-trend-1200.avif differ diff --git a/assets/img/sst-trend-1200.webp b/assets/img/sst-trend-1200.webp new file mode 100644 index 0000000..53cea28 Binary files /dev/null and b/assets/img/sst-trend-1200.webp differ diff --git a/assets/img/sst-trend-800.avif b/assets/img/sst-trend-800.avif new file mode 100644 index 0000000..0ba7ff5 Binary files /dev/null and b/assets/img/sst-trend-800.avif differ diff --git a/assets/img/sst-trend-800.webp b/assets/img/sst-trend-800.webp new file mode 100644 index 0000000..b8458bb Binary files /dev/null and b/assets/img/sst-trend-800.webp differ diff --git a/assets/img/superior-eiche-1200.avif b/assets/img/superior-eiche-1200.avif new file mode 100644 index 0000000..01f335b Binary files /dev/null and b/assets/img/superior-eiche-1200.avif differ diff --git a/assets/img/superior-eiche-1200.webp b/assets/img/superior-eiche-1200.webp new file mode 100644 index 0000000..b78f5d7 Binary files /dev/null and b/assets/img/superior-eiche-1200.webp differ diff --git a/assets/img/superior-eiche-800.avif b/assets/img/superior-eiche-800.avif new file mode 100644 index 0000000..fb23b41 Binary files /dev/null and b/assets/img/superior-eiche-800.avif differ diff --git a/assets/img/superior-eiche-800.webp b/assets/img/superior-eiche-800.webp new file mode 100644 index 0000000..d592fe2 Binary files /dev/null and b/assets/img/superior-eiche-800.webp differ diff --git a/assets/img/superior-graualu-1200.avif b/assets/img/superior-graualu-1200.avif new file mode 100644 index 0000000..df8b855 Binary files /dev/null and b/assets/img/superior-graualu-1200.avif differ diff --git a/assets/img/superior-graualu-1200.webp b/assets/img/superior-graualu-1200.webp new file mode 100644 index 0000000..fc33a38 Binary files /dev/null and b/assets/img/superior-graualu-1200.webp differ diff --git a/assets/img/superior-graualu-800.avif b/assets/img/superior-graualu-800.avif new file mode 100644 index 0000000..c978492 Binary files /dev/null and b/assets/img/superior-graualu-800.avif differ diff --git a/assets/img/superior-graualu-800.webp b/assets/img/superior-graualu-800.webp new file mode 100644 index 0000000..7997474 Binary files /dev/null and b/assets/img/superior-graualu-800.webp differ diff --git a/assets/img/superior-linea-1200.avif b/assets/img/superior-linea-1200.avif new file mode 100644 index 0000000..eb1ad6c Binary files /dev/null and b/assets/img/superior-linea-1200.avif differ diff --git a/assets/img/superior-linea-1200.webp b/assets/img/superior-linea-1200.webp new file mode 100644 index 0000000..6f85271 Binary files /dev/null and b/assets/img/superior-linea-1200.webp differ diff --git a/assets/img/superior-linea-800.avif b/assets/img/superior-linea-800.avif new file mode 100644 index 0000000..2be7599 Binary files /dev/null and b/assets/img/superior-linea-800.avif differ diff --git a/assets/img/superior-linea-800.webp b/assets/img/superior-linea-800.webp new file mode 100644 index 0000000..a12b6a7 Binary files /dev/null and b/assets/img/superior-linea-800.webp differ diff --git a/assets/img/topline-iso-800.avif b/assets/img/topline-iso-800.avif new file mode 100644 index 0000000..9637054 Binary files /dev/null and b/assets/img/topline-iso-800.avif differ diff --git a/assets/img/topline-iso-800.webp b/assets/img/topline-iso-800.webp new file mode 100644 index 0000000..3386813 Binary files /dev/null and b/assets/img/topline-iso-800.webp differ diff --git a/assets/img/variant-800.avif b/assets/img/variant-800.avif new file mode 100644 index 0000000..d456c72 Binary files /dev/null and b/assets/img/variant-800.avif differ diff --git a/assets/img/variant-800.webp b/assets/img/variant-800.webp new file mode 100644 index 0000000..8639e7c Binary files /dev/null and b/assets/img/variant-800.webp differ diff --git a/assets/site.css b/assets/site.css new file mode 100644 index 0000000..f8e20fb --- /dev/null +++ b/assets/site.css @@ -0,0 +1,431 @@ +/* HEB Bauelemente — Tokens und Komponenten. Entscheidungen: siehe DESIGN.md */ + +@font-face { font-family: "Barlow"; font-weight: 400; font-display: swap; src: url("fonts/barlow-latin-400.woff2") format("woff2"); } +@font-face { font-family: "Barlow"; font-weight: 500; font-display: swap; src: url("fonts/barlow-latin-500.woff2") format("woff2"); } +@font-face { font-family: "Barlow"; font-weight: 600; font-display: swap; src: url("fonts/barlow-latin-600.woff2") format("woff2"); } +@font-face { font-family: "Barlow Condensed"; font-weight: 600; font-display: swap; src: url("fonts/barlow-condensed-latin-600.woff2") format("woff2"); } +@font-face { font-family: "Barlow Condensed"; font-weight: 700; font-display: swap; src: url("fonts/barlow-condensed-latin-700.woff2") format("woff2"); } + +:root { + --blue: #00569d; /* exakt aus dem HEB-Logo */ + --blue-d: #00437a; + --blue-l: #e7eff7; + --sky: #7cbcf0; /* Blau auf dunklem Grund */ + --amber: #f2ad33; /* nur für die 24-h-Uhr */ + --ink: #14202c; + --text: #2d3843; + --muted: #5d6a77; + --paper: #f4f2ed; + --white: #fff; + --steel: #1a232c; + --steel-2: #253039; + --line: #dcd8cf; + --line-dark: rgba(255, 255, 255, .14); + + --f-body: "Barlow", system-ui, -apple-system, "Segoe UI", sans-serif; + --f-head: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif; + + --gutter: clamp(16px, 4vw, 40px); + --max: 1200px; + --r: 4px; + --header-h: 72px; + --ease: cubic-bezier(.2, .7, .2, 1); +} + +*, *::before, *::after { box-sizing: border-box; } +html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 16px); } +body { margin: 0; background: var(--white); color: var(--text); font: 400 1.0625rem/1.6 var(--f-body); -webkit-font-smoothing: antialiased; } +img { display: block; max-width: 100%; height: auto; } +picture { display: block; } +a { color: inherit; } +h1, h2, h3 { margin: 0; color: var(--ink); font-family: var(--f-head); font-weight: 700; line-height: 1.02; letter-spacing: -.005em; } +p { margin: 0 0 1em; } +p:last-child { margin-bottom: 0; } +address { font-style: normal; } +button { font: inherit; color: inherit; } +:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; } +.section--dark :focus-visible, .cta-band :focus-visible, .site-footer :focus-visible, .hero :focus-visible { outline-color: var(--sky); } + +.wrap { width: 100%; max-width: calc(var(--max) + 2 * var(--gutter)); margin-inline: auto; padding-inline: var(--gutter); } +.skip-link { position: absolute; left: 16px; top: -60px; z-index: 100; padding: 10px 16px; background: var(--blue); color: #fff; text-decoration: none; border-radius: var(--r); } +.skip-link:focus { top: 12px; } +.i { width: 1.15em; height: 1.15em; flex: none; } + +/* ---------- Typo-Bausteine ---------- */ +.eyebrow { display: inline-flex; align-items: center; gap: 10px; margin: 0 0 14px; color: var(--blue); font: 600 .8125rem/1 var(--f-body); letter-spacing: .14em; text-transform: uppercase; } +.eyebrow::before { content: ""; width: 22px; height: 3px; background: currentColor; } +.eyebrow--light { color: var(--sky); } +.h2 { font-size: clamp(2.25rem, 5vw, 3.75rem); } +.h3 { font-size: clamp(1.75rem, 3vw, 2.25rem); margin-bottom: .5em; } +.lead { max-width: 60ch; font-size: clamp(1.0625rem, 1.6vw, 1.25rem); color: var(--muted); } +.section--dark .lead { color: rgba(255, 255, 255, .72); } +.link { color: var(--blue); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; } +.link:hover { text-decoration-thickness: 2px; } +.link--icon { display: inline-flex; align-items: center; gap: 6px; } + +/* ---------- Buttons ---------- */ +.btn { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 52px; padding: 0 24px; border: 2px solid transparent; border-radius: var(--r); font: 600 1.0625rem/1 var(--f-body); text-decoration: none; cursor: pointer; transition: background .2s, color .2s, border-color .2s, transform .2s var(--ease); } +.btn .i:last-child:not(:first-child) { transition: transform .25s var(--ease); } +.btn:hover .i:last-child:not(:first-child) { transform: translateX(4px); } +.btn--blue { background: var(--blue); color: #fff; } +.btn--blue:hover { background: var(--blue-d); } +.btn--outline { border-color: var(--ink); color: var(--ink); } +.btn--outline:hover { background: var(--ink); color: #fff; } +.btn--ghost-light { border-color: rgba(255, 255, 255, .55); color: #fff; } +.btn--ghost-light:hover { background: #fff; color: var(--ink); border-color: #fff; } +.btn--sm { min-height: 44px; padding: 0 16px; font-size: 1rem; } +.btn--block { width: 100%; } + +/* ---------- Header ---------- */ +.site-header { position: sticky; top: 0; z-index: 50; background: rgba(255, 255, 255, .94); backdrop-filter: saturate(1.4) blur(10px); -webkit-backdrop-filter: saturate(1.4) blur(10px); border-bottom: 1px solid transparent; transition: border-color .3s, box-shadow .3s; } +.site-header.is-scrolled { border-color: var(--line); box-shadow: 0 6px 24px -18px rgba(20, 32, 44, .5); } +.site-header__inner { display: flex; align-items: center; gap: 24px; height: var(--header-h); } +.brand { display: inline-flex; align-items: center; gap: 12px; color: var(--blue); text-decoration: none; } +.heb { width: 64px; height: auto; flex: none; } +.brand__text { display: flex; flex-direction: column; line-height: 1.1; } +.brand__text b { font: 700 1.25rem/1 var(--f-head); letter-spacing: .02em; text-transform: uppercase; } +.brand__text span { margin-top: 3px; color: var(--muted); font-size: .8125rem; } +.nav { display: flex; gap: 4px; margin-left: auto; } +.nav__link { position: relative; padding: 10px 12px; color: var(--ink); font-weight: 500; font-size: .98rem; text-decoration: none; white-space: nowrap; } +.nav__link::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: 4px; height: 2px; background: var(--blue); transform: scaleX(0); transform-origin: left; transition: transform .25s var(--ease); } +.nav__link:hover::after, .nav__link[aria-current="page"]::after { transform: scaleX(1); } +.header-call { flex: none; } +.menu-btn { display: none; width: 48px; height: 48px; margin-left: auto; padding: 0; border: 0; background: none; cursor: pointer; position: relative; } +.menu-btn span { position: absolute; left: 12px; right: 12px; height: 2px; background: var(--ink); transition: transform .3s var(--ease); } +.menu-btn span:first-child { top: 19px; } +.menu-btn span:last-child { top: 27px; } +.menu-btn[aria-expanded="true"] span:first-child { transform: translateY(4px) rotate(45deg); } +.menu-btn[aria-expanded="true"] span:last-child { transform: translateY(-4px) rotate(-45deg); } +.mobile-nav { border-top: 1px solid var(--line); background: #fff; padding: 8px var(--gutter) 24px; } +.mobile-nav ul { list-style: none; margin: 0 0 16px; padding: 0; } +.mobile-nav li a { display: block; padding: 14px 0; border-bottom: 1px solid var(--line); color: var(--ink); font: 600 1.5rem/1.1 var(--f-head); text-decoration: none; } +.mobile-nav .btn { width: 100%; } + +@media (max-width: 1100px) { + .nav { display: none; } + .menu-btn { display: block; } + .header-call { margin-left: auto; } + .header-call + .menu-btn { margin-left: 0; } +} +@media (max-width: 560px) { + .header-call { display: none; } + .header-call + .menu-btn { margin-left: auto; margin-right: -8px; } + .brand__text span { display: none; } + .heb { width: 54px; } +} + +/* ---------- Hero ---------- */ +.hero { position: relative; display: flex; align-items: flex-end; min-height: min(calc(100svh - var(--header-h)), 820px); padding: 120px 0 clamp(48px, 8vw, 96px); overflow: hidden; background: var(--steel); color: #fff; isolation: isolate; } +.hero__img { position: absolute; inset: 0; z-index: -2; } +.hero__img img { width: 100%; height: 100%; object-fit: cover; object-position: 60% 60%; } +.hero::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient(90deg, rgba(14, 22, 30, .82) 0%, rgba(14, 22, 30, .55) 45%, rgba(14, 22, 30, .05) 80%), linear-gradient(0deg, rgba(14, 22, 30, .6) 0%, rgba(14, 22, 30, 0) 45%); } +.hero__title { max-width: 11em; color: #fff; font-size: clamp(3rem, 8.5vw, 6.75rem); line-height: .94; } +.hero__title span { display: block; } +.hero__title span + span { color: var(--sky); } +.hero__lead { max-width: 34em; margin: 24px 0 32px; color: rgba(255, 255, 255, .86); font-size: clamp(1.0625rem, 1.8vw, 1.3125rem); } +.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; } +@media (max-width: 640px) { .hero__img img { object-position: 82% 60%; } } + +/* Signatur: Ein Sektionaltor fährt beim ersten Besuch hoch und gibt das Bild frei */ +.gate { display: none; } +.has-intro .gate { position: absolute; inset: 0; z-index: 5; display: flex; flex-direction: column; transition: transform 1.7s cubic-bezier(.62, .02, .28, 1); will-change: transform; } +.gate__panel { flex: 1; position: relative; background: linear-gradient(180deg, #5b6670 0%, #4a545e 48%, #424b54 100%); border-bottom: 2px solid #2b3238; box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16); } +.gate__panel::after { content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: rgba(0, 0, 0, .18); box-shadow: 0 1px 0 rgba(255, 255, 255, .08); } +.gate__plate { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 18px 26px 14px; background: #fff; color: var(--blue); border-radius: 3px; box-shadow: 0 10px 30px -10px rgba(0, 0, 0, .5); } +.gate__plate .heb { width: 110px; } +.gate__plate span { color: var(--muted); font: 600 .75rem/1 var(--f-body); letter-spacing: .2em; text-transform: uppercase; } +.gate__light { position: absolute; left: 0; right: 0; top: 100%; height: 90px; background: linear-gradient(180deg, rgba(255, 244, 214, .5), rgba(255, 244, 214, 0)); opacity: 0; transition: opacity .4s; } +.gate.is-moving .gate__light { opacity: 1; } +.gate.is-open { transform: translateY(-101%); } +.has-intro .hero__img img { transform: scale(1.08); transition: transform 2.4s cubic-bezier(.3, .6, .2, 1); } +.has-intro.intro-go .hero__img img { transform: none; } +.has-intro .hero__inner > * { opacity: 0; transform: translateY(28px); transition: opacity .8s var(--ease), transform .8s var(--ease); } +.has-intro.intro-go .hero__inner > * { opacity: 1; transform: none; } +.has-intro.intro-go .hero__inner > :nth-child(1) { transition-delay: .9s; } +.has-intro.intro-go .hero__inner > :nth-child(2) { transition-delay: 1.05s; } +.has-intro.intro-go .hero__inner > :nth-child(3) { transition-delay: 1.2s; } +.has-intro.intro-go .hero__inner > :nth-child(4) { transition-delay: 1.35s; } + +/* ---------- Vertrauensleiste ---------- */ +.trust { background: var(--blue); color: #fff; } +.trust__list { display: grid; grid-template-columns: repeat(4, 1fr); margin: 0 auto; padding-block: 0; list-style: none; } +.trust li { display: flex; flex-direction: column; gap: 2px; padding: 22px 20px; border-left: 1px solid rgba(255, 255, 255, .22); } +.trust li:first-child { border-left: 0; padding-left: 0; } +.trust b { font: 700 1.375rem/1.1 var(--f-head); } +.trust span { color: rgba(255, 255, 255, .78); font-size: .9375rem; line-height: 1.35; } +@media (max-width: 900px) { + .trust__list { grid-template-columns: 1fr 1fr; } + .trust li { padding: 18px 16px; border-left: 0; } + .trust li:nth-child(odd) { padding-left: 0; } + .trust li:nth-child(even) { border-left: 1px solid rgba(255, 255, 255, .22); } + .trust li:nth-child(n + 3) { border-top: 1px solid rgba(255, 255, 255, .22); } +} + +/* ---------- Sektionen ---------- */ +.section { padding-block: clamp(64px, 10vw, 128px); } +.section--tight { padding-block: clamp(40px, 6vw, 72px) clamp(64px, 9vw, 112px); } +.section--paper { background: var(--paper); } +.section--dark { background: var(--steel); color: rgba(255, 255, 255, .86); } +.section--dark h2, .section--dark h3 { color: #fff; } +.section__head { max-width: 760px; margin-bottom: clamp(32px, 5vw, 56px); } +.section__head .h2 { margin-bottom: 16px; } +.section__head--row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 24px; max-width: none; } +.section__head--row .h2 { margin-bottom: 0; } + +/* ---------- Leistungen ---------- */ +.cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; } +.card { display: flex; flex-direction: column; background: #fff; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; } +.card__img { aspect-ratio: 4 / 3; overflow: hidden; background: var(--paper); } +.card__img picture { height: 100%; } +.card__img img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease); } +.card:hover .card__img img { transform: scale(1.04); } +.card__body { display: flex; flex-direction: column; flex: 1; padding: 22px 22px 24px; } +.card__num { color: var(--blue); font: 700 .875rem/1 var(--f-head); letter-spacing: .1em; } +.card__title { margin: 8px 0 10px; font-size: 1.75rem; } +.card p { color: var(--muted); font-size: 1rem; } +.tags { display: flex; flex-wrap: wrap; gap: 6px; margin: auto 0 0; padding: 14px 0 0; list-style: none; } +.tags li { padding: 4px 10px; background: var(--blue-l); color: var(--blue-d); border-radius: 99px; font-size: .8125rem; font-weight: 500; } +.card--feature { position: relative; padding: 22px 22px 24px; background: var(--blue); border-color: var(--blue); color: rgba(255, 255, 255, .88); text-decoration: none; transition: background .25s; } +.card--feature:hover { background: var(--blue-d); } +.card--feature .card__num { color: var(--sky); } +.card--feature .card__title { color: #fff; } +.card--feature p { color: rgba(255, 255, 255, .82); } +.card__big { display: block; margin: auto 0 4px; padding-top: 32px; color: #fff; font: 700 clamp(5rem, 9vw, 7.5rem)/.85 var(--f-head); letter-spacing: -.02em; } +.card__more { display: inline-flex; align-items: center; gap: 8px; margin-top: 8px; color: #fff; font-weight: 600; } +.card--feature:hover .card__more .i { transform: translateX(4px); } +.card__more .i { transition: transform .25s var(--ease); } +@media (max-width: 1100px) { .cards { grid-template-columns: 1fr 1fr; } } +@media (max-width: 640px) { .cards { grid-template-columns: 1fr; } .card__big { padding-top: 8px; } } + +/* ---------- 24-h-Renovierung ---------- */ +.renovation { background-image: repeating-linear-gradient(180deg, transparent 0 119px, rgba(255, 255, 255, .03) 119px 120px); } +.renovation__grid { display: grid; grid-template-columns: 5fr 6fr; gap: clamp(32px, 6vw, 96px); align-items: start; } +.renovation__sticky { position: sticky; top: calc(var(--header-h) + 48px); } +.renovation__sticky .h2 { margin-bottom: 20px; } +.clock { position: relative; width: 168px; height: 168px; margin-top: 40px; } +.clock svg { width: 100%; height: 100%; transform: rotate(-90deg); } +.clock circle { fill: none; stroke-width: 7; } +.clock__track { stroke: var(--line-dark); } +.clock__bar { stroke: var(--amber); stroke-dasharray: 100 200; stroke-dashoffset: 100; stroke-linecap: butt; transition: stroke-dashoffset .8s var(--ease); } +.clock__label { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; } +.clock__label b { color: #fff; font: 700 3.25rem/1 var(--f-head); font-variant-numeric: tabular-nums; } +.clock__label span { color: rgba(255, 255, 255, .6); font-size: .875rem; } +.steps { margin: 0; padding: 0; list-style: none; } +.step { display: grid; grid-template-columns: 64px 1fr; gap: 20px; padding: 36px 0; border-top: 1px solid var(--line-dark); transition: opacity .5s var(--ease); } +.ready .step:not(.is-active):not(.is-done):not(.step--photo) { opacity: .4; } +.step__num { display: grid; place-items: center; width: 64px; height: 64px; border: 2px solid var(--line-dark); border-radius: 50%; color: #fff; font: 700 1.75rem/1 var(--f-head); transition: background .4s, border-color .4s, color .4s; } +.step.is-active .step__num, .step.is-done .step__num { background: var(--amber); border-color: var(--amber); color: var(--steel); } +.step__title { margin: 6px 0 8px; font-size: clamp(1.75rem, 3vw, 2.25rem); } +.step p { color: rgba(255, 255, 255, .72); font-size: 1.0625rem; } +.step--photo { display: block; opacity: 1; padding-bottom: 0; } +.renovation__photo { margin: 0 0 24px; } +.renovation__photo img { border-radius: var(--r); } +.renovation__photo figcaption { margin-top: 10px; color: rgba(255, 255, 255, .6); font-size: .9375rem; } +@media (max-width: 900px) { + .renovation__grid { grid-template-columns: 1fr; } + .renovation__sticky { position: static; } + .clock { display: none; } + .step { grid-template-columns: 52px 1fr; } + .step__num { width: 52px; height: 52px; font-size: 1.5rem; } +} + +/* ---------- Projekt-Streifen ---------- */ +.strip { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: max(var(--gutter), calc((100vw - var(--max)) / 2)); padding: 0 max(var(--gutter), calc((100vw - var(--max)) / 2)) 16px; scrollbar-width: thin; scrollbar-color: var(--line) transparent; } +.strip__item { flex: none; width: clamp(260px, 34vw, 440px); scroll-snap-align: start; color: var(--ink); text-decoration: none; } +.strip__item picture { aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--r); background: var(--paper); } +.strip__item img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease); } +.strip__item:hover img { transform: scale(1.04); } +.strip__cap { display: flex; flex-direction: column; padding-top: 12px; color: var(--muted); font-size: .9375rem; } +.strip__cap b { color: var(--ink); font: 700 1.375rem/1.1 var(--f-head); } + +/* ---------- Über uns ---------- */ +.about { display: grid; grid-template-columns: 5fr 7fr; gap: clamp(32px, 6vw, 96px); align-items: center; } +.about__photo { position: relative; margin: 0; } +.about__photo picture { aspect-ratio: 4 / 5; overflow: hidden; border-radius: var(--r); } +.about__photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; } +.about__photo figcaption { position: absolute; left: 0; bottom: 24px; display: flex; flex-direction: column; padding: 14px 20px; background: var(--blue); color: rgba(255, 255, 255, .8); font-size: .875rem; } +.about__photo figcaption b { color: #fff; font: 700 1.375rem/1.1 var(--f-head); } +.about__text .h2 { margin-bottom: 28px; } +.quote { margin: 0 0 28px; padding: 0 0 0 24px; border-left: 4px solid var(--blue); } +.quote p { color: var(--ink); font: 600 clamp(1.375rem, 2.4vw, 1.75rem)/1.25 var(--f-head); } +.signature { margin-top: 28px; color: var(--blue); font: 600 1.5rem/1 var(--f-head); } +@media (max-width: 860px) { + .about { grid-template-columns: 1fr; } + .about__photo { max-width: 440px; } +} + +/* ---------- Vergleich ---------- */ +.compare { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } +.compare__col { padding: clamp(24px, 4vw, 40px); border: 1px solid var(--line); border-radius: var(--r); } +.compare__col--us { border: 2px solid var(--blue); background: var(--blue-l); } +.compare__h { margin-bottom: 20px; font-size: 1.75rem; } +.compare ul { margin: 0; padding: 0; list-style: none; } +.compare li { display: flex; gap: 12px; padding: 12px 0; border-top: 1px solid var(--line); } +.compare li .i { margin-top: 3px; } +.compare__col:not(.compare__col--us) .i { color: #b4543c; } +.compare__col--us li { border-color: rgba(0, 86, 157, .18); color: var(--ink); } +.compare__col--us .i { color: var(--blue); } +.compare__quote { max-width: 30em; margin: clamp(32px, 5vw, 56px) auto 0; color: var(--ink); text-align: center; font: 600 clamp(1.5rem, 3vw, 2.125rem)/1.2 var(--f-head); } +.compare__quote::before, .compare__quote::after { color: var(--blue); } +.compare__quote::before { content: "„"; } +.compare__quote::after { content: "“"; } +@media (max-width: 760px) { .compare { grid-template-columns: 1fr; } } + +/* ---------- Partner ---------- */ +.partners { padding-block: clamp(56px, 8vw, 96px); } +.partners__grid { display: grid; grid-template-columns: 6fr 5fr; gap: clamp(32px, 6vw, 96px); align-items: center; } +.partners__grid p { color: var(--muted); } +.partners__list { margin: 0; padding: 0; list-style: none; } +.partners__list li { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 18px 0; border-top: 1px solid var(--line); } +.partners__list li:last-child { border-bottom: 1px solid var(--line); } +.partners__list b { color: var(--ink); font: 700 clamp(1.75rem, 3vw, 2.375rem)/1 var(--f-head); } +.partners__list span { color: var(--muted); font-size: .9375rem; text-align: right; } +@media (max-width: 860px) { .partners__grid { grid-template-columns: 1fr; } } + +/* ---------- CTA-Band und Footer ---------- */ +.cta-band { background: var(--blue); color: rgba(255, 255, 255, .86); padding-block: clamp(56px, 8vw, 96px); background-image: repeating-linear-gradient(180deg, transparent 0 79px, rgba(255, 255, 255, .06) 79px 80px); } +.cta-band__inner { display: grid; grid-template-columns: 1fr auto; gap: 32px 64px; align-items: end; } +.cta-band__title { color: #fff; font-size: clamp(2.5rem, 6vw, 4.5rem); margin-bottom: 16px; } +.cta-band__text { max-width: 36em; } +.cta-band__actions { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; } +.cta-band__phone { display: inline-flex; align-items: center; gap: 14px; color: #fff; font: 700 clamp(2.25rem, 5vw, 3.5rem)/1 var(--f-head); text-decoration: none; white-space: nowrap; } +.cta-band__phone .i { width: .8em; height: .8em; } +.cta-band__phone:hover span { text-decoration: underline; text-decoration-thickness: 3px; text-underline-offset: 6px; } +@media (max-width: 860px) { .cta-band__inner { grid-template-columns: 1fr; } } + +.site-footer { background: var(--steel); color: rgba(255, 255, 255, .72); font-size: .9375rem; } +.site-footer .brand { color: #fff; } +.site-footer .brand__text span { color: rgba(255, 255, 255, .6); } +.site-footer .link { color: rgba(255, 255, 255, .86); } +.site-footer .link:hover { color: #fff; } +.site-footer__grid { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 40px; padding-block: 64px 48px; } +.site-footer .site-footer__brand p { max-width: 26em; margin-top: 20px; } +.site-footer__h { margin-bottom: 14px; color: #fff; font: 600 .8125rem/1 var(--f-body); letter-spacing: .14em; text-transform: uppercase; } +.site-footer address { margin-bottom: 10px; } +.site-footer p { margin: 0 0 6px; } +.site-footer__nav { margin: 0; padding: 0; list-style: none; } +.site-footer__nav li { margin-bottom: 6px; } +.site-footer__bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px 24px; padding-block: 20px 28px; background: linear-gradient(var(--line-dark), var(--line-dark)) top center / calc(100% - 2 * var(--gutter)) 1px no-repeat; font-size: .875rem; } +.site-footer__credit { color: rgba(255, 255, 255, .5); } +@media (max-width: 900px) { .site-footer__grid { grid-template-columns: 1fr 1fr; } .site-footer__brand { grid-column: 1 / -1; } } +@media (max-width: 520px) { .site-footer__grid { grid-template-columns: 1fr; } } + +/* Mobile Anrufleiste */ +.callbar { display: none; } +@media (max-width: 760px) { + body { padding-bottom: 64px; } + .callbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: grid; grid-template-columns: 1fr 1fr; padding-bottom: env(safe-area-inset-bottom); background: var(--steel); box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, .4); } + .callbar a { display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 64px; color: #fff; font-weight: 600; text-decoration: none; } + .callbar a:first-child { background: var(--blue); } +} + +/* ---------- Unterseiten ---------- */ +.page-hero { padding-block: clamp(48px, 8vw, 96px) clamp(32px, 5vw, 56px); background: var(--paper); background-image: repeating-linear-gradient(180deg, transparent 0 59px, rgba(20, 32, 44, .05) 59px 60px); border-bottom: 1px solid var(--line); } +.page-hero__title { font-size: clamp(3rem, 8vw, 5.5rem); margin-bottom: 16px; } +.page-hero__lead { max-width: 40em; color: var(--muted); font-size: clamp(1.0625rem, 1.8vw, 1.25rem); } + +/* Galerie */ +.filters { display: flex; flex-wrap: wrap; gap: 8px; } +.chip { min-height: 44px; padding: 0 18px; border: 1px solid var(--line); border-radius: 99px; background: #fff; color: var(--ink); font-weight: 500; cursor: pointer; transition: background .2s, border-color .2s, color .2s; } +.chip:hover { border-color: var(--ink); } +.chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; } +.filters__count { margin: 16px 0 24px; color: var(--muted); font-size: .9375rem; } +.gallery { display: grid; grid-template-columns: repeat(4, 1fr); gap: 28px 20px; margin: 0; padding: 0; list-style: none; } +.gallery__item[hidden] { display: none; } +.gallery__btn { display: block; width: 100%; padding: 0; border: 0; background: none; text-align: left; cursor: zoom-in; } +.gallery__btn picture { aspect-ratio: 4 / 3; overflow: hidden; border-radius: var(--r); background: var(--paper); } +.gallery__btn img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s var(--ease); } +.gallery__btn:hover img { transform: scale(1.05); } +.gallery__cap { display: flex; flex-direction: column; padding-top: 10px; color: var(--muted); font-size: .875rem; line-height: 1.35; } +.gallery__cap b { color: var(--ink); font: 700 1.1875rem/1.15 var(--f-head); } +.gallery__item.is-target .gallery__btn picture { outline: 3px solid var(--blue); outline-offset: 3px; } +.note { max-width: 46em; margin-top: 48px; padding: 20px 24px; border-left: 4px solid var(--blue); background: var(--blue-l); color: var(--ink); } +@media (max-width: 1100px) { .gallery { grid-template-columns: repeat(3, 1fr); } } +@media (max-width: 640px) { .gallery { grid-template-columns: 1fr 1fr; gap: 20px 12px; } .gallery__cap b { font-size: 1.0625rem; } } + +.lightbox { width: 100vw; height: 100dvh; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: rgba(12, 17, 22, .96); color: #fff; } +.lightbox::backdrop { background: rgba(12, 17, 22, .8); } +.lightbox[open] { display: grid; place-items: center; } +.lightbox__fig { display: flex; flex-direction: column; align-items: center; gap: 14px; margin: 0; padding: 72px 72px 32px; max-width: 100%; max-height: 100%; } +.lightbox__fig img { max-width: min(1200px, 100%); max-height: calc(100dvh - 180px); width: auto; height: auto; object-fit: contain; border-radius: var(--r); } +.lightbox__fig figcaption { display: flex; flex-direction: column; align-items: center; text-align: center; color: rgba(255, 255, 255, .7); } +.lightbox__fig figcaption b { color: #fff; font: 700 1.5rem/1.1 var(--f-head); } +.lightbox__btn { position: absolute; display: grid; place-items: center; width: 52px; height: 52px; border: 1px solid rgba(255, 255, 255, .25); border-radius: 50%; background: rgba(255, 255, 255, .06); color: #fff; cursor: pointer; transition: background .2s; } +.lightbox__btn:hover { background: rgba(255, 255, 255, .18); } +.lightbox__btn .i { width: 22px; height: 22px; } +.lightbox__close { top: 16px; right: 16px; } +.lightbox__prev { left: 16px; top: 50%; transform: translateY(-50%); } +.lightbox__next { right: 16px; top: 50%; transform: translateY(-50%); } +@media (max-width: 640px) { + .lightbox__fig { padding: 72px 12px 96px; } + .lightbox__prev, .lightbox__next { top: auto; bottom: 24px; transform: none; } + .lightbox__prev { left: calc(50% - 64px); } + .lightbox__next { right: calc(50% - 64px); } +} + +/* Kontakt */ +.contact { display: grid; grid-template-columns: 4fr 7fr; gap: clamp(32px, 6vw, 80px); align-items: start; } +.contact__info { display: flex; flex-direction: column; gap: 28px; } +.contact__phone { display: flex; align-items: center; gap: 16px; padding: 24px; background: var(--blue); color: #fff; border-radius: var(--r); text-decoration: none; transition: background .2s; } +.contact__phone:hover { background: var(--blue-d); } +.contact__phone .i { width: 32px; height: 32px; } +.contact__phone span { display: flex; flex-direction: column; font: 700 2.25rem/1 var(--f-head); } +.contact__phone small { margin-bottom: 6px; color: rgba(255, 255, 255, .75); font: 600 .75rem/1 var(--f-body); letter-spacing: .14em; text-transform: uppercase; } +.contact__h { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 1.5rem; } +.contact__h .i { color: var(--blue); } +.contact__block address { margin-bottom: 8px; } +.contact__block p { color: var(--muted); } +.contact__person { display: flex; align-items: center; gap: 16px; margin: 0; padding-top: 24px; border-top: 1px solid var(--line); } +.contact__person picture { width: 72px; height: 72px; flex: none; border-radius: 50%; overflow: hidden; } +.contact__person img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 25%; } +.contact__person figcaption { display: flex; flex-direction: column; color: var(--muted); font-size: .9375rem; } +.contact__person b { color: var(--ink); font: 700 1.25rem/1.1 var(--f-head); } +@media (max-width: 900px) { .contact { grid-template-columns: 1fr; } } + +.form { position: relative; padding: clamp(24px, 4vw, 44px); border: 1px solid var(--line); border-radius: var(--r); background: #fff; box-shadow: 0 24px 60px -40px rgba(20, 32, 44, .4); } +.form__group { margin: 0 0 24px; padding: 0; border: 0; } +.form__group legend { margin-bottom: 10px; padding: 0; color: var(--ink); font-weight: 600; } +.choices { display: flex; flex-wrap: wrap; gap: 8px; } +.choice { position: relative; cursor: pointer; } +.choice input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; } +.choice span { display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; border: 1px solid var(--line); border-radius: 99px; font-weight: 500; transition: background .2s, border-color .2s, color .2s; } +.choice input:checked + span { background: var(--blue); border-color: var(--blue); color: #fff; } +.choice input:focus-visible + span { outline: 3px solid var(--blue); outline-offset: 2px; } +.choice:hover span { border-color: var(--ink); } +.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; } +@media (max-width: 560px) { .form__row { grid-template-columns: 1fr; } } +.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; } +.field > span { color: var(--ink); font-size: .9375rem; font-weight: 600; } +.field input, .field textarea { width: 100%; min-height: 50px; padding: 12px 14px; border: 1px solid #c9c4b9; border-radius: var(--r); background: #fff; color: var(--ink); font: inherit; transition: border-color .2s, box-shadow .2s; } +.field textarea { resize: vertical; min-height: 128px; } +.field input:focus, .field textarea:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px rgba(0, 86, 157, .18); } +.field.is-invalid input { border-color: #b4362a; box-shadow: 0 0 0 3px rgba(180, 54, 42, .12); } +.field > .field__error { color: #b4362a; font-size: .875rem; font-weight: 500; } +.check { display: flex; gap: 12px; margin: 4px 0 20px; color: var(--muted); font-size: .9375rem; cursor: pointer; } +.check input { width: 20px; height: 20px; flex: none; margin-top: 2px; accent-color: var(--blue); } +.check.is-invalid span { color: #b4362a; } +.form__demo { margin-bottom: 16px; padding: 12px 14px; background: #fff7e6; border-left: 3px solid var(--amber); color: #6b4a0d; font-size: .875rem; } +.form__done { display: flex; gap: 14px; align-items: flex-start; margin-top: 20px; padding: 18px 20px; background: var(--blue-l); border-radius: var(--r); color: var(--ink); } +.form__done[hidden] { display: none; } +.form__done .i { width: 28px; height: 28px; color: var(--blue); } +.form__done div { display: flex; flex-direction: column; } + +/* Rechtstexte */ +.legal { max-width: 800px; } +.legal h2 { margin: 40px 0 10px; font-size: 1.75rem; } +.legal h2:first-child { margin-top: 0; } +.legal address, .legal p { margin-bottom: 12px; } +.todo { display: block; margin: 8px 0 16px; padding: 10px 14px; background: #fdecea; border-left: 3px solid #b4362a; color: #7d1f16; font-size: .9375rem; } + +/* ---------- Einblendungen beim Scrollen ---------- */ +@media (prefers-reduced-motion: no-preference) { + .js .reveal { opacity: 0; transform: translateY(28px); transition: opacity .9s var(--ease), transform .9s var(--ease); } + .js .reveal.is-in { opacity: 1; transform: none; } + .js .cards .reveal:nth-child(2) { transition-delay: .08s; } + .js .cards .reveal:nth-child(3) { transition-delay: .16s; } + .js .cards .reveal:nth-child(4) { transition-delay: .24s; } +} +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, *::before, *::after { transition-duration: .01ms !important; } +} diff --git a/assets/site.js b/assets/site.js new file mode 100644 index 0000000..5f82d41 --- /dev/null +++ b/assets/site.js @@ -0,0 +1,215 @@ +/* HEB Bauelemente: Intro-Tor, Navigation, Einblendungen, 24-h-Uhr, Galerie, Formular. Ohne Bibliotheken. */ +(function () { + "use strict"; + var root = document.documentElement; + root.classList.add("ready"); + var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + + /* ---------- Intro: Sektionaltor fährt hoch (einmal pro Sitzung) ---------- */ + var gate = document.querySelector(".gate"); + if (gate && root.classList.contains("has-intro")) { + try { sessionStorage.setItem("heb-intro", "1"); } catch (e) {} + var started = false; + var open = function () { + if (started) return; + started = true; + gate.classList.add("is-moving"); + requestAnimationFrame(function () { + gate.classList.add("is-open"); + root.classList.add("intro-go"); + }); + }; + gate.addEventListener("transitionend", function (e) { + if (e.target === gate && e.propertyName === "transform") gate.remove(); + }); + gate.addEventListener("click", open); + var img = document.querySelector(".hero__img img"); + setTimeout(function () { + if (!img || img.complete) return open(); + img.addEventListener("load", open); + setTimeout(open, 1200); + }, 650); + } else if (gate) { + gate.remove(); + } + + /* ---------- Header ---------- */ + var header = document.querySelector(".site-header"); + var onScroll = function () { header.classList.toggle("is-scrolled", scrollY > 8); }; + addEventListener("scroll", onScroll, { passive: true }); + onScroll(); + + var menuBtn = document.querySelector(".menu-btn"); + var mobileNav = document.getElementById("mobile-nav"); + if (menuBtn && mobileNav) { + var setMenu = function (open) { + menuBtn.setAttribute("aria-expanded", String(open)); + menuBtn.setAttribute("aria-label", open ? "Menü schließen" : "Menü öffnen"); + mobileNav.hidden = !open; + }; + menuBtn.addEventListener("click", function () { setMenu(mobileNav.hidden); }); + mobileNav.addEventListener("click", function (e) { if (e.target.closest("a")) setMenu(false); }); + addEventListener("keydown", function (e) { if (e.key === "Escape" && !mobileNav.hidden) { setMenu(false); menuBtn.focus(); } }); + } + + document.querySelectorAll("[data-year]").forEach(function (el) { el.textContent = new Date().getFullYear(); }); + + /* ---------- Einblendungen ---------- */ + var reveals = document.querySelectorAll(".reveal"); + if ("IntersectionObserver" in window && !reduced) { + var io = new IntersectionObserver(function (entries) { + entries.forEach(function (en) { + if (en.isIntersecting) { en.target.classList.add("is-in"); io.unobserve(en.target); } + }); + }, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" }); + reveals.forEach(function (el) { io.observe(el); }); + } else { + reveals.forEach(function (el) { el.classList.add("is-in"); }); + } + + /* ---------- 24-h-Uhr: füllt sich mit den Arbeitsschritten ---------- */ + var steps = Array.prototype.slice.call(document.querySelectorAll(".step[data-hours]")); + var bar = document.querySelector(".clock__bar"); + var num = document.querySelector("[data-clock]"); + if (steps.length && bar && num) { + var shown = 0, target = 0, tween = null; + var animateTo = function (h) { + target = h; + bar.style.strokeDashoffset = String(100 - (h / 24) * 100); + if (reduced) { shown = h; num.textContent = h; return; } + if (tween) return; + tween = setInterval(function () { + if (shown === target) { clearInterval(tween); tween = null; return; } + shown += shown < target ? 1 : -1; + num.textContent = shown; + }, 45); + }; + var ticking = false; + var update = function () { + ticking = false; + var line = innerHeight * 0.6, active = -1; + steps.forEach(function (s, i) { if (s.getBoundingClientRect().top < line) active = i; }); + steps.forEach(function (s, i) { + s.classList.toggle("is-active", i === active); + s.classList.toggle("is-done", i < active); + }); + var h = active < 0 ? 0 : +steps[active].dataset.hours; + if (h !== target) animateTo(h); + }; + addEventListener("scroll", function () { if (!ticking) { ticking = true; requestAnimationFrame(update); } }, { passive: true }); + update(); + } + + /* ---------- Galerie: Filter ---------- */ + var chips = document.querySelectorAll(".chip[data-filter]"); + var items = Array.prototype.slice.call(document.querySelectorAll(".gallery__item")); + var count = document.querySelector("[data-count]"); + chips.forEach(function (chip) { + chip.addEventListener("click", function () { + var f = chip.dataset.filter, n = 0; + chips.forEach(function (c) { c.setAttribute("aria-pressed", String(c === chip)); }); + items.forEach(function (it) { + var show = f === "alle" || it.dataset.tags.split(" ").indexOf(f) > -1; + it.hidden = !show; + if (show) n++; + }); + if (count) count.textContent = n; + }); + }); + if (location.hash && items.length) { + var hit = document.getElementById(location.hash.slice(1)); + if (hit && hit.classList.contains("gallery__item")) hit.classList.add("is-target"); + } + + /* ---------- Galerie: Großansicht ---------- */ + var box = document.querySelector(".lightbox"); + if (box && typeof box.showModal === "function") { + var bImg = box.querySelector("img"), bTitle = box.querySelector("figcaption b"), bSub = box.querySelector("figcaption span"); + var list = [], idx = 0, opener = null; + var show = function (i) { + idx = (i + list.length) % list.length; + var b = list[idx]; + bImg.src = b.dataset.full; + bImg.alt = b.dataset.title + ", " + b.dataset.sub; + bTitle.textContent = b.dataset.title; + bSub.textContent = b.dataset.sub; + }; + document.querySelectorAll(".gallery__btn").forEach(function (btn) { + btn.addEventListener("click", function () { + list = items.filter(function (it) { return !it.hidden; }).map(function (it) { return it.querySelector(".gallery__btn"); }); + opener = btn; + show(list.indexOf(btn)); + box.showModal(); + }); + }); + box.querySelector(".lightbox__close").addEventListener("click", function () { box.close(); }); + box.querySelector(".lightbox__prev").addEventListener("click", function () { show(idx - 1); }); + box.querySelector(".lightbox__next").addEventListener("click", function () { show(idx + 1); }); + box.addEventListener("click", function (e) { if (e.target === box) box.close(); }); + box.addEventListener("close", function () { if (opener) opener.focus(); }); + box.addEventListener("keydown", function (e) { + if (e.key === "ArrowLeft") show(idx - 1); + if (e.key === "ArrowRight") show(idx + 1); + }); + var x0 = null; + box.addEventListener("touchstart", function (e) { x0 = e.touches[0].clientX; }, { passive: true }); + box.addEventListener("touchend", function (e) { + if (x0 === null) return; + var dx = e.changedTouches[0].clientX - x0; + if (Math.abs(dx) > 50) show(idx + (dx < 0 ? 1 : -1)); + x0 = null; + }); + } + + /* ---------- Anfrageformular ---------- */ + var form = document.getElementById("anfrage"); + if (form) { + var pre = new URLSearchParams(location.search).get("anliegen"); + var preInput = pre && form.querySelector('input[name="anliegen"][value="' + pre.replace(/[^a-z]/g, "") + '"]'); + if (preInput) preInput.checked = true; + + var setError = function (input, msg) { + var wrap = input.closest(".field, .check"); + var old = wrap.querySelector(".field__error"); + if (old) old.remove(); + wrap.classList.toggle("is-invalid", !!msg); + input.setAttribute("aria-invalid", msg ? "true" : "false"); + if (msg && wrap.classList.contains("field")) { + var err = document.createElement("span"); + err.className = "field__error"; + err.id = "err-" + input.name; + err.textContent = msg; + wrap.appendChild(err); + input.setAttribute("aria-describedby", err.id); + } else { + input.removeAttribute("aria-describedby"); + } + }; + var check = function (input) { + var v = input.type === "checkbox" ? input.checked : input.value.trim(); + var msg = ""; + if (input.required && !v) msg = input.type === "checkbox" ? "Bitte bestätigen." : "Bitte ausfüllen."; + else if (input.name === "telefon" && v && v.replace(/\D/g, "").length < 6) msg = "Bitte eine gültige Telefonnummer angeben."; + else if (input.type === "email" && v && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v)) msg = "Bitte eine gültige E-Mail-Adresse angeben."; + setError(input, msg); + return !msg; + }; + var fields = form.querySelectorAll("input[required], input[type=email]"); + fields.forEach(function (f) { + f.addEventListener(f.type === "checkbox" ? "change" : "blur", function () { check(f); }); + }); + form.addEventListener("submit", function (e) { + e.preventDefault(); + var firstBad = null; + fields.forEach(function (f) { if (!check(f) && !firstBad) firstBad = f; }); + if (firstBad) { firstBad.focus(); return; } + var done = form.querySelector(".form__done"); + if (form.querySelector(".form__demo")) { + done.querySelector("[data-done-text]").textContent = "Im Entwurf wird nichts verschickt. Nach dem Launch landet die Anfrage direkt bei Wolfgang Haas."; + } + form.querySelector("button[type=submit]").hidden = true; + done.hidden = false; + done.focus(); + }); + } +})(); diff --git a/datenschutz.html b/datenschutz.html new file mode 100644 index 0000000..6c20a64 --- /dev/null +++ b/datenschutz.html @@ -0,0 +1,115 @@ + + + + + +Datenschutz – HEB Bauelemente + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Rechtliches

+

Datenschutz

+

Diese Website setzt keine Cookies und verwendet kein Tracking.

+
+
+
+ +
+
+
+
+
+

Fragen kostet nichts

+

Rufen Sie einfach an.

+

Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

+
+ +
+
+ + + + diff --git a/impressum.html b/impressum.html new file mode 100644 index 0000000..fd66635 --- /dev/null +++ b/impressum.html @@ -0,0 +1,113 @@ + + + + + +Impressum – HEB Bauelemente + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Rechtliches

+

Impressum

+

Angaben gemäß § 5 Digitale-Dienste-Gesetz (DDG).

+
+
+
+ +
+
+
+
+
+

Fragen kostet nichts

+

Rufen Sie einfach an.

+

Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

+
+ +
+
+ + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..1e43717 --- /dev/null +++ b/index.html @@ -0,0 +1,277 @@ + + + + + +HEB Bauelemente – Garagentore & Antriebe in Essenbach | Wolfgang Haas + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ Weißes Einfamilienhaus mit anthrazitfarbenem Normstahl-Sektionaltor in der Doppelgarage + +
+

Normstahl-Fachbetrieb in Essenbach

+

Ihr Garagentor. In guten Händen.

+

Beratung, Aufmaß, Montage und Reparatur aus einer Hand, seit über 25 Jahren im Großraum Landshut und Rottenburg.

+ +
+
+ +
+
    +
  • 25+ JahreErfahrung mit Normstahl
  • +
  • Ein Ansprechpartnervon der Beratung bis zur Montage
  • +
  • Sommer-Servicepartnermit regelmäßigen Schulungen
  • +
  • Festpreisbei der Garagen-Renovierung
  • +
+
+ +
+
+
+

Leistungen

+

Alles rund ums Garagentor.

+

Wir verkaufen Tore nicht nur, wir bauen sie auch ein und sind danach weiter für Sie da.

+
+
+ +
+
Neue Garagentore
+
+ 01 +

Neue Garagentore

+

Schwing-, Deckensektional- und Seitensektionaltore von Normstahl, dazu Industrietore. Wir beraten bei Ihnen vor Ort, nehmen Maß und montieren selbst.

+
  • Schwingtore
  • Sektionaltore
  • Industrietore
+
+
+
+
Torantriebe
+
+ 02 +

Torantriebe

+

Antriebe von Sommer und Aperto für Garagen- und Hoftore. Für Reihengaragen ohne Stromanschluss gibt es Akku-Antriebe mit Wechselakku, auf Wunsch mit Solarmodul.

+
  • Garagentor
  • Hoftor
  • Akku & Solar
+
+
+
+
Reparatur & Wartung
+
+ 03 +

Reparatur & Wartung

+

Das Tor klemmt, eine Feder ist gebrochen oder der Antrieb streikt? Wir haben alle gängigen Ersatzteile und bringen auch ältere Tore wieder in Gang.

+
  • Ersatzteile
  • Federn
  • Instandhaltung
+
+
+ + 04 + 24 h +

Garagen-Renovierung

+

Altes Tor raus, neues Tor rein, alles verputzt, in nur einem Tag und zum Festpreis.

+ So läuft es ab +
+
+
+
+ +
+
+
+

Unser Serviceangebot

+

Alt raus. Neu rein.
In 24 Stunden.

+

Wir renovieren Ihre Garage innerhalb eines Tages, zum vorher vereinbarten Festpreis. Danach ist alles sauber, und das Tor läuft.

+ +
+
    + +
  1. + 1 +

    Altes Tor ausbauen

    Wir demontieren Ihr altes Garagentor fachgerecht.

    +
  2. +
  3. + 2 +

    Neues Tor einbauen

    Das neue Normstahl-Tor wird passgenau montiert, auf Wunsch gleich mit Antrieb.

    +
  4. +
  5. + 3 +

    Beiputzen & Estrich

    Sturz und Laibung werden beigeputzt, fehlender Estrich wird eingebracht.

    +
  6. +
  7. + 4 +

    Besenrein übergeben

    Sie bekommen Ihre Garage besenrein und voll funktionsfähig zurück.

    +
  8. +
  9. +
    + Garagenhof mit drei Toren: links ein altes Schwingtor, daneben zwei neue Sektionaltore +
    Alt neben neu: ein Garagenhof aus unseren Projekten.
    +
    + Festpreis anfragen +
  10. +
+
+
+ +
+
+
+
+

Tore & Projekte

+

Aus Essenbach, Rottenburg und Umgebung.

+
+ Alle 27 Projekte ansehen +
+
+ +
+ +
+
+
+ Porträt von Wolfgang Haas, Inhaber von HEB Bauelemente +
Wolfgang HaasInhaber · Essenbach-Mirskofen
+
+
+

Über uns

+

Bei uns sprechen Sie mit dem Chef.

+
+

„Seit mehr als 25 Jahren befasse ich mich mit Normstahl-Toren. Von der Planung bis zur Beratung, vom Aufmaß bis zur Montage bekommen Sie bei mir alles aus einer Hand.“

+
+

Weil nicht immer alles passt, setze ich bei Antrieben auf Sommer Antriebs- und Funktechnik aus Deutschland. Auch die können keinen Kreis gerade biegen, aber sie bieten Lösungen, wo andere aufgeben müssen.

+

Damit wir technisch auf dem neuesten Stand bleiben, besuchen wir regelmäßig die Servicepartner-Schulungen unserer Lieferanten.

+

Ihr Wolfgang Haas

+
+
+
+ +
+
+
+

Fachbetrieb statt Discount

+

Warum nicht einfach online bestellen?

+

Ein Tor aus dem Internet ist schnell gekauft. Spätestens bei der ersten Störung zeigt sich, ob jemand in der Nähe ist, der hilft.

+
+
+
+

Tor aus dem Internet

+
    +
  • Maße nehmen Sie selbst und hoffen, dass sie stimmen
  • +
  • Den Einbau übernehmen Sie oder jemand anderes
  • +
  • Bei Störungen hilft nur eine Hotline in der Ferne
  • +
+
+
+

HEB Bauelemente vor Ort

+
    +
  • Beratung und Aufmaß bei Ihnen zu Hause
  • +
  • Fachgerechte Montage aus einer Hand
  • +
  • Im Ernstfall schnell da, mit allen gängigen Ersatzteilen
  • +
+
+
+

Die Freude an der richtigen Entscheidung währt länger als die Freude am billigen Einkauf.

+
+
+ +
+
+
+

Unsere Partner

+

Marken, auf die wir uns verlassen.

+

Wir sind Komplettanbieter für Normstahl-Tore samt Ersatzteilen und Zubehör. Bei Antrieben arbeiten wir mit Sommer und der Fachhandelsmarke Aperto. Auf Wunsch leiten wir Ihre Anfrage auch an die Vertretung weiter, die für Ihre Postleitzahl zuständig ist.

+
+
    +
  • NormstahlGaragen- und Industrietore
  • +
  • SommerAntriebs- und Funktechnik
  • +
  • ApertoGaragen- und Hoftorantriebe
  • +
+
+
+ +
+
+
+
+

Fragen kostet nichts

+

Rufen Sie einfach an.

+

Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

+
+ +
+
+ + + + diff --git a/kontakt.html b/kontakt.html new file mode 100644 index 0000000..6ec8dc9 --- /dev/null +++ b/kontakt.html @@ -0,0 +1,148 @@ + + + + + +Kontakt – HEB Bauelemente, Wolfgang Haas + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Wir sind für Sie da

+

Kontakt

+

Am schnellsten geht es per Telefon. Oder Sie schreiben uns kurz, worum es geht, und wir rufen zurück.

+
+
+
+
+ + +
+

Anfrage senden

+
+ Worum geht es? +
+ +
+
+
+ + +
+
+ + +
+ +
+ Wann dürfen wir zurückrufen? +
+ +
+
+ +

Hinweis: Dies ist ein Entwurf. Das Formular verschickt noch nichts, denn der Versand wird erst beim Launch eingerichtet.

+ + +
+
+
+
+
+
+
+

Fragen kostet nichts

+

Rufen Sie einfach an.

+

Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

+
+ +
+
+ + + + diff --git a/projekte.html b/projekte.html new file mode 100644 index 0000000..4e95485 --- /dev/null +++ b/projekte.html @@ -0,0 +1,114 @@ + + + + + +Tore & Projekte – HEB Bauelemente Essenbach + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+

Galerie

+

Tore & Projekte

+

Eine Auswahl an Toren, die wir eingebaut haben, und an Modellen, die wir anbieten. Ein Klick vergrößert das Bild.

+
+
+
+
+
+ +
+

27 Tore

+ +

Ihr Wunschmodell ist nicht dabei? Normstahl bietet viele weitere Oberflächen, Farben und Größen. Wir bringen Muster mit, wenn wir zu Ihnen kommen.

+
+
+ + + + + + +
+
+
+
+

Fragen kostet nichts

+

Rufen Sie einfach an.

+

Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

+
+ +
+
+ + + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..1f53798 --- /dev/null +++ b/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / diff --git a/tools/build_images.py b/tools/build_images.py new file mode 100644 index 0000000..393a1e9 --- /dev/null +++ b/tools/build_images.py @@ -0,0 +1,75 @@ +"""Wandelt die Originalfotos aus tools/orig in AVIF und WebP (800 px und volle Breite) für assets/img um. + +Die Originale stammen von der alten Seite normtor.de. Neue Fotos in tools/orig ablegen, unten in +IMAGES eintragen und neu erzeugen: + + python3 tools/build_images.py (braucht Pillow mit AVIF: pip install pillow) +""" +from pathlib import Path +from PIL import Image, ImageOps + +ROOT = Path(__file__).resolve().parent.parent +SRC = ROOT / "tools/orig" +OUT = ROOT / "assets/img" + +# Ziel-Name: (Quelldatei, Zuschnitt als (links, oben, rechts, unten) in Pixeln oder None) +IMAGES = { + "hero": ("dst_superior_style_graualu.jpg", None), + "haas": ("haas.jpg", (80, 0, 880, 960)), + "flair-1": ("flair_db_703_1.jpg", None), + "flair-2": ("flair_db_703_2.jpg", None), + "garagenhof": ("web1.jpg", None), + "doppelgarage-beige": ("web2.jpg", None), + "anthrazit-solo": ("web3.jpg", None), + "doppelgarage-hell": ("web4.jpg", None), + "anthrazit-iso": ("web5.jpg", None), + "condor-granit": ("condoor_granit.jpg", None), + "condor-schraegschnitt": ("condoor_schraegschnitt1.jpg", None), + "condor-ligna": ("condoor_wood_ligna_light2.jpg", None), + "condor-schlupftuer": ("condor-mit-schlupftuerer.jpg", None), + "smart-style": ("dst_smart_style_anth_glatt.jpg", None), + "smart-trend": ("dst_smart_trend_eiche_dunkel_woodgrain.jpg", None), + "superior-eiche": ("dst_superior_eiche_hell_style_glatt_01.jpg", None), + "superior-linea": ("dst_superior_linea_verkehrsweiss_glatt.jpg", None), + "superior-graualu": ("dst_superior_style_graualu.jpg", None), + "sst-style": ("sst_style_anthrazitgrau_01.jpg", None), + "sst-trend": ("sst_trend_anthrazitgrau_01.jpg", None), + "sst-design": ("sst.gif", None), + "mueller-muenster": ("mueller_muenster.jpg", None), + "essenbach-ohu": ("essenbach-ohu1.jpg", None), + "elegant": ("elegant-taubenblau.gif", None), + "variant": ("modell-variant.jpg", None), + "rustiko": ("eder2.jpg", None), + "prominent-rottenburg": ("jahnstr-rottenburg.jpg", None), + "eurostyle-iso": ("muech3.jpg", None), + "topline-iso": ("rott2.jpg", None), +} + + +def save(im, name, w): + if im.width > w: + im = im.resize((w, round(im.height * w / im.width)), Image.LANCZOS) + im.save(OUT / f"{name}-{w}.webp", quality=80, method=6) + im.save(OUT / f"{name}-{w}.avif", quality=58, speed=4) + + +def main(): + OUT.mkdir(parents=True, exist_ok=True) + for old in OUT.glob("*"): + old.unlink() + for name, (src, crop) in IMAGES.items(): + im = ImageOps.exif_transpose(Image.open(SRC / src)).convert("RGB") + if crop: + im = im.crop(crop) + # Kleine Altbilder (350 px) gibt es nur als -800 (in Originalgröße), große zusätzlich als -1200 + save(im, name, 800) + if im.width > 800: + save(im, name, 1200) + # Vorschaubild für Messenger und soziale Netzwerke + hero = Image.open(SRC / IMAGES["hero"][0]).convert("RGB") + ImageOps.fit(hero, (1200, 630), centering=(0.5, 0.6)).save(OUT / "og.jpg", quality=82) + print(f"{len(IMAGES)} Bilder nach {OUT.relative_to(ROOT)} geschrieben") + + +if __name__ == "__main__": + main() diff --git a/tools/build_pages.py b/tools/build_pages.py new file mode 100644 index 0000000..655ecfd --- /dev/null +++ b/tools/build_pages.py @@ -0,0 +1,567 @@ +"""Erzeugt die HTML-Seiten aus gemeinsamen Bausteinen (Header, Footer, Galerie-Daten). + +Quelle der Wahrheit für Inhalt und Struktur ist diese Datei — die HTML-Dateien nicht von Hand +ändern, sondern hier ändern und neu erzeugen: + + python3 tools/build_images.py (nur wenn sich Fotos geändert haben) + python3 tools/build_pages.py (braucht Pillow: pip install pillow) +""" +import json +from html import escape +from pathlib import Path +from urllib.parse import quote +from PIL import Image + +OUT = Path(__file__).resolve().parent.parent +IMG = OUT / "assets/img" +SITE = "https://normtor.de" +DEMO = True # Entwurf: noindex + Hinweis im Formular. Vor dem Launch auf False setzen. + +FIRMA = "HEB Bauelemente" +INHABER = "Wolfgang Haas" +STRASSE = "Am Ring 20" +ORT = "84051 Essenbach / Mirskofen" +TEL = "08703 / 13 62" +TEL_HREF = "tel:+4987031362" +MAPS = "https://www.google.com/maps/search/?api=1&query=" + quote("Am Ring 20, 84051 Essenbach") +FACEBOOK = "https://www.facebook.com/heb.bauelemente" + +# ---------- Icons ---------- +I_ARROW = '' +I_PHONE = '' +I_PIN = '' +I_MAIL = '' +I_CHECK = '' +I_X = '' +I_PREV = '' +I_NEXT = '' +I_FB = '' + +# Logo nachgezeichnet: H und E teilen sich den Mittelstrich, wie im Original-Rasterlogo +HEB_MARK = ('') + + +def pic(name, alt, sizes="100vw", eager=False, cls=""): + w, h = Image.open(IMG / f"{name}-800.webp").size + big = (IMG / f"{name}-1200.webp").exists() + if big: + w, h = Image.open(IMG / f"{name}-1200.webp").size + load = 'fetchpriority="high"' if eager else 'loading="lazy" decoding="async"' + c = f' class="{cls}"' if cls else "" + + def srcset(ext): + return f"assets/img/{name}-800.{ext} 800w" + (f", assets/img/{name}-1200.{ext} 1200w" if big else "") + src = f"assets/img/{name}-{1200 if big else 800}.webp" + return (f'' + f'' + f'' + f'{escape(alt)}') + + +# ---------- Galerie ---------- +# (Bildname, Titel, Unterzeile, Kategorien) — Titel von der alten Seite übernommen, wo vorhanden. +# Kategorien: dst = Deckensektionaltor, sst = Seitensektionaltor, schwing = Schwingtor, +# holz = Holz & Sonderlösungen, region = Projekte aus der Region +GALLERY = [ + ("flair-1", "Normstahl Flair", "Farbe DB 703", "dst region"), + ("garagenhof", "Garagenhof", "Alt und neu nebeneinander", "dst region"), + ("condor-schraegschnitt", "Sektionaltor mit Schrägschnitt", "320 mm Gefälle ausgeglichen", "dst holz region"), + ("anthrazit-iso", "Normstahl Style für Fertiggaragen", "Anthrazit, glatt", "dst region"), + ("condor-ligna", "Condor Wood Ligna Light", "Mit Schlupftür und Glassektion", "holz region"), + ("essenbach-ohu", "Deckensektionaltor Eurostyle", "Mit Sturzblende, Essenbach-Ohu", "dst region"), + ("superior-graualu", "Superior Style", "Graualuminium", "dst"), + ("doppelgarage-beige", "Doppelgarage", "Zwei Sektionaltore", "dst region"), + ("sst-style", "Seitensektionaltor Style", "Anthrazitgrau", "sst"), + ("condor-granit", "Condor Granit", "Sektionaltor, glatt", "dst region"), + ("rustiko", "Normstahl Rustiko", "Holz mit aufgesetzter Zarge", "schwing holz region"), + ("smart-trend", "Smart Trend", "Eiche dunkel, Woodgrain", "dst"), + ("anthrazit-solo", "Sektionaltor in Anthrazit", "Mit Verkleidung", "dst region"), + ("prominent-rottenburg", "Schwingtor Prominent", "Taubenblau, Rottenburg", "schwing region"), + ("superior-linea", "Superior Linea", "Verkehrsweiß, glatt", "dst"), + ("condor-schlupftuer", "Condor mit Schlupftür", "Holzoptik mit Fenstern", "holz region"), + ("sst-trend", "Seitensektionaltor Trend", "Anthrazitgrau", "sst"), + ("flair-2", "Normstahl Flair", "Farbe DB 703", "dst region"), + ("eurostyle-iso", "Eurostyle ISO", "Weißaluminium mit Design-Motiven", "dst region"), + ("smart-style", "Smart Style", "Anthrazit, glatt", "dst"), + ("variant", "Normstahl Variant", "Vom Kunden selbst belegt", "schwing holz region"), + ("doppelgarage-hell", "Doppelgarage", "Zwei helle Sektionaltore", "dst region"), + ("superior-eiche", "Superior Style", "Eiche hell", "dst"), + ("sst-design", "Seitensektionaltor", "Mit Design-Applikation", "sst region"), + ("elegant", "Schwingtor Elegant", "Taubenblau mit Messinggriff", "schwing region"), + ("topline-iso", "Topline ISO", "Moosgrün, links laufend", "schwing region"), + ("mueller-muenster", "Sektionaltor", "Anthrazit", "dst region"), +] +FILTERS = [("alle", "Alle"), ("region", "Aus der Region"), ("dst", "Deckensektionaltore"), + ("sst", "Seitensektionaltore"), ("schwing", "Schwingtore"), ("holz", "Holz & Sonderlösungen")] + +NAV = [("index.html#leistungen", "Leistungen"), ("index.html#renovierung", "24-h-Renovierung"), + ("projekte.html", "Tore & Projekte"), ("index.html#ueber-uns", "Über uns"), ("kontakt.html", "Kontakt")] + +HEAD_SCRIPT = ("(function(r){r.classList.add('js');try{if(r.hasAttribute('data-intro')&&!sessionStorage.getItem('heb-intro')" + "&&matchMedia('(prefers-reduced-motion: no-preference)').matches)r.classList.add('has-intro')}catch(e){}" + "setTimeout(function(){if(!r.classList.contains('ready'))r.classList.remove('has-intro')},3500)})(document.documentElement)") + +LD = { + "@context": "https://schema.org", + "@type": "HomeAndConstructionBusiness", + "name": "HEB Bauelemente – Wolfgang Haas", + "description": "Garagentore, Torantriebe, Reparatur und Garagen-Renovierung in 24 Stunden im Raum Landshut und Rottenburg.", + "url": SITE + "/", + "telephone": "+49 8703 1362", + "founder": {"@type": "Person", "name": INHABER}, + "address": {"@type": "PostalAddress", "streetAddress": STRASSE, "postalCode": "84051", + "addressLocality": "Essenbach", "addressRegion": "Bayern", "addressCountry": "DE"}, + "areaServed": ["Landshut", "Rottenburg an der Laaber", "Essenbach"], + "brand": ["Normstahl", "Sommer", "Aperto"], + "image": SITE + "/assets/img/og.jpg", + "sameAs": [FACEBOOK], +} + + +def head(title, desc, file, intro=False): + ld = f'' if file == "index.html" else "" + canonical = SITE + "/" + ("" if file == "index.html" else file) + robots = '\n' if DEMO else "" + return f''' + + + + +{title} + +{robots} + + + + + + + + + + + + + + +{ld} +''' + + +def brand(): + return (f'{HEB_MARK}' + 'BauelementeWolfgang Haas · Essenbach') + + +def header(active): + def cur(f): + return ' aria-current="page"' if f == active else "" + links = "".join(f'{n}' for f, n in NAV) + mlinks = "".join(f'
  • {n}
  • ' for f, n in NAV) + return f''' + +''' + + +FOOTER = f''' +
    +
    +
    +

    Fragen kostet nichts

    +

    Rufen Sie einfach an.

    +

    Ob neues Tor, klemmender Antrieb oder gebrochene Feder: Sie sprechen direkt mit Wolfgang Haas, ganz ohne Warteschleife und Callcenter.

    +
    + +
    +
    + +''' + + +def page(file, title, desc, body, intro=False, active=None): + html = head(title, desc, file, intro) + "\n" + header(active or file) + \ + f'\n
    \n{body}\n
    ' + FOOTER + "\n\n\n" + (OUT / file).write_text(html, encoding="utf-8") + print("geschrieben:", file) + + +def page_hero(title, lead, eyebrow): + return f''' +
    +
    +

    {eyebrow}

    +

    {title}

    +

    {lead}

    +
    +
    ''' + + +# ============================== Startseite ============================== +GATE = '' + +LEISTUNGEN = [ + ("superior-linea", "Neue Garagentore", + "Schwing-, Deckensektional- und Seitensektionaltore von Normstahl, dazu Industrietore. Wir beraten bei Ihnen vor Ort, nehmen Maß und montieren selbst.", + ["Schwingtore", "Sektionaltore", "Industrietore"]), + ("sst-style", "Torantriebe", + "Antriebe von Sommer und Aperto für Garagen- und Hoftore. Für Reihengaragen ohne Stromanschluss gibt es Akku-Antriebe mit Wechselakku, auf Wunsch mit Solarmodul.", + ["Garagentor", "Hoftor", "Akku & Solar"]), + ("flair-2", "Reparatur & Wartung", + "Das Tor klemmt, eine Feder ist gebrochen oder der Antrieb streikt? Wir haben alle gängigen Ersatzteile und bringen auch ältere Tore wieder in Gang.", + ["Ersatzteile", "Federn", "Instandhaltung"]), +] + +STEPS = [ + ("Altes Tor ausbauen", "Wir demontieren Ihr altes Garagentor fachgerecht."), + ("Neues Tor einbauen", "Das neue Normstahl-Tor wird passgenau montiert, auf Wunsch gleich mit Antrieb."), + ("Beiputzen & Estrich", "Sturz und Laibung werden beigeputzt, fehlender Estrich wird eingebracht."), + ("Besenrein übergeben", "Sie bekommen Ihre Garage besenrein und voll funktionsfähig zurück."), +] + +TEASER = ["flair-1", "anthrazit-iso", "doppelgarage-hell", "condor-granit", "doppelgarage-beige"] +G = {g[0]: g for g in GALLERY} + +INDEX = f''' +
    + {pic("hero", "Weißes Einfamilienhaus mit anthrazitfarbenem Normstahl-Sektionaltor in der Doppelgarage", eager=True, cls="hero__img")} + {GATE} +
    +

    Normstahl-Fachbetrieb in Essenbach

    +

    Ihr Garagentor. In guten Händen.

    +

    Beratung, Aufmaß, Montage und Reparatur aus einer Hand, seit über 25 Jahren im Großraum Landshut und Rottenburg.

    + +
    +
    + +
    +
      +
    • 25+ JahreErfahrung mit Normstahl
    • +
    • Ein Ansprechpartnervon der Beratung bis zur Montage
    • +
    • Sommer-Servicepartnermit regelmäßigen Schulungen
    • +
    • Festpreisbei der Garagen-Renovierung
    • +
    +
    + +
    +
    +
    +

    Leistungen

    +

    Alles rund ums Garagentor.

    +

    Wir verkaufen Tore nicht nur, wir bauen sie auch ein und sind danach weiter für Sie da.

    +
    +
    + {"".join(f""" +
    +
    {pic(img, title, sizes="(min-width: 960px) 30vw, 100vw")}
    +
    + 0{i + 1} +

    {title}

    +

    {text}

    +
      {"".join(f"
    • {t}
    • " for t in tags)}
    +
    +
    """ for i, (img, title, text, tags) in enumerate(LEISTUNGEN))} + + 04 + 24 h +

    Garagen-Renovierung

    +

    Altes Tor raus, neues Tor rein, alles verputzt, in nur einem Tag und zum Festpreis.

    + So läuft es ab {I_ARROW} +
    +
    +
    +
    + +
    +
    +
    +

    Unser Serviceangebot

    +

    Alt raus. Neu rein.
    In 24 Stunden.

    +

    Wir renovieren Ihre Garage innerhalb eines Tages, zum vorher vereinbarten Festpreis. Danach ist alles sauber, und das Tor läuft.

    + +
    +
      + {"".join(f""" +
    1. + {i + 1} +

      {t}

      {d}

      +
    2. """ for i, ((t, d), h) in enumerate(zip(STEPS, (6, 12, 19, 24))))} +
    3. +
      + {pic("garagenhof", "Garagenhof mit drei Toren: links ein altes Schwingtor, daneben zwei neue Sektionaltore", sizes="(min-width: 960px) 50vw, 100vw")} +
      Alt neben neu: ein Garagenhof aus unseren Projekten.
      +
      + Festpreis anfragen {I_ARROW} +
    4. +
    +
    +
    + +
    +
    +
    +
    +

    Tore & Projekte

    +

    Aus Essenbach, Rottenburg und Umgebung.

    +
    + Alle {len(GALLERY)} Projekte ansehen {I_ARROW} +
    +
    + +
    + +
    +
    +
    + {pic("haas", "Porträt von Wolfgang Haas, Inhaber von HEB Bauelemente", sizes="(min-width: 960px) 40vw, 100vw")} +
    Wolfgang HaasInhaber · Essenbach-Mirskofen
    +
    +
    +

    Über uns

    +

    Bei uns sprechen Sie mit dem Chef.

    +
    +

    „Seit mehr als 25 Jahren befasse ich mich mit Normstahl-Toren. Von der Planung bis zur Beratung, vom Aufmaß bis zur Montage bekommen Sie bei mir alles aus einer Hand.“

    +
    +

    Weil nicht immer alles passt, setze ich bei Antrieben auf Sommer Antriebs- und Funktechnik aus Deutschland. Auch die können keinen Kreis gerade biegen, aber sie bieten Lösungen, wo andere aufgeben müssen.

    +

    Damit wir technisch auf dem neuesten Stand bleiben, besuchen wir regelmäßig die Servicepartner-Schulungen unserer Lieferanten.

    +

    Ihr Wolfgang Haas

    +
    +
    +
    + +
    +
    +
    +

    Fachbetrieb statt Discount

    +

    Warum nicht einfach online bestellen?

    +

    Ein Tor aus dem Internet ist schnell gekauft. Spätestens bei der ersten Störung zeigt sich, ob jemand in der Nähe ist, der hilft.

    +
    +
    +
    +

    Tor aus dem Internet

    +
      +
    • {I_X}Maße nehmen Sie selbst und hoffen, dass sie stimmen
    • +
    • {I_X}Den Einbau übernehmen Sie oder jemand anderes
    • +
    • {I_X}Bei Störungen hilft nur eine Hotline in der Ferne
    • +
    +
    +
    +

    HEB Bauelemente vor Ort

    +
      +
    • {I_CHECK}Beratung und Aufmaß bei Ihnen zu Hause
    • +
    • {I_CHECK}Fachgerechte Montage aus einer Hand
    • +
    • {I_CHECK}Im Ernstfall schnell da, mit allen gängigen Ersatzteilen
    • +
    +
    +
    +

    Die Freude an der richtigen Entscheidung währt länger als die Freude am billigen Einkauf.

    +
    +
    + +
    +
    +
    +

    Unsere Partner

    +

    Marken, auf die wir uns verlassen.

    +

    Wir sind Komplettanbieter für Normstahl-Tore samt Ersatzteilen und Zubehör. Bei Antrieben arbeiten wir mit Sommer und der Fachhandelsmarke Aperto. Auf Wunsch leiten wir Ihre Anfrage auch an die Vertretung weiter, die für Ihre Postleitzahl zuständig ist.

    +
    +
      +
    • NormstahlGaragen- und Industrietore
    • +
    • SommerAntriebs- und Funktechnik
    • +
    • ApertoGaragen- und Hoftorantriebe
    • +
    +
    +
    +''' + +# ============================== Projekte ============================== +PROJEKTE = page_hero("Tore & Projekte", + "Eine Auswahl an Toren, die wir eingebaut haben, und an Modellen, die wir anbieten. Ein Klick vergrößert das Bild.", + "Galerie") + f''' +
    +
    +
    + {"".join(f'' for k, n in FILTERS)} +
    +

    {len(GALLERY)} Tore

    + +

    Ihr Wunschmodell ist nicht dabei? Normstahl bietet viele weitere Oberflächen, Farben und Größen. Wir bringen Muster mit, wenn wir zu Ihnen kommen.

    +
    +
    + + + + + +''' + +# ============================== Kontakt ============================== +ANLIEGEN = [("tor", "Neues Garagentor"), ("antrieb", "Antrieb"), ("reparatur", "Reparatur"), ("renovierung", "24-h-Renovierung")] +DEMO_NOTE = ('

    Hinweis: Dies ist ein Entwurf. Das Formular verschickt noch nichts, ' + 'denn der Versand wird erst beim Launch eingerichtet.

    ') if DEMO else "" + +KONTAKT = page_hero("Kontakt", "Am schnellsten geht es per Telefon. Oder Sie schreiben uns kurz, worum es geht, und wir rufen zurück.", "Wir sind für Sie da") + f''' +
    +
    + + +
    +

    Anfrage senden

    +
    + Worum geht es? +
    + {"".join(f'' for k, n in ANLIEGEN)} +
    +
    +
    + + +
    +
    + + +
    + +
    + Wann dürfen wir zurückrufen? +
    + {"".join(f'' for v, n in (("vormittags", "Vormittags"), ("nachmittags", "Nachmittags"), ("egal", "Egal")))} +
    +
    + + {DEMO_NOTE} + + +
    +
    +
    ''' + +# ============================== Rechtliches ============================== +IMPRESSUM = page_hero("Impressum", "Angaben gemäß § 5 Digitale-Dienste-Gesetz (DDG).", "Rechtliches") + f''' +
    + +
    ''' + +DATENSCHUTZ = page_hero("Datenschutz", "Diese Website setzt keine Cookies und verwendet kein Tracking.", "Rechtliches") + f''' +
    + +
    ''' + +page("index.html", "HEB Bauelemente – Garagentore & Antriebe in Essenbach | Wolfgang Haas", + "Normstahl-Garagentore, Sommer-Antriebe, Reparatur und Garagen-Renovierung in 24 Stunden zum Festpreis. Ihr Fachbetrieb im Raum Landshut und Rottenburg.", + INDEX, intro=True) +page("projekte.html", "Tore & Projekte – HEB Bauelemente Essenbach", + "Garagentore aus der Region Landshut und Rottenburg: Deckensektionaltore, Seitensektionaltore, Schwingtore und Holztore von Normstahl.", + PROJEKTE) +page("kontakt.html", "Kontakt – HEB Bauelemente, Wolfgang Haas", + "Rufen Sie an unter 08703 1362 oder senden Sie eine Anfrage für Garagentor, Antrieb, Reparatur oder 24-h-Renovierung.", + KONTAKT) +page("impressum.html", "Impressum – HEB Bauelemente", "Impressum von HEB Bauelemente, Wolfgang Haas, Am Ring 20, 84051 Essenbach.", IMPRESSUM) +page("datenschutz.html", "Datenschutz – HEB Bauelemente", "Datenschutzerklärung von HEB Bauelemente: keine Cookies, kein Tracking.", DATENSCHUTZ) diff --git a/tools/orig/condoor_granit.jpg b/tools/orig/condoor_granit.jpg new file mode 100644 index 0000000..376160c Binary files /dev/null and b/tools/orig/condoor_granit.jpg differ diff --git a/tools/orig/condoor_schraegschnitt1.jpg b/tools/orig/condoor_schraegschnitt1.jpg new file mode 100644 index 0000000..44cba4e Binary files /dev/null and b/tools/orig/condoor_schraegschnitt1.jpg differ diff --git a/tools/orig/condoor_wood_ligna_light2.jpg b/tools/orig/condoor_wood_ligna_light2.jpg new file mode 100644 index 0000000..46627f1 Binary files /dev/null and b/tools/orig/condoor_wood_ligna_light2.jpg differ diff --git a/tools/orig/condor-mit-schlupftuerer.jpg b/tools/orig/condor-mit-schlupftuerer.jpg new file mode 100644 index 0000000..7375ab8 Binary files /dev/null and b/tools/orig/condor-mit-schlupftuerer.jpg differ diff --git a/tools/orig/dst_smart_style_anth_glatt.jpg b/tools/orig/dst_smart_style_anth_glatt.jpg new file mode 100644 index 0000000..1c6b603 Binary files /dev/null and b/tools/orig/dst_smart_style_anth_glatt.jpg differ diff --git a/tools/orig/dst_smart_trend_eiche_dunkel_woodgrain.jpg b/tools/orig/dst_smart_trend_eiche_dunkel_woodgrain.jpg new file mode 100644 index 0000000..2a2b93c Binary files /dev/null and b/tools/orig/dst_smart_trend_eiche_dunkel_woodgrain.jpg differ diff --git a/tools/orig/dst_superior_eiche_hell_style_glatt_01.jpg b/tools/orig/dst_superior_eiche_hell_style_glatt_01.jpg new file mode 100644 index 0000000..c5a0c79 Binary files /dev/null and b/tools/orig/dst_superior_eiche_hell_style_glatt_01.jpg differ diff --git a/tools/orig/dst_superior_linea_verkehrsweiss_glatt.jpg b/tools/orig/dst_superior_linea_verkehrsweiss_glatt.jpg new file mode 100644 index 0000000..e6e4014 Binary files /dev/null and b/tools/orig/dst_superior_linea_verkehrsweiss_glatt.jpg differ diff --git a/tools/orig/dst_superior_style_graualu.jpg b/tools/orig/dst_superior_style_graualu.jpg new file mode 100644 index 0000000..8f4c282 Binary files /dev/null and b/tools/orig/dst_superior_style_graualu.jpg differ diff --git a/tools/orig/eder2.jpg b/tools/orig/eder2.jpg new file mode 100644 index 0000000..3ba4630 Binary files /dev/null and b/tools/orig/eder2.jpg differ diff --git a/tools/orig/elegant-taubenblau.gif b/tools/orig/elegant-taubenblau.gif new file mode 100644 index 0000000..70ef790 Binary files /dev/null and b/tools/orig/elegant-taubenblau.gif differ diff --git a/tools/orig/essenbach-ohu1.jpg b/tools/orig/essenbach-ohu1.jpg new file mode 100644 index 0000000..b974f61 Binary files /dev/null and b/tools/orig/essenbach-ohu1.jpg differ diff --git a/tools/orig/flair_db_703_1.jpg b/tools/orig/flair_db_703_1.jpg new file mode 100644 index 0000000..99bd9d1 Binary files /dev/null and b/tools/orig/flair_db_703_1.jpg differ diff --git a/tools/orig/flair_db_703_2.jpg b/tools/orig/flair_db_703_2.jpg new file mode 100644 index 0000000..bb2d167 Binary files /dev/null and b/tools/orig/flair_db_703_2.jpg differ diff --git a/tools/orig/haas.jpg b/tools/orig/haas.jpg new file mode 100644 index 0000000..97eb85e Binary files /dev/null and b/tools/orig/haas.jpg differ diff --git a/tools/orig/jahnstr-rottenburg.jpg b/tools/orig/jahnstr-rottenburg.jpg new file mode 100644 index 0000000..f73b8cc Binary files /dev/null and b/tools/orig/jahnstr-rottenburg.jpg differ diff --git a/tools/orig/logo.png b/tools/orig/logo.png new file mode 100644 index 0000000..01d6d91 Binary files /dev/null and b/tools/orig/logo.png differ diff --git a/tools/orig/modell-variant.jpg b/tools/orig/modell-variant.jpg new file mode 100644 index 0000000..8d143bb Binary files /dev/null and b/tools/orig/modell-variant.jpg differ diff --git a/tools/orig/muech3.jpg b/tools/orig/muech3.jpg new file mode 100644 index 0000000..c501bff Binary files /dev/null and b/tools/orig/muech3.jpg differ diff --git a/tools/orig/mueller_muenster.jpg b/tools/orig/mueller_muenster.jpg new file mode 100644 index 0000000..d1e88eb Binary files /dev/null and b/tools/orig/mueller_muenster.jpg differ diff --git a/tools/orig/rott2.jpg b/tools/orig/rott2.jpg new file mode 100644 index 0000000..903904e Binary files /dev/null and b/tools/orig/rott2.jpg differ diff --git a/tools/orig/sst.gif b/tools/orig/sst.gif new file mode 100644 index 0000000..b2deb4e Binary files /dev/null and b/tools/orig/sst.gif differ diff --git a/tools/orig/sst_style_anthrazitgrau_01.jpg b/tools/orig/sst_style_anthrazitgrau_01.jpg new file mode 100644 index 0000000..327e1c5 Binary files /dev/null and b/tools/orig/sst_style_anthrazitgrau_01.jpg differ diff --git a/tools/orig/sst_trend_anthrazitgrau_01.jpg b/tools/orig/sst_trend_anthrazitgrau_01.jpg new file mode 100644 index 0000000..1279d4f Binary files /dev/null and b/tools/orig/sst_trend_anthrazitgrau_01.jpg differ diff --git a/tools/orig/web1.jpg b/tools/orig/web1.jpg new file mode 100644 index 0000000..38f67fc Binary files /dev/null and b/tools/orig/web1.jpg differ diff --git a/tools/orig/web2.jpg b/tools/orig/web2.jpg new file mode 100644 index 0000000..577d79c Binary files /dev/null and b/tools/orig/web2.jpg differ diff --git a/tools/orig/web3.jpg b/tools/orig/web3.jpg new file mode 100644 index 0000000..5eb972f Binary files /dev/null and b/tools/orig/web3.jpg differ diff --git a/tools/orig/web4.jpg b/tools/orig/web4.jpg new file mode 100644 index 0000000..c00b4f4 Binary files /dev/null and b/tools/orig/web4.jpg differ diff --git a/tools/orig/web5.jpg b/tools/orig/web5.jpg new file mode 100644 index 0000000..6a86da6 Binary files /dev/null and b/tools/orig/web5.jpg differ