Design rework

pull/6/head
Justin Muravjev 2026-09-29 13:47:23 +02:00
parent 9066763ec6
commit 8b906202b7
11 changed files with 898 additions and 10622 deletions

View File

@ -1,4 +1,35 @@
# PM_Project # PM_Project – vollständige TriHub-Ernährungsseite
Project for ProjectManagement in IB.4 `ernaehrungsberatung.html` im Browser öffnen. Kein Build und keine Installation nötig.
Die vollständige Seite aus TriHub-Design-Original ist integriert: Header, Desktop-/Mobilnavigation,
Hero, Zielgruppenleiste, eigener Vorteilsbereich, drei Pakete, Coaching-Rabatte,
zentrierter Detaildialog, vier Ablaufschritte und Footer.
Animationen: Scroll-Einblendungen mit 720 ms und maximal 240 ms Staffelung, Bild-/Karten-Hover,
sanfte Ankerlinks und Dialog-/Backdrop-Einblendung. `prefers-reduced-motion` wird berücksichtigt.
Inhalte bleiben ohne JavaScript sichtbar; Menü und Dialog benötigen JavaScript.
## Dateien und Zuständigkeiten
- ernaehrungsberatung.html: vollständige Seitenstruktur und Inhalte.
- css/assets.css: Original-CSS der Vorlage mit lokalen Schriftpfaden.
- css/base.css: Grundlagen, Header, Buttons, Footer.
- css/sections/hero.css: Hero, Zielgruppenleiste, Vorteile, gemeinsame Sektionsüberschriften.
- css/sections/angebote.css: Angebote, Rabattbanner und Detaildialog.
- css/sections/ablauf.css: Programmablauf.
- css/responsive.css (neu): responsive Anpassungen und Dialoganimationen; zuletzt eingebunden.
- js/angebote.js: Paketdetails und Rabattauswahl.
- js/main.js: Mobilnavigation und Scrollanimationen.
- assets/fonts/ (neu): 16 lokale Originalschriftdateien für Sora und DM Sans.
- assets/images/: Bilder in den vorhandenen Unterordnern angebote/, hero/ und icons/.
Bestehende Git- und IDE-Dateien wurden nicht geändert. Es wurde kein Commit oder Push ausgeführt.
Keine zusätzlichen index.html-/projekt.html-Kopien: Der Einstieg bleibt ernaehrungsberatung.html.
## Abgleich
HTML-Inhalte und Reihenfolge entsprechen der vollständigen Vorlage. CSS-Regeln wurden auf die
Projektdateien verteilt, JavaScript wurde in Navigation/Animationen und Angebotsdialog aufgeteilt.
Geprüft: vollständige HTML-Struktur und Texte gegen die Vorlage, lokale Dateiverweise, JavaScript-Syntax, responsive CSS sowie DOM-Interaktionen für alle Pakete, Rabatte, Menü und 22 Animationselemente einschließlich reduzierter Bewegung.
Eine visuelle Browserprüfung ist nicht Bestandteil dieser Prüfung.
Angebote bleiben Konzeptvorschläge ohne Buchung, Datenübermittlung oder erfundene Preise.

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,95 +1,179 @@
/* ========================================================= /* Gemeinsame Basis, Header, Buttons und Footer aus TriHub Design Original. */
base.css – gemeinsame Basis für alle Features /* 1. Grundlagen und Typografie */
Änderungen hier nur über eigenen Branch (betrifft alle!)
========================================================= */
/* ---------- Design-Tokens laut tri-hub Design-DNA ---------- */
:root { :root {
/* Hintergrund: tiefes Dunkelblau / Schwarz */ --page: var(--background);
--th-bg: #0d0d1a; --text: #fff;
--th-bg-elevated: #1a1a2e; --muted: rgb(255 255 255 / 70%);
--quiet: rgb(255 255 255 / 56%);
/* Akzent: Teal / Emerald Green – CTAs, Icons, Highlights */ --line: rgb(255 255 255 / 10%);
--th-accent: #10b981; --panel: linear-gradient(#ffffff14, #ffffff08);
--ease: cubic-bezier(.22, 1, .36, 1);
/* Text: Weiß / Hellgrau */ color-scheme: dark;
--th-text: #ffffff;
--th-text-soft: #f8fafc;
--th-text-muted: #94a3b8;
/* Typografie: Serif Display + Sans-Serif Body */
--th-font-display: "Playfair Display", Georgia, serif;
--th-font-body: "Inter", system-ui, sans-serif;
/* Lesbarkeit Zielgruppe 50+ (Definition of Done) */
--th-font-size-body: 1.125rem; /* 18px */
--th-line-height: 1.65;
/* Abstände / Radien für alle Sektionen */
--th-section-space: 5rem;
--th-radius: 1.4rem;
} }
/* ---------- Grundlayout ---------- */ * { box-sizing: border-box; }
html { scroll-behavior: smooth; scroll-padding-top: 120px; }
body { body {
background: var(--th-bg); margin: 0;
color: var(--th-text-soft); background: var(--background);
font-family: var(--th-font-body); color: var(--text);
font-size: var(--th-font-size-body); font-family: var(--font-sans), sans-serif;
line-height: var(--th-line-height); font-size: 1.0625rem;
line-height: 1.7;
-webkit-font-smoothing: antialiased;
} }
main { overflow: clip; }
h1, h2, h3, p, figure { margin: 0; }
h1, h2, h3 { h1, h2, h3 {
font-family: var(--th-font-display); font-family: var(--font-display), sans-serif;
color: var(--th-text); font-weight: 700;
letter-spacing: -0.01em; letter-spacing: -.04em;
text-wrap: balance;
} }
h1, h2 { line-height: 1.12; }
main > section { h3 { line-height: 1.35; }
padding-block: var(--th-section-space); a { color: inherit; text-decoration: none; }
button, select { font: inherit; }
button { cursor: pointer; border: 0; }
button, a, select { -webkit-tap-highlight-color: transparent; }
a, button, select { touch-action: manipulation; }
:where(button, a, select):focus-visible {
outline: 2px solid var(--accent-strong);
outline-offset: 5px;
} }
img { display: block; max-width: 100%; object-fit: cover; }
button:disabled { cursor: default; }
[hidden] { display: none !important; }
.shell { width: min(1280px, calc(100% - 128px)); margin-inline: auto; }
/* Anker nicht unter dem Header verstecken */ /* 2. Kopfbereich – Website-Logo ganz links, wie auf der Referenzseite */
section[id] {
scroll-margin-top: 5rem;
}
/* ---------- Header ---------- */
.site-header { .site-header {
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 40; z-index: 20;
background: rgb(13 13 26 / 0.85); border-bottom: 1px solid var(--line);
backdrop-filter: blur(12px); background: linear-gradient(
border-bottom: 1px solid rgb(255 255 255 / 0.08); color-mix(in oklab, var(--background) 95%, transparent),
color-mix(in oklab, var(--background) 80%, transparent)
);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
} }
.header-inner {
.site-header .container { width: calc(100% - 64px);
min-height: 98px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding-block: 1rem; gap: 24px;
padding-block: 16px;
} }
.brand {
display: inline-flex;
align-items: center;
gap: 16px;
flex-shrink: 0;
}
.brand-mark {
display: flex;
align-items: center;
justify-content: center;
width: 74px;
height: 66px;
padding: 8px 12px;
overflow: hidden;
border: 1px solid rgb(255 255 255 / 12%);
border-radius: 1.1rem;
background: #fff;
box-shadow: 0 14px 34px rgb(0 0 0 / 22%);
}
.brand-logo img { width: 48px; height: 48px; object-fit: contain; }
.brand-copy { display: grid; gap: 3px; }
.brand-name {
color: var(--quiet);
font-size: .75rem;
letter-spacing: .28em;
text-transform: uppercase;
}
.brand-sub { color: var(--muted); font-size: .875rem; line-height: 1.5; }
.header-inner nav { display: flex; align-items: center; gap: 5px; }
.header-inner nav a {
padding: 9px 14px;
border: 1px solid transparent;
border-radius: 999px;
color: var(--muted);
font-size: .875rem;
white-space: nowrap;
transition: color .2s, background .2s, border-color .2s;
}
.header-inner nav a:hover,
.header-inner nav a[aria-current] {
border-color: var(--line);
background: rgb(255 255 255 / 5%);
color: #fff;
}
.menu-toggle { display: none; }
.skip {
position: fixed;
left: 16px;
top: -100px;
z-index: 100;
padding: 12px 20px;
border-radius: 999px;
background: #fff;
color: var(--ink-strong);
}
.skip:focus { top: 12px; }
.site-logo { /* 3. Buttons – weiße Primär-CTAs, zurückhaltende Konturen */
font-family: var(--th-font-display); .button {
font-size: 1.5rem; display: inline-flex;
font-weight: 700; align-items: center;
color: var(--th-text); justify-content: center;
min-height: 50px;
padding: 14px 24px;
border: 1px solid transparent;
border-radius: 999px;
background: #fff;
color: var(--ink-strong);
font-size: .9375rem;
font-weight: 600;
line-height: 1.45;
text-align: center;
transition: background .25s, border-color .25s, transform .25s var(--ease);
} }
.button:hover { background: #e9edf0; transform: translateY(-2px); }
.button.small { min-height: 42px; padding: 10px 20px; font-size: .875rem; }
.button.outline, .header-cta {
border-color: rgb(255 255 255 / 16%);
background: transparent;
color: #fff;
}
.button.outline:hover, .header-cta:hover {
border-color: rgb(255 255 255 / 28%);
background: rgb(255 255 255 / 7%);
}
.text-link {
display: inline-flex;
align-items: center;
min-height: 44px;
padding: 8px 0;
border-bottom: 1px solid rgb(255 255 255 / 28%);
background: transparent;
color: rgb(255 255 255 / 86%);
font-size: .9375rem;
font-weight: 500;
line-height: 1.4;
transition: color .2s, border-color .2s;
}
.text-link:hover { color: #fff; border-color: #fff; }
/* ---------- Barrierefreiheit ---------- */
a:focus-visible,
button:focus-visible {
outline: 3px solid var(--th-accent);
outline-offset: 4px;
border-radius: 0.5rem;
}
@media (prefers-reduced-motion: reduce) { /* Footer */
html { scroll-behavior: auto; } footer { border-top: 1px solid var(--line); background: var(--sidebar); }
*, *::before, *::after { .footer-main { display: flex; align-items: center; gap: 24px; padding-block: 32px; }
transition-duration: 0.01ms !important; .footer-main p { color: var(--muted); font-size: .9375rem; }
animation-duration: 0.01ms !important; .footer-main > a:last-child { margin-left: auto; color: var(--muted); font-size: .875rem; }
} .footer-main > a:last-child:hover { color: #fff; }
} .footer-note { display: flex; justify-content: space-between; gap: 24px; padding-block: 20px; border-top: 1px solid var(--line); color: var(--quiet); font-size: .75rem; }

View File

@ -1,142 +1,8 @@
/* ========================================================= /* 7. Ablauf und Fußbereich */
US01-03: Ablauf sehen .steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 30px; margin: 0; padding: 0; list-style: none; }
Branch: feature_US01-03 .step-number { display: block; margin-bottom: 24px; padding: 8px 0 22px; border-bottom: 1px solid var(--line); color: rgb(255 255 255 / 45%); font-family: var(--font-display); font-size: 2rem; font-weight: 500; letter-spacing: -.04em; }
Nur Selektoren unter #ablauf verwenden. .steps h3 { margin-bottom: 14px; font-size: 1.0625rem; }
Farben/Schriften über var(--th-...) aus base.css. .steps p { color: var(--muted); font-size: 1rem; }
Vorlage: Prototyp (Programmablauf) .process-end { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-top: 48px; padding-top: 30px; border-top: 1px solid var(--line); }
========================================================= */ .process-end p { color: rgb(255 255 255 / 82%); font-size: 1.25rem; }
.process-end .text-link { flex-shrink: 0; }
#ablauf {
--abl-mint: #56d9b1;
--abl-line: rgb(255 255 255 / 0.11);
}
/* ---------- Überschrift ---------- */
#ablauf .ablauf-heading {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 3.5rem;
margin-bottom: 2.75rem;
}
#ablauf .ablauf-eyebrow {
margin: 0;
font-size: 0.875rem;
font-weight: 650;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--abl-mint);
}
#ablauf h2 {
margin: 0.9rem 0 0;
font-size: clamp(2.2rem, 3.4vw, 2.8rem);
font-weight: 400;
line-height: 1.12;
letter-spacing: -0.03em;
}
#ablauf .ablauf-heading > p {
max-width: 24rem;
margin: 0;
color: var(--th-text-muted);
}
/* ---------- Schritte ---------- */
#ablauf .steps {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 2rem;
margin: 0;
padding: 0;
list-style: none;
}
#ablauf .step-number {
display: block;
margin-bottom: 1.4rem;
padding: 0.5rem 0 1.1rem;
border-bottom: 1px solid var(--abl-line);
font-family: var(--th-font-display);
font-size: 2rem;
line-height: 1;
color: var(--abl-mint);
position: relative;
}
/* grüner Fortschrittsstrich unter der Nummer zeigt die Reihenfolge */
#ablauf .step-number::after {
content: "";
position: absolute;
left: 0;
bottom: -1px;
width: 2.5rem;
height: 2px;
background: var(--th-accent);
}
#ablauf .step h3 {
margin: 0 0 0.75rem;
font-family: var(--th-font-body);
font-size: 1.25rem;
font-weight: 600;
line-height: 1.3;
}
#ablauf .step p {
margin: 0;
color: var(--th-text-muted);
}
/* ---------- Abschluss ---------- */
#ablauf .process-end {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5rem;
margin-top: 2.75rem;
padding-top: 1.9rem;
border-top: 1px solid var(--abl-line);
}
#ablauf .process-end p {
margin: 0;
font-family: var(--th-font-display);
font-size: 1.45rem;
font-style: italic;
color: #d3d7e1;
}
#ablauf .process-link {
display: inline-flex;
align-items: center;
gap: 0.75rem;
min-height: 2.75rem;
font-size: 1.125rem;
font-weight: 600;
color: var(--th-text-soft);
white-space: nowrap;
}
#ablauf .process-link:hover {
color: var(--abl-mint);
}
/* ---------- Tablet: 2 × 2 ---------- */
@media (max-width: 1100px) {
#ablauf .ablauf-heading { gap: 2rem; }
#ablauf .steps { grid-template-columns: repeat(2, 1fr); gap: 2rem 1.75rem; }
}
/* ---------- Mobil: untereinander ---------- */
@media (max-width: 760px) {
#ablauf .ablauf-heading { display: block; margin-bottom: 2rem; }
#ablauf .ablauf-heading > p { max-width: none; margin-top: 1.25rem; }
#ablauf .process-end { display: block; }
#ablauf .process-link { margin-top: 1rem; }
}
@media (max-width: 560px) {
#ablauf .steps { grid-template-columns: 1fr; gap: 1.75rem; }
}

