Initiale Unger Reisen Website (statisch, mobil optimiert)

- One-Page-Site nach Design-Handoff: Hero mit scroll-gesteuertem Bus,
  Stats, Leistungen, Foto-Band, Flotte, interaktive MapLibre-Karte, Karriere, Footer
- Vollständige Handy-Optimierung: Hamburger-Menü, responsive Grids, touch-freundliche Karte
- Bus-Foto lokal in assets/

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 17:04:35 +02:00
commit 306ea0862d
9 changed files with 3497 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# OS / editor
.DS_Store
Thumbs.db
*.log
# Claude Code harness config (local)
.claude/
# Dependencies (falls später ein Build dazukommt)
node_modules/
dist/

29
README.md Normal file
View File

@@ -0,0 +1,29 @@
# Unger Reisen — Website
Statische One-Page-Website für das Omnibusunternehmen **Unger Reisen** (Buch am Erlbach & Landshut, Niederbayern).
## Aufbau
```
index.html # Markup aller Sektionen
css/style.css # Styles, Design-Tokens, Animationen, Mobile-Optimierung
js/main.js # Daten-Rendering, Scroll-Bus, Mobil-Menü, MapLibre-Karte
assets/ # Bilder (Bus-Foto)
design_handoff_unger_reisen/ # Original-Design-Handoff (Referenz)
```
## Lokal starten
Beliebiger statischer Server, z. B.:
```bash
npx serve -l 4173 .
```
Dann `http://localhost:4173` öffnen.
## Hinweise
- Fonts (Anton, Archivo) und die Karte (MapLibre GL JS + Carto „dark-matter") werden per CDN geladen.
Für DSGVO-konformen Betrieb ggf. selbst hosten.
- Rechtsseiten (Impressum, Datenschutz, AGB) sind noch als Platzhalter verlinkt.

BIN
assets/startslider-bus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

544
css/style.css Normal file
View File

@@ -0,0 +1,544 @@
/* =========================================================
Unger Reisen — Website Redesign
Design tokens, layout, components & motion
========================================================= */
*{ margin:0; padding:0; box-sizing:border-box; }
:root{
--red:#C8102E; /* primary accent */
--ink:#0a0e18; /* page bg (near-black navy) */
--night:#0a0e18;
--paper:#f4f0e8; /* light text / light cards */
--cream:#efe9dc;
--card:#111725; /* dark card surface */
--card-hover:#151d30;
--line:rgba(255,255,255,.14);
--muted:#8b93a7;
--body:#c8cdda; /* secondary body text on dark */
}
html{ scroll-behavior:smooth; }
body{
background:var(--ink);
color:var(--paper);
font-family:'Archivo', system-ui, sans-serif;
-webkit-font-smoothing:antialiased;
line-height:1.5;
overflow-x:hidden;
}
a{ color:inherit; text-decoration:none; }
::selection{ background:var(--red); color:#fff; }
.display{
font-family:'Anton', sans-serif;
font-weight:400;
text-transform:uppercase;
letter-spacing:.5px;
line-height:.9;
}
.text-red{ color:var(--red); }
/* ---------- Buttons ---------- */
.btn{
display:inline-flex;
align-items:center;
justify-content:center;
gap:10px;
font-size:15px;
font-weight:700;
text-transform:uppercase;
letter-spacing:1px;
padding:17px 34px;
transition:transform .2s ease, border-color .2s ease, background .2s ease;
cursor:pointer;
}
.btn--red{ background:var(--red); color:#fff; }
.btn--red:hover{ transform:translateY(-2px); }
.btn--outline{ border:1.5px solid rgba(255,255,255,.3); color:#fff; padding:17px 30px; }
.btn--outline:hover{ border-color:#fff; }
.btn--white{ background:#fff; color:var(--ink); padding:17px 34px; white-space:nowrap; }
.btn--white:hover{ transform:translateY(-2px); }
.btn--block{ width:100%; padding:16px; }
/* ---------- Marquee ---------- */
.marquee{
background:var(--red);
color:#fff;
overflow:hidden;
white-space:nowrap;
padding:9px 0;
position:relative;
z-index:60;
font-weight:700;
font-size:13px;
letter-spacing:2px;
text-transform:uppercase;
}
.marquee__track{
display:inline-flex;
animation:marquee 24s linear infinite;
will-change:transform;
}
.marquee__group{ display:inline-flex; }
.marquee__item{ padding:0 26px; }
.marquee__sep{ opacity:.55; }
/* ---------- Nav ---------- */
.nav{
position:absolute;
top:39px; left:0; right:0;
z-index:50;
display:flex;
align-items:center;
justify-content:space-between;
padding:22px clamp(20px,5vw,60px);
transition:background .35s ease, padding .35s ease, box-shadow .35s ease, top .35s ease;
}
.nav.is-scrolled{
background:rgba(10,14,24,.9);
-webkit-backdrop-filter:blur(12px);
backdrop-filter:blur(12px);
top:0;
padding:14px clamp(20px,5vw,60px);
box-shadow:0 1px 0 rgba(255,255,255,.08);
}
.logo{ display:flex; align-items:baseline; gap:9px; }
.logo__name{ font-size:26px; color:#fff; }
.logo__name--lg{ font-size:30px; }
.logo__sub{ font-weight:700; font-size:11px; letter-spacing:5px; color:var(--red); }
.nav__right{ display:flex; align-items:center; gap:34px; }
.nav__links{
display:flex; gap:28px;
font-size:13.5px; font-weight:600;
text-transform:uppercase; letter-spacing:1px;
}
.nav__links a{ transition:color .2s ease; }
.nav__links a:hover{ color:var(--red); }
.nav__cta{ font-size:13px; padding:12px 24px; }
/* Hamburger (mobile only) */
.nav__burger{
display:none;
flex-direction:column; align-items:center; justify-content:center;
gap:5px; width:42px; height:42px; padding:0;
background:transparent; border:0; cursor:pointer;
position:relative; z-index:55;
}
.nav__burger span{
display:block; width:24px; height:2px; background:#fff; border-radius:2px;
transition:transform .3s ease, opacity .2s ease;
}
.nav__burger.is-open span:nth-child(1){ transform:translateY(7px) rotate(45deg); }
.nav__burger.is-open span:nth-child(2){ opacity:0; }
.nav__burger.is-open span:nth-child(3){ transform:translateY(-7px) rotate(-45deg); }
/* Mobile menu overlay */
.mobileMenu{
display:none;
position:fixed; inset:0; z-index:45;
background:rgba(10,14,24,.98);
-webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
flex-direction:column; gap:2px;
padding:88px 24px 40px;
}
.mobileMenu.is-open{ display:flex; }
.mobileMenu a:not(.btn){
font-size:22px; font-weight:700; text-transform:uppercase; letter-spacing:1px;
color:#fff; padding:18px 4px; border-bottom:1px solid var(--line);
}
.mobileMenu__cta{ margin-top:24px; }
/* ---------- Hero ---------- */
.hero{
position:relative;
min-height:88vh;
display:flex;
flex-direction:column;
justify-content:center;
overflow:hidden;
background:radial-gradient(120% 90% at 70% 10%, #182238 0%, #0a0e18 60%);
}
.hero__type{
position:relative;
z-index:20;
padding:150px clamp(20px,5vw,60px) 0;
pointer-events:none;
}
.eyebrow{
display:inline-flex; align-items:center; gap:12px;
font-size:12px; font-weight:700; letter-spacing:3px;
text-transform:uppercase; color:var(--red);
margin-bottom:22px; pointer-events:auto;
}
.eyebrow__rule{ width:34px; height:2px; background:var(--red); }
.hero__h1{
font-size:clamp(64px,13vw,190px);
color:#fff; letter-spacing:-1px;
}
.hero__lead{
max-width:520px; margin-top:26px;
font-size:clamp(16px,1.5vw,19px);
color:var(--body); pointer-events:auto;
}
.hero__actions{
display:flex; flex-wrap:wrap; gap:14px;
margin-top:34px; pointer-events:auto;
}
/* ---------- Driving scene ---------- */
.scene{
position:relative;
z-index:10;
height:clamp(280px,42vh,420px);
margin-top:auto;
width:100%;
}
.hillsFar{
position:absolute; bottom:64px; left:0; right:0; height:120px;
background-repeat:repeat-x; background-size:800px 120px;
opacity:.4; animation:scrollFar 40s linear infinite;
background-image:
radial-gradient(120px 120px at 120px 120px, #223052 0 99%, transparent 100%),
radial-gradient(160px 160px at 460px 150px, #1c2743 0 99%, transparent 100%),
radial-gradient(120px 120px at 700px 130px, #223052 0 99%, transparent 100%);
}
.hills{
position:absolute; bottom:58px; left:0; right:0; height:90px;
background-repeat:repeat-x; background-size:360px 90px;
opacity:.7; animation:scrollx 18s linear infinite;
background-image:
radial-gradient(90px 90px at 70px 100px, #2a3a5f 0 99%, transparent 100%),
radial-gradient(120px 120px at 250px 120px, #26355699 0 99%, transparent 100%);
}
.road{
position:absolute; bottom:0; left:0; right:0; height:64px;
background:#11151f; border-top:3px solid #1c2233;
}
.stripes{
position:absolute; bottom:29px; left:0; right:0; height:5px;
background-repeat:repeat-x; background-size:120px 5px;
animation:scrollxFast .5s linear infinite;
background-image:linear-gradient(90deg, rgba(228,0,43,.9) 0 60px, transparent 60px 120px);
}
/* ---------- Bus ---------- */
.bus{
position:absolute; bottom:52px; left:0;
width:clamp(380px,52vw,620px);
will-change:transform;
transform:translateX(-30%);
}
.bus__headlight{
position:absolute; right:-40px; bottom:6px; width:120px; height:60px;
background:radial-gradient(ellipse at left center, rgba(255,220,120,.5), transparent 70%);
animation:headlightPulse 2s ease-in-out infinite;
}
.bus__body{
position:relative;
height:clamp(120px,17vw,178px);
background:linear-gradient(180deg,#f2f0ea 0%,#dfdbd0 100%);
border-radius:22px 30px 12px 12px;
box-shadow:0 18px 30px -10px rgba(0,0,0,.6);
overflow:hidden;
}
.bus__stripe{ position:absolute; bottom:24%; left:0; right:0; height:15%; background:var(--red); }
.bus__windshield{
position:absolute; top:14%; right:5%; width:20%; height:42%;
background:linear-gradient(120deg,#8fb4d8,#4d6f96);
border-radius:6px 14px 6px 6px;
}
.bus__windows{
position:absolute; top:14%; left:6%; width:66%; height:42%; border-radius:6px;
background-color:#4d6f96;
background-image:linear-gradient(90deg, transparent 0 3%, #6f92b8 3% 15%, transparent 15% 18%);
background-size:16.6% 100%; background-repeat:repeat-x;
}
.bus__brand{
position:absolute; bottom:6%; left:7%;
font-size:clamp(11px,1.5vw,15px); color:var(--ink); letter-spacing:1px;
}
.bus__door{
position:absolute; top:14%; right:27%; width:8%; height:70%;
background:#37506f; border-radius:4px;
}
.wheel{
position:absolute; bottom:-14px;
width:clamp(42px,6vw,60px); height:clamp(42px,6vw,60px);
border-radius:50%; background:#14181f; border:6px solid #23262e;
animation:wheel .5s linear infinite;
}
.wheel--front{ left:16%; }
.wheel--rear{ right:18%; }
.wheel__hub{ position:absolute; inset:34%; background:#3a3f49; border-radius:50%; }
.wheel__spoke{
position:absolute; top:2px; left:50%; width:2px; height:40%;
background:#454a55; transform:translateX(-50%);
}
/* ---------- Stats band ---------- */
.stats{
background:var(--red); color:#fff;
padding:clamp(34px,5vw,54px) clamp(20px,5vw,60px);
}
.stats__grid{
display:grid;
grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
gap:30px;
}
.stat__big{ font-size:clamp(44px,6vw,72px); color:#fff; line-height:.85; }
.stat__label{ font-size:14px; font-weight:600; margin-top:10px; color:rgba(255,255,255,.85); }
/* ---------- Generic section ---------- */
.section{ padding:clamp(70px,10vw,130px) clamp(20px,5vw,60px); }
.section--karriere{ padding:clamp(60px,8vw,110px) clamp(20px,5vw,60px); }
.section__head{
display:flex; align-items:flex-end; justify-content:space-between;
flex-wrap:wrap; gap:20px; margin-bottom:54px;
}
.section__head--stack{ display:block; margin-bottom:48px; }
.section__title{ font-size:clamp(40px,7vw,90px); color:#fff; }
.section__title--wide{ max-width:12ch; }
.section__intro{ max-width:320px; color:var(--muted); font-size:16px; }
.eyebrow-label{
font-size:12px; font-weight:700; letter-spacing:3px;
text-transform:uppercase; color:var(--red); margin-bottom:16px;
}
/* ---------- Leistungen ---------- */
.services{
display:grid;
grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
gap:16px;
}
.service{
position:relative;
background:var(--card);
border:1px solid var(--line);
padding:38px 30px 34px;
min-height:260px;
display:flex; flex-direction:column; overflow:hidden;
transition:background .3s ease, transform .3s ease;
}
.service:hover{ background:var(--card-hover); transform:translateY(-4px); }
.service__no{ font-size:60px; color:rgba(228,0,43,.9); line-height:.8; }
.service__title{
font-size:22px; font-weight:800; margin:auto 0 10px;
text-transform:uppercase; letter-spacing:.3px;
}
.service__desc{ font-size:15px; color:var(--muted); line-height:1.55; }
/* ---------- Photo band ---------- */
.photo{
position:relative; height:clamp(320px,48vw,520px);
overflow:hidden; display:flex; align-items:center; justify-content:center;
text-align:center;
}
.photo--fallback{ background:linear-gradient(135deg,#151d30,#0a0e18); }
.photo__img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center; }
.photo__overlay{ position:absolute; inset:0; background:linear-gradient(180deg, rgba(10,14,24,.55), rgba(10,14,24,.75)); }
.photo__text{ position:relative; z-index:2; padding:20px; }
.photo__headline{ font-size:clamp(30px,5vw,64px); color:#fff; max-width:16ch; margin:0 auto; }
/* ---------- Flotte ---------- */
.fleet{
display:grid;
grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
gap:16px;
}
.fleetCard{ border:1px solid var(--line); background:var(--card); display:flex; flex-direction:column; }
.fleetCard__head{
padding:34px 30px 22px; border-bottom:1px solid var(--line);
display:flex; align-items:center; justify-content:space-between; gap:16px;
}
.fleetCard__name{ font-size:30px; color:#fff; }
.fleetCard__seats{ display:flex; align-items:baseline; gap:8px; white-space:nowrap; }
.fleetCard__seatsWord{ font-size:12px; color:var(--muted); text-transform:uppercase; letter-spacing:1px; }
.fleetCard__seatsNum{ font-size:40px; color:var(--red); line-height:1; }
.fleetCard__body{ padding:24px 30px 30px; }
.fleetCard__desc{ font-size:15px; color:var(--muted); line-height:1.55; margin-bottom:20px; }
.fleetCard__tags{ display:flex; flex-wrap:wrap; gap:8px; }
.tag{ font-size:12px; font-weight:600; border:1px solid var(--line); padding:6px 13px; color:var(--body); }
/* ---------- Kontakt + Karte ---------- */
.kontakt__grid{
display:grid;
grid-template-columns:1.35fr .95fr;
gap:20px; align-items:stretch;
}
.mapPanel{
position:relative; border:1px solid var(--line); background:#0d1420;
overflow:hidden; min-height:440px; border-radius:2px;
}
.mapPanel__map{ position:absolute; inset:0; }
.mapPanel__chip{
position:absolute; left:16px; bottom:16px; z-index:2; pointer-events:none;
background:rgba(10,14,24,.85); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
border:1px solid var(--line); padding:10px 14px;
display:flex; align-items:center; gap:10px;
}
.mapPanel__km{ font-size:20px; color:var(--red); }
.mapPanel__chipLabel{
font-size:11px; color:#9aa6c0; text-transform:uppercase;
letter-spacing:1px; font-weight:700; line-height:1.2;
}
.contactCard{
border:1px solid var(--line); background:var(--card);
padding:clamp(28px,3vw,40px);
display:flex; flex-direction:column; gap:30px;
}
.contactCard__row{ display:flex; gap:16px; }
.dot{ width:14px; height:14px; border-radius:50%; flex-shrink:0; margin-top:4px; }
.dot--white{ background:#fff; border:4px solid var(--red); }
.dot--red{ background:var(--red); border:4px solid rgba(255,255,255,.25); }
.contactCard__label{
font-size:11px; font-weight:800; letter-spacing:2px;
text-transform:uppercase; color:var(--red); margin-bottom:6px;
}
.contactCard__addr{ font-size:17px; font-weight:700; color:#fff; line-height:1.4; }
.contactCard__tel{ display:inline-block; margin-top:8px; font-size:15px; font-weight:600; color:var(--body); }
.contactCard__divider{ height:1px; background:var(--line); }
.contactCard__actions{ margin-top:auto; display:flex; flex-direction:column; gap:12px; }
/* ---------- Karriere ---------- */
.karriere{
border:1px solid var(--line); padding:clamp(34px,5vw,60px);
display:flex; justify-content:space-between; align-items:center;
flex-wrap:wrap; gap:28px;
}
.karriere__text{ max-width:600px; }
.karriere__title{ font-size:clamp(34px,5vw,62px); color:#fff; margin-bottom:14px; }
.karriere__lead{ font-size:16px; color:var(--muted); }
/* ---------- Footer ---------- */
.footer{
border-top:1px solid var(--line);
padding:clamp(50px,6vw,80px) clamp(20px,5vw,60px) 40px;
}
.footer__grid{
display:grid; grid-template-columns:1.4fr 1fr 1fr; gap:40px;
padding-bottom:44px; border-bottom:1px solid var(--line);
}
.footer__logo{ margin-bottom:18px; }
.footer__blurb{ font-size:15px; color:var(--muted); max-width:300px; }
.footer__col{ font-size:14.5px; color:var(--muted); line-height:1.9; }
.footer__col a{ color:var(--muted); }
.footer__colHead{
color:#fff; font-weight:700; margin-bottom:12px;
letter-spacing:1.5px; font-size:12px; text-transform:uppercase;
}
.footer__bar{
display:flex; justify-content:space-between; flex-wrap:wrap; gap:14px;
padding-top:24px; font-size:13px; color:#5a6274;
}
.footer__legal{ display:flex; gap:22px; }
.footer__legal a{ color:#5a6274; }
/* ---------- Reveal on scroll ---------- */
[data-reveal]{
opacity:0; transform:translateY(28px);
transition:opacity .7s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.2,.7,.2,1);
}
[data-reveal].is-visible{ opacity:1; transform:none; }
/* ---------- Responsive ---------- */
@media (max-width:980px){
.kontakt__grid{ grid-template-columns:1fr; }
.footer__grid{ grid-template-columns:1fr; gap:30px; }
.section__head{ margin-bottom:44px; }
}
@media (max-width:680px){
/* Nav → fixed mobile header + hamburger */
.marquee{ display:none; }
.nav, .nav.is-scrolled{
position:fixed; top:0; left:0; right:0;
background:rgba(10,14,24,.95);
-webkit-backdrop-filter:blur(12px); backdrop-filter:blur(12px);
padding:12px 20px;
box-shadow:0 1px 0 rgba(255,255,255,.08);
}
.nav__links{ display:none; }
.nav__cta{ display:none; }
.nav__burger{ display:flex; }
/* Hero */
.hero{ min-height:auto; }
.hero__type{ padding:104px 20px 0; }
.hero__h1{ font-size:clamp(44px,12vw,80px); }
.hero__lead{ font-size:16px; margin-top:20px; }
.hero__actions{ margin-top:28px; }
.scene{ height:clamp(200px,32vh,300px); }
.bus{ width:clamp(240px,62vw,440px); }
.bus__body{ height:clamp(80px,15vw,150px); }
.wheel{ width:clamp(34px,9vw,52px); height:clamp(34px,9vw,52px); }
/* Stats — 2 per row */
.stats{ padding:36px 20px; }
.stats__grid{ grid-template-columns:repeat(2,1fr); gap:22px; }
.stat__big{ font-size:clamp(40px,13vw,58px); }
/* Sections */
.section{ padding:64px 20px; }
.section--karriere{ padding:56px 20px; }
.section__head{ flex-direction:column; align-items:flex-start; gap:14px; margin-bottom:34px; }
.section__intro{ max-width:none; }
.section__title{ font-size:clamp(38px,10vw,60px); }
/* Photo band */
.photo__headline{ font-size:clamp(28px,8vw,44px); }
/* Map + contact */
.mapPanel{ min-height:300px; }
/* Karriere */
.karriere{ flex-direction:column; align-items:flex-start; padding:32px 22px; }
.karriere__cta{ width:100%; }
/* Footer */
.footer__bar{ flex-direction:column; gap:10px; }
}
@media (max-width:480px){
.hero__actions{ flex-direction:column; align-items:stretch; }
.hero__actions .btn{ width:100%; }
.fleetCard__head{ flex-wrap:wrap; gap:10px; }
.stats__grid{ gap:18px 20px; }
}
/* ---------- MapLibre marker labels ---------- */
.pin{ display:flex; flex-direction:column; align-items:center; gap:6px; }
.pin__label{
background:rgba(10,14,24,.9); border:1px solid rgba(255,255,255,.16);
padding:5px 10px; white-space:nowrap; text-align:center; border-radius:2px;
}
.pin__title{
font-family:'Anton',sans-serif; text-transform:uppercase; color:#fff;
font-size:14px; letter-spacing:.5px; line-height:1.05;
}
.pin__sub{
font-family:'Archivo',sans-serif; font-size:9px; font-weight:700;
letter-spacing:1.5px; text-transform:uppercase; color:#9aa6c0; margin-top:2px;
}
.pin__dot{ width:18px; height:18px; border-radius:50%; }
/* ---------- Keyframes ---------- */
@keyframes scrollx{ to{ background-position-x:-360px; } }
@keyframes scrollxFast{ to{ background-position-x:-120px; } }
@keyframes scrollFar{ to{ background-position-x:-800px; } }
@keyframes wheel{ to{ transform:rotate(360deg); } }
@keyframes marquee{ to{ transform:translateX(-50%); } }
@keyframes headlightPulse{ 0%,100%{ opacity:.55; } 50%{ opacity:.9; } }
@media (prefers-reduced-motion:reduce){
.hills,.hillsFar,.stripes,.marquee__track,.wheel,.bus__headlight{ animation:none !important; }
html{ scroll-behavior:auto; }
}

View File

@@ -0,0 +1,245 @@
# Handoff: Unger Reisen — Website-Redesign (One-Pager)
## Overview
A modern, bold one-page marketing website for **Unger Reisen**, an omnibus (coach/bus)
company from Niederbayern (Buch am Erlbach & Landshut, Germany). The page pitches their
services (coach hire, corporate/club trips, transfers), shows the fleet, displays both
locations on an interactive map, and drives visitors to call. Language: **German**.
The signature element is a **scroll-driven hero**: a stylized bus drives left→right across
the bottom of the hero as the user scrolls down.
## About the Design Files
The file in this bundle (`Unger Reisen.dc.html`) is a **design reference created in HTML**
a working prototype showing the intended look, motion, and behavior. It is **not** production
code to copy verbatim. The task is to **recreate this design in the target codebase's
environment** (React/Next.js, Vue, Astro, plain Vite, etc.) using that project's established
patterns, component library, and conventions. If no codebase exists yet, pick the most
appropriate stack (a React/Next.js + Tailwind setup fits this well) and implement it there.
> Note on the file format: the prototype is authored as a "Design Component" (`.dc.html`)
> with a small custom template runtime (`<x-dc>`, `{{ }}` holes, `<sc-for>`, a
> `class Component extends DCLogic`). **Ignore that runtime.** It is only a prototyping
> harness. Read it as: HTML structure + inline styles + a plain JS class whose `renderVals()`
> returns the data arrays and event handlers. Reimplement with normal components and state.
## Fidelity
**High-fidelity (hifi).** Final colors, typography, spacing, layout, motion, and copy are all
intentional. Recreate pixel-closely, but re-express the styling in the target system
(e.g. Tailwind classes / styled-components) rather than copying inline styles.
---
## Design Tokens
### Colors
| Token | Value | Usage |
|---|---|---|
| `--red` (accent) | `#C8102E` | Primary accent. Marquee, stat band, buttons, pins, route, section eyebrows. (Prototype default is `#C8102E`; original palette also used `#E4002B`. Curated options: `#E4002B`, `#D21F1F`, `#C8102E`, `#EE3524`.) |
| `--ink` / `--night` | `#0a0e18` | Page background (near-black navy) |
| `--paper` | `#f4f0e8` | Light text on dark; light card backgrounds |
| Card surface | `#111725` | Dark cards (services, fleet, contact) |
| Card surface (hover) | `#151d30` | Service card hover |
| Map panel bg | `#0d1420` | Map container background |
| `--line` | `rgba(255,255,255,.14)` | Hairline borders/dividers on dark |
| `--muted` | `#8b93a7` | Muted body text |
| Body text on dark | `#c8cdda` | Hero paragraph, secondary text |
| Sub-label text | `#9aa6c0` | Pin sublabels, small caps labels |
| Footer legal text | `#5a6274` / `#40485a` | Footer links & disclaimer |
| Input border (light card) | `#d3ccbe` | Form inputs (note: forms were later removed) |
### Typography
- **Display font:** `Anton` (Google Fonts). Used for all big headlines, stat numbers, logo,
pin labels. Always `text-transform: uppercase`, `letter-spacing: .5px`, `line-height: .9`.
- **Body/UI font:** `Archivo` (Google Fonts), weights 400900. Body, nav, labels, buttons.
- Google Fonts import:
`https://fonts.googleapis.com/css2?family=Anton&family=Archivo:wght@400;500;600;700;800;900&display=swap`
- Type scale (fluid, `clamp`):
- Hero H1: `clamp(64px, 13vw, 190px)`
- Section H2 (Anton): `clamp(40px, 7vw, 9096px)`
- Stat numbers: `clamp(44px, 6vw, 72px)`
- Fleet seat number: `40px`
- Body: `1619px`; nav/labels/buttons: `1215px` uppercase, `letter-spacing 13px`
### Spacing / Layout
- Section vertical padding: `clamp(70px, 10vw, 130px)`; horizontal: `clamp(20px, 5vw, 60px)`
- Card grid gaps: `16px`; large column gaps: `clamp(30px, 5vw, 70px)`
- Border radius: mostly **sharp** (02px). Buttons are rectangular (no pill). Only pins/dots are circular.
### Shadows
- Bus body: `0 18px 30px -10px rgba(0,0,0,.6)`
- Map pin dot: `0 0 0 5px <red>40, 0 2px 6px rgba(0,0,0,.55)` (red glow ring + drop)
- Buttons lift on hover via `transform: translateY(-2px)` (transition `.2s ease`)
---
## Screens / Views
Single continuous page. Sections top→bottom:
### 1. Top Marquee
- Full-width red (`--red`) bar, white uppercase text scrolling right→left infinitely.
- Content, `/`-separated: "Reisebusse mit Fahrer / Firmen & Vereine / Tagesfahrten /
Transfers rund um die Uhr / Niederbayern".
- Implementation: duplicate the track and animate `translateX(0 → -50%)` linear infinite (~24s).
- `font-size:13px; font-weight:700; letter-spacing:2px`.
### 2. Nav (fixed/absolute over hero)
- Left: logo — "UNGER" (Anton, 26px, white) + "REISEN" (Archivo, 11px, letter-spacing 5px, red).
- Right: links (uppercase, 13.5px, weight 600) — Leistungen, Flotte, Karriere, Kontakt —
plus a red CTA button **"Jetzt anrufen"** (`href="tel:+498762896"`).
- Sits at `top:39px` (below marquee) when at page top; **on scroll >40px** it solidifies:
background `rgba(10,14,24,.9)`, `backdrop-filter: blur(12px)`, moves to `top:0`, reduces
padding, adds a hairline bottom shadow. Transition `.35s ease`.
### 3. Hero (`#top`, min-height:88vh)
- Dark radial-gradient bg: `radial-gradient(120% 90% at 70% 10%, #182238 0%, #0a0e18 60%)`.
- Eyebrow: red rule + "OMNIBUSUNTERNEHMEN SEIT GENERATIONEN".
- H1 (Anton): "Wir bringen / Sie **weiter.**" — "weiter." in red.
- Paragraph (max 520px, `#c8cdda`): "Moderne Reisebusse mit erfahrenen Fahrern — für Firmen,
Vereine und Gruppen in ganz Bayern und Europa."
- Buttons: red **"Jetzt anrufen →"** (`tel:`), outline **"Unsere Flotte"** (`#flotte`).
- **Driving scene** (bottom of hero, `height: clamp(280px,42vh,420px)`, pinned bottom):
- Layered parallax: far scenery hills, near hills, a dark road strip, red-dashed lane stripes.
Each is a repeating radial/linear-gradient background animated via `background-position-x`
at different speeds (far slow ~40s, near ~18s, stripes fast ~.5s) — evokes forward motion.
- **The bus**: CSS-drawn side view — cream body with red mid-stripe, windshield, window strip,
"UNGER REISEN" wordmark, door, two spinning wheels (`@keyframes wheel` rotate, .5s linear),
a pulsing headlight glow. Bus width `clamp(380px,52vw,620px)`.
- **Scroll-driven horizontal motion:** on scroll, compute
`hp = clamp(scrollY / (heroHeight * 0.9), 0, 1)` and set the bus
`transform: translate(x, bob)` where `x` interpolates from just off-left
(`-busWidth*0.30`) to `sceneWidth - busWidth*0.70`, and `bob = sin(now/380)*4` (gentle
vertical bob; 0 if reduced-motion). Driven by `requestAnimationFrame`.
- Respect `prefers-reduced-motion`: disable marquee, hills, stripes, wheel spin, and bob.
### 4. Stat Band
- Full-width red bg, white. Auto-fit grid of 4 stats (Anton big number + label):
- **2** — Standorte in Bayern
- **60+** — Sitzplätze pro Reisebus
- **24/7** — Für Sie erreichbar
- **∞** — Ziele in ganz Europa
### 5. Leistungen (`#leistungen`)
- Header row: H2 "Das fahren wir für Sie" + intro paragraph.
- Auto-fit grid (min 260px) of 4 dark cards (`#111725`, hairline border, min-height 260px).
Each: big red Anton index (0104), title (uppercase 22px/800), description. Hover: bg
`#151d30` + `translateY(-4px)`.
1. **Busanmietung** — "Moderne Reisebusse mit Fahrer — flexibel für einzelne Fahrten oder mehrtägige Touren."
2. **Firmenreisen** — "Betriebsausflüge, Tagungen und Events. Ihr Team sicher und entspannt am Ziel."
3. **Vereinsfahrten** — "Ausflüge, Feste und Sportevents — komfortabel für die ganze Gruppe."
4. **Transfers** — "Flughafen-, Hotel- und Event-Transfers. Pünktlich und rund um die Uhr planbar."
### 6. Real Photo Band
- Full-bleed band (`height: clamp(320px,48vw,520px)`) with a background bus photo, dark
gradient overlay (`rgba(10,14,24,.55 → .75)`), centered Anton headline:
"Komfort, auf den Sie sich verlassen können."
- Prototype image URL: `https://www.unger-reisen.de/wp-content/uploads/startslider-bus-1024x366.png`
(external; **replace with a licensed/owned photo** — see Assets). Has an `onerror` fallback
to a gradient.
### 7. Flotte (`#flotte`)
- Eyebrow "Unsere Flotte" + H2 "Für jede Gruppengröße".
- Auto-fit grid (min 280px) of 3 cards. Card header row: name (Anton 30px) on the left, and a
right-aligned inline seat unit: small "bis" + big red number (Anton 40px) + small "Sitze".
Body: description + tag chips (hairline-bordered pills of text).
- **Reisebus** — bis **60** — "Der Große für lange Strecken und große Gruppen — voll ausgestattet." Tags: Klimaanlage, WC, Bordküche, WLAN.
- **Midibus** — bis **35** — "Wendig und komfortabel — ideal für mittlere Gruppen und Tagestouren." Tags: Klimaanlage, Komfortbestuhlung.
- **Kleinbus** — bis **19** — "Persönlich und flexibel für kleine Gruppen, Transfers und Sonderfahrten." Tags: Flexibel, Wendig.
### 8. Kontakt + Karte (`#kontakt`)
- Eyebrow "So erreichen Sie uns" + H2 "Zwei Standorte. / Ein Anruf."
- Two-column grid (`1.35fr / .95fr`, stack on mobile):
- **Left — interactive map** (panel `min-height:440px`, bg `#0d1420`, hairline border):
- **MapLibre GL JS** map, dark Carto style
`https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json`.
- Two custom HTML markers (`anchor:'bottom'`): a label box (Anton title + Archivo sub) above
a circular dot with a red glow ring.
- **Landshut** — "Niederlassung" — coord `[12.1508, 48.5449]` — dot red, ring white.
- **Buch a. Erlbach** — "Hauptsitz" — coord `[12.0517, 48.4703]` — dot white, ring red.
- **Route line** between the two coords: a GeoJSON `LineString`, red, `width 3`,
`line-dasharray [1.6,1.4]`, plus a wider low-opacity casing (`width 9, opacity .18`).
- `NavigationControl` (zoom + compass) top-right. **`scrollZoom` disabled** so page scroll
isn't hijacked. `fitBounds` to both points with padding `{top:96,bottom:70,left:70,right:70}`,
`maxZoom:12`. Call `map.resize()` shortly after load.
- Overlay chip (bottom-left, `pointer-events:none`): "**15 km** zwischen den Standorten".
- Requires `maplibre-gl` (JS + CSS). In prototype loaded from
`https://unpkg.com/maplibre-gl@4.7.1/...`; in a real app install the npm package.
Map tiles load **online** — expected for a live site.
- **Right — contact card** (`#111725`, hairline border):
- **Hauptsitz** (white dot / red ring): "Eichenstr. 7+9, Vatersdorf · 84172 Buch am Erlbach"
· Tel. +49 (0)8762 / 896
- divider
- **Niederlassung** (red dot / white ring): "Jenaer Straße 4a · 84034 Landshut"
· Tel. +49 (0)871 / 96 58 586-0
- CTAs pinned to bottom: red **"Jetzt anrufen"** (`tel:+498762896`) + outline
**"info@unger-reisen.de"** (`mailto:`).
### 9. Karriere (`#karriere`)
- Bordered band. Left: eyebrow "Karriere & Jobs", H2 "Fahren Sie mit uns", paragraph
"Wir suchen Busfahrer:innen, Begleitpersonen und Sachbearbeiter:innen — steigen Sie ein."
- Right: white button **"Offene Stellen →"** (`mailto:info@unger-reisen.de`).
### 10. Footer
- 3-column grid on `--ink`, hairline top border:
- Logo + blurb: "Günter Unger e.K. — Omnibus- & Verkehrsunternehmen. Reisen erster Klasse aus Niederbayern."
- **Hauptsitz**: Eichenstr. 7+9, Vatersdorf · 84172 Buch am Erlbach · Tel. +49 (0)8762 / 896
- **Niederlassung**: Jenaer Straße 4a · 84034 Landshut · Tel. +49 (0)871 / 96 58 586-0
- Bottom bar: "© 2026 Unger Reisen · Alle Rechte vorbehalten" + Impressum / Datenschutz / AGB.
- Small disclaimer: "Konzept-Entwurf für ein mögliches Website-Redesign · noch nicht offiziell."
(**Remove this line** once it's the real site.)
---
## Interactions & Behavior
- **Nav solidify on scroll** (>40px) — see §2.
- **Scroll-driven bus** across hero — see §3. Core formula:
`hp = clamp(scrollY/(heroHeight*0.9),0,1)`; `x = lerp(-busW*0.3, sceneW - busW*0.7, hp)`.
- **Reveal on scroll**: elements marked for reveal start `opacity:0; translateY(28px)` and
animate to `opacity:1; none` via IntersectionObserver (threshold ~0.12), transition
`.7s cubic-bezier(.2,.7,.2,1)`. Provide a safety fallback that reveals everything after ~2.5s.
- **Marquee / hills / stripes / wheels**: continuous CSS keyframe loops (see §1, §3).
- **Buttons/links hover**: `translateY(-2px)` or border color → white.
- **Map**: pan/zoom via drag + control buttons; scroll-zoom disabled; compass resets bearing.
- **Smooth anchor scrolling** for nav links (`scroll-behavior: smooth`) to `#leistungen`,
`#flotte`, `#karriere`, `#kontakt`, `#top`.
- **Reduced motion**: disable all decorative animation; keep static end states.
## State Management
Minimal — the page is largely presentational. In a component framework you need:
- Nav-scrolled boolean (or do it with a scroll listener / IntersectionObserver on a sentinel).
- The bus `x`/`bob` transform driven by a scroll + rAF loop (a ref + effect).
- Reveal-on-scroll via IntersectionObserver (a small reusable hook/directive).
- The MapLibre instance held in a ref; init in mount effect, `remove()` on unmount.
- Data arrays for **stats**, **services**, **fleet** (see copy above) — render with `.map`.
- (An earlier version had a controlled contact form; it was **removed** in favor of call/email CTAs.
If you re-add a form, wire name/contact/dest/date/pax/message + a submitted state.)
## Configurable knobs (from the prototype's props)
- **accentColor** — the red. Default `#C8102E`; options `#E4002B / #D21F1F / #C8102E / #EE3524`.
Applied as a CSS variable `--red`; also recolors the map route on change. Expose as a theme
token in the real app.
## Assets
- **Fonts:** Anton + Archivo (Google Fonts) — see import above.
- **Icons:** none required (all visuals are CSS/SVG-drawn or map markers). The original shadcn
map component referenced `lucide-react` for map control icons — use those (Plus, Minus, Locate,
Maximize, Compass/X) if you build custom controls; MapLibre's built-in `NavigationControl`
needs no icon assets.
- **Bus hero photo (§6):** placeholder points at unger-reisen.de. **Replace with an owned or
licensed photo** of an Unger coach. If you need a temporary stand-in, use a royalty-free coach
photo (e.g. an Unsplash coach/bus image) and swap later.
- **Map:** MapLibre GL JS + CSS (npm `maplibre-gl`), Carto "dark-matter" style (free, attribution
required — keep the attribution control). Tiles fetched at runtime.
- **No logo file** — the "UNGER REISEN" logo is set in type (Anton + Archivo).
## Files
- `Unger Reisen.dc.html` — the full design prototype (all sections, styles, and JS behavior).
Read the `<x-dc>…</x-dc>` body for structure/markup, the `<style>` in `<helmet>` for keyframes,
and the `class Component` script for the scroll/bus/map/reveal logic and the data arrays.
## Contact facts (verbatim, safe to reuse)
- Hauptsitz: Eichenstr. 7+9, Vatersdorf, 84172 Buch am Erlbach — Tel. +49 (0)8762 / 896
- Niederlassung: Jenaer Straße 4a, 84034 Landshut — Tel. +49 (0)871 / 96 58 586-0
- E-Mail: info@unger-reisen.de
- Entity: Günter Unger e.K.
- Coordinates used on the map: Buch a. Erlbach `[12.0517, 48.4703]`, Landshut `[12.1508, 48.5449]`.

View File

@@ -0,0 +1,466 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Archivo:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" rel="stylesheet">
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{
--red:#E4002B; --ink:#0a0e18; --night:#0a0e18; --paper:#f4f0e8;
--cream:#efe9dc; --line:rgba(255,255,255,.14); --muted:#8b93a7;
}
html{scroll-behavior:smooth}
body{background:var(--ink); color:var(--paper);
font-family:'Archivo',system-ui,sans-serif; -webkit-font-smoothing:antialiased;
line-height:1.5; overflow-x:hidden}
a{color:inherit; text-decoration:none}
::selection{background:var(--red); color:#fff}
input,textarea,button{font-family:inherit}
input:focus,textarea:focus{outline:none; border-color:var(--ink)!important}
.display{font-family:'Anton',sans-serif; font-weight:400; text-transform:uppercase; letter-spacing:.5px; line-height:.9}
@keyframes scrollx{to{background-position-x:-360px}}
@keyframes scrollxFast{to{background-position-x:-120px}}
@keyframes scrollFar{to{background-position-x:-800px}}
@keyframes wheel{to{transform:rotate(360deg)}}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
@keyframes marquee{to{transform:translateX(-50%)}}
@keyframes headlightPulse{0%,100%{opacity:.55}50%{opacity:.9}}
@keyframes ping{0%{transform:scale(1);opacity:.7}70%,100%{transform:scale(3);opacity:0}}
@keyframes routedraw{to{stroke-dashoffset:0}}
.mapPing{transform-origin:center; transform-box:fill-box; animation:ping 2.6s ease-out infinite}
.mapRoute{stroke-dasharray:6 9; stroke-dashoffset:520; animation:routedraw 3.2s ease-out .3s forwards}
@media (prefers-reduced-motion:reduce){
.hills,.hillsFar,.stripes,.marqueeTrack,.wheelSpin,.busBob,.mapPing,.mapRoute,.mapBus{animation:none!important}
.mapRoute{stroke-dashoffset:0}
}
</style>
</helmet>
<div style="position:relative">
<!-- TOP MARQUEE -->
<div style="background:var(--red); color:#fff; overflow:hidden; white-space:nowrap; padding:9px 0; position:relative; z-index:60; font-weight:700; font-size:13px; letter-spacing:2px; text-transform:uppercase">
<div class="marqueeTrack" style="display:inline-flex; gap:0; animation:marquee 24s linear infinite; will-change:transform">
<span style="display:inline-flex">
<span style="padding:0 26px">Reisebusse mit Fahrer</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Firmen &amp; Vereine</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Tagesfahrten</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Transfers rund um die Uhr</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Niederbayern</span><span style="opacity:.55">/</span>
</span>
<span style="display:inline-flex" aria-hidden="true">
<span style="padding:0 26px">Reisebusse mit Fahrer</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Firmen &amp; Vereine</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Tagesfahrten</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Transfers rund um die Uhr</span><span style="opacity:.55">/</span>
<span style="padding:0 26px">Niederbayern</span><span style="opacity:.55">/</span>
</span>
</div>
</div>
<!-- NAV -->
<nav data-nav="1" style="position:absolute; top:39px; left:0; right:0; z-index:50; display:flex; align-items:center; justify-content:space-between; padding:22px clamp(20px,5vw,60px); transition:background .35s ease, padding .35s ease, box-shadow .35s ease">
<a href="#top" style="display:flex; align-items:baseline; gap:9px">
<span class="display" style="font-size:26px; color:#fff">UNGER</span>
<span style="font-weight:700; font-size:11px; letter-spacing:5px; color:var(--red)">REISEN</span>
</a>
<div style="display:flex; align-items:center; gap:34px">
<div style="display:flex; gap:28px; font-size:13.5px; font-weight:600; text-transform:uppercase; letter-spacing:1px">
<a href="#leistungen" data-navlink>Leistungen</a>
<a href="#flotte" data-navlink>Flotte</a>
<a href="#karriere" data-navlink>Karriere</a>
<a href="#kontakt" data-navlink>Kontakt</a>
</div>
<a href="tel:+498762896" style="background:var(--red); color:#fff; padding:12px 24px; font-size:13px; font-weight:700; text-transform:uppercase; letter-spacing:1px; transition:transform .2s ease" style-hover="transform:translateY(-2px); color:#fff">Jetzt anrufen</a>
</div>
</nav>
<!-- HERO with animated driving scene -->
<header id="top" style="position:relative; min-height:88vh; display:flex; flex-direction:column; justify-content:center; overflow:hidden; background:radial-gradient(120% 90% at 70% 10%, #182238 0%, #0a0e18 60%)">
<!-- Giant type -->
<div style="position:relative; z-index:20; padding:150px clamp(20px,5vw,60px) 0; pointer-events:none">
<div data-reveal style="display:inline-flex; align-items:center; gap:12px; font-size:12px; font-weight:700; letter-spacing:3px; text-transform:uppercase; color:var(--red); margin-bottom:22px; pointer-events:auto">
<span style="width:34px; height:2px; background:var(--red)"></span>Omnibusunternehmen seit Generationen
</div>
<h1 class="display" data-reveal style="font-size:clamp(64px,13vw,190px); color:#fff; letter-spacing:-1px">
Wir bringen<br>Sie <span style="color:var(--red)">weiter.</span>
</h1>
<p data-reveal style="max-width:520px; margin-top:26px; font-size:clamp(16px,1.5vw,19px); color:#c8cdda; pointer-events:auto">
Moderne Reisebusse mit erfahrenen Fahrern — für Firmen, Vereine und Gruppen in ganz Bayern und Europa.
</p>
<div data-reveal style="display:flex; flex-wrap:wrap; gap:14px; margin-top:34px; pointer-events:auto">
<a href="tel:+498762896" style="background:var(--red); color:#fff; padding:17px 34px; font-size:15px; font-weight:700; text-transform:uppercase; letter-spacing:1px; display:inline-flex; align-items:center; gap:10px; transition:transform .2s ease" style-hover="transform:translateY(-2px); color:#fff">Jetzt anrufen →</a>
<a href="#flotte" style="border:1.5px solid rgba(255,255,255,.3); color:#fff; padding:17px 30px; font-size:15px; font-weight:700; text-transform:uppercase; letter-spacing:1px; transition:border-color .2s ease" style-hover="border-color:#fff">Unsere Flotte</a>
</div>
</div>
<!-- DRIVING SCENE -->
<div aria-hidden="true" style="position:relative; z-index:10; height:clamp(280px,42vh,420px); margin-top:auto; width:100%">
<!-- far scenery -->
<div class="hillsFar" style="position:absolute; bottom:64px; left:0; right:0; height:120px; background-repeat:repeat-x; background-size:800px 120px; opacity:.4; animation:scrollFar 40s linear infinite; background-image:radial-gradient(120px 120px at 120px 120px, #223052 0 99%, transparent 100%), radial-gradient(160px 160px at 460px 150px, #1c2743 0 99%, transparent 100%), radial-gradient(120px 120px at 700px 130px, #223052 0 99%, transparent 100%)"></div>
<!-- near hills -->
<div class="hills" style="position:absolute; bottom:58px; left:0; right:0; height:90px; background-repeat:repeat-x; background-size:360px 90px; opacity:.7; animation:scrollx 18s linear infinite; background-image:radial-gradient(90px 90px at 70px 100px, #2a3a5f 0 99%, transparent 100%), radial-gradient(120px 120px at 250px 120px, #26355699 0 99%, transparent 100%)"></div>
<!-- road -->
<div style="position:absolute; bottom:0; left:0; right:0; height:64px; background:#11151f; border-top:3px solid #1c2233"></div>
<!-- lane stripes -->
<div class="stripes" style="position:absolute; bottom:29px; left:0; right:0; height:5px; background-repeat:repeat-x; background-size:120px 5px; animation:scrollxFast .5s linear infinite; background-image:linear-gradient(90deg, rgba(228,0,43,.9) 0 60px, transparent 60px 120px)"></div>
<!-- BUS (scroll-driven left→right) -->
<div data-bus class="busBob" style="position:absolute; bottom:52px; left:0; width:clamp(380px,52vw,620px); will-change:transform; transform:translateX(-30%)">
<!-- headlight glow -->
<div style="position:absolute; right:-40px; bottom:6px; width:120px; height:60px; background:radial-gradient(ellipse at left center, rgba(255,220,120,.5), transparent 70%); animation:headlightPulse 2s ease-in-out infinite"></div>
<!-- body -->
<div style="position:relative; height:clamp(120px,17vw,178px); background:linear-gradient(180deg,#f2f0ea 0%,#dfdbd0 100%); border-radius:22px 30px 12px 12px; box-shadow:0 18px 30px -10px rgba(0,0,0,.6); overflow:hidden">
<!-- red stripe -->
<div style="position:absolute; bottom:24%; left:0; right:0; height:15%; background:var(--red)"></div>
<!-- windshield -->
<div style="position:absolute; top:14%; right:5%; width:20%; height:42%; background:linear-gradient(120deg,#8fb4d8,#4d6f96); border-radius:6px 14px 6px 6px"></div>
<!-- windows -->
<div style="position:absolute; top:14%; left:6%; width:66%; height:42%; border-radius:6px; background-image:linear-gradient(90deg, transparent 0 3%, #6f92b8 3% 15%, transparent 15% 18%); background-size:16.6% 100%; background-repeat:repeat-x; background-color:#4d6f96"></div>
<!-- brand on bus -->
<div class="display" style="position:absolute; bottom:6%; left:7%; font-size:clamp(11px,1.5vw,15px); color:var(--ink); letter-spacing:1px">UNGER<span style="color:var(--red)"> REISEN</span></div>
<!-- door -->
<div style="position:absolute; top:14%; right:27%; width:8%; height:70%; background:#37506f; border-radius:4px"></div>
</div>
<!-- wheels -->
<div class="wheelSpin" style="position:absolute; bottom:-14px; left:16%; width:clamp(42px,6vw,60px); height:clamp(42px,6vw,60px); border-radius:50%; background:#14181f; border:6px solid #23262e; animation:wheel .5s linear infinite">
<div style="position:absolute; inset:34%; background:#3a3f49; border-radius:50%"></div>
<div style="position:absolute; top:2px; left:50%; width:2px; height:40%; background:#454a55; transform:translateX(-50%)"></div>
</div>
<div class="wheelSpin" style="position:absolute; bottom:-14px; right:18%; width:clamp(42px,6vw,60px); height:clamp(42px,6vw,60px); border-radius:50%; background:#14181f; border:6px solid #23262e; animation:wheel .5s linear infinite">
<div style="position:absolute; inset:34%; background:#3a3f49; border-radius:50%"></div>
<div style="position:absolute; top:2px; left:50%; width:2px; height:40%; background:#454a55; transform:translateX(-50%)"></div>
</div>
</div>
</div>
</header>
<!-- STATS BAND -->
<section style="background:var(--red); color:#fff; padding:clamp(34px,5vw,54px) clamp(20px,5vw,60px)">
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:30px">
<sc-for list="{{ stats }}" as="s" hint-placeholder-count="4">
<div data-reveal>
<div class="display" style="font-size:clamp(44px,6vw,72px); color:#fff; line-height:.85">{{ s.big }}</div>
<div style="font-size:14px; font-weight:600; margin-top:10px; color:rgba(255,255,255,.85)">{{ s.label }}</div>
</div>
</sc-for>
</div>
</section>
<!-- LEISTUNGEN -->
<section id="leistungen" style="padding:clamp(70px,10vw,130px) clamp(20px,5vw,60px)">
<div data-reveal style="display:flex; align-items:flex-end; justify-content:space-between; flex-wrap:wrap; gap:20px; margin-bottom:54px">
<h2 class="display" style="font-size:clamp(40px,7vw,96px); color:#fff; max-width:12ch">Das fahren wir für Sie</h2>
<p style="max-width:320px; color:var(--muted); font-size:16px">Ein Ansprechpartner, ein Bus, alles organisiert — von der Anfrage bis zur Rückfahrt.</p>
</div>
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:16px">
<sc-for list="{{ services }}" as="s" hint-placeholder-count="4">
<div data-reveal style="position:relative; background:#111725; border:1px solid var(--line); padding:38px 30px 34px; min-height:260px; display:flex; flex-direction:column; overflow:hidden; transition:background .3s ease, transform .3s ease" style-hover="background:#151d30; transform:translateY(-4px)">
<div class="display" style="font-size:60px; color:rgba(228,0,43,.9); line-height:.8">{{ s.no }}</div>
<h3 style="font-size:22px; font-weight:800; margin:auto 0 10px; text-transform:uppercase; letter-spacing:.3px">{{ s.title }}</h3>
<p style="font-size:15px; color:var(--muted); line-height:1.55">{{ s.desc }}</p>
</div>
</sc-for>
</div>
</section>
<!-- REAL PHOTO BAND -->
<section style="position:relative; height:clamp(320px,48vw,520px); overflow:hidden; display:flex; align-items:center; justify-content:center; text-align:center">
<img src="https://www.unger-reisen.de/wp-content/uploads/startslider-bus-1024x366.png" alt="Unger Reisen Reisebus" style="position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:center" onerror="this.parentNode.style.background='linear-gradient(135deg,#151d30,#0a0e18)'; this.style.display='none'">
<div style="position:absolute; inset:0; background:linear-gradient(180deg, rgba(10,14,24,.55), rgba(10,14,24,.75))"></div>
<div data-reveal style="position:relative; z-index:2; padding:20px">
<div class="display" style="font-size:clamp(30px,5vw,64px); color:#fff; max-width:16ch; margin:0 auto">Komfort, auf den Sie sich verlassen können.</div>
</div>
</section>
<!-- FLOTTE -->
<section id="flotte" style="padding:clamp(70px,10vw,130px) clamp(20px,5vw,60px)">
<div data-reveal style="margin-bottom:48px">
<div style="font-size:12px; font-weight:700; letter-spacing:3px; text-transform:uppercase; color:var(--red); margin-bottom:16px">Unsere Flotte</div>
<h2 class="display" style="font-size:clamp(40px,7vw,90px); color:#fff">Für jede Gruppengröße</h2>
</div>
<div style="display:grid; grid-template-columns:repeat(auto-fit,minmax(280px,1fr)); gap:16px">
<sc-for list="{{ fleet }}" as="f" hint-placeholder-count="3">
<div data-reveal style="border:1px solid var(--line); background:#111725; display:flex; flex-direction:column">
<div style="padding:34px 30px 22px; border-bottom:1px solid var(--line); display:flex; align-items:center; justify-content:space-between; gap:16px">
<h3 class="display" style="font-size:30px; color:#fff">{{ f.name }}</h3>
<div style="display:flex; align-items:baseline; gap:8px; white-space:nowrap">
<span style="font-size:12px; color:var(--muted); text-transform:uppercase; letter-spacing:1px">bis</span>
<span class="display" style="font-size:40px; color:var(--red); line-height:1">{{ f.seats }}</span>
<span style="font-size:12px; color:var(--muted); text-transform:uppercase; letter-spacing:1px">Sitze</span>
</div>
</div>
<div style="padding:24px 30px 30px">
<p style="font-size:15px; color:var(--muted); line-height:1.55; margin-bottom:20px">{{ f.desc }}</p>
<div style="display:flex; flex-wrap:wrap; gap:8px">
<sc-for list="{{ f.tags }}" as="t" hint-placeholder-count="3">
<span style="font-size:12px; font-weight:600; border:1px solid var(--line); padding:6px 13px; color:#c8cdda">{{ t }}</span>
</sc-for>
</div>
</div>
</div>
</sc-for>
</div>
</section>
<!-- KONTAKT + KARTE -->
<section id="kontakt" style="padding:clamp(70px,10vw,130px) clamp(20px,5vw,60px)">
<div data-reveal style="margin-bottom:48px">
<div style="font-size:12px; font-weight:700; letter-spacing:3px; text-transform:uppercase; color:var(--red); margin-bottom:16px">So erreichen Sie uns</div>
<h2 class="display" style="font-size:clamp(40px,7vw,90px); color:#fff">Zwei Standorte.<br>Ein Anruf.</h2>
</div>
<div style="display:grid; grid-template-columns:1.35fr .95fr; gap:20px; align-items:stretch">
<!-- SPECIAL MAP (interactive MapLibre) -->
<div data-reveal style="position:relative; border:1px solid var(--line); background:#0d1420; overflow:hidden; min-height:440px; border-radius:2px">
<div id="unger-map" style="position:absolute; inset:0"></div>
<div style="position:absolute; left:16px; bottom:16px; z-index:2; pointer-events:none; background:rgba(10,14,24,.85); backdrop-filter:blur(6px); border:1px solid var(--line); padding:10px 14px; display:flex; align-items:center; gap:10px">
<span class="display" style="font-size:20px; color:var(--red)">15 km</span>
<span style="font-size:11px; color:#9aa6c0; text-transform:uppercase; letter-spacing:1px; font-weight:700; line-height:1.2">zwischen<br>den Standorten</span>
</div>
</div>
<!-- CONTACT CARD -->
<div data-reveal style="border:1px solid var(--line); background:#111725; padding:clamp(28px,3vw,40px); display:flex; flex-direction:column; gap:30px">
<div style="display:flex; gap:16px">
<div style="width:14px; height:14px; border-radius:50%; background:#fff; border:4px solid var(--red); flex-shrink:0; margin-top:4px"></div>
<div>
<div style="font-size:11px; font-weight:800; letter-spacing:2px; text-transform:uppercase; color:var(--red); margin-bottom:6px">Hauptsitz</div>
<div style="font-size:17px; font-weight:700; color:#fff; line-height:1.4">Eichenstr. 7+9, Vatersdorf<br>84172 Buch am Erlbach</div>
<a href="tel:+498762896" style="display:inline-block; margin-top:8px; font-size:15px; font-weight:600; color:#c8cdda">Tel. +49 (0)8762 / 896</a>
</div>
</div>
<div style="height:1px; background:var(--line)"></div>
<div style="display:flex; gap:16px">
<div style="width:14px; height:14px; border-radius:50%; background:var(--red); border:4px solid rgba(255,255,255,.25); flex-shrink:0; margin-top:4px"></div>
<div>
<div style="font-size:11px; font-weight:800; letter-spacing:2px; text-transform:uppercase; color:var(--red); margin-bottom:6px">Niederlassung</div>
<div style="font-size:17px; font-weight:700; color:#fff; line-height:1.4">Jenaer Straße 4a<br>84034 Landshut</div>
<a href="tel:+4987196585860" style="display:inline-block; margin-top:8px; font-size:15px; font-weight:600; color:#c8cdda">Tel. +49 (0)871 / 96 58 586-0</a>
</div>
</div>
<div style="margin-top:auto; display:flex; flex-direction:column; gap:12px">
<a href="tel:+498762896" style="background:var(--red); color:#fff; padding:16px; text-align:center; font-size:14px; font-weight:700; text-transform:uppercase; letter-spacing:1px; transition:transform .2s ease" style-hover="transform:translateY(-2px); color:#fff">Jetzt anrufen</a>
<a href="mailto:info@unger-reisen.de" style="border:1.5px solid var(--line); color:#fff; padding:16px; text-align:center; font-size:14px; font-weight:700; text-transform:uppercase; letter-spacing:1px; transition:border-color .2s ease" style-hover="border-color:#fff">info@unger-reisen.de</a>
</div>
</div>
</div>
</section>
<!-- KARRIERE -->
<section id="karriere" style="padding:clamp(60px,8vw,110px) clamp(20px,5vw,60px)">
<div data-reveal style="border:1px solid var(--line); padding:clamp(34px,5vw,60px); display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:28px">
<div style="max-width:600px">
<div style="font-size:12px; font-weight:700; letter-spacing:3px; text-transform:uppercase; color:var(--red); margin-bottom:16px">Karriere &amp; Jobs</div>
<h2 class="display" style="font-size:clamp(34px,5vw,62px); color:#fff; margin-bottom:14px">Fahren Sie mit uns</h2>
<p style="font-size:16px; color:var(--muted)">Wir suchen Busfahrer:innen, Begleitpersonen und Sachbearbeiter:innen — steigen Sie ein.</p>
</div>
<a href="mailto:info@unger-reisen.de" style="background:#fff; color:var(--ink); padding:17px 34px; font-size:14px; font-weight:700; text-transform:uppercase; letter-spacing:1px; white-space:nowrap; transition:transform .2s ease" style-hover="transform:translateY(-2px); color:var(--ink)">Offene Stellen →</a>
</div>
</section>
<!-- FOOTER -->
<footer style="border-top:1px solid var(--line); padding:clamp(50px,6vw,80px) clamp(20px,5vw,60px) 40px">
<div style="display:grid; grid-template-columns:1.4fr 1fr 1fr; gap:40px; padding-bottom:44px; border-bottom:1px solid var(--line)">
<div>
<div style="display:flex; align-items:baseline; gap:9px; margin-bottom:18px">
<span class="display" style="font-size:30px; color:#fff">UNGER</span>
<span style="font-weight:700; font-size:11px; letter-spacing:5px; color:var(--red)">REISEN</span>
</div>
<p style="font-size:15px; color:var(--muted); max-width:300px">Günter Unger e.K. — Omnibus- &amp; Verkehrsunternehmen. Reisen erster Klasse aus Niederbayern.</p>
</div>
<div style="font-size:14.5px; color:var(--muted); line-height:1.9">
<div style="color:#fff; font-weight:700; margin-bottom:12px; letter-spacing:1.5px; font-size:12px; text-transform:uppercase">Hauptsitz</div>
Eichenstr. 7+9, Vatersdorf<br>84172 Buch am Erlbach<br><a href="tel:+498762896" style="color:var(--muted)">Tel. +49 (0)8762 / 896</a>
</div>
<div style="font-size:14.5px; color:var(--muted); line-height:1.9">
<div style="color:#fff; font-weight:700; margin-bottom:12px; letter-spacing:1.5px; font-size:12px; text-transform:uppercase">Niederlassung</div>
Jenaer Straße 4a<br>84034 Landshut<br><a href="tel:+4987196585860" style="color:var(--muted)">Tel. +49 (0)871 / 96 58 586-0</a>
</div>
</div>
<div style="display:flex; justify-content:space-between; flex-wrap:wrap; gap:14px; padding-top:24px; font-size:13px; color:#5a6274">
<div>© 2026 Unger Reisen · Alle Rechte vorbehalten</div>
<div style="display:flex; gap:22px">
<a href="#" style="color:#5a6274">Impressum</a>
<a href="#" style="color:#5a6274">Datenschutz</a>
<a href="#" style="color:#5a6274">AGB</a>
</div>
</div>
<div style="margin-top:26px; font-size:11px; color:#40485a">Konzept-Entwurf für ein mögliches Website-Redesign · noch nicht offiziell.</div>
</footer>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;$preview&quot;:{&quot;width&quot;:1280,&quot;height&quot;:900},&quot;accentColor&quot;:{&quot;editor&quot;:&quot;color&quot;,&quot;default&quot;:&quot;#C8102E&quot;,&quot;tsType&quot;:&quot;string&quot;,&quot;options&quot;:[&quot;#E4002B&quot;,&quot;#D21F1F&quot;,&quot;#C8102E&quot;,&quot;#EE3524&quot;],&quot;section&quot;:&quot;Design&quot;}}">
class Component extends DCLogic {
state = { submitted:false, name:'', contact:'', dest:'', date:'', pax:'', msg:'' };
componentDidMount(){
this.applyTweaks();
this.initMap();
// Scroll-driven bus: drives left→right across the hero as you scroll
this._reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion:reduce)').matches;
this._tick = () => {
const bus = document.querySelector('.busBob');
const hero = document.getElementById('top');
if(bus && hero){
const hp = Math.min(1, Math.max(0, window.scrollY / (hero.offsetHeight * 0.9)));
const scene = bus.parentNode;
const sceneW = scene ? scene.offsetWidth : window.innerWidth;
const busW = bus.offsetWidth || 400;
const minX = -busW * 0.30;
const maxX = sceneW - busW * 0.70;
const x = minX + (maxX - minX) * hp;
const bob = this._reduce ? 0 : Math.sin(Date.now() / 380) * 4;
bus.style.transform = 'translate(' + x.toFixed(1) + 'px,' + bob.toFixed(1) + 'px)';
}
this._raf = requestAnimationFrame(this._tick);
};
this._raf = requestAnimationFrame(this._tick);
const nav = document.querySelector('[data-nav="1"]');
const onScroll = () => {
if(!nav) return;
if(window.scrollY > 40){
nav.style.background = 'rgba(10,14,24,.9)';
nav.style.backdropFilter = 'blur(12px)';
nav.style.webkitBackdropFilter = 'blur(12px)';
nav.style.top = '0px';
nav.style.padding = '14px clamp(20px,5vw,60px)';
nav.style.boxShadow = '0 1px 0 rgba(255,255,255,.08)';
} else {
nav.style.background = 'transparent';
nav.style.backdropFilter = 'none';
nav.style.webkitBackdropFilter = 'none';
nav.style.top = '39px';
nav.style.padding = '22px clamp(20px,5vw,60px)';
nav.style.boxShadow = 'none';
}
};
this._onScroll = onScroll;
window.addEventListener('scroll', onScroll, {passive:true});
onScroll();
const els = Array.from(document.querySelectorAll('[data-reveal]'));
els.forEach(el=>{ el.style.opacity='0'; el.style.transform='translateY(28px)'; el.style.transition='opacity .7s cubic-bezier(.2,.7,.2,1), transform .7s cubic-bezier(.2,.7,.2,1)'; });
if('IntersectionObserver' in window){
const io = new IntersectionObserver((ents)=>{
ents.forEach(e=>{ if(e.isIntersecting){ e.target.style.opacity='1'; e.target.style.transform='none'; io.unobserve(e.target); } });
},{threshold:0.12});
els.forEach(el=>io.observe(el));
this._io = io;
} else { els.forEach(el=>{ el.style.opacity='1'; el.style.transform='none'; }); }
this._fallback = setTimeout(()=>els.forEach(el=>{ el.style.opacity='1'; el.style.transform='none'; }), 2500);
}
componentDidUpdate(){ this.applyTweaks(); }
componentWillUnmount(){
if(this._onScroll) window.removeEventListener('scroll', this._onScroll);
if(this._io) this._io.disconnect();
if(this._fallback) clearTimeout(this._fallback);
if(this._raf) cancelAnimationFrame(this._raf);
if(this._mapTimer) clearTimeout(this._mapTimer);
if(this._map){ try{ this._map.remove(); }catch(e){} this._map=null; }
}
initMap(){
const HOME=[12.0517,48.4703]; // Buch am Erlbach / Vatersdorf
const BRANCH=[12.1508,48.5449]; // Landshut
const RED = this.props.accentColor || '#C8102E';
const makePin=(fill, ring, title, sub)=>{
const wrap=document.createElement('div');
wrap.style.cssText='display:flex;flex-direction:column;align-items:center;gap:6px';
const label=document.createElement('div');
label.style.cssText='background:rgba(10,14,24,.9);border:1px solid rgba(255,255,255,.16);padding:5px 10px;white-space:nowrap;text-align:center;border-radius:2px';
label.innerHTML='<div style="font-family:Anton,sans-serif;text-transform:uppercase;color:#fff;font-size:14px;letter-spacing:.5px;line-height:1.05">'+title+'</div><div style="font-family:Archivo,sans-serif;font-size:9px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:#9aa6c0;margin-top:2px">'+sub+'</div>';
const dot=document.createElement('div');
dot.style.cssText='width:18px;height:18px;border-radius:50%;background:'+fill+';border:3px solid '+ring+';box-shadow:0 0 0 5px '+RED+'40, 0 2px 6px rgba(0,0,0,.55)';
wrap.appendChild(label); wrap.appendChild(dot);
return wrap;
};
const start=()=>{
if(!window.maplibregl){ this._mapTimer=setTimeout(start,120); return; }
const el=document.getElementById('unger-map');
if(!el){ this._mapTimer=setTimeout(start,120); return; }
if(this._map) return;
const map=new maplibregl.Map({
container: el,
style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
center: [(HOME[0]+BRANCH[0])/2, (HOME[1]+BRANCH[1])/2],
zoom: 10.4,
attributionControl: { compact: true }
});
this._map=map;
map.scrollZoom.disable();
map.addControl(new maplibregl.NavigationControl({ showCompass:true, visualizePitch:false }), 'top-right');
new maplibregl.Marker({ element: makePin(RED,'#fff','Landshut','Niederlassung'), anchor:'bottom' }).setLngLat(BRANCH).addTo(map);
new maplibregl.Marker({ element: makePin('#fff',RED,'Buch a. Erlbach','Hauptsitz'), anchor:'bottom' }).setLngLat(HOME).addTo(map);
map.on('load',()=>{
map.addSource('unger-route',{ type:'geojson', data:{ type:'Feature', properties:{}, geometry:{ type:'LineString', coordinates:[HOME,BRANCH] } } });
map.addLayer({ id:'unger-route-casing', type:'line', source:'unger-route', layout:{'line-join':'round','line-cap':'round'}, paint:{'line-color':RED,'line-width':9,'line-opacity':.18} });
map.addLayer({ id:'unger-route-line', type:'line', source:'unger-route', layout:{'line-join':'round','line-cap':'round'}, paint:{'line-color':RED,'line-width':3,'line-dasharray':[1.6,1.4]} });
map.fitBounds([HOME,BRANCH],{ padding:{top:96,bottom:70,left:70,right:70}, duration:0, maxZoom:12 });
setTimeout(()=>{ try{ map.resize(); }catch(e){} }, 60);
});
};
start();
}
applyTweaks(){
const root = document.documentElement;
if(this.props.accentColor) root.style.setProperty('--red', this.props.accentColor);
const RED = this.props.accentColor;
if(RED && this._map && this._map.getLayer && this._map.getLayer('unger-route-line')){
try{ this._map.setPaintProperty('unger-route-line','line-color',RED);
this._map.setPaintProperty('unger-route-casing','line-color',RED); }catch(e){}
}
}
field(key){ return (e)=> this.setState({[key]: e.target.value}); }
renderVals(){
return {
submitted:this.state.submitted,
notSubmitted:!this.state.submitted,
f_name:this.state.name, f_contact:this.state.contact, f_dest:this.state.dest,
f_date:this.state.date, f_pax:this.state.pax, f_msg:this.state.msg,
onName:this.field('name'), onContact:this.field('contact'), onDest:this.field('dest'),
onDate:this.field('date'), onPax:this.field('pax'), onMsg:this.field('msg'),
onSubmit:()=>this.setState({submitted:true}),
stats:[
{big:'2', label:'Standorte in Bayern'},
{big:'60+', label:'Sitzplätze pro Reisebus'},
{big:'24/7', label:'Für Sie erreichbar'},
{big:'∞', label:'Ziele in ganz Europa'}
],
services:[
{no:'01', title:'Busanmietung', desc:'Moderne Reisebusse mit Fahrer — flexibel für einzelne Fahrten oder mehrtägige Touren.'},
{no:'02', title:'Firmenreisen', desc:'Betriebsausflüge, Tagungen und Events. Ihr Team sicher und entspannt am Ziel.'},
{no:'03', title:'Vereins­fahrten', desc:'Ausflüge, Feste und Sportevents — komfortabel für die ganze Gruppe.'},
{no:'04', title:'Transfers', desc:'Flughafen-, Hotel- und Event-Transfers. Pünktlich und rund um die Uhr planbar.'}
],
fleet:[
{name:'Reisebus', seats:'60', desc:'Der Große für lange Strecken und große Gruppen — voll ausgestattet.', tags:['Klimaanlage','WC','Bordküche','WLAN']},
{name:'Midibus', seats:'35', desc:'Wendig und komfortabel — ideal für mittlere Gruppen und Tagestouren.', tags:['Klimaanlage','Komfort­bestuhlung']},
{name:'Kleinbus', seats:'19', desc:'Persönlich und flexibel für kleine Gruppen, Transfers und Sonderfahrten.', tags:['Flexibel','Wendig']}
]
};
}
}
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

240
index.html Normal file
View File

@@ -0,0 +1,240 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Unger Reisen — Reisebusse mit Fahrer aus Niederbayern</title>
<meta name="description" content="Unger Reisen — Omnibusunternehmen aus Niederbayern. Moderne Reisebusse mit erfahrenen Fahrern für Firmen, Vereine und Gruppen in ganz Bayern und Europa.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Archivo:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<link href="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<!-- TOP MARQUEE -->
<div class="marquee">
<div class="marquee__track">
<span class="marquee__group">
<span class="marquee__item">Reisebusse mit Fahrer</span><span class="marquee__sep">/</span>
<span class="marquee__item">Firmen &amp; Vereine</span><span class="marquee__sep">/</span>
<span class="marquee__item">Tagesfahrten</span><span class="marquee__sep">/</span>
<span class="marquee__item">Transfers rund um die Uhr</span><span class="marquee__sep">/</span>
<span class="marquee__item">Niederbayern</span><span class="marquee__sep">/</span>
</span>
<span class="marquee__group" aria-hidden="true">
<span class="marquee__item">Reisebusse mit Fahrer</span><span class="marquee__sep">/</span>
<span class="marquee__item">Firmen &amp; Vereine</span><span class="marquee__sep">/</span>
<span class="marquee__item">Tagesfahrten</span><span class="marquee__sep">/</span>
<span class="marquee__item">Transfers rund um die Uhr</span><span class="marquee__sep">/</span>
<span class="marquee__item">Niederbayern</span><span class="marquee__sep">/</span>
</span>
</div>
</div>
<!-- NAV -->
<nav class="nav" id="nav">
<a href="#top" class="logo">
<span class="display logo__name">UNGER</span>
<span class="logo__sub">REISEN</span>
</a>
<div class="nav__right">
<div class="nav__links">
<a href="#leistungen">Leistungen</a>
<a href="#flotte">Flotte</a>
<a href="#karriere">Karriere</a>
<a href="#kontakt">Kontakt</a>
</div>
<a href="tel:+498762896" class="btn btn--red nav__cta">Jetzt anrufen</a>
<button class="nav__burger" id="nav-burger" type="button" aria-label="Menü öffnen" aria-expanded="false" aria-controls="mobile-menu">
<span></span><span></span><span></span>
</button>
</div>
</nav>
<!-- MOBILE MENU -->
<div class="mobileMenu" id="mobile-menu">
<a href="#leistungen">Leistungen</a>
<a href="#flotte">Flotte</a>
<a href="#karriere">Karriere</a>
<a href="#kontakt">Kontakt</a>
<a href="tel:+498762896" class="btn btn--red btn--block mobileMenu__cta">Jetzt anrufen</a>
</div>
<!-- HERO with animated driving scene -->
<header id="top" class="hero">
<div class="hero__type">
<div class="eyebrow" data-reveal>
<span class="eyebrow__rule"></span>Omnibusunternehmen seit Generationen
</div>
<h1 class="display hero__h1" data-reveal>
Wir bringen<br>Sie <span class="text-red">weiter.</span>
</h1>
<p class="hero__lead" data-reveal>
Moderne Reisebusse mit erfahrenen Fahrern — für Firmen, Vereine und Gruppen in ganz Bayern und Europa.
</p>
<div class="hero__actions" data-reveal>
<a href="tel:+498762896" class="btn btn--red">Jetzt anrufen →</a>
<a href="#flotte" class="btn btn--outline">Unsere Flotte</a>
</div>
</div>
<!-- DRIVING SCENE -->
<div class="scene" aria-hidden="true">
<div class="hillsFar"></div>
<div class="hills"></div>
<div class="road"></div>
<div class="stripes"></div>
<!-- BUS (scroll-driven left→right) -->
<div class="bus" id="bus">
<div class="bus__headlight"></div>
<div class="bus__body">
<div class="bus__stripe"></div>
<div class="bus__windshield"></div>
<div class="bus__windows"></div>
<div class="display bus__brand">UNGER<span class="text-red"> REISEN</span></div>
<div class="bus__door"></div>
</div>
<div class="wheel wheel--front">
<div class="wheel__hub"></div>
<div class="wheel__spoke"></div>
</div>
<div class="wheel wheel--rear">
<div class="wheel__hub"></div>
<div class="wheel__spoke"></div>
</div>
</div>
</div>
</header>
<!-- STATS BAND -->
<section class="stats">
<div class="stats__grid" id="stats-grid"></div>
</section>
<!-- LEISTUNGEN -->
<section id="leistungen" class="section">
<div class="section__head" data-reveal>
<h2 class="display section__title section__title--wide">Das fahren wir für Sie</h2>
<p class="section__intro">Ein Ansprechpartner, ein Bus, alles organisiert — von der Anfrage bis zur Rückfahrt.</p>
</div>
<div class="services" id="services-grid"></div>
</section>
<!-- REAL PHOTO BAND -->
<section class="photo">
<img class="photo__img" src="assets/startslider-bus.png"
alt="Unger Reisen Reisebus"
onerror="this.closest('.photo').classList.add('photo--fallback'); this.remove();">
<div class="photo__overlay"></div>
<div class="photo__text" data-reveal>
<div class="display photo__headline">Komfort, auf den Sie sich verlassen können.</div>
</div>
</section>
<!-- FLOTTE -->
<section id="flotte" class="section">
<div class="section__head section__head--stack" data-reveal>
<div class="eyebrow-label">Unsere Flotte</div>
<h2 class="display section__title">Für jede Gruppengröße</h2>
</div>
<div class="fleet" id="fleet-grid"></div>
</section>
<!-- KONTAKT + KARTE -->
<section id="kontakt" class="section">
<div class="section__head section__head--stack" data-reveal>
<div class="eyebrow-label">So erreichen Sie uns</div>
<h2 class="display section__title">Zwei Standorte.<br>Ein Anruf.</h2>
</div>
<div class="kontakt__grid">
<!-- INTERACTIVE MAP -->
<div class="mapPanel" data-reveal>
<div id="unger-map" class="mapPanel__map"></div>
<div class="mapPanel__chip">
<span class="display mapPanel__km">15 km</span>
<span class="mapPanel__chipLabel">zwischen<br>den Standorten</span>
</div>
</div>
<!-- CONTACT CARD -->
<div class="contactCard" data-reveal>
<div class="contactCard__row">
<div class="dot dot--white"></div>
<div>
<div class="contactCard__label">Hauptsitz</div>
<div class="contactCard__addr">Eichenstr. 7+9, Vatersdorf<br>84172 Buch am Erlbach</div>
<a href="tel:+498762896" class="contactCard__tel">Tel. +49 (0)8762 / 896</a>
</div>
</div>
<div class="contactCard__divider"></div>
<div class="contactCard__row">
<div class="dot dot--red"></div>
<div>
<div class="contactCard__label">Niederlassung</div>
<div class="contactCard__addr">Jenaer Straße 4a<br>84034 Landshut</div>
<a href="tel:+4987196585860" class="contactCard__tel">Tel. +49 (0)871 / 96 58 586-0</a>
</div>
</div>
<div class="contactCard__actions">
<a href="tel:+498762896" class="btn btn--red btn--block">Jetzt anrufen</a>
<a href="mailto:info@unger-reisen.de" class="btn btn--outline btn--block">info@unger-reisen.de</a>
</div>
</div>
</div>
</section>
<!-- KARRIERE -->
<section id="karriere" class="section section--karriere">
<div class="karriere" data-reveal>
<div class="karriere__text">
<div class="eyebrow-label">Karriere &amp; Jobs</div>
<h2 class="display karriere__title">Fahren Sie mit uns</h2>
<p class="karriere__lead">Wir suchen Busfahrer:innen, Begleitpersonen und Sachbearbeiter:innen — steigen Sie ein.</p>
</div>
<a href="mailto:info@unger-reisen.de" class="btn btn--white karriere__cta">Offene Stellen →</a>
</div>
</section>
<!-- FOOTER -->
<footer class="footer">
<div class="footer__grid">
<div>
<div class="logo footer__logo">
<span class="display logo__name logo__name--lg">UNGER</span>
<span class="logo__sub">REISEN</span>
</div>
<p class="footer__blurb">Günter Unger e.K. — Omnibus- &amp; Verkehrsunternehmen. Reisen erster Klasse aus Niederbayern.</p>
</div>
<div class="footer__col">
<div class="footer__colHead">Hauptsitz</div>
Eichenstr. 7+9, Vatersdorf<br>84172 Buch am Erlbach<br>
<a href="tel:+498762896">Tel. +49 (0)8762 / 896</a>
</div>
<div class="footer__col">
<div class="footer__colHead">Niederlassung</div>
Jenaer Straße 4a<br>84034 Landshut<br>
<a href="tel:+4987196585860">Tel. +49 (0)871 / 96 58 586-0</a>
</div>
</div>
<div class="footer__bar">
<div>© 2026 Unger Reisen · Alle Rechte vorbehalten</div>
<div class="footer__legal">
<a href="#">Impressum</a>
<a href="#">Datenschutz</a>
<a href="#">AGB</a>
</div>
</div>
</footer>
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<script src="js/main.js"></script>
</body>
</html>

260
js/main.js Normal file
View File

@@ -0,0 +1,260 @@
/* =========================================================
Unger Reisen — behaviour
- render data-driven sections (stats / services / fleet)
- scroll-driven bus across the hero
- nav solidify on scroll
- reveal-on-scroll (IntersectionObserver)
- interactive MapLibre map (two locations + route)
========================================================= */
(function () {
'use strict';
var ACCENT = '#C8102E';
var reduceMotion = window.matchMedia &&
window.matchMedia('(prefers-reduced-motion:reduce)').matches;
/* ---------- Data ---------- */
var stats = [
{ big: '2', label: 'Standorte in Bayern' },
{ big: '60+', label: 'Sitzplätze pro Reisebus' },
{ big: '24/7', label: 'Für Sie erreichbar' },
{ big: '∞', label: 'Ziele in ganz Europa' }
];
var services = [
{ no: '01', title: 'Busanmietung', desc: 'Moderne Reisebusse mit Fahrer — flexibel für einzelne Fahrten oder mehrtägige Touren.' },
{ no: '02', title: 'Firmenreisen', desc: 'Betriebsausflüge, Tagungen und Events. Ihr Team sicher und entspannt am Ziel.' },
{ no: '03', title: 'Vereinsfahrten', desc: 'Ausflüge, Feste und Sportevents — komfortabel für die ganze Gruppe.' },
{ no: '04', title: 'Transfers', desc: 'Flughafen-, Hotel- und Event-Transfers. Pünktlich und rund um die Uhr planbar.' }
];
var fleet = [
{ name: 'Reisebus', seats: '60', desc: 'Der Große für lange Strecken und große Gruppen — voll ausgestattet.', tags: ['Klimaanlage', 'WC', 'Bordküche', 'WLAN'] },
{ name: 'Midibus', seats: '35', desc: 'Wendig und komfortabel — ideal für mittlere Gruppen und Tagestouren.', tags: ['Klimaanlage', 'Komfortbestuhlung'] },
{ name: 'Kleinbus', seats: '19', desc: 'Persönlich und flexibel für kleine Gruppen, Transfers und Sonderfahrten.', tags: ['Flexibel', 'Wendig'] }
];
function el(tag, cls, html) {
var n = document.createElement(tag);
if (cls) n.className = cls;
if (html != null) n.innerHTML = html;
return n;
}
/* ---------- Render sections ---------- */
function renderStats() {
var grid = document.getElementById('stats-grid');
if (!grid) return;
stats.forEach(function (s) {
var wrap = el('div', null);
wrap.setAttribute('data-reveal', '');
wrap.appendChild(el('div', 'display stat__big', s.big));
wrap.appendChild(el('div', 'stat__label', s.label));
grid.appendChild(wrap);
});
}
function renderServices() {
var grid = document.getElementById('services-grid');
if (!grid) return;
services.forEach(function (s) {
var card = el('div', 'service');
card.setAttribute('data-reveal', '');
card.appendChild(el('div', 'display service__no', s.no));
card.appendChild(el('h3', 'service__title', s.title));
card.appendChild(el('p', 'service__desc', s.desc));
grid.appendChild(card);
});
}
function renderFleet() {
var grid = document.getElementById('fleet-grid');
if (!grid) return;
fleet.forEach(function (f) {
var card = el('div', 'fleetCard');
card.setAttribute('data-reveal', '');
var head = el('div', 'fleetCard__head');
head.appendChild(el('h3', 'display fleetCard__name', f.name));
var seats = el('div', 'fleetCard__seats',
'<span class="fleetCard__seatsWord">bis</span>' +
'<span class="display fleetCard__seatsNum">' + f.seats + '</span>' +
'<span class="fleetCard__seatsWord">Sitze</span>');
head.appendChild(seats);
card.appendChild(head);
var body = el('div', 'fleetCard__body');
body.appendChild(el('p', 'fleetCard__desc', f.desc));
var tags = el('div', 'fleetCard__tags');
f.tags.forEach(function (t) { tags.appendChild(el('span', 'tag', t)); });
body.appendChild(tags);
card.appendChild(body);
grid.appendChild(card);
});
}
/* ---------- Nav solidify on scroll ---------- */
function initNav() {
var nav = document.getElementById('nav');
if (!nav) return;
var onScroll = function () {
nav.classList.toggle('is-scrolled', window.scrollY > 40);
};
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
}
/* ---------- Mobile menu ---------- */
function initMobileMenu() {
var burger = document.getElementById('nav-burger');
var menu = document.getElementById('mobile-menu');
if (!burger || !menu) return;
function setOpen(open) {
menu.classList.toggle('is-open', open);
burger.classList.toggle('is-open', open);
burger.setAttribute('aria-expanded', open ? 'true' : 'false');
burger.setAttribute('aria-label', open ? 'Menü schließen' : 'Menü öffnen');
document.body.style.overflow = open ? 'hidden' : '';
}
burger.addEventListener('click', function () {
setOpen(!menu.classList.contains('is-open'));
});
menu.querySelectorAll('a').forEach(function (a) {
a.addEventListener('click', function () { setOpen(false); });
});
window.addEventListener('keydown', function (e) {
if (e.key === 'Escape') setOpen(false);
});
// Close if we grow past the mobile breakpoint.
window.addEventListener('resize', function () {
if (window.innerWidth > 680) setOpen(false);
});
}
/* ---------- Scroll-driven bus ---------- */
function initBus() {
var bus = document.getElementById('bus');
var hero = document.getElementById('top');
if (!bus || !hero) return;
function tick() {
var hp = Math.min(1, Math.max(0, window.scrollY / (hero.offsetHeight * 0.9)));
var scene = bus.parentNode;
var sceneW = scene ? scene.offsetWidth : window.innerWidth;
var busW = bus.offsetWidth || 400;
var minX = -busW * 0.30;
var maxX = sceneW - busW * 0.70;
var x = minX + (maxX - minX) * hp;
var bob = reduceMotion ? 0 : Math.sin(Date.now() / 380) * 4;
bus.style.transform = 'translate(' + x.toFixed(1) + 'px,' + bob.toFixed(1) + 'px)';
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
/* ---------- Reveal on scroll ---------- */
function initReveal() {
var els = Array.prototype.slice.call(document.querySelectorAll('[data-reveal]'));
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
e.target.classList.add('is-visible');
io.unobserve(e.target);
}
});
}, { threshold: 0.12 });
els.forEach(function (n) { io.observe(n); });
} else {
els.forEach(function (n) { n.classList.add('is-visible'); });
}
// Safety fallback: reveal everything after 2.5s regardless.
setTimeout(function () {
els.forEach(function (n) { n.classList.add('is-visible'); });
}, 2500);
}
/* ---------- Interactive map ---------- */
function initMap() {
var HOME = [12.0517, 48.4703]; // Buch am Erlbach / Vatersdorf
var BRANCH = [12.1508, 48.5449]; // Landshut
var container = document.getElementById('unger-map');
if (!container || typeof maplibregl === 'undefined') return;
function makePin(fill, ring, title, sub) {
var wrap = el('div', 'pin');
wrap.appendChild(el('div', 'pin__label',
'<div class="pin__title">' + title + '</div>' +
'<div class="pin__sub">' + sub + '</div>'));
var dot = el('div', 'pin__dot');
dot.style.background = fill;
dot.style.border = '3px solid ' + ring;
dot.style.boxShadow = '0 0 0 5px ' + ACCENT + '40, 0 2px 6px rgba(0,0,0,.55)';
wrap.appendChild(dot);
return wrap;
}
var map = new maplibregl.Map({
container: container,
style: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
center: [(HOME[0] + BRANCH[0]) / 2, (HOME[1] + BRANCH[1]) / 2],
zoom: 10.4,
attributionControl: { compact: true }
});
map.scrollZoom.disable();
map.addControl(new maplibregl.NavigationControl({ showCompass: true, visualizePitch: false }), 'top-right');
// On phones, keep the map static so a one-finger drag scrolls the page
// instead of getting trapped panning the map.
if (window.matchMedia && window.matchMedia('(max-width:680px)').matches) {
map.dragPan.disable();
if (map.touchZoomRotate) map.touchZoomRotate.disable();
if (map.touchPitch) map.touchPitch.disable();
}
new maplibregl.Marker({ element: makePin(ACCENT, '#fff', 'Landshut', 'Niederlassung'), anchor: 'bottom' })
.setLngLat(BRANCH).addTo(map);
new maplibregl.Marker({ element: makePin('#fff', ACCENT, 'Buch a. Erlbach', 'Hauptsitz'), anchor: 'bottom' })
.setLngLat(HOME).addTo(map);
map.on('load', function () {
map.addSource('unger-route', {
type: 'geojson',
data: { type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: [HOME, BRANCH] } }
});
map.addLayer({
id: 'unger-route-casing', type: 'line', source: 'unger-route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': ACCENT, 'line-width': 9, 'line-opacity': 0.18 }
});
map.addLayer({
id: 'unger-route-line', type: 'line', source: 'unger-route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: { 'line-color': ACCENT, 'line-width': 3, 'line-dasharray': [1.6, 1.4] }
});
map.fitBounds([HOME, BRANCH], { padding: { top: 96, bottom: 70, left: 70, right: 70 }, duration: 0, maxZoom: 12 });
setTimeout(function () { try { map.resize(); } catch (e) {} }, 60);
});
}
/* ---------- Boot ---------- */
function init() {
renderStats();
renderServices();
renderFleet();
initNav();
initMobileMenu();
initBus();
initReveal();
initMap();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();