View File

@ -1,381 +1,90 @@
/* ========================================================= /* 6. Angebote – Bildkarten statt Vergleichstabelle */
US01-02: Angebote und Pakete (+ Verweis auf Coaching-Rabatte) .offers-section { border-block: 1px solid var(--line); background: var(--surface-elevated); }
Branch: feature_US01-02 .offers-section .section-heading { margin-bottom: 28px; }
Nur Selektoren unter #angebote verwenden. .concept-note { display: flex; gap: 8px; margin-bottom: 30px; color: var(--quiet); font-size: .8125rem; }
Farben/Schriften über var(--th-...) aus base.css. .package-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
Vorlage: Prototyp (Angebotsbereich + Detaildialog) .package {
========================================================= */
#angebote {
--ang-mint: #56d9b1;
--ang-line: rgb(255 255 255 / 0.11);
background: linear-gradient(135deg, #161625, #11111e);
border-block: 1px solid var(--ang-line);
}
/* ---------- Überschrift ---------- */
#angebote .angebote-heading {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 3.5rem;
margin-bottom: 1.5rem;
}
#angebote .angebote-eyebrow {
margin: 0;
font-size: 0.875rem;
font-weight: 650;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ang-mint);
}
#angebote h2 {
margin: 0.9rem 0 0;
font-size: clamp(2.2rem, 3.4vw, 2.8rem);
font-weight: 400;
line-height: 1.12;
letter-spacing: -0.03em;
}
#angebote .angebote-heading > p {
max-width: 24rem;
margin: 0;
color: var(--th-text-muted);
}
#angebote .concept-note {
display: flex;
gap: 0.5rem;
margin: 0 0 2rem;
font-size: 1rem;
color: var(--th-text-muted);
}
/* ---------- Paketkarten ---------- */
#angebote .package-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); /* minmax(0,…) verhindert Überlaufen */
gap: 1.5rem;
}
#angebote .package {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 0.75rem; padding: 16px;
border: 1px solid var(--ang-line);
border-radius: 18px;
background: linear-gradient(140deg, #202032, #181827);
transition: border-color 0.2s;
}
#angebote .package:hover,
#angebote .package.featured {
border-color: rgb(16 185 129 / 0.45);
}
#angebote .package-image {
position: relative;
overflow: hidden; overflow: hidden;
border-radius: 10px; border: 1px solid var(--line);
border-radius: 2rem;
background: linear-gradient(#ffffff0f, #ffffff05), linear-gradient(#08151ee6, #091822fa);
box-shadow: 0 18px 48px rgb(0 0 0 / 16%);
transition: border-color .3s, transform .3s var(--ease), box-shadow .3s;
} }
.package:hover { border-color: rgb(255 255 255 / 22%); transform: translateY(-4px); box-shadow: 0 24px 52px rgb(0 0 0 / 22%); }
#angebote .package-image img { .package.featured { border-color: color-mix(in srgb, var(--accent-muted) 35%, transparent); }
width: 100%; .package-image { position: relative; overflow: hidden; border-radius: 1.35rem; }
aspect-ratio: 1.65; .package-image img { width: 100%; aspect-ratio: 1.65; transition: transform .65s var(--ease); }
object-fit: cover; .package:hover .package-image img { transform: scale(1.025); }
transition: transform 0.4s; .package-badge {
}
#angebote .package:hover img {
transform: scale(1.03);
}
#angebote .package-badge {
position: absolute; position: absolute;
left: 0.75rem; bottom: 12px;
bottom: 0.75rem; left: 12px;
padding: 0.45rem 0.75rem; max-width: calc(100% - 24px);
border-radius: 5px; padding: 6px 12px;
background: var(--th-accent); border: 1px solid color-mix(in srgb, var(--accent-muted) 32%, transparent);
color: #06261a; border-radius: 999px;
font-size: 0.75rem; background: rgb(7 27 34 / 90%);
font-weight: 750; color: color-mix(in srgb, var(--accent-strong) 74%, white);
letter-spacing: 0.08em; font-size: .6875rem;
text-transform: uppercase;
}
#angebote .package-body {
display: flex;
flex-direction: column;
flex: 1;
padding: 1.5rem 0.9rem 0.9rem;
}
#angebote .package-label {
margin: 0;
font-size: 0.875rem;
font-weight: 650;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ang-mint);
}
#angebote .package-body h3 {
margin: 0.8rem 0 1rem;
font-size: 1.9rem;
font-weight: 400;
line-height: 1.15;
}
#angebote .package-text {
margin: 0;
color: var(--th-text-muted);
}
#angebote .package-body ul {
flex: 1;
margin: 1.25rem 0 0;
padding: 1.25rem 0 1.5rem;
list-style: none;
border-top: 1px solid var(--ang-line);
}
#angebote .package-body li {
position: relative;
padding: 0.3rem 0 0.3rem 1.5rem;
}
#angebote .package-body li::before {
content: "✓";
position: absolute;
left: 0;
color: var(--ang-mint);
}
/* ---------- Buttons ---------- */
#angebote .package-button,
#angebote .dialog-done {
display: inline-flex;
align-items: center;
justify-content: space-between;
width: 100%;
min-height: 3.25rem;
padding: 0.9rem 1rem;
border: 1px solid rgb(255 255 255 / 0.15);
border-radius: 7px;
background: transparent;
color: var(--th-text-soft);
font-size: 1.0625rem;
font-weight: 650;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
}
#angebote .package-button:hover {
border-color: var(--th-accent);
background: rgb(16 185 129 / 0.08);
}
#angebote .package-button.is-primary,
#angebote .dialog-done {
border-color: transparent;
background: var(--th-accent);
color: #071b16;
}
#angebote .package-button.is-primary:hover,
#angebote .dialog-done:hover {
background: #37c99b;
}
/* ---------- Coaching-Rabatt ---------- */
#angebote .coaching-banner {
display: flex;
align-items: center;
gap: 1.5rem;
margin-top: 1.75rem;
padding: 1.6rem 1.9rem;
border: 1px solid rgb(16 185 129 / 0.19);
border-radius: 12px;
background: rgb(16 185 129 / 0.04);
}
#angebote .bonus-symbol {
padding-right: 1.5rem;
border-right: 1px solid rgb(16 185 129 / 0.25);
font-family: var(--th-font-display);
font-size: 2.2rem;
color: var(--ang-mint);
}
#angebote .coaching-banner h3 {
margin: 0 0 0.3rem;
font-family: var(--th-font-body);
font-size: 1.2rem;
font-weight: 600; font-weight: 600;
letter-spacing: .13em;
} }
.package-body { display: flex; flex: 1; flex-direction: column; padding: 24px 8px 8px; }
.package-body h3 { margin-block: 14px 20px; font-size: clamp(1.5rem, 2.05vw, 1.875rem); line-height: 1.3; }
.package-body > p:not(.eyebrow) { color: var(--muted); font-size: 1rem; line-height: 1.8; }
.package-body ul { flex: 1; margin-top: 24px; padding: 20px 0 24px; border-top: 1px solid var(--line); list-style: none; font-size: .9375rem; }
.package-body li { position: relative; padding: 6px 0 6px 23px; color: rgb(255 255 255 / 82%); }
.package-body li::before { content: '✓'; position: absolute; left: 0; color: var(--quiet); }
.package .button { width: 100%; font-size: .9375rem; }
.coaching-banner { display: flex; align-items: center; gap: 24px; margin-top: 30px; padding: 28px 30px; border: 1px solid var(--line); border-radius: 1.5rem; background: var(--panel); }
.bonus-symbol { display: grid; flex-shrink: 0; place-items: center; width: 52px; height: 52px; border: 1px solid var(--line); border-radius: 1rem; background: rgb(255 255 255 / 3%); color: rgb(255 255 255 / 80%); font-size: 1.5rem; }
.coaching-banner h3 { margin-bottom: 6px; font-size: 1.0625rem; }
.coaching-banner p { color: var(--muted); font-size: .9375rem; }
.coaching-banner .text-link { margin-left: auto; flex-shrink: 0; font-size: .875rem; }
#angebote .coaching-banner p { /* 8. Produktdetails – weiterhin zentriert und innerhalb des Fensters scrollbar */
margin: 0; dialog {
color: var(--th-text-muted); position: fixed;
} inset: 0;
margin: auto;
#angebote .bonus-button { width: min(720px, calc(100% - 32px));
margin-left: auto;
min-height: 2.75rem;
border: 0;
background: none;
color: var(--ang-mint);
font-size: 1.0625rem;
font-weight: 600;
white-space: nowrap;
cursor: pointer;
}
/* ---------- Dialog ---------- */
#angebote dialog {
margin: auto; /* Kunden-CSS setzt margin:0 → Dialog wieder zentrieren */
width: min(680px, calc(100% - 2rem));
max-height: 90dvh; max-height: 90dvh;
padding: 0; padding: 0;
border: 1px solid rgb(255 255 255 / 0.15); overflow-y: auto;
border-radius: 20px; overscroll-behavior: contain;
background: #171726; border: 1px solid rgb(255 255 255 / 16%);
color: var(--th-text-soft); border-radius: 1.75rem;
box-shadow: 0 30px 100px rgb(0 0 0 / 0.55); background: linear-gradient(#ffffff07, #ffffff00), var(--surface-elevated);
color: var(--text);
box-shadow: 0 30px 100px rgb(0 0 0 / 55%);
scrollbar-color: #46545c transparent;
scrollbar-width: thin;
} }
dialog::backdrop { background: rgb(2 8 12 / 78%); backdrop-filter: blur(7px); }
.dialog-inner { position: relative; padding: 42px; }
.close-dialog { position: absolute; top: 14px; right: 14px; width: 42px; height: 42px; border: 1px solid var(--line); border-radius: 50%; background: rgb(255 255 255 / 4%); color: var(--muted); font-size: 1.75rem; line-height: 1; }
.close-dialog:hover { background: rgb(255 255 255 / 10%); color: #fff; }
.dialog-inner > .eyebrow { padding-right: 24px; }
.dialog-inner h2 { margin: 16px 18px 22px 0; font-size: 2rem; line-height: 1.2; }
.dialog-inner > p:not(.eyebrow) { color: var(--muted); font-size: 1rem; }
.dialog-inner h3 { margin: 28px 0 14px; font-size: 1.125rem; }
#dialog-features { padding-left: 20px; color: rgb(255 255 255 / 82%); font-size: 1rem; }
#dialog-features li { padding-block: 5px; }
#dialog-features li::marker { color: var(--quiet); }
.detail-note, .discount-help { color: var(--quiet); font-size: .8125rem; line-height: 1.7; }
.detail-note { margin-top: 12px; }
.discount-box { margin-block: 28px; padding: 24px; border: 1px solid var(--line); border-radius: 1.25rem; background: var(--panel); }
.discount-box h3 { margin: 10px 0 22px; }
.discount-box label { display: block; margin-bottom: 10px; color: rgb(255 255 255 / 82%); font-size: .9375rem; }
.discount-box select { width: 100%; min-height: 50px; padding: 12px; border: 1px solid rgb(255 255 255 / 20%); border-radius: .75rem; background: var(--background); color: #fff; font-size: .9375rem; }
#discount-result { padding-block: 20px; color: rgb(255 255 255 / 88%); font-size: 1.0625rem; }
#discount-result strong { color: color-mix(in srgb, var(--accent-strong) 70%, white); font-size: 1.5rem; font-weight: 600; }
.discount-box a { display: inline-block; margin-top: 16px; color: rgb(255 255 255 / 84%); font-size: .875rem; text-decoration: underline; text-underline-offset: 4px; }
.discount-box a:hover { color: #fff; }
.dialog-done { width: 100%; }
body:has(dialog[open]) { overflow: hidden; }
#angebote dialog::backdrop {
background: rgb(5 5 11 / 0.8);
backdrop-filter: blur(6px);
}
#angebote .dialog-inner {
position: relative;
padding: 2.5rem;
}
#angebote .close-dialog {
position: absolute;
top: 0.75rem;
right: 1rem;
width: 2.75rem;
height: 2.75rem;
border: 0;
background: transparent;
color: var(--th-text-muted);
font-size: 1.9rem;
cursor: pointer;
}
#angebote .dialog-inner h2 {
margin: 0.8rem 2.2rem 1.1rem 0;
font-size: 2.2rem;
}
#angebote #dialog-description {
margin: 0;
color: var(--th-text-muted);
}
#angebote .dialog-inner h3 {
margin: 1.6rem 0 0.75rem;
font-family: var(--th-font-body);
font-size: 1.2rem;
font-weight: 600;
}
#angebote #dialog-features {
margin: 0;
padding-left: 1.25rem;
}
#angebote #dialog-features li {
padding: 0.25rem 0;
}
#angebote .dialog-hint {
margin: 0.75rem 0 0;
font-size: 1rem;
color: var(--th-text-muted);
}
#angebote .discount-box {
margin: 1.6rem 0;
padding: 1.4rem;
border: 1px solid rgb(16 185 129 / 0.27);
border-radius: 12px;
background: rgb(16 185 129 / 0.035);
}
#angebote .discount-box h3 {
margin-top: 0.4rem;
}
#angebote .discount-box label {
display: block;
margin-bottom: 0.5rem;
}
#angebote .discount-box select {
width: 100%;
padding: 0.75rem;
border: 1px solid rgb(255 255 255 / 0.25);
border-radius: 7px;
background: #11111f;
color: var(--th-text-soft);
font-size: 1.0625rem;
}
#angebote #discount-result {
margin: 0;
padding: 1rem 0 0;
color: var(--ang-mint);
}
#angebote #discount-result strong {
font-size: 1.6rem;
}
#angebote .discount-box a {
display: inline-block;
margin-top: 0.9rem;
color: var(--ang-mint);
}
#angebote select:focus-visible {
outline: 3px solid var(--th-accent);
outline-offset: 3px;
}
/* ---------- Tablet ---------- */
@media (max-width: 1100px) {
#angebote .angebote-heading { gap: 2rem; }
#angebote .package-body h3 { font-size: 1.7rem; }
#angebote .coaching-banner { flex-wrap: wrap; }
#angebote .bonus-button { margin-left: 0; }
}
/* ---------- schmale Tablets: 2 Karten nebeneinander ---------- */
@media (max-width: 1000px) {
#angebote .package-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* ---------- Mobil ---------- */
@media (max-width: 760px) {
#angebote .angebote-heading { display: block; }
#angebote .angebote-heading > p { max-width: none; margin-top: 1.25rem; }
#angebote .package-grid { grid-template-columns: 1fr; }
#angebote .package-image img { aspect-ratio: 1.8; }
#angebote .coaching-banner { padding: 1.4rem; gap: 1rem; }
#angebote .coaching-banner > div:nth-child(2) { flex: 1; min-width: 12rem; }
#angebote .bonus-symbol { display: none; }
#angebote .dialog-inner { padding: 2rem 1.4rem; }
#angebote .dialog-inner h2 { font-size: 1.9rem; }
}
@media (prefers-reduced-motion: reduce) {
#angebote .package img { transition: none; }
}

View File

@ -1,264 +1,107 @@
/* ========================================================= /* 4. Hero – Sans Serif, Original-Hintergrund und gerahmtes Foto */
US01-01: Angebot und Zielgruppe sofort sehen (Hero) .hero {
Branch: feature_US01-01 position: relative;
Nur Selektoren unter #hero verwenden. isolation: isolate;
Farben/Schriften über var(--th-...) aus base.css.
Vorlage: Prototyp (Hero-Bereich)
========================================================= */
#hero {
--hero-mint: #56d9b1; /* helle Akzentvariante aus dem Prototyp */
--hero-line: rgb(255 255 255 / 0.11);
padding-block: clamp(1.5rem, 4svh, 3.5rem); /* überschreibt Sektionsabstand aus base.css */
min-height: calc(100svh - 4.5rem); /* ohne Scrollen sichtbar (minus Header) */
display: flex;
align-items: center;
}
#hero .hero-grid {
display: grid; display: grid;
grid-template-columns: 1.08fr 1fr; grid-template-columns: 1.08fr 1fr;
gap: 4rem; gap: 56px;
align-items: center; align-items: center;
width: 100%; padding-block: 64px 72px;
} }
.hero::before {
/* ---------- Text ---------- */ content: '';
#hero h1 { position: absolute;
margin: 0; z-index: -1;
font-weight: 400; inset: 0 calc((100vw - 100%) / -2);
pointer-events: none;
background: linear-gradient(rgb(4 14 20 / 40%), rgb(4 14 20 / 82%)),
url('../../assets/images/hero/trihub-ribbon.webp') center / cover;
opacity: .2;
} }
.eyebrow {
#hero .hero-kicker { color: var(--quiet);
display: flex; font-size: .75rem;
align-items: center;
gap: 0.75rem;
font-family: var(--th-font-body);
font-size: 1rem;
font-weight: 650;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--hero-mint);
line-height: 1.5;
}
#hero .hero-line {
flex: 0 0 1.5rem;
height: 1px;
background: var(--th-accent);
}
#hero .hero-headline {
display: block;
margin-top: 1.5rem;
font-family: var(--th-font-display);
font-size: clamp(2.6rem, 4.8vw, 4.5rem);
line-height: 1.1;
letter-spacing: -0.03em;
color: var(--th-text);
}
#hero .hero-headline em {
color: var(--hero-mint);
}
#hero .hero-intro {
margin-top: 1.5rem;
max-width: 32rem;
font-size: 1.1875rem;
line-height: 1.75;
color: var(--th-text-muted);
}
/* ---------- Aktionen ---------- */
#hero .hero-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1.75rem;
margin-top: 2rem;
}
#hero .hero-button {
display: inline-flex;
align-items: center;
gap: 1.25rem;
min-height: 3.25rem; /* große Klickfläche */
padding: 0.9rem 1.6rem;
border-radius: 7px;
background: var(--th-accent);
color: #071b16;
font-size: 1.125rem;
font-weight: 650;
transition: background 0.2s, transform 0.2s;
}
#hero .hero-button:hover {
background: #37c99b;
transform: translateY(-2px);
}
#hero .hero-link {
display: inline-flex;
align-items: center;
gap: 0.75rem;
min-height: 2.75rem;
font-size: 1.125rem;
font-weight: 600; font-weight: 600;
color: var(--th-text-soft); letter-spacing: .22em;
line-height: 1.65;
} }
.hero-copy > .eyebrow {
#hero .hero-link:hover { display: inline-flex;
color: var(--hero-mint); align-items: center;
gap: 10px;
padding: 8px 14px;
border: 1px solid var(--line);
border-radius: 999px;
background: rgb(255 255 255 / 4%);
color: var(--muted);
font-size: .6875rem;
letter-spacing: .16em;
} }
.tiny-line { width: 16px; height: 1px; flex-shrink: 0; background: var(--accent-warm); }
/* ---------- Bild ---------- */ h1 {
#hero .hero-visual { margin-block: 28px;
font-size: clamp(2.75rem, 4.65vw, 4.5rem);
font-weight: 600;
letter-spacing: -.05em;
line-height: 1.06;
}
h1 em { color: inherit; font: inherit; }
.hero-intro { max-width: 510px; color: rgb(255 255 255 / 74%); font-size: 1.125rem; line-height: 1.8; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 24px; margin-top: 32px; }
.hero-note { margin-top: 26px; color: var(--quiet); font-size: .875rem; }
.check { margin-right: 7px; color: rgb(255 255 255 / 72%); }
.hero-visual {
position: relative; position: relative;
margin: 0; height: 570px;
height: clamp(320px, 52svh, 540px); padding: 12px;
border-radius: 15px;
overflow: hidden; overflow: hidden;
background: var(--th-bg-elevated); border: 1px solid rgb(255 255 255 / 12%);
border-radius: 2rem;
background: var(--panel);
box-shadow: 0 24px 60px rgb(0 0 0 / 22%);
} }
.hero-visual > img { width: 100%; height: 100%; border-radius: 1.4rem; object-position: center; }
#hero .hero-visual img { .hero-visual figcaption {
width: 100%;
height: 100%;
object-fit: cover;
}
#hero .hero-visual::after { /* Verlauf für lesbare Bildunterschrift */
content: "";
position: absolute; position: absolute;
inset: 35% 0 0; right: 28px;
background: linear-gradient(transparent, rgb(5 15 18 / 0.94)); bottom: 28px;
left: 28px;
padding: 22px 24px;
border: 1px solid rgb(255 255 255 / 14%);
border-radius: 1.4rem;
background: rgb(3 13 18 / 84%);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
} }
.hero-visual figcaption span { color: var(--quiet); font-size: .75rem; letter-spacing: .19em; }
#hero figcaption { .hero-visual figcaption p { margin-top: 10px; font-size: 1.25rem; font-weight: 500; line-height: 1.5; }
.image-tag {
position: absolute; position: absolute;
left: 2rem; top: 28px;
bottom: 2rem; right: 28px;
z-index: 1; padding: 10px 16px;
} border: 1px solid rgb(255 255 255 / 18%);
border-radius: 1rem;
#hero .hero-caption-label { background: rgb(3 13 18 / 78%);
display: block;
font-size: 0.8rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #b5efda;
}
#hero .hero-caption-text {
display: block;
margin-top: 0.6rem;
font-family: var(--th-font-display);
font-size: 1.75rem;
line-height: 1.3;
color: var(--th-text);
}
#hero .hero-tag {
position: absolute;
top: 1.25rem;
right: 1.25rem;
padding: 0.75rem 1rem;
border: 1px solid rgb(255 255 255 / 0.19);
border-radius: 8px;
background: rgb(13 13 26 / 0.8);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
font-size: 0.7rem; color: var(--muted);
letter-spacing: 0.1em; font-size: .6875rem;
text-transform: uppercase; letter-spacing: .13em;
text-align: center; text-align: center;
color: var(--th-text-soft);
} }
.image-tag b { display: block; margin-top: 2px; color: #fff; font-family: var(--font-display); font-size: 1.8rem; font-weight: 600; letter-spacing: -.04em; line-height: 1.2; }
.values-strip { border-block: 1px solid var(--line); background: rgb(255 255 255 / 2%); }
.values-strip .shell { display: flex; justify-content: center; align-items: center; gap: 60px; padding-block: 22px; color: var(--muted); font-size: .875rem; }
.values-strip i { color: rgb(255 255 255 / 24%); font-size: .75rem; font-style: normal; }
#hero .hero-tag b { /* 5. Vorteile und wiederkehrende Sektionsüberschriften */
display: block; .section { padding-block: 96px; }
margin-top: 0.25rem; .section-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 56px; margin-bottom: 44px; }
font-family: var(--th-font-display); .section-heading h2 { margin-top: 16px; font-size: clamp(2rem, 3.1vw, 3rem); }
font-size: 2rem; .section-heading > p { max-width: 350px; color: var(--muted); font-size: 1rem; line-height: 1.85; }
line-height: 1.1; .benefit-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; }
letter-spacing: 0; .benefit-grid article { padding-top: 28px; border-top: 1px solid var(--line); }
color: var(--hero-mint); .benefit-icon { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; margin-bottom: 22px; border: 1px solid var(--line); border-radius: .875rem; background: rgb(255 255 255 / 4%); color: rgb(255 255 255 / 74%); font-size: 1.5rem; }
} .benefit-grid h3 { margin-bottom: 14px; font-size: 1.25rem; }
.benefit-grid p { max-width: 365px; color: var(--muted); font-size: 1rem; }
/* ---------- 3 Vorteile unter dem Hero ---------- */
#hero .hero-benefits {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
margin: 2.5rem 0 0;
padding: 1.5rem 0 0;
list-style: none;
border-top: 1px solid var(--hero-line);
}
#hero .hero-benefits li {
display: flex;
gap: 1rem;
align-items: flex-start;
}
#hero .hero-benefit-icon {
flex: 0 0 2.5rem;
display: flex;
align-items: center;
justify-content: center;
height: 2.5rem;
border-radius: 10px;
background: rgb(16 185 129 / 0.1);
color: var(--hero-mint);
font-size: 1.3rem;
}
#hero .hero-benefits h2 {
margin: 0;
font-family: var(--th-font-body);
font-size: 1.125rem;
font-weight: 600;
line-height: 1.35;
letter-spacing: -0.01em;
color: var(--th-text);
}
#hero .hero-benefits p {
margin: 0.3rem 0 0;
font-size: 1rem;
line-height: 1.55;
color: var(--th-text-muted);
}
/* ---------- Tablet ---------- */
@media (max-width: 1100px) {
#hero .hero-grid { gap: 2rem; }
#hero .hero-benefits { gap: 1.25rem; }
}
/* ---------- Mobil: Text zuerst, Bild darunter ---------- */
@media (max-width: 760px) {
#hero {
min-height: 0;
padding-block: 2rem 3rem;
}
#hero .hero-grid {
grid-template-columns: 1fr;
gap: 2rem;
}
#hero .hero-kicker { font-size: 0.875rem; }
#hero .hero-headline { margin-top: 1rem; font-size: clamp(2.3rem, 10vw, 3.2rem); }
#hero .hero-intro { margin-top: 1rem; font-size: 1.125rem; }
#hero .hero-actions { margin-top: 1.5rem; gap: 1.25rem; }
#hero .hero-visual { height: 340px; }
#hero figcaption { left: 1.25rem; bottom: 1.25rem; }
#hero .hero-caption-text { font-size: 1.4rem; }
#hero .hero-benefits { grid-template-columns: 1fr; gap: 1.25rem; margin-top: 2rem; }
}
@media (max-width: 390px) {
#hero .hero-button { width: 100%; justify-content: space-between; }
}

View File

@ -1,270 +1,389 @@
<!DOCTYPE html> <!doctype html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ernährungsberatung für Triathleten 50+ | tri-hub</title> <title>Ernährungsberatung ab 50 | TriHub</title>
<meta name="description" content="Ernährungsberatung für Triathletinnen und Triathleten ab 50."> <meta
name="description"
<!-- Kunden-CSS (Tailwind-Build von tri-hub.de) – NICHT verändern --> content="Ernährung, die zu deinem Triathlon und deinem Leben passt. Entdecke drei Konzepte für persönliche Ernährungsberatung bei TriHub."
<link rel="stylesheet" href="css/assets.css"> />
<link
<!-- Schriften laut Design-DNA: Playfair Display (Überschriften) + Inter (Text) --> rel="icon"
<link rel="preconnect" href="https://fonts.googleapis.com"> type="image/jpeg"
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> href="assets/images/icons/trihub-logo.jpg"
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Playfair+Display:wght@600;700&display=swap"> />
<link rel="stylesheet" href="css/assets.css" />
<!-- Projektweite Basis (Design-Tokens, Lesbarkeit) --> <link rel="stylesheet" href="css/base.css" />
<link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="css/sections/hero.css" />
<link rel="stylesheet" href="css/sections/angebote.css" />
<!-- Ein Stylesheet pro User Story – jede Datei gehört genau einem Feature-Branch --> <link rel="stylesheet" href="css/sections/ablauf.css" />
<link rel="stylesheet" href="css/sections/hero.css"> <!-- US01-01 --> <link rel="stylesheet" href="css/responsive.css" />
<link rel="stylesheet" href="css/sections/angebote.css"> <!-- US01-02 --> <script src="js/angebote.js" defer></script>
<link rel="stylesheet" href="css/sections/ablauf.css"> <!-- US01-03 --> <script src="js/main.js" defer></script>
</head> </head>
<body> <body>
<a class="skip" href="#inhalt">Zum Inhalt</a>
<header class="site-header"> <header class="site-header">
<div class="container"> <div class="shell header-inner">
<a href="/" class="site-logo">tri-hub</a> <a
<!-- US01-05 Navigation: späterer Sprint --> class="brand brand-logo"
</div> href="https://tri-hub.de/"
</header> aria-label="TriHub Startseite"
><span class="brand-mark"
<main> ><img
src="assets/images/icons/trihub-logo.jpg"
<!-- ========== US01-01: Angebot und Zielgruppe (Hero) ========== width="1024"
Branch: feature/US01-01-hero · Styles: css/sections/hero.css height="1024"
Nur innerhalb dieser <section> arbeiten. --> alt="Trihub – fit und vital ab 50!" /></span
<section id="hero" aria-labelledby="hero-title"> ><span class="brand-copy"
<div class="container"> ><span class="brand-name">Tri-hub</span
<div class="hero-grid"> ><span class="brand-sub"
>Ernährungsberatung für Triathlon ab 50</span
></span
></a
>
<nav aria-label="Hauptnavigation">
<a href="https://tri-hub.de/coaching-1zu1">Coaching 1zu1</a
><a href="#inhalt" aria-current="page">Ernährungsberatung</a
><a href="#ablauf">So läuft’s ab</a>
</nav>
<a class="button small header-cta" href="#angebote"
>Angebote entdecken</a
>
<button
class="menu-toggle"
aria-expanded="false"
aria-controls="mobile-nav"
aria-label="Menü öffnen"
>
☰
</button>
</div>
<nav id="mobile-nav" aria-label="Mobile Navigation" hidden>
<a href="#angebote">Angebote entdecken</a
><a href="#vorteile">Deine Vorteile</a
><a href="#ablauf">So läuft’s ab</a
><a href="https://tri-hub.de/coaching-1zu1">Coaching 1zu1</a>
</nav>
</header>
<main id="inhalt">
<section class="hero shell" aria-labelledby="hero-title">
<div class="hero-copy"> <div class="hero-copy">
<div class="eyebrow">
<span class="tiny-line"></span> ERNÄHRUNGSBERATUNG FÜR TRIATHLON AB
50
</div>
<h1 id="hero-title"> <h1 id="hero-title">
<span class="hero-kicker"><span class="hero-line" aria-hidden="true"></span>Ernährungsberatung für Triathlon ab&nbsp;50</span> Gut versorgt.<br />Für alles, was<br /><em>noch vor dir liegt.</em>
<span class="hero-headline">Gut versorgt.<br>Für alles, was<br><em>noch vor dir liegt.</em></span>
</h1> </h1>
<p class="hero-intro"> <p class="hero-intro">
Dein nächstes Ziel beginnt auch auf dem Teller. Finde eine Ernährung, Dein nächstes Ziel beginnt auch auf dem Teller. Finde eine
die zu deinem Training, deinem Körper und deinem Leben passt. Ernährung, die zu deinem Training, deinem Körper und deinem Leben
passt.
</p> </p>
<div class="hero-actions"> <div class="hero-actions">
<a class="hero-button" href="#angebote">Mehr erfahren <span aria-hidden="true">↓</span></a> <a class="button" href="#angebote">Deine Begleitung finden</a
<a class="hero-link" href="#ablauf">So funktioniert’s <span aria-hidden="true">→</span></a> ><a class="text-link" href="#ablauf">So funktioniert’s</a>
</div>
<div class="hero-note">
<span class="check" aria-hidden="true">✓</span> Persönlich.
Alltagstauglich. Auf Augenhöhe.
</div> </div>
</div> </div>
<figure class="hero-visual"> <figure class="hero-visual">
<img src="assets/images/hero/hero.webp" width="1200" height="800" <img
alt="Zwei sportlich aktive Menschen ab 50 besprechen einen Plan neben einem Fahrrad am See" src="assets/images/hero/hero.webp"
fetchpriority="high"> width="1200"
height="800"
alt="Zwei sportlich aktive Menschen ab 50 besprechen einen Plan neben einem Fahrrad am See"
fetchpriority="high"
/>
<figcaption> <figcaption>
<span class="hero-caption-label">Dein Tempo. Deine Ziele.</span> <span>DEIN TEMPO. DEINE ZIELE.</span>
<span class="hero-caption-text">Mehr Freude am Sport.<br>Mehr Energie fürs Leben.</span> <p>Mehr Freude am Sport.<br />Mehr Energie fürs Leben.</p>
</figcaption> </figcaption>
<div class="hero-tag" aria-hidden="true">Triathlon <b>50+</b></div> <div class="image-tag">TRIATHLON <b>50+</b></div>
</figure> </figure>
</section>
</div> <div class="values-strip">
<div class="shell">
<!-- 3 Vorteile mit Überschrift (Checkliste US01-01) --> <span>Für deinen Einstieg</span><i aria-hidden="true">✦</i
<ul class="hero-benefits" aria-label="Deine Vorteile auf einen Blick"> ><span>Für deinen Wiedereinstieg</span><i aria-hidden="true">✦</i
<li> ><span>Für dein nächstes Ziel</span>
<span class="hero-benefit-icon" aria-hidden="true">↗</span>
<div>
<h2>Energie gezielt einplanen</h2>
<p>Mahlzeiten passend zu Ruhetagen, harten Einheiten und langen Ausfahrten.</p>
</div>
</li>
<li>
<span class="hero-benefit-icon" aria-hidden="true">↻</span>
<div>
<h2>Regeneration mitdenken</h2>
<p>Ernährung, die deinen Körper nach dem Training gezielt unterstützt.</p>
</div>
</li>
<li>
<span class="hero-benefit-icon" aria-hidden="true">✓</span>
<div>
<h2>Im Alltag dranbleiben</h2>
<p>Routinen, die zu Arbeit, Familie und deinen Vorlieben passen.</p>
</div>
</li>
</ul>
</div>
</section>
<!-- ========== /US01-01 ========== -->
<!-- ========== US01-02: Angebote und Pakete ==========
Branch: feature_US01-02 · Styles: css/sections/angebote.css · Script: js/angebote.js
Nur innerhalb dieser <section> arbeiten. -->
<section id="angebote" aria-labelledby="angebote-title">
<div class="container">
<div class="angebote-heading">
<div>
<p class="angebote-eyebrow">Deine Ernährung. Deine Begleitung.</p>
<h2 id="angebote-title">So viel Unterstützung,<br>wie du brauchst.</h2>
</div>
<p>Ein klarer Einstieg oder eine enge Begleitung: Wähle den Rahmen, der sich für dich richtig anfühlt.</p>
</div> </div>
</div>
<p class="concept-note"><span aria-hidden="true">ⓘ</span> Angebotskonzept · Leistungen zur Abstimmung. Preise und Laufzeiten sind noch offen.</p> <section id="vorteile" class="shell section benefits">
<div class="section-heading">
<div class="package-grid"> <div>
<article class="package"> <p class="eyebrow">WAS DU DAVON HAST</p>
<div class="package-image"> <h2>Dein Training verdient<br />die passende Ernährung.</h2>
<img src="assets/images/angebote/starter.webp" width="700" height="420" alt="Gemeinsame Planung am See für den sportlichen Einstieg" loading="lazy"> </div>
</div> <p>
<div class="package-body"> Du bringst die Motivation mit. Gemeinsam schaffen wir Orientierung
<p class="package-label">Starter</p> für das Essen und Trinken rund um deinen Sport.
<h3>Dein erster<br>guter Schritt.</h3> </p>
<p class="package-text">Für Einsteiger und Wiedereinsteiger, die ihre Ernährung verstehen und gezielt verändern möchten.</p> </div>
<ul> <div class="benefit-grid">
<li>Persönliche Bestandsaufnahme</li> <article>
<li>Analyse deiner Essgewohnheiten</li> <span class="benefit-icon" aria-hidden="true">↗</span>
<li>Konkrete Empfehlungen für den Alltag</li> <h3>Energie gezielt einplanen</h3>
</ul> <p>
<button type="button" class="package-button" data-package="starter">Starter entdecken <span aria-hidden="true">↗</span></button> Verstehe, wie du deine Mahlzeiten auf ruhige Tage, intensive
</div> Einheiten und lange Ausfahrten abstimmst.
</p>
</article> </article>
<article class="package featured"> <article>
<div class="package-image"> <span class="benefit-icon" aria-hidden="true">↻</span>
<img src="assets/images/angebote/standard.webp" width="700" height="420" alt="Sportler und Coach besprechen gemeinsam einen Plan am Fahrrad" loading="lazy"> <h3>Regeneration mitdenken</h3>
<span class="package-badge">Mit individuellem Plan</span> <p>
</div> Gib Erholung einen festen Platz – mit einer Ernährung, die deinen
<div class="package-body"> Körper nach dem Training unterstützt.
<p class="package-label">Standard</p> </p>
<h3>Aus guten Vorsätzen<br>wird dein Plan.</h3>
<p class="package-text">Für alle, die Ernährung und Training aufeinander abstimmen und neue Routinen festigen möchten.</p>
<ul>
<li>Alles aus Starter</li>
<li>Individueller Ernährungsrahmen</li>
<li>Feedback und gemeinsame Anpassung</li>
</ul>
<button type="button" class="package-button is-primary" data-package="standard">Standard entdecken <span aria-hidden="true">↗</span></button>
</div>
</article> </article>
<article class="package"> <article>
<div class="package-image"> <span class="benefit-icon" aria-hidden="true">✓</span>
<img src="assets/images/angebote/premium.webp" width="700" height="420" alt="Persönliches Coachinggespräch auf einer Bank neben einem Triathlonrad" loading="lazy"> <h3>Im Alltag dranbleiben</h3>
</div> <p>
<div class="package-body"> Entwickle Routinen, die zu Arbeit, Familie und deinen Vorlieben
<p class="package-label">Premium</p> passen. Ohne komplizierte Regeln.
<h3>Dein Ziel.<br>Ganz persönlich.</h3> </p>
<p class="package-text">Für ambitionierte Ziele und alle, die sich eine besonders enge, individuelle Begleitung wünschen.</p>
<ul>
<li>Alles aus Standard</li>
<li>Verpflegungsstrategie für Wettkämpfe</li>
<li>Engmaschige Begleitung und Anpassung</li>
</ul>
<button type="button" class="package-button" data-package="premium">Premium entdecken <span aria-hidden="true">↗</span></button>
</div>
</article> </article>
</div> </div>
</section>
<!-- Verweis auf Rabatte aus dem Coaching 1zu1 --> <section id="angebote" class="offers-section">
<div class="coaching-banner"> <div class="shell section">
<div class="bonus-symbol" aria-hidden="true">%</div> <div class="section-heading">
<div>
<p class="eyebrow">DEINE ERNÄHRUNG. DEINE BEGLEITUNG.</p>
<h2>So viel Unterstützung,<br />wie du brauchst.</h2>
</div>
<p>
Ein klarer Einstieg oder eine enge Begleitung: Wähle den Rahmen,
der sich für dich richtig anfühlt.
</p>
</div>
<div class="concept-note">
<span aria-hidden="true">ⓘ</span> Angebotskonzept · Leistungen zur
Abstimmung. Preise und Laufzeiten sind noch offen.
</div>
<div class="package-grid">
<article class="package">
<div class="package-image">
<img
src="assets/images/angebote/starter.webp"
width="700"
height="420"
alt="Gemeinsame Planung am See für den sportlichen Einstieg"
loading="lazy"
/>
</div>
<div class="package-body">
<p class="eyebrow">STARTER</p>
<h3>Dein erster<br />guter Schritt.</h3>
<p>
Für Einsteiger und Wiedereinsteiger, die ihre Ernährung
verstehen und gezielt verändern möchten.
</p>
<ul>
<li>Persönliche Bestandsaufnahme</li>
<li>Analyse deiner Essgewohnheiten</li>
<li>Konkrete Empfehlungen für den Alltag</li>
</ul>
<button
class="button outline package-open"
data-package="starter"
>
Starter entdecken
</button>
</div>
</article>
<article class="package featured">
<div class="package-image">
<img
src="assets/images/angebote/standard.webp"
width="700"
height="420"
alt="Sportler und Coach besprechen gemeinsam einen Plan am Fahrrad"
loading="lazy"
/><span class="package-badge">MIT INDIVIDUELLEM PLAN</span>
</div>
<div class="package-body">
<p class="eyebrow">STANDARD</p>
<h3>Aus guten Vorsätzen<br />wird dein Plan.</h3>
<p>
Für alle, die Ernährung und Training aufeinander abstimmen und
neue Routinen festigen möchten.
</p>
<ul>
<li>Alles aus Starter</li>
<li>Individueller Ernährungsrahmen</li>
<li>Feedback und gemeinsame Anpassung</li>
</ul>
<button class="button package-open" data-package="standard">
Standard entdecken
</button>
</div>
</article>
<article class="package">
<div class="package-image">
<img
src="assets/images/angebote/premium.webp"
width="700"
height="420"
alt="Persönliches Coachinggespräch auf einer Bank neben einem Triathlonrad"
loading="lazy"
/>
</div>
<div class="package-body">
<p class="eyebrow">PREMIUM</p>
<h3>Dein Ziel.<br />Ganz persönlich.</h3>
<p>
Für ambitionierte Ziele und alle, die sich eine besonders
enge, individuelle Begleitung wünschen.
</p>
<ul>
<li>Alles aus Standard</li>
<li>Verpflegungsstrategie für Wettkämpfe</li>
<li>Engmaschige Begleitung und Anpassung</li>
</ul>
<button
class="button outline package-open"
data-package="premium"
>
Premium entdecken
</button>
</div>
</article>
</div>
<div class="coaching-banner">
<div class="bonus-symbol" aria-hidden="true">%</div>
<div>
<h3>Schon im Coaching 1zu1? Dein Vorteil kommt mit.</h3>
<p>
Spare mit deinem bestehenden Coaching-Paket bei der
Ernährungsberatung.
</p>
</div>
<button id="bonus-open" class="text-link">Rabatte ansehen</button>
</div>
</div>
</section>
<section id="ablauf" class="shell section process">
<div class="section-heading">
<div> <div>
<h3>Schon im Coaching 1zu1? Dein Vorteil kommt mit.</h3> <p class="eyebrow">SO LÄUFT’S AB</p>
<p>Mit deinem Coaching-Paket sparst du bei der Ernährungsberatung: Starter 3&nbsp;%, Standard 5&nbsp;%, Premium 10&nbsp;%.</p> <h2>Vom ersten Gespräch<br />zu deiner eigenen Routine.</h2>
</div> </div>
<button type="button" class="bonus-button" data-package="bonus">Rabatte ansehen <span aria-hidden="true">↗</span></button> <p>
Du musst nicht schon alles wissen. Wir beginnen bei dir und gehen
Schritt für Schritt weiter.
</p>
</div> </div>
</div>
<!-- Detaildialog (öffnet nur per Klick, kein Pop-up beim Laden) -->
<dialog id="package-dialog" aria-labelledby="dialog-title">
<div class="dialog-inner">
<button type="button" class="close-dialog" aria-label="Details schließen">×</button>
<p id="dialog-eyebrow" class="angebote-eyebrow"></p>
<h2 id="dialog-title"></h2>
<p id="dialog-description"></p>
<div id="detail-section">
<h3>Deine Begleitung im Überblick</h3>
<ul id="dialog-features"></ul>
<p class="dialog-hint">Konzeptvorschlag: Leistungsumfang, Kontaktfrequenz, Laufzeit und Preis werden noch abgestimmt.</p>
</div>
<div class="discount-box">
<p class="angebote-eyebrow">Dein Coaching-Vorteil</p>
<h3>Du bist schon im Coaching 1zu1?</h3>
<label for="coaching-select">Dein bestehendes Coaching-Paket</label>
<select id="coaching-select">
<option value="0">Noch kein Coaching 1zu1</option>
<option value="3">Starter-Coaching</option>
<option value="5">Standard-Coaching</option>
<option value="10">Premium-Coaching (24 oder 52 Wochen)</option>
</select>
<p id="discount-result" aria-live="polite"></p>
<p class="dialog-hint">Der Rabatt richtet sich nach deinem bestehenden Coaching 1zu1, unabhängig vom gewählten Ernährungsangebot.</p>
<a href="https://tri-hub.de/coaching-1zu1" target="_blank" rel="noopener">Coaching 1zu1 bei TriHub ansehen ↗</a>
</div>
<button type="button" class="dialog-done">Zurück zu den Angeboten</button>
</div>
</dialog>
</section>
<!-- ========== /US01-02 ========== -->
<!-- ========== US01-03: Ablauf sehen ==========
Branch: feature_US01-03 · Styles: css/sections/ablauf.css
Nur innerhalb dieser <section> arbeiten. -->
<section id="ablauf" aria-labelledby="ablauf-title">
<div class="container">
<div class="ablauf-heading">
<div>
<p class="ablauf-eyebrow">So läuft’s ab</p>
<h2 id="ablauf-title">Vom ersten Gespräch<br>zu deiner eigenen Routine.</h2>
</div>
<p>Du musst nicht schon alles wissen. Wir beginnen bei dir und gehen Schritt für Schritt weiter.</p>
</div>
<ol class="steps"> <ol class="steps">
<li class="step"> <li>
<span class="step-number" aria-hidden="true">01</span> <span class="step-number">01</span>
<h3><span class="sr-only">Schritt 1: </span>Dich kennenlernen</h3> <h3>Dich kennenlernen</h3>
<p>Wir besprechen deine Ziele, deinen Alltag und deine sportliche Ausgangslage.</p> <p>
Wir besprechen deine Ziele, deinen Alltag und deine sportliche
Ausgangslage.
</p>
</li> </li>
<li class="step"> <li>
<span class="step-number" aria-hidden="true">02</span> <span class="step-number">02</span>
<h3><span class="sr-only">Schritt 2: </span>Gewohnheiten verstehen</h3> <h3>Gewohnheiten verstehen</h3>
<p>Ein Ernährungsprotokoll macht sichtbar, was bereits passt und wo du ansetzen kannst.</p> <p>
Ein Ernährungsprotokoll macht sichtbar, was bereits passt und wo
du ansetzen kannst.
</p>
</li> </li>
<li class="step"> <li>
<span class="step-number" aria-hidden="true">03</span> <span class="step-number">03</span>
<h3><span class="sr-only">Schritt 3: </span>Deinen Weg planen</h3> <h3>Deinen Weg planen</h3>
<p>Du erhältst konkrete Empfehlungen – je nach Paket ergänzt um einen individuellen Ernährungsrahmen.</p> <p>
Du erhältst konkrete Empfehlungen – je nach Paket ergänzt um einen
individuellen Ernährungsrahmen.
</p>
</li> </li>
<li class="step"> <li>
<span class="step-number" aria-hidden="true">04</span> <span class="step-number">04</span>
<h3><span class="sr-only">Schritt 4: </span>Umsetzen &amp; anpassen</h3> <h3>Umsetzen & anpassen</h3>
<p>Du erprobst die Empfehlungen. In Standard und Premium begleiten Feedback und Anpassungen deinen Weg.</p> <p>
Du erprobst die Empfehlungen. In Standard und Premium begleiten
Feedback und Anpassungen deinen Weg.
</p>
</li> </li>
</ol> </ol>
<div class="process-end"> <div class="process-end">
<p>Der beste Plan ist der, der in dein Leben passt.</p> <p>Der beste Plan ist der, der in dein Leben passt.</p>
<a class="process-link" href="#angebote">Dein Angebot entdecken <span aria-hidden="true">↑</span></a> <a class="text-link" href="#angebote">Dein Angebot entdecken</a>
</div> </div>
</section>
</main>
<footer>
<div class="shell footer-main">
<a
class="brand brand-logo"
href="https://tri-hub.de/"
aria-label="TriHub Startseite"
><span class="brand-mark"
><img
src="assets/images/icons/trihub-logo.jpg"
width="1024"
height="1024"
alt="Trihub – fit und vital ab 50!" /></span
></a>
<p>Dein sportliches Zuhause ab 50.</p>
<a href="https://tri-hub.de/">Zur TriHub-Website</a>
</div> </div>
</section> <div class="shell footer-note">
<!-- ========== /US01-03 ========== --> <span>Projektprototyp · Keine Buchung oder Datenübermittlung</span
><span
</main> >Ernährungsangebote sind Konzeptvorschläge, keine veröffentlichten
TriHub-Tarife.</span
<script src="js/main.js" defer></script> >
<script src="js/angebote.js" defer></script> <!-- US01-02 --> </div>
</body> </footer>
<dialog id="package-dialog" aria-labelledby="dialog-title">
<div class="dialog-inner">
<button class="close-dialog" aria-label="Details schließen">×</button>
<p id="dialog-eyebrow" class="eyebrow"></p>
<h2 id="dialog-title"></h2>
<p id="dialog-description"></p>
<section id="detail-section">
<h3>Deine Begleitung im Überblick</h3>
<ul id="dialog-features"></ul>
<p class="detail-note">
Konzeptvorschlag: Leistungsumfang, Kontaktfrequenz, Laufzeit und
Preis werden noch abgestimmt.
</p>
</section>
<section class="discount-box">
<p class="eyebrow">DEIN COACHING-VORTEIL</p>
<h3>Du bist schon im Coaching 1zu1?</h3>
<label for="coaching-select">Dein bestehendes Coaching-Paket</label
><select id="coaching-select">
<option value="0">Noch kein Coaching 1zu1</option>
<option value="3">Starter-Coaching</option>
<option value="5">Standard-Coaching</option>
<option value="10">Premium-Coaching (24 oder 52 Wochen)</option>
</select>
<div id="discount-result" aria-live="polite"></div>
<p class="discount-help">
Der Rabatt richtet sich nach deinem bestehenden Coaching 1zu1,
unabhängig vom gewählten Ernährungsangebot. Er gilt während der
Coaching-Laufzeit auf den aktuellen Verkaufspreis der
Ernährungsberatung.
</p>
<a
href="https://tri-hub.de/coaching-1zu1"
target="_blank"
rel="noopener"
>Coaching 1zu1 bei TriHub ansehen</a
>
</section>
<button class="button dialog-done">Zurück zu den Angeboten</button>
</div>
</dialog>
</body>
</html> </html>

View File

@ -1,105 +1,115 @@
// US01-02: Paketdetails und Coaching-Rabatt im Dialog (Vorlage: Prototyp) // Vollständige Paketdetails und Rabattdialog aus TriHub Design Original.
'use strict';
(() => { (() => {
const section = document.querySelector('#angebote'); "use strict";
if (!section) return;
const packages = { const packages = {
starter: { starter: {
name: 'Starter', name: "Starter",
title: 'Dein erster guter Schritt.', title: "Dein erster guter Schritt.",
description: 'Du möchtest wissen, wo du stehst und was du konkret verändern kannst? Starter schafft eine persönliche Grundlage für deinen Einstieg.', description:
"Du möchtest wissen, wo du stehst und was du konkret verändern kannst? Starter schafft eine persönliche Grundlage für deinen Einstieg.",
features: [ features: [
'Gemeinsames Erstgespräch zu Zielen, Training, Alltag und Vorlieben.', "Gemeinsames Erstgespräch zu Zielen, Training, Alltag und Vorlieben.",
'Auswertung eines Ernährungsprotokolls mit Blick auf deine bisherigen Gewohnheiten.', "Auswertung eines Ernährungsprotokolls mit Blick auf deine bisherigen Gewohnheiten.",
'Persönliche Handlungsempfehlungen für Essen und Trinken im Alltag und rund um dein Training.', "Persönliche Handlungsempfehlungen für Essen und Trinken im Alltag und rund um dein Training.",
'Abschlussgespräch zur Einordnung deiner nächsten Schritte.' "Abschlussgespräch zur Einordnung deiner nächsten Schritte.",
] ],
}, },
standard: { standard: {
name: 'Standard', name: "Standard",
title: 'Aus guten Vorsätzen wird dein Plan.', title: "Aus guten Vorsätzen wird dein Plan.",
description: 'Du möchtest aus einzelnen Empfehlungen eine verlässliche Routine machen? Standard verbindet die Bestandsaufnahme mit einem individuellen Plan und persönlichem Feedback.', description:
"Du möchtest aus einzelnen Empfehlungen eine verlässliche Routine machen? Standard verbindet die Bestandsaufnahme mit einem individuellen Plan und persönlichem Feedback.",
features: [ features: [
'Alle vorgeschlagenen Leistungen aus Starter.', "Alle vorgeschlagenen Leistungen aus Starter.",
'Ein individueller Ernährungsrahmen für Trainings- und Ruhetage.', "Ein individueller Ernährungsrahmen für Trainings- und Ruhetage.",
'Praktische Mahlzeitenideen, abgestimmt auf deine Vorlieben und deinen Alltag.', "Praktische Mahlzeitenideen, abgestimmt auf deine Vorlieben und deinen Alltag.",
'Feedbackgespräche, um Erfahrungen zu besprechen und den Plan gemeinsam anzupassen.' "Feedbackgespräche, um Erfahrungen zu besprechen und den Plan gemeinsam anzupassen.",
] ],
}, },
premium: { premium: {
name: 'Premium', name: "Premium",
title: 'Dein Ziel. Ganz persönlich.', title: "Dein Ziel. Ganz persönlich.",
description: 'Du möchtest deine Ernährung gezielt auf sportliche Herausforderungen abstimmen? Premium sieht eine besonders enge Begleitung bis in die Wettkampfvorbereitung vor.', description:
"Du möchtest deine Ernährung gezielt auf sportliche Herausforderungen abstimmen? Premium sieht eine besonders enge Begleitung bis in die Wettkampfvorbereitung vor.",
features: [ features: [
'Alle vorgeschlagenen Leistungen aus Standard.', "Alle vorgeschlagenen Leistungen aus Standard.",
'Eine individuelle Strategie für Essen und Trinken vor, während und nach Wettkämpfen.', "Eine individuelle Strategie für Essen und Trinken vor, während und nach Wettkämpfen.",
'Erprobung der Verpflegung im Training und Anpassung an deine Erfahrungen.', "Erprobung der Verpflegung im Training und Anpassung an deine Erfahrungen.",
'Engmaschiger persönlicher Austausch und laufende Anpassungen an deine Trainingsphasen.' "Engmaschiger persönlicher Austausch und laufende Anpassungen an deine Trainingsphasen.",
] ],
} },
}; };
const dialog = document.querySelector("#package-dialog");
const dialog = section.querySelector('#package-dialog'); const select = document.querySelector("#coaching-select");
const select = section.querySelector('#coaching-select'); let trigger;
const result = section.querySelector('#discount-result');
let trigger = null;
function updateDiscount() { function updateDiscount() {
const rate = Number(select.value); const rate = Number(select.value);
const result = document.querySelector("#discount-result");
result.replaceChildren(); result.replaceChildren();
if (rate) { if (rate) {
const strong = document.createElement('strong'); const strong = document.createElement("strong");
strong.textContent = `${rate} % Rabatt`; strong.textContent = rate + " % Rabatt";
result.append(strong, document.createTextNode(' auf deine Ernährungsberatung.')); result.append(
} else { strong,
result.textContent = 'Mit Coaching 1zu1: Starter 3 %, Standard 5 %, Premium 10 % Rabatt.'; document.createTextNode(" auf deine Ernährungsberatung."),
} );
} else
result.textContent =
"Mit Coaching 1zu1: Starter 3 %, Standard 5 %, Premium 10 % Rabatt.";
} }
function openDetails(key, button) { function openDetails(key, button) {
trigger = button; trigger = button;
const data = packages[key]; const data = packages[key];
document.querySelector("#dialog-eyebrow").textContent = data
section.querySelector('#dialog-eyebrow').textContent = ? "ERNÄHRUNGSBERATUNG · " + data.name.toUpperCase()
data ? `Ernährungsberatung · ${data.name}` : 'Coaching 1zu1 · Bonusregelung'; : "COACHING 1ZU1 · BONUSREGELUNG";
section.querySelector('#dialog-title').textContent = document.querySelector("#dialog-title").textContent = data
data ? data.title : 'Dein Coaching zahlt sich aus.'; ? data.title
section.querySelector('#dialog-description').textContent = data : "Dein Coaching zahlt sich aus.";
document.querySelector("#dialog-description").textContent = data
? data.description ? data.description
: 'Dein bestehendes Coaching-Paket bringt dir einen Preisvorteil bei der Ernährungsberatung. Wähle es aus und sieh deinen Rabatt.'; : "Dein bestehendes Coaching-Paket bringt dir einen Preisvorteil bei der Ernährungsberatung. Wähle es aus und entdecke deinen Rabatt.";
document.querySelector("#detail-section").hidden = !data;
section.querySelector('#detail-section').hidden = !data; const list = document.querySelector("#dialog-features");
const list = section.querySelector('#dialog-features');
list.replaceChildren(); list.replaceChildren();
if (data) { if (data)
data.features.forEach((text) => { data.features.forEach((text) => {
const li = document.createElement('li'); const li = document.createElement("li");
li.textContent = text; li.textContent = text;
list.append(li); list.append(li);
}); });
}
updateDiscount(); updateDiscount();
dialog.showModal(); dialog.showModal();
} }
document
section.querySelectorAll('[data-package]').forEach((button) => { .querySelectorAll(".package-open")
button.addEventListener('click', () => openDetails(button.dataset.package, button)); .forEach((button) =>
button.addEventListener("click", () =>
openDetails(button.dataset.package, button),
),
);
document
.querySelector("#bonus-open")
.addEventListener("click", (event) =>
openDetails(null, event.currentTarget),
);
select.addEventListener("change", updateDiscount);
document
.querySelectorAll(".close-dialog,.dialog-done")
.forEach((button) =>
button.addEventListener("click", () => dialog.close()),
);
dialog.addEventListener("click", (event) => {
if (event.target === dialog) {
const r = dialog.getBoundingClientRect();
if (
event.clientX < r.left ||
event.clientX > r.right ||
event.clientY < r.top ||
event.clientY > r.bottom
)
dialog.close();
}
}); });
dialog.addEventListener("close", () => trigger?.focus());
select.addEventListener('change', updateDiscount);
section.querySelectorAll('.close-dialog, .dialog-done').forEach((button) => {
button.addEventListener('click', () => dialog.close());
});
// Klick auf den abgedunkelten Hintergrund schließt den Dialog
dialog.addEventListener('click', (event) => {
if (event.target === dialog) dialog.close();
});
// Fokus zurück auf den auslösenden Button (Barrierefreiheit)
dialog.addEventListener('close', () => trigger?.focus());
})(); })();

View File

@ -1,3 +1,87 @@
// Einstiegspunkt für Skripte. // Mobile Navigation und alle Scrollanimationen aus TriHub Design Original.
// Feature-Code in eigene Dateien auslagern (z. B. js/scroll.js für US01-01) "use strict";
// und hier bzw. in index.html einbinden. (() => {
const menu = document.querySelector(".menu-toggle"),
nav = document.querySelector("#mobile-nav");
menu.addEventListener("click", () => {
const open = menu.getAttribute("aria-expanded") !== "true";
menu.setAttribute("aria-expanded", String(open));
menu.setAttribute("aria-label", open ? "Menü schließen" : "Menü öffnen");
nav.hidden = !open;
});
nav.querySelectorAll("a").forEach((a) =>
a.addEventListener("click", () => {
nav.hidden = true;
menu.setAttribute("aria-expanded", "false");
menu.setAttribute("aria-label", "Menü öffnen");
}),
);
// Progressive enhancement: content stays visible without JS or animation support.
(() => {
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
if (!("IntersectionObserver" in window) || !Element.prototype.animate)
return;
const running = new Map();
const played = new WeakSet();
const delays = new WeakMap();
const targets = [
...document.querySelectorAll(
".hero-copy > *, .hero-visual, .section-heading, .benefit-grid > article, " +
".concept-note, .package-grid > article, .coaching-banner, .steps > li, .process-end",
),
];
document
.querySelectorAll(".hero-copy, .benefit-grid, .package-grid, .steps")
.forEach((group) => {
[...group.children].forEach((child, index) =>
delays.set(child, Math.min(index * 80, 240)),
);
});
function reveal(element) {
if (played.has(element)) return;
played.add(element);
if (reducedMotion.matches) return;
const animation = element.animate(
[
{ opacity: 0, transform: "translateY(24px)" },
{ opacity: 1, transform: "translateY(0)" },
],
{
duration: 720,
delay: delays.get(element) || 0,
easing: "cubic-bezier(.22, 1, .36, 1)",
fill: "backwards",
},
);
running.set(element, animation);
const cleanup = () => {
if (running.get(element) === animation) running.delete(element);
};
animation.onfinish = cleanup;
animation.oncancel = cleanup;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
observer.unobserve(entry.target);
// A direct anchor jump must not animate already-scrolled-past content.
if (entry.boundingClientRect.bottom > 0) reveal(entry.target);
});
},
{ threshold: 0, rootMargin: "0px 0px -24px 0px" },
);
targets.forEach((element) => observer.observe(element));
// Keyboard users never wait for a focused control to become visible.
document.addEventListener("focusin", (event) => {
running.forEach((animation, element) => {
if (element.contains(event.target)) animation.cancel();
});
});
reducedMotion.addEventListener("change", () => {
if (reducedMotion.matches)
running.forEach((animation) => animation.cancel());
});
})();
})();