Compare commits

..

56 Commits

Author SHA1 Message Date
luay 052791eb56 Merge remote-tracking branch 'origin/V-S-US-01-03-Pakete-Online-buchen' into US-03-01-Online-Terminbuchung 2026-09-30 17:49:45 +02:00
Vincent Laux d4ea167f62 links repariert 2026-09-30 17:48:41 +02:00
luay 46780e6dca online Buchung fertig 2026-09-30 17:20:58 +02:00
Vincent Laux f2504995c2 inhalt der premiumseite eingefügt 2026-09-30 16:55:19 +02:00
Vincent Laux f28538a638 inhalt der standard seite eingefügt 2026-09-30 16:50:14 +02:00
Vincent Laux 33698b11b0 banner ausrichtung für alle 3 seiten behoben 2026-09-30 16:33:17 +02:00
Vincent Laux 6c9133edf9 design für starter.html verbessert, banner Zentriert und stylesheet allein auf navigation und und produktdeatils.ss ausgelegt für bessere übersicht 2026-09-30 16:18:10 +02:00
koriboh ed49e09fc1 feat: Implement online appointment booking feature
- Added new HTML page for appointment booking (terminbuchung.html) with form for user input and summary display.
- Created JavaScript file (terminbuchung-form.js) to handle form validation, tag management for allergies, and email submission.
- Developed JavaScript file (terminbuchung.js) for calendar pop-up functionality on the homepage, allowing users to select available dates and times.
- Introduced JSON file (terminbuchung.json) to store availability data for trainers, linking dates to available time slots.
2026-09-30 15:59:10 +02:00
3015418 aad7588301 Paket-Starter + css 2026-09-30 14:38:44 +02:00
3015418 c210cad0b9 Inhalt der Paketseite ergänzt 2026-09-30 14:17:41 +02:00
3015418 38fcc2f233 Datei für Paketbuchung angelegt 2026-09-30 14:16:53 +02:00
3015418 d75916d142 Kontaktbutton korrigiert 2026-09-30 12:27:20 +02:00
Vincent Laux 673942e0fb paket empfehlungsbadge alignment angepasst 2026-09-30 12:15:12 +02:00
3015418 6ba39a9659 Kontaktformular und Pakete korrigiert
Kommentare ergänzt, Menüabstände angepasst und Paketbild aktualisiert.
2026-09-29 21:44:20 +02:00
koriboh c592bff01a Refactor navigation structure and styles for improved consistency and usability 2026-09-29 19:46:41 +02:00
Vincent Laux dc0de8df76 kontakt link fix 2026-09-29 18:24:41 +02:00
Vincent Laux d3ca135d9d git 2026-09-29 18:15:08 +02:00
Shahnam Javidnia f62c4e2e89 Update index.html 2026-09-29 18:08:21 +02:00
koriboh 662c8ddf3c Refactor code structure for improved readability and maintainability 2026-09-29 17:53:04 +02:00
3015418 7a2d0d8e26 Responsive Paketbilder korrigiert und Menüfarbe angepasst
Paketbilder proportional mit oberer Ausrichtung dargestellt und eine zweispaltige Zwischenansicht ergänzt. Gespeicherte Farbanpassung des Menüknopfs übernommen.
2026-09-29 17:42:03 +02:00
Vincent Laux 03c2bc7ab8 style changes 2026-09-29 17:10:35 +02:00
Vincent Laux a32d6f688f new font color muted 2 2026-09-29 16:52:21 +02:00
3015418 1ba8844415 US 04-05: Responsive Navigation und Startseitenlink ergänzt
Mobiles Menü mit Tastaturbedienung, Hintergrund und passenden Sprungabständen ergänzt. Logo abgerundet und mit der Startseite verlinkt. Kontakt bleibt vorerst deaktiviert; das neue Supportbild liegt im Bilderordner bereit.
2026-09-29 16:42:38 +02:00
koriboh 169e72ca35 Remove unused kontakt.html file 2026-09-29 16:19:25 +02:00
Vincent Laux 39952a0c36 navigationsbar um Übersicht, Pakete, Berater, Infos & Kontakt erweitert und placeholders für Links in Index.html 2026-09-29 16:15:08 +02:00
koriboh f5a650134a Add contact page with form and styling; implement scroll-reveal and form validation logic 2026-09-29 15:59:01 +02:00
Vincent Laux f58ee5e483 hardcoded values refactored 2026-09-29 15:58:38 +02:00
3015418 5d1272008e US 04-05: Dauerhaft sichtbare Kopfzeile für die Navigation vorbereitet
Kopfzeile aus der Produktübersicht ausgelagert und Navigationsstile in einer eigenen CSS-Datei zusammengefasst. Aktuellen Zwischenstand mit angepasstem Logo übernommen.
2026-09-29 15:43:01 +02:00
Vincent Laux 45f5b8e9b6 aligned 2026-09-29 15:40:29 +02:00
3015418 0828e3de7c Korrekturen von Shahnam und Vincent für die Navigation zusammengeführt
Produktübersicht und Pakete unverändert aus den Fehlerbehebungsbranches kombiniert. Laufende lokale Navigationsarbeiten bleiben außerhalb dieses Commits.
2026-09-29 15:31:49 +02:00
Vincent Laux abeb935792 pakete Bilder eingefügt, Borderfarbe angepasst, texte aligned und CTA angepasst 2026-09-29 15:27:55 +02:00
3015418 1da0029c9b US 04-01: Fehler in der Produktübersicht behoben
Platzhalter durch Beratungstexte ersetzt, neue Ernährungsbilder ergänzt und die Bilddarstellung sowie Textfarben angepasst. Den auskommentierten Platzhalter-Footer entfernt.
2026-09-29 14:55:47 +02:00
Vincent Laux 0338cb738d neuer branch für Pakete fehler behebung, neue bilder eingefügt 2026-09-29 14:44:36 +02:00
Ahmad Luay Eleiwy 2a4f8a089d Merge pull request 'integration/ernaehrung-review' (#2) from integration/ernaehrung-review into main
Reviewed-on: #2
2026-09-29 10:40:47 +02:00
3015418 be0f003d1d Nummerierung der Vorteile an die Akzentfarbe angepasst 2026-09-29 10:37:54 +02:00
3015418 7427dcad4c Trihub-Logo hinzugefügt
Logo im Kopfbereich eingebunden und responsiv angepasst. Instagram-Links der Berater aktualisiert.
2026-09-29 10:33:30 +02:00
3015418 2ba23222ed Hide product footer placeholder text temporarily 2026-09-29 00:25:02 +02:00
Vincent Laux 3c51e5abd8 Variablen Namen innerhalb der css dateien standardisiert (steht als kommentar wies früher ging), Farben für text, hintergrund, panels und Accent standardisiert, logik für Bild-Gradiant optimiert 2026-09-28 23:40:11 +02:00
luay 28028e76df Titel und Text für Berater eingefügt 2026-09-28 20:43:13 +02:00
3015418 4b3b09b70b docs: add simplified integration review 2026-09-28 15:43:03 +02:00
3015418 8bee9115e7 Consolidate image assets 2026-09-28 12:48:41 +02:00
3015418 9bcf0568b8 Keep integration notes local 2026-09-28 12:42:58 +02:00
3015418 108d65e647 Fix mobile overflow and document integration 2026-09-28 12:35:00 +02:00
3015418 ead4b10185 Merge consultation information 2026-09-28 12:29:45 +02:00
3015418 fa74f8bf61 Merge advisor profiles 2026-09-28 12:28:44 +02:00
3015418 92d06eb5ae Merge service packages 2026-09-28 12:28:03 +02:00
3015418 b34ac27023 Scope product styles for integration 2026-09-28 12:27:23 +02:00
3015418 8535e18b41 Merge product overview 2026-09-28 12:24:48 +02:00
3015418 f7605d86a1 Update responsive hero layout 2026-09-28 12:13:06 +02:00
luay d945d0a60d Zweite Beraterin eingefügt mit gleichen Infos 2026-09-28 11:45:11 +02:00
koriboh 6b2bcb4435 feat(nutrition-site): add initial homepage structure and design system
Implement the main landing page for the nutrition counseling service, including sections for services, team qualifications, consultation details, and product overview. Add global CSS variables defining theme.
2026-09-25 00:03:40 +02:00
3015418 5e093dd33b Clean up product section and add hero image 2026-09-24 18:25:14 +02:00
luay 3b40ba08c3 lili Beraterin hinzugefügt 2026-09-24 18:14:39 +02:00
luay ee19a9a01f dynamisch Infos in json-Datei bearbeiten 2026-09-24 18:01:24 +02:00
luay fb01091e61 erste funktionierende Berater-Sektion 2026-09-24 17:46:29 +02:00
luay dfdcd70d24 Sektinen in index.html aufgeteilt 2026-09-24 16:01:42 +02:00
49 changed files with 7067 additions and 326 deletions

BIN
.DS_Store vendored

Binary file not shown.

199
css/berater.css 100644
View File

@ -0,0 +1,199 @@
/* Ahmad: Styles bleiben in .berater-section; Karten passen auch auf Mobilgeraete. */
/* ==========================================================
Globale Design-Variablen - Angepasst an die Design-DNA von Tri-hub
========================================================== */
.berater-section {
/* Farben und variabel-namen wurden standarttisiert siehe Kommentare */
--background: ##0714198a; /* früher "--bg-dark-1" */
--background-rgb: 7 20 25; /* gleiche Basisfarbe wie --background,
aber ohne fixes Alpha – für Gradient-Stops
wird benutzt um Bildern einen gradient zu verpassen */
--background-panel: #152027; /* früher "--bg-dark-2" */
--accent: #4597A3; /* früher "accent-teal" */
--text-primary: #ffffff; /* früher "text-white" */
--text-muted: #94A3B8;
--font-display: 'Inter', system-ui, sans-serif;
--font-body: 'Inter', system-ui, sans-serif;
}
.berater-section {
margin: 0;
/* Padding vereinheitlicht, um dem Pakete-Bereich zu entsprechen */
padding: 5rem 1.5rem;
font-family: var(--font-body);
background-color: var(--background);
color: var(--text-white);
display: flex;
flex-direction: column;
align-items: center;
/* Gesamten Inhalt vertikal zentrieren */
}
/* =========================================
Styling für Header (Titel und Untertitel) passend zu den Paketen
========================================= */
.berater-section .section-header {
text-align: center;
max-width: 42rem;
margin: 0 auto 3rem;
}
.berater-section .section-title {
font-family: var(--font-display);
font-weight: 700;
font-size: clamp(1.8rem, 3vw, 2.4rem);
color: var(--text-white);
margin: 0 0 0.75rem;
}
.berater-section .section-subtitle {
color: var(--text-muted);
line-height: 1.6;
margin: 0;
}
/* =========================================
Styling für den Container und die Berater-Karten
========================================= */
.berater-section .berater-container {
display: flex;
gap: 30px;
width: 100%;
max-width: 1000px;
flex-wrap: wrap;
justify-content: center;
}
.berater-section .berater-card {
background-color: var(--background-panel);
/* Leichte Umrandung hinzugefügt, ähnlich wie bei den Pakete-Karten */
border: 1px solid rgba(248, 250, 252, 0.08);
/* Border-Radius angepasst, passend zu den Paketen */
border-radius: 14px;
padding: 24px;
width: min(380px, 100%);
box-sizing: border-box;
display: flex;
flex-direction: column;
}
/* ---------- Profilbild, Name und Rolle innerhalb einer Karte ---------- */
.berater-section .berater-image-wrapper {
background-color: var(--background);
border-radius: 10px;
padding: 10px;
margin-bottom: 24px;
}
.berater-section .berater-image-wrapper img {
width: 100%;
height: 350px;
object-fit: cover;
border-radius: 8px;
display: block;
}
.berater-section .berater-name {
margin: 0 0 8px 0;
font-family: var(--font-display);
font-size: 1.4rem;
/* Schriftgröße des Namens harmonisiert */
font-weight: 600;
color: var(--text-white);
}
.berater-section .berater-rolle {
margin: 0 0 24px 0;
font-family: var(--font-body);
font-size: 14px;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 1.5px;
font-weight: 600;
}
/* ---------- Biografie und ergänzende Angaben aus berater.json ---------- */
.berater-section .berater-bio-box {
/* Hintergrund entfernt, um sich nahtlos in die Karte einzufügen */
background-color: transparent;
border-radius: 16px;
/* Überschüssiges Padding entfernt */
padding: 0;
flex-grow: 1;
}
.berater-section .bio-title {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
}
.berater-section .bio-text {
margin: 0;
font-size: 15px;
line-height: 1.6;
color: var(--text-muted);
}
/* =========================================
Styling für dynamische Listen (z. B. Zertifikate)
========================================= */
.berater-section .berater-liste {
margin: 8px 0 0 0;
padding-left: 1.4rem;
color: var(--text-muted);
font-size: 14px;
line-height: 1.6;
list-style: none;
/* Standard-Listenpunkte entfernt für benutzerdefiniertes Styling */
}
.berater-section .berater-liste li {
position: relative;
margin-bottom: 0.7rem;
color: var(--text-primary);
}
/* Grünes quadratisches Aufzählungszeichen hinzugefügt, exakt wie im Pakete-Bereich */
.berater-section .berater-liste li::before {
content: "";
position: absolute;
left: -1.4rem;
top: 0.4em;
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
}
/* =========================================
Styling für dynamische Links (Buttons)
========================================= */
.berater-section .berater-links {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.berater-section .berater-link-btn {
display: inline-block;
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
color: var(--background);
background: var(--accent);
border: none;
border-radius: 8px;
padding: 0.85rem 1rem;
cursor: pointer;
text-decoration: none;
transition: filter 0.15s ease;
}
.berater-section .berater-link-btn:hover,
.berater-section .berater-link-btn:focus-visible {
filter: brightness(1.1);
}

418
css/beratung.css 100644
View File

@ -0,0 +1,418 @@
/* Dennis: Seitenweite Regeln sind auf den Beratungsbereich begrenzt. */
.beratung-bereich {
/* Farben – Tokens von tri-hub.de (Hex-Fallback + oklch) */
--background: #0714198a;
--background-rgb: 7 20 25; /* gleiche Basisfarbe wie --background,
aber ohne fixes Alpha – für Gradient-Stops
wird benutzt um Bildern einen gradient zu verpassen */
--background-panel: #152027; /* früher "surface" */
--text-primary: #ffffff; /* früher "foreground" */
--text-muted: #9ba7aa; /* früher "muted-fg" */
--accent: #4597A3; /* früher "primary" */
--border: rgba(234, 8, 8, 0.1);
/* Typografie */
--font-display: "Sora", sans-serif;
--font-body: "DM Sans", Arial, Helvetica, sans-serif;
/* Layout */
--container: 1120px;
--radius: 1.1rem;
}
/* ---------- Basic ---------- */
.beratung-bereich,
.beratung-bereich *,
.beratung-bereich::before,
.beratung-bereich *::before,
.beratung-bereich::after,
.beratung-bereich *::after {
box-sizing: border-box;
}
.beratung-bereich {
scroll-behavior: smooth;
}
.beratung-bereich {
margin: 0;
background-color: var(--background);
color: var(--foreground);
font-family: var(--font-body);
line-height: 1.625;
-webkit-font-smoothing: antialiased;
position: relative;
isolation: isolate;
}
/* Licht*/
.beratung-bereich::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background:
radial-gradient(900px 520px at 78% -10%, rgba(0, 179, 194, 0.13), transparent 65%),
radial-gradient(700px 480px at -10% 30%, rgba(35, 137, 148, 0.1), transparent 60%),
radial-gradient(600px 420px at 90% 95%, rgba(247, 194, 48, 0.05), transparent 60%);
pointer-events: none;
}
.beratung-bereich h1,
.beratung-bereich h2,
.beratung-bereich h3 {
margin-top: 0;
font-family: var(--font-display);
color: var(--foreground);
line-height: 1.2;
letter-spacing: -0.01em;
}
.beratung-bereich p {
margin: 0 0 1em;
}
.beratung-bereich .container {
width: min(100% - 48px, var(--container));
margin-inline: auto;
}
/* ---------- Typo-Helfer ---------- */
.beratung-bereich .kicker {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 18px;
}
.beratung-bereich .kicker::before {
content: "";
width: 28px;
height: 2px;
border-radius: 2px;
background: var(--accent);
}
.beratung-bereich .lead {
font-size: 1.15rem;
color: var(--text-muted);
max-width: 660px;
}
.beratung-bereich .muted {
color: var(--text-muted);
}
/* ---------- Button ---------- */
.beratung-bereich .btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.8rem 1.5rem;
border-radius: 999px;
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
line-height: 1.2;
text-decoration: none;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease,
background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.beratung-bereich .btn-accent {
background: var(--accent);
color: #03242b;
}
.beratung-bereich .btn-accent:hover {
transform: translateY(-1px);
box-shadow: 0 10px 28px rgba(0, 179, 194, 0.28);
}
/* ---------- Sektionen ---------- */
.beratung-bereich .section {
padding: 96px 0;
}
.beratung-bereich .section-head {
max-width: 720px;
margin-bottom: 56px;
}
.beratung-bereich .section-head h2 {
font-size: clamp(1.9rem, 4vw, 2.7rem);
margin-bottom: 18px;
}
/* ---------- Boxen & Grid ---------- */
.beratung-bereich .grid-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;
}
.beratung-bereich .box {
/* Lange Woerter duerfen die Karten auf kleinen Displays nicht verbreitern. */
min-width: 0;
overflow-wrap: anywhere;
hyphens: auto;
background-color: var(--background-panel);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 32px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* ---------- Checklisten ---------- */
.beratung-bereich .check-list {
list-style: none;
padding: 0;
margin: 20px 0 0;
display: grid;
gap: 12px;
}
.beratung-bereich .check-list li {
position: relative;
padding-left: 34px;
color: var(--foreground);
}
.beratung-bereich .check-list li::before {
content: "✓";
position: absolute;
left: 0;
top: 0;
color: var(--accent);
font-weight: bold;
font-size: 1.1rem;
}
/* ---------- Ablauf-Schritte ---------- */
.beratung-bereich .beratung-ablauf {
list-style: none;
counter-reset: schritt;
padding: 0;
margin: 24px 0 0;
display: grid;
gap: 20px;
}
.beratung-bereich .beratung-ablauf li {
counter-increment: schritt;
position: relative;
padding-left: 60px;
}
.beratung-bereich .beratung-ablauf li::before {
content: counter(schritt, decimal-leading-zero);
position: absolute;
left: 0;
top: 2px;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
font-family: var(--font-display);
font-size: 0.9rem;
font-weight: 600;
color: var(--accent);
background: rgba(0, 179, 194, 0.1);
border: 1px solid rgba(0, 179, 194, 0.3);
}
.beratung-bereich .beratung-ablauf strong {
display: block;
font-family: var(--font-display);
font-size: 1.02rem;
color: var(--foreground);
margin-bottom: 4px;
}
.beratung-bereich .beratung-ablauf p {
margin: 0;
color: var(--text-muted);
font-size: 0.95rem;
}
/* ---------- CTA-Band ---------- */
.beratung-bereich .cta-band {
margin-top: 96px;
padding: 64px 40px;
text-align: center;
border: 1px solid var(--border);
border-radius: calc(var(--radius) + 8px);
background: linear-gradient(135deg, #071216, #0a1b1f 46%, #050e16);
}
.beratung-bereich .cta-band h2 {
font-size: clamp(1.8rem, 4vw, 2.5rem);
margin-bottom: 16px;
}
.beratung-bereich .cta-band p {
color: var(--text-muted);
max-width: 560px;
margin-inline: auto;
}
/* ---------- Footer ---------- */
.beratung-bereich .logo {
font-family: var(--font-display);;
font-size: 1.35rem;
letter-spacing: -0.02em;
color: var(--text-muted);
text-decoration: none;
white-space: nowrap;
}
.beratung-bereich .logo span {
color: var(--accent);
}
.beratung-bereich .site-footer {
margin-top: 96px;
padding: 64px 0 40px;
border-top: 1px solid var(--border);
background: rgba(255, 255, 255, 0.015);
}
.beratung-bereich .footer-grid {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 48px;
}
.beratung-bereich .footer-brand p {
color: var(--text-muted);
font-size: 0.95rem;
max-width: 340px;
margin-top: 14px;
}
.beratung-bereich .footer-col h3 {
font-size: 0.85rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 16px;
}
.beratung-bereich .footer-col ul {
list-style: none;
margin: 0;
padding: 0;
}
.beratung-bereich .footer-col a {
display: block;
padding: 5px 0;
color: var(--text-muted);
text-decoration: none;
font-size: 0.92rem;
transition: color 0.15s ease;
}
.beratung-bereich .footer-col a:hover {
color: var(--foreground);
}
.beratung-bereich .footer-legal {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px;
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border);
color: var(--text-muted);
font-size: 0.85rem;
}
.beratung-bereich .footer-legal a {
color: inherit;
text-decoration: none;
}
.beratung-bereich .footer-legal a:hover {
color: var(--foreground);
}
/* ---------- Scroll-Reveal ---------- */
.beratung-bereich .reveal {
opacity: 0;
transform: translateY(18px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.beratung-bereich .reveal.visible {
opacity: 1;
transform: none;
}
/* ---------- Responsiv ---------- */
@media (max-width: 980px) {
.beratung-bereich .footer-grid {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 760px) {
.beratung-bereich .grid-2 {
grid-template-columns: minmax(0, 1fr);
}
.beratung-bereich .section {
padding: 72px 0;
}
.beratung-bereich .footer-grid {
grid-template-columns: 1fr;
gap: 32px;
}
.beratung-bereich .cta-band {
padding: 48px 24px;
}
}
/* ---------- Reduzierte Bewegung ---------- */
@media (prefers-reduced-motion: reduce) {
.beratung-bereich {
scroll-behavior: auto;
}
.beratung-bereich .reveal {
opacity: 1;
transform: none;
transition: none;
}
.beratung-bereich,
.beratung-bereich * {
transition-duration: 0.01ms !important;
}
}

644
css/kontakt.css 100644
View File

@ -0,0 +1,644 @@
/* Dennis: Kontaktseite – Regeln sind auf .kontakt-seite begrenzt.
Tokens entsprechen css/beratung.css (tri-hub.de-Farbwelt). */
.kontakt-seite {
/* Farben – Tokens von tri-hub.de */
--background: #0714198a;
--background-rgb: 7 20 25;
--background-panel: #152027;
--text-primary: #ffffff;
--text-muted: #9ba7aa;
--accent: #4597A3;
--accent-warm: #f7c230;
--border: rgba(234, 8, 8, 0.1);
/* Typografie */
--font-display: "Sora", sans-serif;
--font-body: "DM Sans", Arial, Helvetica, sans-serif;
/* Layout */
--container: 1120px;
--radius: 1.1rem;
}
/* ---------- Basic ---------- */
.kontakt-seite,
.kontakt-seite *,
.kontakt-seite::before,
.kontakt-seite *::before,
.kontakt-seite::after,
.kontakt-seite *::after {
box-sizing: border-box;
}
.kontakt-seite {
min-height: 100vh;
margin: 0;
background-color: var(--background);
color: var(--text-primary);
font-family: var(--font-body);
line-height: 1.625;
-webkit-font-smoothing: antialiased;
position: relative;
isolation: isolate;
scroll-behavior: smooth;
}
/* Licht */
.kontakt-seite::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background:
radial-gradient(900px 520px at 78% -10%, rgba(0, 179, 194, 0.13), transparent 65%),
radial-gradient(700px 480px at -10% 30%, rgba(35, 137, 148, 0.1), transparent 60%),
radial-gradient(600px 420px at 90% 95%, rgba(247, 194, 48, 0.05), transparent 60%);
pointer-events: none;
}
.kontakt-seite h1,
.kontakt-seite h2,
.kontakt-seite h3 {
margin-top: 0;
font-family: var(--font-display);
color: var(--text-primary);
line-height: 1.2;
letter-spacing: -0.01em;
}
.kontakt-seite p {
margin: 0 0 1em;
}
.kontakt-seite .container {
width: min(100% - 48px, var(--container));
margin-inline: auto;
}
/* ---------- Typo-Helfer ---------- */
.kontakt-seite .kicker {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 18px;
}
.kontakt-seite .kicker::before {
content: "";
width: 28px;
height: 2px;
border-radius: 2px;
background: var(--accent);
}
.kontakt-seite .lead {
font-size: 1.15rem;
color: var(--text-muted);
max-width: 660px;
}
/* Die Navigationsleiste oben wird von css/navigation.css gestylt (.site-navigation). */
/* ---------- Hero ---------- */
.kontakt-seite .hero {
padding: 72px 0 88px;
}
.kontakt-seite .hero__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
align-items: center;
}
.kontakt-seite .hero h1 {
font-size: clamp(2.6rem, 6vw, 4rem);
font-weight: 500;
letter-spacing: -0.04em;
margin-bottom: 20px;
}
.kontakt-seite .hero__text {
font-size: 1.15rem;
color: var(--text-muted);
max-width: 560px;
}
.kontakt-seite .hero__image {
display: block;
width: 100%;
height: auto;
border-radius: calc(var(--radius) + 1rem);
border: 1px solid var(--border);
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.35);
}
.hero--ribbon {
position: relative;
background-image: url("../images/trihub-ribbon-abstract.webp");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-color: var(--background); /* Fallback / Grundton */
isolation: isolate; /* hält z-index sauber */
}
.hero--ribbon::before {
content: "";
position: absolute;
inset: 0;
background:
linear-gradient(0deg, rgb(var(--background-rgb) / 96%), transparent 42%),
linear-gradient(90deg, rgb(var(--background-rgb) / 94%) 0%, rgb(var(--background-rgb) / 79%) 35%, rgb(var(--background-rgb) / 38%) 72%, rgb(var(--background-rgb) / 25%) 100%);
content: ""; /* dunkler Schleier */
z-index: 0;
pointer-events: none;
}
.hero--ribbon .hero__grid {
position: relative;
z-index: 1;
}
/* ---------- Sektionen ---------- */
.kontakt-seite .section {
padding: 88px 0;
}
.kontakt-seite .section + .section {
border-top: 1px solid var(--border);
}
.kontakt-seite .section-head {
max-width: 720px;
margin-bottom: 48px;
}
.kontakt-seite .section-head h2 {
font-size: clamp(1.9rem, 4vw, 2.7rem);
margin-bottom: 16px;
}
/* ---------- Karten (Nachricht) ---------- */
.kontakt-seite .cards-3 {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
}
.kontakt-seite .card {
background-color: var(--background-panel);
border: 1px solid var(--border);
border-radius: calc(var(--radius) + 0.4rem);
padding: 28px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.kontakt-seite .card__icon {
display: grid;
place-items: center;
width: 48px;
height: 48px;
margin-bottom: 16px;
border-radius: 12px;
font-size: 1.5rem;
background: rgba(69, 151, 163, 0.2);
}
.kontakt-seite .card--warm .card__icon {
background: rgba(247, 194, 48, 0.16);
}
.kontakt-seite .card--neutral .card__icon {
background: rgba(255, 255, 255, 0.1);
}
.kontakt-seite .card h3 {
font-size: 1.08rem;
margin-bottom: 8px;
}
.kontakt-seite .card p {
margin: 0;
color: var(--text-muted);
font-size: 0.95rem;
}
/* ---------- Formular + Kontaktinfos ---------- */
.kontakt-seite .kontakt-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 48px;
align-items: start;
}
.kontakt-seite form {
margin-top: 32px;
display: grid;
gap: 20px;
}
.kontakt-seite .field label {
display: block;
font-size: 0.9rem;
font-weight: 500;
color: var(--text-primary);
margin-bottom: 8px;
}
.kontakt-seite .field input,
.kontakt-seite .field select,
.kontakt-seite .field textarea {
width: 100%;
padding: 12px 16px;
border-radius: 10px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.04);
color: var(--text-primary);
font-family: var(--font-body);
font-size: 0.95rem;
transition: border-color 0.15s ease, background-color 0.15s ease;
}
.kontakt-seite .field input::placeholder,
.kontakt-seite .field textarea::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.kontakt-seite .field input:focus,
.kontakt-seite .field select:focus,
.kontakt-seite .field textarea:focus {
outline: none;
border-color: var(--accent);
background: rgba(255, 255, 255, 0.06);
}
.kontakt-seite .field select option {
background: #0b1a20;
color: var(--text-primary);
}
.kontakt-seite .field textarea {
min-height: 140px;
resize: vertical;
}
/* kontakt.js setzt die Zustandsklassen für ungültige Felder und Rückmeldungen. */
.kontakt-seite .field input.invalid,
.kontakt-seite .field select.invalid,
.kontakt-seite .field textarea.invalid {
border-color: #ef4444;
}
.kontakt-seite .field input.valid,
.kontakt-seite .field textarea.valid {
border-color: #22c55e;
}
.kontakt-seite .field__error {
display: none;
margin-top: 6px;
font-size: 0.8rem;
color: #f87171;
}
.kontakt-seite .field__hint {
display: none;
margin-top: 6px;
font-size: 0.8rem;
color: #4ade80;
}
.kontakt-seite .field.has-error .field__error {
display: block;
}
.kontakt-seite .field.is-ok .field__hint {
display: block;
}
/* ---------- Button ---------- */
.kontakt-seite .btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.8rem 1.5rem;
border-radius: 999px;
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
line-height: 1.2;
text-decoration: none;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease,
background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
.kontakt-seite .btn-accent {
background: var(--accent);
color: #03242b;
}
.kontakt-seite .btn-accent:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 10px 28px rgba(0, 179, 194, 0.28);
}
.kontakt-seite .btn-block {
width: 100%;
}
.kontakt-seite .btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.kontakt-seite .form-status {
display: none;
font-size: 0.9rem;
}
.kontakt-seite .form-status.is-visible {
display: block;
}
.kontakt-seite .form-status--ok {
color: #4ade80;
}
.kontakt-seite .form-status--error {
color: #f87171;
}
/* ---------- Kontaktinformations-Karten ---------- */
.kontakt-seite .info-stack {
display: grid;
gap: 20px;
}
.kontakt-seite .info-card {
display: flex;
align-items: flex-start;
gap: 16px;
background-color: var(--background-panel);
border: 1px solid var(--border);
border-radius: calc(var(--radius) + 0.4rem);
padding: 22px;
transition: border-color 0.15s ease;
}
.kontakt-seite .info-card:hover {
border-color: rgba(69, 151, 163, 0.5);
}
.kontakt-seite .info-card__icon {
display: grid;
place-items: center;
flex-shrink: 0;
width: 40px;
height: 40px;
margin-top: 2px;
border-radius: 10px;
background: rgba(69, 151, 163, 0.2);
color: var(--accent);
}
.kontakt-seite .info-card__label {
font-size: 0.7rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 4px;
}
.kontakt-seite .info-card a,
.kontakt-seite .info-card p {
color: var(--text-primary);
font-weight: 500;
text-decoration: none;
margin: 0;
}
.kontakt-seite .info-card a:hover {
color: var(--accent);
}
.kontakt-seite .info-card p small,
.kontakt-seite .info-card p.info-sub {
display: block;
font-size: 0.85rem;
font-weight: 400;
color: var(--text-muted);
}
/* ---------- Socials ---------- */
.kontakt-seite .socials {
display: flex;
gap: 12px;
}
.kontakt-seite .socials a {
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 10px;
border: 1px solid var(--border);
background: rgba(255, 255, 255, 0.04);
color: var(--text-primary);
text-decoration: none;
font-weight: 600;
font-size: 0.8rem;
transition: border-color 0.15s ease, background-color 0.15s ease;
}
.kontakt-seite .socials a:hover {
border-color: var(--accent);
background: rgba(69, 151, 163, 0.12);
}
/* ---------- Standort / Karte ---------- */
.kontakt-seite .map-frame {
border-radius: calc(var(--radius) + 0.9rem);
border: 1px solid var(--border);
overflow: hidden;
height: 500px;
background: var(--background-panel);
}
.kontakt-seite .map-frame iframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}
/* ---------- Footer ---------- */
.kontakt-seite .logo {
font-family: var(--font-display);
font-size: 1.35rem;
letter-spacing: -0.02em;
color: var(--text-muted);
text-decoration: none;
white-space: nowrap;
}
.kontakt-seite .logo span {
color: var(--accent);
}
.kontakt-seite .site-footer {
margin-top: 96px;
padding: 64px 0 40px;
border-top: 1px solid var(--border);
background: rgba(255, 255, 255, 0.015);
}
.kontakt-seite .footer-grid {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 48px;
}
.kontakt-seite .footer-brand p {
color: var(--text-muted);
font-size: 0.95rem;
max-width: 340px;
margin-top: 14px;
}
.kontakt-seite .footer-col h3 {
font-size: 0.85rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 16px;
}
.kontakt-seite .footer-col ul {
list-style: none;
margin: 0;
padding: 0;
}
.kontakt-seite .footer-col a {
display: block;
padding: 5px 0;
color: var(--text-muted);
text-decoration: none;
font-size: 0.92rem;
transition: color 0.15s ease;
}
.kontakt-seite .footer-col a:hover {
color: var(--text-primary);
}
.kontakt-seite .footer-legal {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px;
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border);
color: var(--text-muted);
font-size: 0.85rem;
}
.kontakt-seite .footer-legal a {
color: inherit;
text-decoration: none;
}
.kontakt-seite .footer-legal a:hover {
color: var(--text-primary);
}
/* ---------- Scroll-Reveal ---------- */
.kontakt-seite .reveal {
opacity: 0;
transform: translateY(18px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.kontakt-seite .reveal.visible {
opacity: 1;
transform: none;
}
/* ---------- Responsiv ---------- */
@media (max-width: 980px) {
.kontakt-seite .hero__grid,
.kontakt-seite .kontakt-grid {
grid-template-columns: minmax(0, 1fr);
}
.kontakt-seite .cards-3 {
grid-template-columns: minmax(0, 1fr);
}
.kontakt-seite .footer-grid {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 760px) {
.kontakt-seite .section {
padding: 64px 0;
}
.kontakt-seite .hero {
padding: 56px 0 64px;
}
.kontakt-seite .footer-grid {
grid-template-columns: 1fr;
gap: 32px;
}
.kontakt-seite .map-frame {
height: 380px;
}
}
/* ---------- Reduzierte Bewegung ---------- */
@media (prefers-reduced-motion: reduce) {
.kontakt-seite {
scroll-behavior: auto;
}
.kontakt-seite .reveal {
opacity: 1;
transform: none;
transition: none;
}
.kontakt-seite,
.kontakt-seite * {
transition-duration: 0.01ms !important;
}
}

245
css/leistungen.css 100644
View File

@ -0,0 +1,245 @@
/* Vincent: Gemeinsame Klassennamen und Variablen sind auf .pakete begrenzt. */
/* ==========================================================
tri-hub.de – Farben angeglichen an Produktübersicht (Shahnam)
Hintergrund: #071419 / #1b2830
Akzent: #4597A3 (gedämpftes Cyan-Teal)
Primärtext: #ffffff / #b7bec2 / #7e858a
Typografie: Sora (Display) + Inter (Body)
Werte per Pixel-Sampling aus dem Screenshot der Produktübersicht ermittelt.
========================================================== */
.pakete {
/* Werte per Pixel-Sampling aus dem Referenz-Screenshot (Produktübersicht) übernommen */
--background: #0714198a; /* früher "bg-dark */
--background-rgb: 7 20 25; /* gleiche Basisfarbe wie --background,
aber ohne fixes Alpha – für Gradient-Stops
wird benutzt um Bildern einen gradient zu verpassen */
--background-panel: #152027; /* früher "primary */
--accent: #4597A3;
--accent-soft: rgba(69, 151, 163, 0.14);
--text-primary: #ffffff;
--text-muted: #7e858a;
--font-display: "Sora", "Inter", system-ui, sans-serif;
--font-body: "Inter", system-ui, sans-serif;
}
.pakete,
.pakete * {
box-sizing: border-box;
}
.pakete {
margin: 0;
background: var(--background);
color: var(--text-primary);
font-family: var(--font-body);
}
/* ---------- Leistungsübersicht ---------- */
.pakete {
padding: 5rem 1.5rem;
max-width: 1120px;
margin: 0 auto;
}
.pakete .pakete__intro {
text-align: center;
max-width: 42rem;
margin: 0 auto 3rem;
}
.pakete .pakete__titel {
font-family: var(--font-display);
font-weight: 700;
font-size: clamp(1.8rem, 3vw, 2.4rem);
color: var(--text-primary);
margin: 0 0 0.75rem;
}
.pakete .pakete__text {
color: var(--text-muted);
line-height: 1.6;
margin: 0;
}
/* Kästchen nebeneinander, jede Karte für sich */
.pakete .pakete__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1.5rem;
align-items: stretch;
}
.pakete .paket-karte {
display: flex;
flex-direction: column;
min-width: 0;
background: var(--background-panel);
border: 1px solid rgba(248, 250, 252, 0.08);
border-radius: 14px;
padding: 0; /* Entfernt, damit das Bild oben bündig anliegt */
overflow: hidden;
position: relative;
}
/* Empfehlung kommt aus der JSON-Datei; die Auswahlklasse setzt leistungen.js beim Klick. */
.pakete .paket-karte--empfohlen {
border-color: 1px solid rgba(248, 250, 252, 0.08);
box-shadow: 0 0 0 1px var(--accent-soft);
}
.pakete .paket-karte--ausgewaehlt {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent);
}
.pakete .paket-karte__badge {
display: inline-block;
background: var(--accent-soft);
color: var(--accent);
border: 1px solid var(--accent);
font-size: 0.7rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 0.25rem 0.65rem;
border-radius: 999px;
margin: 0; /* Kein Abstand nach unten, da er jetzt im Header neben der Überschrift steht */
line-height: 1;
}
.pakete .paket-karte__image-wrapper {
width: 100%;
/* Proportional skalieren, damit beim Spaltenwechsel keine extremen Bildausschnitte entstehen. */
aspect-ratio: 3 / 2;
flex-shrink: 0;
overflow: hidden;
position: relative;
}
.pakete .paket-karte__image {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center top;
display: block;
}
/* ---------- Karteninhalt: Paketname, Preis, Beschreibung und Leistungen ---------- */
.pakete .paket-karte__body {
padding: 1.75rem;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.pakete .paket-karte__header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.5rem;
flex-wrap: nowrap;
}
.pakete .paket-karte__name {
font-family: var(--font-display);
font-weight: 600;
font-size: 1.4rem;
color: var(--text-primary);
margin: 0 0 0.5rem;
}
.pakete .paket-karte__preis {
margin: 0 0 0.75rem;
display: flex;
align-items: baseline;
gap: 0.35rem;
}
.pakete .paket-karte__betrag {
font-family: var(--font-display);
font-size: 2rem;
font-weight: 700;
color: var(--text-primary);
}
.pakete .paket-karte__zeitraum {
color: var(--text-muted);
font-size: 0.9rem;
}
.pakete .paket-karte__beschreibung {
color: var(--text-muted);
line-height: 1.55;
margin: 0 0 1.5rem;
min-height: 3.3em;
}
.pakete .paket-karte__leistungen {
list-style: none;
margin: 0 0 2rem;
padding: 0;
flex-grow: 1;
}
.pakete .paket-karte__leistungen li {
position: relative;
padding-left: 1.4rem;
margin-bottom: 0.7rem;
line-height: 1.4;
color: var(--text-primary);
}
.pakete .paket-karte__leistungen li::before {
content: "";
position: absolute;
left: 0;
top: 0.5em;
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
}
/* ---------- Paketauswahl und Tastaturfokus ---------- */
.pakete .paket-karte__cta {
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
color: var(--text-primary);
background: var(--background);
border: 1px solid rgba(248, 250, 252, 0.08);
border-radius: 8px;
padding: 0.85rem 1rem;
cursor: pointer;
transition: filter 0.15s ease;
}
.pakete .paket-karte__cta:hover,
.pakete .paket-karte__cta:focus-visible {
filter: brightness(1.1);
}
.pakete .paket-karte__cta:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: 2px;
}
/* ---------- Spaltenwechsel: drei, zwei, dann eine; Empfehlung bleibt mobil zuerst ---------- */
@media (max-width: 860px) {
.pakete .pakete__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.pakete .paket-karte--empfohlen {
order: -1;
}
}
@media (max-width: 620px) {
.pakete .pakete__grid {
grid-template-columns: minmax(0, 480px);
justify-content: center;
}
}

261
css/navigation.css 100644
View File

@ -0,0 +1,261 @@
/* Shahnam und Vincent: Gemeinsame Navigation für US 04-05. */
:root {
/* navigation.js ergänzt den tatsächlich gemessenen Abstand für Anker-Sprünge. */
--navigation-height: 99px;
scroll-padding-top: calc(var(--navigation-offset, var(--navigation-height)) + 16px);
}
.site-navigation {
color-scheme: dark;
--background: #071419;
--background-panel: #152027;
--accent: #4597a3;
--accent-soft: rgb(69 151 163 / 14%);
--text-primary: #fff;
--text-muted: #7e858a;
--text-muted2: #b7bec2;
--border: rgb(255 255 255 / 16%);
/* Bleibt beim Scrollen sichtbar und behält ihren Platz im Layout. */
position: sticky;
top: 0;
z-index: 100;
border-top: 3px solid var(--background);
background: var(--background);
color: var(--text-primary);
font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
line-height: 1.6;
box-shadow: 0 4px 16px rgb(0 0 0 / 16%);
}
.site-navigation,
.site-navigation * {
box-sizing: border-box;
}
/* ---------- Kopfzeile: Logo, Begleittext und Menü ---------- */
.site-navigation__bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
width: min(1680px, calc(100% - clamp(40px, 10vw, 192px)));
margin-inline: auto;
}
.site-navigation .brand {
display: flex;
align-items: center;
gap: 20px;
min-width: 0;
min-height: calc(var(--navigation-height) - 3px);
padding-block: 8px;
}
/* ---------- Navigationslinks und ihre Klickflächen ---------- */
.site-navigation__links ul {
display: flex;
align-items: center;
gap: clamp(60px, 2.5vw, 32px);
margin: 0;
padding: 0;
list-style: none;
}
.site-navigation__links a,
.site-navigation .navigation-disabled {
display: block;
padding-block: 10px;
padding-inline: 14px;
color: var(--text-muted);
font-size: 19px;
font-weight: 250;
text-decoration: none;
white-space: nowrap;
}
/* Hover-Highlight, übernommen aus der ursprünglichen Navigationsleiste in kontakt.css. */
.site-navigation__links a {
border-radius: 8px;
transition: background-color 0.15s ease, color 0.15s ease;
}
.site-navigation__links a:hover,
.site-navigation__links a:focus-visible {
background: rgb(255 255 255 / 6%);
color: var(--text-primary);
}
.site-navigation__links a:focus-visible,
.site-navigation .menu-toggle:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
border-radius: 4px;
}
.site-navigation .navigation-disabled {
opacity: 0.5;
cursor: default;
}
/* ---------- Logo und Markenname ---------- */
.site-navigation .brand__logo {
display: grid;
place-items: center;
flex-shrink: 0;
width: 60px;
height: 60px;
border-radius: 22px;
overflow: hidden;
background: var(--text-primary);
}
.site-navigation .brand__logo img {
display: block;
width: 60px;
height: 60px;
object-fit: contain;
border-radius: inherit;
}
.site-navigation .brand__logo:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 4px;
}
.site-navigation .brand__text {
display: flex;
flex-direction: column;
gap: 4px;
min-width: 0;
}
.site-navigation .brand__name {
color: var(--text-muted);
font-size: 14px;
font-weight: 200;
line-height: 1.4;
letter-spacing: 0.25em;
}
.site-navigation .brand__label {
color: var(--text-muted2);
font-size: 17px;
font-weight: 300;
line-height: 1.4;
}
/* ---------- Menüknopf und Hintergrund für die kompakte Ansicht ---------- */
.site-navigation .menu-toggle {
display: none;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
padding: 0;
border: 1px solid var(--border);
border-radius: 10px;
background: transparent;
color: var(--text-muted);
font: inherit;
font-size: 26px;
line-height: 1;
cursor: pointer;
}
.site-navigation .menu-toggle:hover {
background: var(--accent-soft);
}
.site-navigation__backdrop {
display: none;
}
/* Der gleiche Grenzwert wird in navigation.js verwendet. */
@media (max-width: 1100px) {
.site-navigation .menu-toggle {
display: inline-flex;
}
.site-navigation__links {
display: none;
position: absolute;
z-index: 2;
top: 100%;
right: 0;
width: min(360px, 100%);
max-height: calc(100vh - var(--navigation-offset, var(--navigation-height)));
max-height: calc(100dvh - var(--navigation-offset, var(--navigation-height)));
overflow-y: auto;
overscroll-behavior: contain;
padding: 16px 24px 24px;
border: 1px solid var(--border);
border-top: 0;
border-radius: 0 0 0 16px;
background: var(--background);
box-shadow: -8px 12px 24px rgb(0 0 0 / 20%);
}
.site-navigation__links ul {
align-items: stretch;
flex-direction: column;
gap: 4px;
}
.site-navigation__links a,
.site-navigation .navigation-disabled {
padding: 12px 16px;
border-radius: 8px;
}
.site-navigation__links a:hover {
background: var(--accent-soft);
}
.site-navigation.is-open .site-navigation__links,
.site-navigation.is-open .site-navigation__backdrop {
display: block;
}
.site-navigation__backdrop {
position: fixed;
z-index: 1;
inset: var(--navigation-offset, var(--navigation-height)) 0 0;
background: rgb(0 0 0 / 45%);
}
}
/* Kleinere Kopfzeile und Schriftgrößen auf schmalen Bildschirmen. */
@media (max-width: 700px) {
:root {
--navigation-height: 81px;
}
.site-navigation__bar {
gap: 12px;
}
.site-navigation .brand {
gap: 12px;
}
.site-navigation .brand__logo {
width: 66px;
height: 62px;
border-radius: 17px;
}
.site-navigation .brand__logo img {
width: 46px;
height: 46px;
}
.site-navigation .brand__name {
font-size: 12px;
}
.site-navigation .brand__label {
font-size: 13px;
}
}

View File

@ -0,0 +1,716 @@
/* ==========================================================================
Paketdetails CSS (Standalone)
Inhalt: Layout, Hero, Facts, Leistungen, Ablauf, Preise & Footer
========================================================================== */
.paket-detail {
/* CSS Variablen */
--background: #0714198a;
--background-rgb: 7 20 25;
--background-panel: #152027;
--accent: #4597A3;
--accent-soft: rgba(69, 151, 163, 0.14);
--text-primary: #ffffff;
--text-muted: #7e858a;
--border: rgba(255, 255, 255, 0.12);
/* Typografie & Layout */
--font-display: "Sora", "Inter", system-ui, sans-serif;
--font-body: "Inter", "DM Sans", system-ui, sans-serif;
--radius: 1.1rem;
color-scheme: dark;
margin: 0;
background-color: var(--background);
color: var(--text-primary);
font-family: var(--font-body);
line-height: 1.625;
-webkit-font-smoothing: antialiased;
position: relative;
isolation: isolate;
}
.paket-detail *,
.paket-detail *::before,
.paket-detail *::after {
box-sizing: border-box;
}
.paket-detail p,
.paket-detail h1,
.paket-detail h2,
.paket-detail h3 {
margin: 0;
}
.paket-detail h1,
.paket-detail h2,
.paket-detail h3 {
font-family: var(--font-display);
line-height: 1.2;
}
.paket-detail .container {
width: min(1680px, calc(100% - clamp(40px, 10vw, 192px)));
margin-inline: auto;
}
/* ==========================================================================
Typo-Helfer & Globale Bausteine
========================================================================== */
.paket-detail .kicker {
display: inline-flex;
align-items: center;
gap: 10px;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: 18px;
}
.paket-detail .kicker::before {
content: "";
width: 28px;
height: 2px;
border-radius: 2px;
background: var(--accent);
}
.paket-detail .lead {
font-size: 1.15rem;
color: var(--text-muted);
max-width: 660px;
}
.paket-detail .muted {
color: var(--text-muted);
}
/* Buttons */
.paket-detail .btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.8rem 1.5rem;
border-radius: 999px;
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
line-height: 1.2;
text-decoration: none;
border: 1px solid transparent;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, background-color 0.18s ease;
}
.paket-detail .btn-accent {
background: var(--accent);
color: #03242b;
}
.paket-detail .btn-accent:hover {
transform: translateY(-1px);
box-shadow: 0 10px 28px rgba(0, 179, 194, 0.28);
}
/* Sektionen, Boxen & Grids */
.paket-detail .section {
padding: 96px 0;
}
.paket-detail .section-head {
max-width: 720px;
margin-bottom: 56px;
}
.paket-detail .section-head h2 {
font-size: clamp(1.9rem, 4vw, 2.7rem);
margin-bottom: 18px;
}
.paket-detail .grid-2 {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 24px;
}
.paket-detail .box {
min-width: 0;
overflow-wrap: anywhere;
hyphens: auto;
background-color: var(--background-panel);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 32px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* ==========================================================================
Breadcrumb
========================================================================== */
.paket-detail .paket-detail__breadcrumb {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-block: 24px;
color: var(--text-muted);
font-size: 0.85rem;
}
.paket-detail__breadcrumb a,
.paket-detail .paket-detail__text-link {
color: inherit;
text-decoration: none;
}
.paket-detail__breadcrumb > span {
font-size: 0.7rem;
letter-spacing: 0.18em;
}
.paket-detail .paket-detail__text-link {
display: inline-block;
color: var(--text-primary);
text-underline-offset: 5px;
}
.paket-detail__breadcrumb a:hover,
.paket-detail .paket-detail__text-link:hover {
color: var(--accent);
text-decoration: underline;
}
/* ==========================================================================
Hero-Sektion
========================================================================== */
.paket-detail .paket-detail__hero {
position: relative;
isolation: isolate;
display: flex;
flex: 1;
min-width: 0;
min-height: 600px;
border-block: 1px solid var(--border);
overflow: hidden;
}
.paket-detail .paket-detail__hero .hero__image,
.paket-detail .paket-detail__hero::after {
position: absolute;
z-index: -1;
inset: 0;
width: 100%;
height: 100%;
}
/* Das Hintergrundbild erstreckt sich vollflächig über den gesamten Hero-Bereich
Das ist die Standardausrichtung für für die Banner */
.paket-detail__hero .hero__image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: right top; /* Bildschwerpunkt rechts ausrichten */
z-index: -2;
}
/* Starter-Paket Ausrichtung*/
body.paket-starter .hero__image {
object-position: right top;
}
/* 2. Standard-Paket */
body.paket-standard .hero__image {
object-position: 60% 30%;
}
/* 3. Premium-Paket */
body.paket-premium .hero__image {
object-position: 30% center;
}
.paket-detail__hero::after {
position: absolute;
inset: 0;
z-index: -1;
content: "";
background:
linear-gradient(0deg, rgb(var(--background-rgb) / 96%), transparent 42%),
linear-gradient(90deg, rgb(var(--background-rgb) / 94%) 0%, rgb(var(--background-rgb) / 79%) 35%, rgb(var(--background-rgb) / 38%) 72%, rgb(var(--background-rgb) / 25%) 100%);
content: "";
}
.paket-detail__hero .hero__inner {
display: flex;
flex-direction: column;
padding-block: clamp(32px, 5vh, 64px) 28px;
position: relative;
z-index: 1;
}
.paket-detail__hero .hero__content {
width: min(100%, 720px); /* Textbereich kompakt halten */
margin-block: auto;
padding-block: 24px 64px;
}
.paket-detail__hero h1 {
color: var(--text-primary);
max-width: 100%;
font-size: clamp(42px, 4.9vw, 88px);
font-weight: 700;
line-height: 1.1;
letter-spacing: -0.055em;
text-wrap: balance;
overflow-wrap: break-word;
}
.paket-detail__hero h1 span {
display: block;
margin-top: 8px;
color: var(--text-primary);
}
.paket-detail__hero .hero__eyebrow {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 26px;
color: var(--text-muted);
font-size: 11px;
font-weight: 650;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.paket-detail__hero .hero__eyebrow::before {
width: 28px;
height: 1px;
flex-shrink: 0;
background: currentColor;
content: "";
}
.paket-detail__hero .hero__description {
max-width: 65ch;
margin-top: 30px;
color: #d1d7df;
font-size: clamp(16px, 1.15vw, 19px);
line-height: 1.85;
}
.paket-detail__actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 24px;
margin-top: 30px;
}
/* ==========================================================================
Paketangaben / Facts
========================================================================== */
.paket-detail__facts {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 24px;
margin: 0;
padding: 36px 0;
border-bottom: 1px solid var(--border);
}
.paket-detail__facts > div {
padding-left: 20px;
border-left: 2px solid var(--accent);
}
.paket-detail__facts dt {
margin-bottom: 8px;
font-size: 0.7rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--text-muted);
}
.paket-detail__facts dd {
margin: 0;
font-family: var(--font-display);
font-size: 1.4rem;
font-weight: 600;
}
.paket-detail__facts small {
display: block;
margin-top: 6px;
font-family: var(--font-body);
font-size: 0.85rem;
font-weight: 400;
color: var(--text-muted);
}
/* ==========================================================================
Leistungen / Services
========================================================================== */
.paket-detail__services .box {
box-shadow: none;
}
.paket-detail__number {
display: block;
margin-bottom: 24px;
color: var(--accent);
font-size: 0.85rem;
font-variant-numeric: tabular-nums;
}
.paket-detail__services h3 {
font-size: 1.25rem;
line-height: 1.4;
margin-bottom: 12px;
}
.paket-detail__services p {
margin-bottom: 0;
color: var(--text-muted);
}
/* ==========================================================================
Ablauf & Listen
========================================================================== */
.paket-detail__process {
border-block: 1px solid var(--border);
}
.paket-detail__split {
display: grid;
grid-template-columns: 1.1fr 1fr;
gap: clamp(32px, 6vw, 88px);
align-items: start;
}
.paket-detail__split > * {
min-width: 0;
}
.paket-detail__split h2 {
font-size: clamp(1.9rem, 3.4vw, 2.7rem);
}
.paket-detail .beratung-ablauf {
list-style: none;
counter-reset: schritt;
padding: 0;
margin: 0;
display: grid;
gap: 32px;
}
.paket-detail .beratung-ablauf li {
counter-increment: schritt;
position: relative;
padding-left: 60px;
}
.paket-detail .beratung-ablauf li::before {
content: counter(schritt, decimal-leading-zero);
position: absolute;
left: 0;
top: 2px;
display: grid;
place-items: center;
width: 44px;
height: 44px;
border-radius: 50%;
font-family: var(--font-display);
font-size: 0.9rem;
font-weight: 600;
color: var(--accent);
background: rgba(0, 179, 194, 0.1);
border: 1px solid rgba(0, 179, 194, 0.3);
}
.paket-detail .beratung-ablauf strong {
display: block;
font-family: var(--font-display);
font-size: 1.02rem;
color: var(--text-primary);
margin-bottom: 4px;
}
.paket-detail .beratung-ablauf p {
margin: 0;
color: var(--text-muted);
font-size: 0.95rem;
}
/* ==========================================================================
Preisübersicht & Checkliste
========================================================================== */
.paket-detail .check-list {
list-style: none;
padding: 0;
margin: 20px 0 0;
display: grid;
gap: 12px;
}
.paket-detail .check-list li {
position: relative;
padding-left: 34px;
color: var(--text-primary);
}
.paket-detail .check-list li::before {
content: "✓";
position: absolute;
left: 0;
top: 0;
color: var(--accent);
font-weight: bold;
font-size: 1.1rem;
}
.paket-detail .paket-detail__alternative {
margin-top: 32px;
color: var(--text-muted);
}
.paket-detail .paket-detail__summary {
border-color: var(--accent);
padding: clamp(24px, 3vw, 40px);
}
.paket-detail__summary h3 {
font-size: 1.5rem;
margin-bottom: 16px;
}
.paket-detail__price {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 10px;
color: var(--text-muted);
}
.paket-detail__price strong {
color: var(--text-primary);
font-family: var(--font-display);
font-size: 3rem;
line-height: 1.2;
}
.paket-detail__summary .check-list {
padding-block: 24px;
border-block: 1px solid var(--border);
margin-bottom: 28px;
}
.paket-detail__summary .btn {
width: 100%;
min-height: 48px;
text-align: center;
}
.paket-detail .paket-detail__note {
margin: 16px 0 0;
font-size: 0.8rem;
color: var(--text-muted);
}
/* ==========================================================================
Footer
========================================================================== */
.paket-detail .logo {
font-family: var(--font-display);
font-size: 1.35rem;
letter-spacing: -0.02em;
color: var(--text-muted);
text-decoration: none;
white-space: nowrap;
}
.paket-detail .site-footer {
margin-top: 96px;
padding: 64px 0 40px;
border-top: 1px solid var(--border);
background: rgba(255, 255, 255, 0.015);
}
.paket-detail .footer-grid {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
gap: 48px;
}
.paket-detail .footer-brand p {
color: var(--text-muted);
font-size: 0.95rem;
max-width: 340px;
margin-top: 14px;
}
.paket-detail .footer-col h3 {
font-size: 0.85rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--text-muted);
margin-bottom: 16px;
}
.paket-detail .footer-col ul {
list-style: none;
margin: 0;
padding: 0;
}
.paket-detail .footer-col a {
display: block;
padding: 5px 0;
color: var(--text-muted);
text-decoration: none;
font-size: 0.92rem;
transition: color 0.15s ease;
}
.paket-detail .footer-col a:hover {
color: var(--text-primary);
}
.paket-detail .footer-legal {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 12px;
margin-top: 48px;
padding-top: 24px;
border-top: 1px solid var(--border);
color: var(--text-muted);
font-size: 0.85rem;
}
.paket-detail .footer-legal a {
color: inherit;
text-decoration: none;
}
.paket-detail .footer-legal a:hover {
color: var(--text-primary);
}
/* ==========================================================================
Responsive Design
========================================================================== */
@media (max-width: 1100px) {
.paket-detail__hero::after {
background:
linear-gradient(0deg, rgb(var(--background-rgb) / 96%), transparent 42%),
linear-gradient(90deg, rgb(var(--background-rgb) / 92%), rgb(var(--background-rgb) / 70%) 65%, rgb(var(--background-rgb) / 48%));
}
.paket-detail__hero .hero__content {
max-width: 85%;
}
}
@media (max-width: 980px) {
.paket-detail .footer-grid {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 900px) {
.paket-detail__facts {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 28px;
}
.paket-detail__split {
grid-template-columns: minmax(0, 1fr);
}
}
@media (max-width: 760px) {
.paket-detail .grid-2 {
grid-template-columns: minmax(0, 1fr);
}
.paket-detail .section {
padding: 72px 0;
}
.paket-detail .footer-grid {
grid-template-columns: 1fr;
gap: 32px;
}
}
@media (max-width: 700px) {
.paket-detail__hero .hero__image {
inset: 0;
width: 100%;
max-width: 100%;
height: 100%;
object-fit: cover;
object-position: 60% bottom;
mask-image: none;
}
.paket-detail__hero::after {
background: linear-gradient(90deg, rgb(var(--background-rgb) / 87%), rgb(var(--background-rgb) / 65%)),
linear-gradient(0deg, var(--background), transparent 55%);
}
.paket-detail__hero .hero__content {
max-width: 100%;
padding-block: 12px 40px;
}
.paket-detail__hero .hero__eyebrow {
gap: 8px;
font-size: 10px;
letter-spacing: 0.11em;
}
.paket-detail__hero .hero__eyebrow::before {
width: 20px;
}
.paket-detail__hero .hero__description {
margin-top: 24px;
}
.paket-detail__actions {
align-items: flex-start;
flex-direction: column;
gap: 20px;
}
.paket-detail__facts {
grid-template-columns: 1fr;
gap: 16px;
}
.paket-detail__facts > div {
padding-left: 12px;
border-left-width: 1px;
}
.paket-detail__facts dd {
font-size: 1.1rem;
}
}

View File

@ -0,0 +1,353 @@
/* Shahnam: Styles und Variablen bleiben innerhalb der Produktuebersicht. */
.produktuebersicht {
color-scheme: dark;
--background: #0714198a;
--background-rgb: 7 20 25; /* gleiche Basisfarbe wie --background,
aber ohne fixes Alpha – für Gradient-Stops
wird benutzt um Bildern einen gradient zu verpassen */
/* es fehlt eine andere backgroundfarbe für das Panel */
--accent: #4597A3;
--text-primary: #ffffff; /*früher "text"*/
--text-muted: #b7bec2; /*früher "muted"*/
--border: rgb(255 255 255 / 16%);
font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
color: var(--text-primary);
background: var(--background);
}
/* ---------- Grundlayout: Der Hero nutzt die Fensterhöhe unter der Navigation ---------- */
.produktuebersicht,
.produktuebersicht *,
.produktuebersicht::before,
.produktuebersicht *::before,
.produktuebersicht::after,
.produktuebersicht *::after {
box-sizing: border-box;
}
.produktuebersicht {
display: flex;
flex-direction: column;
min-height: calc(100vh - var(--navigation-height));
min-height: calc(100svh - var(--navigation-height));
margin: 0;
line-height: 1.6;
}
.produktuebersicht p,
.produktuebersicht h1 {
margin: 0;
}
.produktuebersicht .container {
width: min(1680px, calc(100% - clamp(40px, 10vw, 192px)));
margin-inline: auto;
}
.produktuebersicht .produktuebersicht__main {
display: flex;
flex: 1;
}
/* ---------- Hero-Bild und dunkle Verläufe für lesbaren Text ---------- */
.produktuebersicht .hero {
position: relative;
isolation: isolate;
display: flex;
flex: 1;
min-width: 0;
border-block: 1px solid var(--border);
}
.produktuebersicht .hero__image,
.produktuebersicht .hero::after {
position: absolute;
z-index: -1;
inset: 0;
width: 100%;
height: 100%;
}
.produktuebersicht .hero__image {
inset: 0 0 auto auto;
width: auto;
max-width: 94%;
height: 94%;
object-fit: contain;
object-position: right top;
mask-image: linear-gradient(to right, transparent, #000 18%),
linear-gradient(to bottom, #000 82%, transparent);
mask-composite: intersect;
}
.produktuebersicht .hero::after {
/*background: var(--background);
/* ich bekomme das nicht richtig hin muss Shahmad machen */
background:
linear-gradient(0deg, rgb(var(--background-rgb) / 96%), transparent 42%),
linear-gradient(90deg, rgb(var(--background-rgb) / 94%) 0%, rgb(var(--background-rgb) / 79%) 35%, rgb(var(--background-rgb) / 38%) 72%, rgb(var(--background-rgb) / 25%) 100%);
content: "";
}
/* ---------- Überschrift, Einleitung und Ausrichtung im Hero ---------- */
.produktuebersicht .hero__inner {
display: flex;
flex-direction: column;
padding-block: clamp(32px, 5vh, 64px) 28px;
}
.produktuebersicht .hero__content {
width: min(100%, 1000px);
margin-block: auto;
padding-block: 24px 64px;
}
.produktuebersicht .hero__eyebrow {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 26px;
color: var(--text-muted);
font-size: 11px;
font-weight: 650;
letter-spacing: 0.16em;
text-transform: uppercase;
}
.produktuebersicht .hero__eyebrow::before {
width: 28px;
height: 1px;
flex-shrink: 0;
background: currentColor;
content: "";
}
.produktuebersicht h1 {
color: var(--text-primary);
max-width: 100%;
font-size: clamp(42px, 4.9vw, 88px);
font-weight: 550;
line-height: 1.1;
letter-spacing: -0.055em;
text-wrap: balance;
overflow-wrap: break-word;
}
.produktuebersicht h1 span {
display: block;
margin-top: 8px;
color: var(--text-primary);
}
.produktuebersicht .hero__description {
max-width: 65ch;
margin-top: 30px;
color: #d1d7df;
font-size: clamp(16px, 1.15vw, 19px);
line-height: 1.85;
}
/* ---------- Vorteile und ergänzender Slogan unter dem Haupttext ---------- */
.produktuebersicht .hero__details {
display: grid;
grid-template-columns: minmax(0, 1fr) 250px;
align-items: center;
gap: 48px;
padding-top: 28px;
border-top: 1px solid var(--border);
}
.produktuebersicht .benefits {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
margin: 0;
padding: 0;
list-style: none;
}
.produktuebersicht .benefits li {
display: flex;
align-items: baseline;
gap: 14px;
}
.produktuebersicht .benefits__number {
color: var(--accent);
font-size: 11px;
font-variant-numeric: tabular-nums;
}
.produktuebersicht .benefits p {
font-size: 14px;
font-weight: 500;
}
.produktuebersicht .benefits p span {
display: block;
margin-top: 4px;
color: var(--text-muted);
font-size: 12px;
font-weight: 400;
}
.produktuebersicht .hero__caption {
padding-left: 24px;
border-left: 1px solid var(--border);
}
.produktuebersicht .hero__caption-label {
color: var(--text-muted);
font-size: 10px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.produktuebersicht .hero__slogan {
margin-top: 8px;
font-size: 22px;
font-weight: 450;
line-height: 1.4;
letter-spacing: -0.025em;
}
/* Reservierte Styles für den derzeit nicht eingebundenen Produkt-Footer. */
.produktuebersicht .site-footer__inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
min-height: 88px;
padding-block: 24px;
font-size: 12px;
}
.produktuebersicht .site-footer__inner p > span {
margin-left: 12px;
color: var(--text-muted);
}
.produktuebersicht .site-footer__note {
display: flex;
align-items: center;
gap: 9px;
color: var(--text-muted);
}
.produktuebersicht .site-footer__note > span {
width: 5px;
height: 5px;
flex-shrink: 0;
border-radius: 50%;
background: var(--accent);
}
/* ---------- Anpassungen für Tablet und Mobilgeräte ---------- */
@media (max-width: 1100px) {
.produktuebersicht .hero::after {
background:
linear-gradient(0deg, rgb(var(--background-rgb) / 96%), transparent 42%),
linear-gradient(90deg, rgb(var(--background-rgb) / 92%), rgb(var(--background-rgb) / 70%) 65%, rgb(var(--background-rgb) / 48%));
}
.produktuebersicht .hero__content {
max-width: 85%;
}
.produktuebersicht .hero__details {
grid-template-columns: minmax(0, 1fr) 200px;
gap: 28px;
}
.produktuebersicht .benefits li {
flex-direction: column;
gap: 8px;
}
}
@media (max-width: 700px) {
.produktuebersicht .hero__image {
inset: 0;
width: 100%;
max-width: 100%;
height: 100%;
object-fit: cover;
object-position: 60% bottom;
mask-image: none;
}
.produktuebersicht .hero::after {
background: linear-gradient(90deg, rgb(var(--background-rgb) / 87%), rgb(var(--background-rgb) / 65%)),
linear-gradient(0deg, var(--background), transparent 55%);
}
.produktuebersicht .hero__content {
max-width: 100%;
padding-block: 12px 40px;
}
.produktuebersicht .hero__eyebrow {
gap: 8px;
font-size: 10px;
letter-spacing: 0.11em;
}
.produktuebersicht .hero__eyebrow::before {
width: 20px;
}
.produktuebersicht .hero__description {
margin-top: 24px;
}
.produktuebersicht .hero__details {
grid-template-columns: 1fr;
gap: 28px;
padding-top: 24px;
}
.produktuebersicht .benefits {
gap: 16px;
}
.produktuebersicht .benefits p {
font-size: 12px;
}
.produktuebersicht .hero__caption {
padding-left: 16px;
border-left-color: var(--accent);
}
.produktuebersicht .hero__slogan {
font-size: 20px;
}
.produktuebersicht .site-footer__inner {
align-items: flex-start;
flex-direction: column;
gap: 14px;
}
.produktuebersicht .site-footer__inner p > span {
margin-left: 0;
}
.produktuebersicht .site-footer__inner > p:first-child span {
display: block;
margin-top: 4px;
}
}
@media (max-width: 360px) {
.produktuebersicht .benefits {
grid-template-columns: 1fr;
}
.produktuebersicht .benefits li {
flex-direction: row;
gap: 14px;
}
}

11
css/site.css 100644
View File

@ -0,0 +1,11 @@
/* Gemeinsame Grundwerte für Übersichts- und Kontaktseite; Bereichsstile liegen in eigenen Dateien. */
:root {
color-scheme: dark;
font-family: system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
color: #f8fafc;
background: #0714198a;
}
body {
margin: 0;
}

View File

@ -1,194 +0,0 @@
/* ==========================================================
tri-hub.de – Design-DNA (Block 4, Projekt-Kickoff)
Hintergrund: #0d0d1a / #1a1a2e
Akzent: #10B981 (Teal / Emerald Green)
Primärtext: #ffffff / #F8FAFC / #94A3B8
Typografie: Playfair Display (Display) + Inter (Body)
Ton: Du-Ansprache, Premium, direkt
========================================================== */
:root {
--bg-dark: #0d0d1a;
--bg-panel: #1a1a2e;
--accent: #10B981;
--accent-soft: rgba(16, 185, 129, 0.12);
--text-primary: #ffffff;
--text-secondary: #F8FAFC;
--text-muted: #94A3B8;
--font-display: "Inter", system-ui, sans-serif;
--font-body: "Inter", system-ui, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--bg-dark);
color: var(--text-secondary);
font-family: var(--font-body);
}
/* ---------- Leistungsübersicht ---------- */
.pakete {
padding: 5rem 1.5rem;
max-width: 1120px;
margin: 0 auto;
}
.pakete__intro {
text-align: center;
max-width: 42rem;
margin: 0 auto 3rem;
}
.pakete__titel {
font-family: var(--font-display);
font-weight: 700;
font-size: clamp(1.8rem, 3vw, 2.4rem);
color: var(--text-primary);
margin: 0 0 0.75rem;
}
.pakete__text {
color: var(--text-muted);
line-height: 1.6;
margin: 0;
}
/* Kästchen nebeneinander, jede Karte für sich */
.pakete__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
align-items: stretch;
}
.paket-karte {
display: flex;
flex-direction: column;
background: var(--bg-panel);
border: 1px solid rgba(248, 250, 252, 0.08);
border-radius: 14px;
padding: 2rem 1.75rem;
position: relative;
}
.paket-karte--empfohlen {
border-color: var(--accent);
box-shadow: 0 0 0 1px var(--accent-soft);
}
.paket-karte--ausgewaehlt {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent);
}
.paket-karte__badge {
position: absolute;
top: -0.7rem;
left: 1.75rem;
background: var(--accent);
color: var(--bg-dark);
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 999px;
margin: 0;
}
.paket-karte__name {
font-family: var(--font-display);
font-weight: 600;
font-size: 1.4rem;
color: var(--text-primary);
margin: 0 0 0.5rem;
}
.paket-karte__preis {
margin: 0 0 0.75rem;
display: flex;
align-items: baseline;
gap: 0.35rem;
}
.paket-karte__betrag {
font-family: var(--font-display);
font-size: 2rem;
font-weight: 700;
color: var(--text-primary);
}
.paket-karte__zeitraum {
color: var(--text-muted);
font-size: 0.9rem;
}
.paket-karte__beschreibung {
color: var(--text-muted);
line-height: 1.55;
margin: 0 0 1.5rem;
min-height: 3.3em;
}
.paket-karte__leistungen {
list-style: none;
margin: 0 0 2rem;
padding: 0;
flex-grow: 1;
}
.paket-karte__leistungen li {
position: relative;
padding-left: 1.4rem;
margin-bottom: 0.7rem;
line-height: 1.4;
color: var(--text-secondary);
}
.paket-karte__leistungen li::before {
content: "";
position: absolute;
left: 0;
top: 0.5em;
width: 8px;
height: 8px;
border-radius: 2px;
background: var(--accent);
}
.paket-karte__cta {
font-family: var(--font-body);
font-weight: 600;
font-size: 0.95rem;
color: var(--bg-dark);
background: var(--accent);
border: none;
border-radius: 8px;
padding: 0.85rem 1rem;
cursor: pointer;
transition: filter 0.15s ease;
}
.paket-karte__cta:hover,
.paket-karte__cta:focus-visible {
filter: brightness(1.1);
}
.paket-karte__cta:focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: 2px;
}
@media (max-width: 860px) {
.pakete__grid {
grid-template-columns: 1fr;
}
.paket-karte--empfohlen {
order: -1;
}
}

1148
css/terminbuchung.css 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
# Ernährungsseite – Integrationsänderungen
## Dateien und Zuständigkeiten
Die Bereiche stehen gemeinsam in [index.html](../index.html), in dieser Reihenfolge: Produktübersicht → Pakete → Berater → Beratungsinformationen mit Footer.
| Bereich | CSS | JavaScript | Daten |
| --- | --- | --- | --- |
| Shahnam: Produktübersicht | [produktuebersicht.css](../css/produktuebersicht.css) | – | Direkt im HTML |
| Vincent: Pakete | [leistungen.css](../css/leistungen.css) | [leistungen.js](../js/leistungen.js) | [pakete.json](../json/pakete.json) |
| Ahmad: Berater | [berater.css](../css/berater.css) | [berater.js](../js/berater.js) | [berater.json](../json/berater.json) |
| Dennis: Beratung und Footer | [beratung.css](../css/beratung.css) | [beratung.js](../js/beratung.js) | Direkt im HTML |
[site.css](../css/site.css) enthält nur die gemeinsame Seitengrundlage: Hintergrund, Textfarbe, Standardschrift und den entfernten Außenabstand des `body`.
### Umbenannte und verschobene Dateien
Die alten Pfade beziehen sich auf den jeweiligen Quellbranch.
| Bereich | Vorher | Jetzt |
| --- | --- | --- |
| Shahnam | `css/style.css` | `css/produktuebersicht.css` |
| Vincent | `css/style.css` | `css/leistungen.css` |
| Vincent | `js/script.js` | `js/leistungen.js` |
| Ahmad | `css/style.css` | `css/berater.css` |
| Dennis | `style.css` | `css/beratung.css` |
| Dennis | `script.js` | `js/beratung.js` |
| Beraterbilder | `img/relindis_agethen.png` | `images/relindis_agethen.png` |
| Beraterbilder | `img/relindis_agethen_1.jpg` | `images/relindis_agethen_1.jpg` |
Die Bildpfade in `json/berater.json` wurden entsprechend angepasst.
## Gemeinsames HTML und CSS
**HTML:** Die unterschiedlichen `index.html`-Versionen wurden zu einem Dokument mit einem `head`, einem `body` und einem übergeordneten `main` zusammengesetzt. Alle benötigten CSS- und JS-Dateien sind dort eingebunden; die Skripte laden mit `defer`. Die Schriftarten werden einmal im gemeinsamen Head angefordert.
**CSS:** Regeln für `body`, Überschriften, `.container` und Farbvariablen waren ursprünglich teilweise seitenweit gültig. Sie sind jetzt auf die jeweiligen Bereiche begrenzt:
| Bereich | Äußerer Selektor |
| --- | --- |
| Produktübersicht | `.produktuebersicht` |
| Pakete | `.pakete` |
| Berater | `.berater-section` |
| Beratung einschließlich Footer | `.beratung-bereich` |
Beispiel: `.produktuebersicht .container` gilt nur im Produktbereich, `.beratung-bereich .container` nur im Beratungsbereich. Gleichnamige Klassen und Variablen können dadurch in beiden Bereichen bestehen bleiben.
**Beim Ergänzen von Styles:** Den jeweiligen äußeren Selektor beibehalten. Neue globale Regeln wie `h2 { ... }`, `.container { ... }` oder Variablen in `:root` können wieder andere Bereiche beeinflussen. Ein anderer CSS-Dateiname allein verhindert das nicht.
## Änderungen je Bereich
### Shahnam – Produktübersicht
- Der gesamte Bereich liegt jetzt innerhalb von `.produktuebersicht`; die Styles und Variablen wurden entsprechend begrenzt.
- Das bisherige innere `main` wurde zu `div.produktuebersicht__main`, damit es nur einen übergeordneten `main` gibt. Der innere `.hero` ist ebenfalls ein `div`.
### Vincent – Pakete
| Stelle | Änderung | Grund |
| --- | --- | --- |
| Datenabruf in `js/leistungen.js` | `data/pakete.json` → `json/pakete.json` | Der bisherige Pfad passte nicht zum vorhandenen Ordner. |
| Initialisierung | Prüfung auf `#leistungen`; Elementsuche innerhalb dieses Bereichs | Das Skript greift nur auf seinen eigenen Bereich zu. |
| Datenabruf | HTTP-Statusprüfung und `try/catch` mit sichtbarer Fehlermeldung | Ein Ladefehler soll nicht als leerer Paketbereich enden. |
| Paketbuttons | Klickfunktion wieder angeschlossen | Die Auswahl aus der ersten Umsetzung fehlte nach der Umstellung auf JSON. |
| Auswahlstatus | Genau eine markierte Karte und `aria-pressed` für die Buttons | Auswahl sichtbar und für Hilfstechnologien erkennbar halten. |
Nach einer Auswahl wird auf `document` das Event `paket:ausgewaehlt` ausgelöst. `event.detail.paket` enthält den `dataPaket`-Wert aus der JSON-Datei. Eine spätere Buchungsfunktion kann daran anschließen; der Button führt aktuell keine Buchung aus.
### Ahmad – Berater
- Kartenbreite von `380px` auf `min(380px, 100%)` angepasst; der Kartencontainer erhält `width: 100%` bei weiterhin maximal `1000px`. So passen die Karten auf schmale Displays.
- CSS auf `.berater-section` begrenzt. Die HTML-Sektion erhielt `id="berater"` und eine zugängliche Bereichsbezeichnung.
- `json/berater.json`: die beiden Bildpfade auf `images/` umgestellt.
### Dennis – Beratung und Footer
- CSS auf `.beratung-bereich` begrenzt. Die vorhandenen allgemeinen Klassennamen bleiben innerhalb dieses Containers nutzbar.
- Der dekorative Hintergrund ist jetzt innerhalb des Bereichs absolut positioniert und durch `isolation: isolate` begrenzt. Er liegt nicht mehr fest hinter der gesamten Seite.
- Scroll-Reveal sucht in `js/beratung.js` nach `.beratung-bereich .reveal` statt seitenweit nach `.reveal`.
- Grid-Spalten verwenden `minmax(0, 1fr)`. Die Karten erhalten `min-width: 0`, `overflow-wrap: anywhere` und `hyphens: auto`. Das behebt den beim 320-Pixel-Test festgestellten horizontalen Überlauf durch lange Wörter.
## Lokal starten und prüfen
`index.html` über einen lokalen HTTP-Server öffnen, zum Beispiel VS Code Live Server. Beim direkten Öffnen als `file://` können die JSON-Abfragen scheitern.
Die vollständige Integrationsprüfung erfolgte automatisiert in Chrome, ergänzt durch die Ansicht von Screenshots:
- Breiten: **320, 390, 768, 1366 und 1920 px**; nach den Korrekturen kein horizontaler Überlauf.
- **3 Pakete und 2 Beraterkarten**, funktionierende Bilder und eindeutige IDs.
- Paketwechsel: genau eine Auswahl und korrektes `paket:ausgewaehlt`-Event.
- Sichtbare Fehlermeldungen bei fehlender Paket- beziehungsweise Berater-JSON.
- Sichtbare Beratungsinhalte bei aktivierter Einstellung für reduzierte Bewegung.
- Keine unbehandelten JavaScript-Fehler oder fehlgeschlagenen lokalen Ressourcen im normalen Testlauf.
Die Browserprüfung fand vor dem Verschieben der Bilder statt; danach wurden die Bildpfade und Dateiinhalte geprüft. Andere Browser und echte Mobilgeräte wurden nicht getestet.
## Offene Punkte für das Team
| Bereich | Noch zu prüfen |
| --- | --- |
| Produktübersicht | Platzhalter durch endgültige Texte ersetzen. |
| Pakete | Bezeichnung `STANDART`, Preise und Paketangaben prüfen; Buchungsfunktion noch offen. |
| Berater | Zwei gleich benannte Profile mit gleichen Informationen und unterschiedlichen Bildern sind aus dem Quellstand übernommen. Prüfen, ob das so vorgesehen ist. |
| Kontakt und externe Links | Ziele und Angaben fachlich prüfen. |
| Gesamtgestaltung | Unterschiede bei Farben, Abständen und Schriftgrößen gemeinsam bewerten. |

BIN
html/.DS_Store vendored 100644

Binary file not shown.

254
html/kontakt.html 100644
View File

@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kontakt | Ernährungsberatung für Triathleten | tri-hub</title>
<!-- Gemeinsame Schriften und Navigation; kontakt.css ergänzt die Styles dieser Seite. -->
<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=DM+Sans:wght@400;500;600;700&amp;family=Inter:wght@400;500;600;700&amp;family=Sora:wght@400;600;700&amp;display=swap"
rel="stylesheet">
<link rel="stylesheet" href="../css/site.css">
<link rel="stylesheet" href="../css/navigation.css">
<link rel="stylesheet" href="../css/kontakt.css">
<!-- Gemeinsames Menü sowie Formularprüfung und Scroll-Reveal für die Kontaktseite. -->
<script src="../js/navigation.js" defer></script>
<script src="../js/kontakt.js" defer></script>
</head>
<body>
<div class="kontakt-seite">
<!-- Gemeinsame Navigation (Styles: css/navigation.css) -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="../index.html" aria-label="Zurück zur Übersicht">
<img src="../images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="1024" height="1024">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Kontakt &amp; Unterstützung</span>
</div>
</div>
<button class="menu-toggle" type="button" aria-controls="navigation-links" aria-expanded="false"
aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Seitennavigation">
<ul>
<li><a href="#nachricht">Nachricht</a></li>
<li><a href="#kontaktinfos">Kontaktinfos</a></li>
<li><a href="#standort">Standort</a></li>
<li><a href="../index.html">Zurück zur Übersicht</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<main id="main-content">
<!-- Hero -->
<section class="hero hero--ribbon">
<div class="container hero__grid">
<div class="reveal">
<span class="kicker">Kontakt</span>
<h1>Von der Frage zur richtigen Strategie</h1>
<p class="hero__text">
Du möchtest mehr über unsere Ernährungsberatung erfahren oder hast konkrete Fragen?
Nimm Kontakt mit uns auf - gemeinsam finden wir heraus, was für dich und deine Ziele am besten passt. Wir freuen uns auf deine Nachricht!
</p>
</div>
<div class="reveal">
<img class="hero__image" src="../images/contact-support-trihub.png"
alt="Gruppe beim Radfahren und Laufen in einer Berglandschaft im Abendlicht" width="1920"
height="1080">
</div>
</div>
</section>
<!-- Nachricht: drei Karten -->
<section id="nachricht" class="section">
<div class="container">
<div class="cards-3">
<div class="card reveal">
<div class="card__icon" aria-hidden="true">💬</div>
<h3>Persönlicher Austausch</h3>
<p>Wir nehmen uns Zeit für deine Fragen und Anliegen – schnell und unkompliziert.</p>
</div>
<div class="card card--warm reveal">
<div class="card__icon" aria-hidden="true">⚡</div>
<h3>Schnelle Antworten</h3>
<p>Deine Fragen beantworten wir zeitnah – per Mail, Telefon oder persönlich.</p>
</div>
<div class="card card--neutral reveal">
<div class="card__icon" aria-hidden="true">🤝</div>
<h3>Echte Beziehung</h3>
<p>Für uns zählt nicht nur die Antwort – sondern der Mensch hinter der Frage.</p>
</div>
</div>
</div>
</section>
<!-- Kontaktinfos: Formular + Kontaktdaten -->
<section id="kontaktinfos" class="section">
<div class="container kontakt-grid">
<div class="reveal">
<span class="kicker">Nachricht</span>
<h2>Schreib uns eine Nachricht</h2>
<!-- kontakt.js prüft die Eingaben und öffnet zum Absenden das E-Mail-Programm. -->
<form id="kontakt-formular" novalidate>
<div class="field">
<label for="kf-name">Name *</label>
<input type="text" id="kf-name" name="name" placeholder="Dein Name" required>
<p class="field__error">Bitte gib deinen Namen an.</p>
</div>
<div class="field">
<label for="kf-email">Email *</label>
<input type="email" id="kf-email" name="email" placeholder="deine@email.de" required>
<p class="field__error">Bitte gib eine gültige E-Mail-Adresse an.</p>
</div>
<div class="field">
<label for="kf-phone">Telefon</label>
<input type="tel" id="kf-phone" name="phone" placeholder="06203/1764877">
</div>
<div class="field">
<label for="kf-type">Anfrage-Typ *</label>
<select id="kf-type" name="anfragetyp" required>
<option value="" selected disabled>Bitte wählen …</option>
<option value="ernaehrungsberatung">Ernährungsberatung anfragen</option>
<option value="kennenlerngespraech">Kennenlerngespräch vereinbaren</option>
<option value="allgemein">Allgemeine Fragen</option>
</select>
<p class="field__error">Bitte wähle einen Anfrage-Typ.</p>
</div>
<div class="field">
<label for="kf-subject">Betreff *</label>
<input type="text" id="kf-subject" name="betreff" placeholder="Worum geht es?" required>
<p class="field__error">Bitte gib einen kurzen Betreff an.</p>
</div>
<div class="field">
<label for="kf-message">Nachricht * (mind. 10 Zeichen)</label>
<textarea id="kf-message" name="nachricht" rows="5" placeholder="Deine Nachricht…" required></textarea>
<p class="field__error">Bitte beschreibe dein Anliegen in wenigen Sätzen.</p>
<p class="field__hint">✓ Nachricht ist lang genug</p>
</div>
<button type="submit" class="btn btn-accent btn-block" disabled>Nachricht senden</button>
<p id="form-status" class="form-status" role="status" aria-live="polite"></p>
</form>
</div>
<div class="reveal">
<h3 style="font-size: 1.25rem; margin-bottom: 24px;">Kontaktinformationen</h3>
<div class="info-stack">
<div class="info-card">
<div class="info-card__icon" aria-hidden="true">✉</div>
<div>
<p class="info-card__label">Allgemeine Fragen &amp; Beratung</p>
<a href="mailto:info@trihub.de">info@trihub.de</a>
</div>
</div>
<div class="info-card">
<div class="info-card__icon" aria-hidden="true">✆</div>
<div>
<p class="info-card__label">Telefon</p>
<a href="tel:+4962031764877">06203 / 1764877</a>
</div>
</div>
<div class="info-card">
<div class="info-card__icon" aria-hidden="true">⚲</div>
<div>
<p class="info-card__label">Adresse</p>
<p>Schloßstr. 11
<span class="info-sub">68535 Edingen-Neckarhausen</span>
</p>
</div>
</div>
<div class="info-card">
<div>
<p class="info-card__label" style="margin-bottom: 12px;">Folge uns auf</p>
<div class="socials">
<a href="https://www.facebook.com/profile.php?id=61569002603339" target="_blank"
rel="noopener noreferrer" aria-label="Tri-hub auf Facebook">FB</a>
<a href="https://www.linkedin.com/company/tri-hub-gmbh" target="_blank"
rel="noopener noreferrer" aria-label="Tri-hub auf LinkedIn">IN</a>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Standort -->
<section id="standort" class="section">
<div class="container">
<div class="reveal">
<span class="kicker">Standort</span>
<h2 style="margin-bottom: 32px;">Unser Standort</h2>
<div class="map-frame">
<iframe
title="Karte: Tri-hub, Schloßstr. 11, 68535 Edingen-Neckarhausen"
src="https://www.openstreetmap.org/export/embed.html?bbox=8.5958662%2C49.4613437%2C8.6038662%2C49.4673437&amp;layer=mapnik&amp;marker=49.4643437%2C8.5998662"
loading="lazy"></iframe>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich
Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="../index.html#beratung">Ablauf der Beratung</a></li>
<li><a href="#kontaktinfos">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>Tri-hub</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span>
<a href="https://tri-hub.de/impressum">Impressum</a> ·
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
<a href="https://tri-hub.de/agb">AGB</a>
</span>
</div>
</div>
</footer>
</main>
</div>
</body>
</html>

View File

@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Premium-Paket | Ernährungsberatung | Tri-hub</title>
<meta name="description" content="Volle Betreuung für ambitionierte Triathleten ab 50: Wöchentliche Beratungen, Supplement-Empfehlungen, Wettkampftag-Support & Priorität für 249 € pro Monat.">
<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=DM+Sans:wght@400;500;600;700&amp;family=Sora:wght@400;500;600;700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="../css/site.css">
<link rel="stylesheet" href="../css/navigation.css">
<link rel="stylesheet" href="../css/paketdetails.css">
<script src="../js/navigation.js" defer></script>
</head>
<body class="paket-premium">
<!-- Gemeinsame Navigation -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="https://tri-hub.de/" aria-label="Zur Trihub-Startseite">
<img src="../images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="500" height="500">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Triathlon als sportliches Zuhause ab 50</span>
</div>
</div>
<button class="menu-toggle" type="button" aria-controls="navigation-links" aria-expanded="false" aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Hauptnavigation">
<ul>
<li><a href="../index.html#produktuebersicht">Übersicht</a></li>
<li><a href="../index.html#leistungen">Pakete</a></li>
<li><a href="../index.html#berater">Berater</a></li>
<li><a href="../index.html#beratung">Infos</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<div class="beratung-bereich paket-detail">
<main id="main-content">
<!-- Einstieg / Hero -->
<section class="produktuebersicht paket-detail__hero" aria-labelledby="paket-title">
<div class="hero">
<img class="hero__image" src="../images/paket-premium.jpg" alt="Ein engagierter Triathlet bereitet sich professionell auf seinen Wettkampf vor." width="1200" height="800" fetchpriority="high">
<div class="container hero__inner">
<div class="hero__content">
<p class="hero__eyebrow">Ernährungsberatung · Premium</p>
<h1 id="paket-title">Maximale Betreuung.<span>Keine Kompromisse.</span></h1>
<p class="hero__description">Volle Betreuung für ambitionierte Athletinnen und Athleten ab 50. Wir stehen dir wöchentlich zur Seite, stimmen deine Nahrungsergänzung ab und begleiten dich direkt an deinem Wettkampftag.</p>
<div class="paket-detail__actions">
<a class="btn btn-accent" href="#anfrage">Premium-Paket ansehen <span aria-hidden="true">↗</span></a>
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
</div>
</div>
</div>
</div>
</section>
<!-- Fakten-Grid -->
<div class="container">
<dl class="paket-detail__facts" aria-label="Premium auf einen Blick">
<div><dt>Dein Paket</dt><dd>Premium<small>Volle Betreuung</small></dd></div>
<div><dt>Monatspreis</dt><dd>249 €<small>pro Monat</small></dd></div>
<div><dt>Dein Plan</dt><dd>Profi-Standard<small>Inkl. Supplement-Strategie</small></dd></div>
<div><dt>Persönlicher Kontakt</dt><dd>Wöchentlich<small>Priorisierter Support &amp; Race-Day</small></dd></div>
</dl>
</div>
<!-- Leistungen -->
<section class="section" id="leistungen" aria-labelledby="leistungen-title">
<div class="container">
<div class="section-head">
<span class="kicker">Das steckt in Premium</span>
<h2 id="leistungen-title">Höchste Präzision.<br>Für deine Höchstleistung.</h2>
<p class="lead">Das Komplettpaket für maximale Ergebnisse: Wöchentlicher Austausch, punktgenaue Mikronährstoff-Beratung und direkte Unterstützung bei deinen Rennen.</p>
</div>
<div class="grid-2 paket-detail__services">
<article class="box">
<span class="paket-detail__number" aria-hidden="true">01</span>
<h3>Alles aus dem Standard-Paket</h3>
<p>Enthält alle Bausteine aus Starter &amp; Standard – inklusive flexibler Trainingsphasen-Anpassung und Wettkampfstrategie.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">02</span>
<h3>Wöchentliche Beratungsgespräche</h3>
<p>Engmaschiges Coaching im 7-Tage-Rhythmus, um deine Ernährung, Regeneration und Verdauung unter hoher Belastung kontinuierlich zu optimieren.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">03</span>
<h3>Individuelle Supplement-Empfehlung</h3>
<p>Wissenschaftlich fundierte Feinabstimmung von Nahrungsergänzungsmitteln (z. B. Magnesium, Omega-3, Aminosäuren), genau abgestimmt auf deine Blutwerte und Trainingsziele.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">04</span>
<h3>Wettkampftag-Support &amp; Prio-Support</h3>
<p>Direkter Check-in vor dem Start &amp; nach dem Ziel. Zudem genießt du priorisierte Antworten auf all deine Fragen im Chat.</p>
</article>
</div>
</div>
</section>
<!-- Ablauf -->
<section class="section paket-detail__process" id="ablauf" aria-labelledby="ablauf-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">So läuft deine Betreuung</span>
<h2 id="ablauf-title">Maximale Begleitung durch die Saison.</h2>
<p class="lead">Keine Unsicherheiten mehr vor Schlüsseleinheiten oder Wettkämpfen. Wir begleiten dich intensiv durch jede Trainingswoche.</p>
<a class="paket-detail__text-link" href="../index.html#berater">Unsere Berater kennenlernen <span aria-hidden="true">↗</span></a>
</div>
<ol class="beratung-ablauf">
<li><strong>Deep-Dive Analyse</strong><p>Wir analysieren deinen Ist-Zustand, Ernährungs-Gewohnheiten, Unverträglichkeiten und bei Bedarf Blutwerte.</p></li>
<li><strong>Wöchentlicher Call</strong><p>Jede Woche besprechen wir Magenverträglichkeit, Energielevel und die optimale Verpflegung für die kommenden Einheiten.</p></li>
<li><strong>Supplement-Grooming</strong><p>Gezielter Einsatz von Nahrungsergänzungsmitteln zur Beschleunigung der Regeneration ab 50.</p></li>
<li><strong>Betreuung am Wettkampftag</strong><p>Finaler Verpflegungs-Check vor dem Start, Anpassungen bei Wetterumschwung und gezielte Refueling-Strategie nach dem Zielleinlauf.</p></li>
</ol>
</div>
</section>
<!-- Preisübersicht & Anfrage -->
<section class="section" id="anfrage" aria-labelledby="anfrage-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">Dein nächster Schritt</span>
<h2 id="anfrage-title">Passt Premium zu dir?</h2>
<p class="lead">Das Paket ist perfekt für dich, wenn du ambitionierte Ziele verfolgst, deine Regeneration maximieren möchtest und dir eine engmaschige 1:1-Betreuung wünschst.</p>
<ul class="check-list">
<li>Du trainierst auf Mitteldistanz, Langdistanz oder anspruchsvolle Saison-Highlights.</li>
<li>Du möchtest eine fundierte Strategie für Nahrungsergänzungsmittel.</li>
<li>Du willst am Wettkampftag nichts dem Zufall überlassen.</li>
</ul>
<p class="paket-detail__alternative">Du suchst nach einer ruhigeren, monatlichen Begleitung?<br><a class="paket-detail__text-link" href="paket-standard.html">Das Standard-Paket ansehen <span aria-hidden="true">→</span></a></p>
</div>
<aside class="box paket-detail__summary" aria-labelledby="summary-title">
<span class="kicker">Höchste Betreuung</span>
<h3 id="summary-title">PREMIUM</h3>
<p class="paket-detail__price"><strong>249 €</strong><span>/ Monat</span></p>
<p class="muted">Volle Betreuung für ambitionierte Athletinnen und Athleten.</p>
<ul class="check-list">
<li>Alles aus STANDARD</li>
<li>Wöchentliche Beratungsgespräche</li>
<li>Individuelle Supplement-Empfehlung</li>
<li>Betreuung am Wettkampftag</li>
<li>Priorisierter Support</li>
</ul>
<a class="btn btn-accent" href="kontakt.html#kontaktinfos">Premium-Paket anfragen <span aria-hidden="true">↗</span></a>
<p class="paket-detail__note">Du gelangst zum Kontaktformular. Mit deiner Anfrage wird noch keine Buchung abgeschlossen.</p>
</aside>
</div>
</section>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="../index.html#beratung">Ablauf der Beratung</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>TRI-HUB</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span><a href="https://tri-hub.de/impressum">Impressum</a> · <a href="https://tri-hub.de/datenschutz">Datenschutz</a> · <a href="https://tri-hub.de/agb">AGB</a></span>
</div>
</div>
</footer>
</div>
</body>
</html>

View File

@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Standard-Paket | Ernährungsberatung | Tri-hub</title>
<meta name="description" content="Laufende Begleitung für Triathleten ab 50: Plan-Anpassung je Trainingsphase, zwei Beratungsgespräche pro Monat, Wettkampfstrategie & Chat für 149 € pro Monat.">
<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=DM+Sans:wght@400;500;600;700&amp;family=Sora:wght@400;500;600;700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="../css/site.css">
<link rel="stylesheet" href="../css/navigation.css">
<link rel="stylesheet" href="../css/paketdetails.css">
<script src="../js/navigation.js" defer></script>
</head>
<body class="paket-standard">
<!-- Gemeinsame Navigation -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="https://tri-hub.de/" aria-label="Zur Trihub-Startseite">
<img src="../images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="500" height="500">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Triathlon als sportliches Zuhause ab 50</span>
</div>
</div>
<button class="menu-toggle" type="button" aria-controls="navigation-links" aria-expanded="false" aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Hauptnavigation">
<ul>
<li><a href="../index.html#produktuebersicht">Übersicht</a></li>
<li><a href="../index.html#leistungen">Pakete</a></li>
<li><a href="../index.html#berater">Berater</a></li>
<li><a href="../index.html#beratung">Infos</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<div class="beratung-bereich paket-detail">
<main id="main-content">
<!-- Einstieg / Hero -->
<section class="produktuebersicht paket-detail__hero" aria-labelledby="paket-title">
<div class="hero">
<img class="hero__image" src="../images/paket-standard.jpg" alt="Zwei ältere Sportler besprechen ihren Ernährungsplan neben einem Fahrrad am See." width="1200" height="800" fetchpriority="high">
<div class="container hero__inner">
<div class="hero__content">
<p class="hero__eyebrow">Ernährungsberatung · Standard</p>
<h1 id="paket-title">Regelmäßig begleitet.<span>Optimal vorbereitet.</span></h1>
<p class="hero__description">Laufende Betreuung für Triathletinnen und Triathleten, die regelmäßig trainieren. Wir passen deine Ernährung flexibel an deine Trainingsphasen an und entwickeln deine Wettkampfstrategie.</p>
<div class="paket-detail__actions">
<a class="btn btn-accent" href="#anfrage">Standard-Paket ansehen <span aria-hidden="true">↗</span></a>
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
</div>
</div>
</div>
</div>
</section>
<!-- Fakten-Grid -->
<div class="container">
<dl class="paket-detail__facts" aria-label="Standard auf einen Blick">
<div><dt>Dein Paket</dt><dd>Standard<small>Laufende Begleitung</small></dd></div>
<div><dt>Monatspreis</dt><dd>149 €<small>pro Monat</small></dd></div>
<div><dt>Dein Plan</dt><dd>Dynamisch<small>Je Trainingsphase angepasst</small></dd></div>
<div><dt>Persönlicher Kontakt</dt><dd>2x Monatlich<small>Plus direkter Chat-Support</small></dd></div>
</dl>
</div>
<!-- Leistungen -->
<section class="section" id="leistungen" aria-labelledby="leistungen-title">
<div class="container">
<div class="section-head">
<span class="kicker">Das steckt in Standard</span>
<h2 id="leistungen-title">Gezielte Steuerung.<br>Für Training &amp; Wettkampf.</h2>
<p class="lead">Alle Leistungen aus dem Starter-Paket kombiniert mit engerer Betreuung, flexibler Plananpassung und konkreter Wettkampfvorbereitung.</p>
</div>
<div class="grid-2 paket-detail__services">
<article class="box">
<span class="paket-detail__number" aria-hidden="true">01</span>
<h3>Alles aus dem Starter-Paket</h3>
<p>Du erhältst dein fundiertes Erstgespräch, die individuelle Bedarfsanalyse und vollen Zugriff auf aufbereitungsgerechte Beispielpläne.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">02</span>
<h3>Plan-Anpassung je Trainingsphase</h3>
<p>Ob Grundlagenausdauer, intensive Intervalle oder Tapering: Dein Ernährungsplan wird dynamisch an deinen aktuellen Trainingsload angepasst.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">03</span>
<h3>Zwei Beratungsgespräche pro Monat</h3>
<p>Alle zwei Wochen besprechen wir deinen Fortschritt, klären offene Fragen und nehmen Feinjustierungen an deiner Ernährung vor.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">04</span>
<h3>Wettkampf-Ernährungsstrategie &amp; Chat</h3>
<p>Wir erarbeiten ein genaues Timing für Kohlenhydrate, Flüssigkeit und Elektrolyte am Renntag. Bei Fragen erreichst du uns direkt per Chat.</p>
</article>
</div>
</div>
</section>
<!-- Ablauf -->
<section class="section paket-detail__process" id="ablauf" aria-labelledby="ablauf-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">So läuft deine Betreuung</span>
<h2 id="ablauf-title">Kontinuierlich am Ball bleiben.</h2>
<p class="lead">Eine erfolgreiche Ernährung wächst mit deinem Training. Wir begleiten dich durch jede Saisonphase mit persönlicher Rücksprache.</p>
<a class="paket-detail__text-link" href="../index.html#berater">Unsere Berater kennenlernen <span aria-hidden="true">↗</span></a>
</div>
<ol class="beratung-ablauf">
<li><strong>Analyse &amp; Startplan</strong><p>Wir erfassen deine Ziele, Trainingspläne und bisherigen Gewohnheiten zur Erstellung deines Grundplans.</p></li>
<li><strong>14-tägige Abstimmung</strong><p>In zwei Monatsgesprächen prüfen wir Bekömmlichkeit, Energielevel und regenerative Fortschritte.</p></li>
<li><strong>Wettkampffokus</strong><p>Vor deinen Hauptwettkämpfen simulieren wir das Race-Day-Nutri-Timing bereits im Training.</p></li>
<li><strong>Direkter Chat-Draht</strong><p>Kurze Fragen vor oder nach dem Training klären wir unkompliziert zwischendurch im Chat.</p></li>
</ol>
</div>
</section>
<!-- Preisübersicht & Anfrage -->
<section class="section" id="anfrage" aria-labelledby="anfrage-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">Dein nächster Schritt</span>
<h2 id="anfrage-title">Passt Standard zu dir?</h2>
<p class="lead">Das Paket ist ideal für dich, wenn du aktiv auf ein Ziel hin trainierst und deine Ernährung gezielt als Leistungs- und Regeneationsfaktor nutzen möchtest.</p>
<ul class="check-list">
<li>Du trainierst regelmäßig und suchst dynamische Anpassung.</li>
<li>Du willst deinen Wettkampftag ernährungstechnisch perfekt planen.</li>
<li>Du schätzt kurze Abstimmungswege per Chat und Video-Call.</li>
</ul>
<p class="paket-detail__alternative">Möchtest du eine Rundum-Betreuung inklusive Support am Wettkampftag?<br><a class="paket-detail__text-link" href="paket-premium.html">Zum Premium-Paket wechseln <span aria-hidden="true">→</span></a></p>
</div>
<aside class="box paket-detail__summary" aria-labelledby="summary-title">
<span class="kicker">Beliebteste Wahl</span>
<h3 id="summary-title">STANDARD</h3>
<p class="paket-detail__price"><strong>149 €</strong><span>/ Monat</span></p>
<p class="muted">Laufende Begleitung für alle, die regelmäßig für Wettkämpfe trainieren.</p>
<ul class="check-list">
<li>Alles aus Starter</li>
<li>Plan-Anpassung je Trainingsphase</li>
<li>Zwei Beratungsgespräche pro Monat</li>
<li>Wettkampf-Ernährungsstrategie</li>
<li>Direkter Chat mit deiner Beraterin</li>
</ul>
<a class="btn btn-accent" href="kontakt.html#kontaktinfos">Standard-Paket anfragen <span aria-hidden="true">↗</span></a>
<p class="paket-detail__note">Du gelangst zum Kontaktformular. Mit deiner Anfrage wird noch keine Buchung abgeschlossen.</p>
</aside>
</div>
</section>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="../index.html#beratung">Ablauf der Beratung</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>TRI-HUB</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span><a href="https://tri-hub.de/impressum">Impressum</a> · <a href="https://tri-hub.de/datenschutz">Datenschutz</a> · <a href="https://tri-hub.de/agb">AGB</a></span>
</div>
</div>
</footer>
</div>
</body>
</html>

View File

@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Starter-Paket | Ernährungsberatung | Tri-hub</title>
<meta name="description" content="Dein Einstieg in die Ernährungsberatung für Triathleten ab 50: Erstgespräch, individueller Ernährungsplan und monatliches Check-in für 79 € pro Monat.">
<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=DM+Sans:wght@400;500;600;700&amp;family=Sora:wght@400;500;600;700&amp;display=swap" rel="stylesheet">
<link rel="stylesheet" href="../css/site.css">
<link rel="stylesheet" href="../css/navigation.css">
<link rel="stylesheet" href="../css/paketdetails.css">
<script src="../js/navigation.js" defer></script>
</head>
<body class="paket-starter">
<!-- Gemeinsame Navigation -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="https://tri-hub.de/" aria-label="Zur Trihub-Startseite">
<img src="../images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="500" height="500">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Triathlon als sportliches Zuhause ab 50</span>
</div>
</div>
<button class="menu-toggle" type="button" aria-controls="navigation-links" aria-expanded="false" aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Hauptnavigation">
<ul>
<li><a href="../index.html#produktuebersicht">Übersicht</a></li>
<li><a href="../index.html#leistungen">Pakete</a></li>
<li><a href="../index.html#berater">Berater</a></li>
<li><a href="../index.html#beratung">Infos</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<div class="beratung-bereich paket-detail">
<main id="main-content">
<!-- Einstieg / Hero -->
<section class="produktuebersicht paket-detail__hero" aria-labelledby="paket-title">
<div class="hero">
<img class="hero__image" src="../images/paket-starter.jpg" alt="Zwei ältere Sportler besprechen ihren Ernährungsplan neben einem Fahrrad am See." width="1200" height="800" fetchpriority="high">
<div class="container hero__inner">
<div class="hero__content">
<p class="hero__eyebrow">Ernährungsberatung · Starter</p>
<h1 id="paket-title">Dein erster Schritt.<span>Gut versorgt starten.</span></h1>
<p class="hero__description">Du möchtest deine Ernährung bewusster auf dein Training abstimmen? Wir begleiten deinen Einstieg – mit einem persönlichen Gespräch, einem Plan für dich und konkreten Ideen für deinen Alltag.</p>
<div class="paket-detail__actions">
<a class="btn btn-accent" href="#anfrage">Starter-Paket ansehen <span aria-hidden="true">↗</span></a>
<a class="paket-detail__text-link" href="#leistungen">Was ist enthalten? <span aria-hidden="true">↓</span></a>
</div>
</div>
</div>
</div>
</section>
<!-- Fakten-Grid -->
<div class="container">
<dl class="paket-detail__facts" aria-label="Starter auf einen Blick">
<div><dt>Dein Paket</dt><dd>Starter<small>Für deinen Einstieg</small></dd></div>
<div><dt>Monatspreis</dt><dd>79 €<small>pro Monat</small></dd></div>
<div><dt>Dein Plan</dt><dd>Individuell<small>Passend zu deinem Alltag</small></dd></div>
<div><dt>Persönlicher Kontakt</dt><dd>Monatlich<small>Check-in per E-Mail</small></dd></div>
</dl>
</div>
<!-- Leistungen -->
<section class="section" id="leistungen" aria-labelledby="leistungen-title">
<div class="container">
<div class="section-head">
<span class="kicker">Das steckt in Starter</span>
<h2 id="leistungen-title">Eine gute Grundlage.<br>Für Training und Alltag.</h2>
<p class="lead">Vier Bausteine helfen dir dabei, deine Ernährungsgewohnheiten besser zu verstehen und erste Schritte umzusetzen.</p>
</div>
<div class="grid-2 paket-detail__services">
<article class="box">
<span class="paket-detail__number" aria-hidden="true">01</span>
<h3>Erstgespräch &amp; Bedarfsanalyse</h3>
<p>Wir sprechen über deine Ziele, deinen Trainingsalltag und deine bisherigen Gewohnheiten. So finden wir heraus, wo du ansetzen kannst.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">02</span>
<h3>Individueller Ernährungsplan</h3>
<p>Dein Plan orientiert sich an dir: an deinem Training, deinen Vorlieben und dem, was sich in deinem Alltag umsetzen lässt.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">03</span>
<h3>Monatliches Check-in per E-Mail</h3>
<p>Du kannst deine Erfahrungen und Fragen mit uns teilen. Gemeinsam schauen wir darauf, wie es dir mit deinem Plan geht.</p>
</article>
<article class="box">
<span class="paket-detail__number" aria-hidden="true">04</span>
<h3>Zugang zu Beispielplänen</h3>
<p>Beispiele geben dir Orientierung und Ideen für Mahlzeiten. Du bekommst Anregungen, die du auf deinen eigenen Alltag übertragen kannst.</p>
</article>
</div>
</div>
</section>
<!-- Ablauf -->
<section class="section paket-detail__process" id="ablauf" aria-labelledby="ablauf-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">So beginnt deine Beratung</span>
<h2 id="ablauf-title">Ein Schritt nach dem anderen.</h2>
<p class="lead">Du musst noch nicht auf alles eine Antwort haben. Bring deine Fragen mit – wir finden gemeinsam einen passenden Einstieg.</p>
<a class="paket-detail__text-link" href="../index.html#berater">Unsere Berater kennenlernen <span aria-hidden="true">↗</span></a>
</div>
<ol class="beratung-ablauf">
<li><strong>Kontakt aufnehmen</strong><p>Schreib uns, dass du dich für das Starter-Paket interessierst, und erzähle kurz von deinem Ziel.</p></li>
<li><strong>Gemeinsam hinschauen</strong><p>Im Erstgespräch besprechen wir deinen Bedarf und deine aktuellen Ernährungsgewohnheiten.</p></li>
<li><strong>Im Alltag starten</strong><p>Mit deinem individuellen Plan und den Beispielplänen setzt du die ersten Schritte um.</p></li>
<li><strong>In Verbindung bleiben</strong><p>Beim monatlichen Check-in per E-Mail ist Raum für deine Erfahrungen und Fragen.</p></li>
</ol>
</div>
</section>
<!-- Preisübersicht & Anfrage -->
<section class="section" id="anfrage" aria-labelledby="anfrage-title">
<div class="container paket-detail__split">
<div>
<span class="kicker">Dein nächster Schritt</span>
<h2 id="anfrage-title">Passt Starter zu dir?</h2>
<p class="lead">Das Paket ist für dich gedacht, wenn du eine persönliche Grundlage für deine Ernährung suchst und mit überschaubaren Schritten beginnen möchtest.</p>
<ul class="check-list">
<li>Du möchtest Ernährung und Training besser verbinden.</li>
<li>Du suchst Orientierung statt allgemeiner Ernährungstipps.</li>
<li>Du wünschst dir einen Plan und einen festen Kontaktpunkt.</li>
</ul>
<p class="paket-detail__alternative">Du möchtest eine engere Begleitung?<br><a class="paket-detail__text-link" href="../index.html#leistungen">Standard und Premium vergleichen <span aria-hidden="true">→</span></a></p>
</div>
<aside class="box paket-detail__summary" aria-labelledby="summary-title">
<span class="kicker">Ernährungsberatung</span>
<h3 id="summary-title">STARTER</h3>
<p class="paket-detail__price"><strong>79 €</strong><span>/ Monat</span></p>
<p class="muted">Für den Einstieg in eine bewusste Ernährung neben dem Training.</p>
<ul class="check-list">
<li>Erstgespräch &amp; Bedarfsanalyse</li>
<li>Individueller Ernährungsplan</li>
<li>Monatliches Check-in per E-Mail</li>
<li>Zugang zu Beispielplänen</li>
</ul>
<a class="btn btn-accent" href="kontakt.html#kontaktinfos">Starter-Paket anfragen <span aria-hidden="true">↗</span></a>
<p class="paket-detail__note">Du gelangst zum Kontaktformular. Mit deiner Anfrage wird noch keine Buchung abgeschlossen.</p>
</aside>
</div>
</section>
</main>
<!-- Footer -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="../index.html#beratung">Ablauf der Beratung</a></li>
<li><a href="kontakt.html">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>TRI-HUB</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span><a href="https://tri-hub.de/impressum">Impressum</a> · <a href="https://tri-hub.de/datenschutz">Datenschutz</a> · <a href="https://tri-hub.de/agb">AGB</a></span>
</div>
</div>
</footer>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

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.0">
<title>Terminbuchung | Ernährungsberatung für Triathleten | tri-hub</title>
<!-- Gemeinsame Schriften und Navigation; terminbuchung.css ergänzt die Styles dieser Seite (Stil wie css/kontakt.css). -->
<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=DM+Sans:wght@400;500;600;700&amp;family=Inter:wght@400;500;600;700&amp;family=Sora:wght@400;600;700&amp;display=swap"
rel="stylesheet">
<link rel="stylesheet" href="../css/site.css">
<link rel="stylesheet" href="../css/navigation.css">
<link rel="stylesheet" href="../css/terminbuchung.css">
<!-- Gemeinsames Menü sowie Zusammenfassung, Tag-Eingabe und Formularprüfung für die Buchungsseite. -->
<script src="../js/navigation.js" defer></script>
<script src="../js/terminbuchung-form.js" defer></script>
</head>
<body>
<div class="terminbuchung-seite">
<!-- Gemeinsame Navigation (Styles: css/navigation.css) -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="../index.html" aria-label="Zurück zur Übersicht">
<img src="../images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="1024"
height="1024">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Terminbuchung</span>
</div>
</div>
<button class="menu-toggle" type="button" aria-controls="navigation-links" aria-expanded="false"
aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Seitennavigation">
<ul>
<li><a href="#termin">Termin</a></li>
<li><a href="#formular">Deine Angaben</a></li>
<li><a href="../index.html">Zurück zur Übersicht</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<main id="main-content">
<!-- Kopfzeile mit dem gewählten Termin -->
<section id="termin" class="hero">
<div class="container">
<div class="reveal">
<span class="kicker">Online-Terminbuchung</span>
<h1>Dein Termin ist fast gebucht</h1>
<p class="hero__text">
Du hast dir einen Termin im Kalender ausgesucht. Bestätige ihn hier – und gib noch ein paar
Details zur Beratung sowie deine Kontaktdaten an.
</p>
<!-- terminbuchung-form.js füllt die Werte aus der URL (berater, datum, zeit). -->
<div class="tb-summary" id="termin-zusammenfassung">
<div class="tb-summary__item">
<span class="tb-summary__label">Berater/in</span>
<p class="tb-summary__value" id="tb-berater">–</p>
</div>
<div class="tb-summary__item">
<span class="tb-summary__label">Datum</span>
<p class="tb-summary__value" id="tb-datum">–</p>
</div>
<div class="tb-summary__item">
<span class="tb-summary__label">Uhrzeit</span>
<p class="tb-summary__value" id="tb-zeit">–</p>
</div>
</div>
</div>
</div>
</section>
<!-- Formular: Art der Beratung, Anamnese-Tags und Kontaktdaten -->
<section id="formular" class="section">
<div class="container">
<div class="reveal">
<span class="kicker">Deine Angaben</span>
<h2>Noch ein paar Details zur Beratung</h2>
<p class="lead">Damit dein Gegenüber den Termin gut vorbereiten kann: Wähle die Art der
Ernährungsberatung und das Format, beschreibe kurz dein Anliegen und nimm Allergien oder
Unverträglichkeiten als Tags auf.</p>
<!-- Vor dem E-Mail-Versand werden alle Angaben in einem zweiten Schritt geprüft. -->
<form id="terminbuchung-formular" novalidate>
<div class="field">
<label for="tb-art">Art der Ernährungsberatung *</label>
<select id="tb-art" name="art" required>
<option value="" selected disabled>Bitte wählen …</option>
<option value="leistungsoptimierung">Leistungsoptimierung – Training &amp; Wettkampf
</option>
<option value="gewichtsmanagement">Gewichtsmanagement</option>
<option value="unvertraeglichkeiten">Ernährung bei Unverträglichkeiten</option>
<option value="allgemein">Allgemeine Ernährungsberatung</option>
</select>
<p class="field__error">Bitte wähle eine Art der Ernährungsberatung.</p>
</div>
<div class="field">
<label for="tb-format">Beratungsformat *</label>
<select id="tb-format" name="format" required>
<option value="" selected disabled>Bitte wählen …</option>
<option value="persoenlich">Persönlich (Vor-Ort)</option>
<option value="videocall">Videocall-Beratung</option>
</select>
<p class="field__error">Bitte wähle ein Beratungsformat.</p>
</div>
<div class="field">
<label for="tb-gespraech">Gesprächsart *</label>
<select id="tb-gespraech" name="gespraech" required>
<option value="" selected disabled>Bitte wählen …</option>
<option value="erstgespraech">Erstgespräch</option>
<option value="folgegespraech">Folgegespräch</option>
</select>
<p class="field__error">Bitte wähle aus, ob es ein Erst- oder Folgegespräch ist.</p>
</div>
<div class="field">
<label for="tb-beschreibung">Kurzbeschreibung</label>
<textarea id="tb-beschreibung" name="beschreibung" rows="3"
placeholder="Worum geht es? Deine Ziele, deine aktuelle Situation … (optional)"></textarea>
</div>
<div class="field">
<label for="tag-input">Anamnese – Allergien &amp; Unverträglichkeiten</label>
<div class="tb-tag-row">
<input type="text" id="tag-input" name="anamnese-eintrag"
placeholder="z. B. Laktoseintoleranz, Nussallergie …">
<button type="button" class="tb-tag-add" id="tag-add">+ Hinzufügen</button>
</div>
<ul class="tb-tags" id="tag-list"></ul>
<p class="field__hint">Enter oder der Button fügt den Eintrag als Tag hinzu – so behält
dein Gegenüber die Anamnese im Blick.</p>
</div>
<div class="field">
<label for="tb-name">Name *</label>
<input type="text" id="tb-name" name="name" placeholder="Dein Name" required>
<p class="field__error">Bitte gib deinen Namen an.</p>
</div>
<div class="field">
<label for="tb-email">Email *</label>
<input type="email" id="tb-email" name="email" placeholder="deine@email.de" required>
<p class="field__error">Bitte gib eine gültige E-Mail-Adresse an.</p>
</div>
<div class="field">
<label for="tb-phone">Telefon</label>
<input type="tel" id="tb-phone" name="phone" placeholder="06203/1764877">
</div>
<button type="submit" class="btn btn-accent btn-block" disabled>Angaben prüfen</button>
<p id="form-status" class="form-status" role="status" aria-live="polite"></p>
</form>
</div>
</div>
</section>
<section id="anfrage-pruefen" class="section" hidden aria-labelledby="pruefen-titel">
<div class="container">
<div>
<span class="kicker">Letzter Schritt</span>
<h2 id="pruefen-titel">Prüfe deine Angaben</h2>
<p class="lead">Kontrolliere die Zusammenfassung. Du kannst zurückgehen und Angaben ändern oder
die Anfrage senden.</p>
<dl class="tb-review-list" id="anfrage-pruefen-details"></dl>
<div class="tb-review-actions">
<button type="button" class="tb-review-button tb-review-button--secondary"
id="anfrage-bearbeiten">Zurück und bearbeiten</button>
<a class="tb-review-button tb-review-button--primary" id="anfrage-senden" href="#">Anfrage
senden</a>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich
Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="../index.html#beratung">Ablauf der Beratung</a></li>
<li><a href="../html/kontakt.html">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>Tri-hub</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span>
<a href="https://tri-hub.de/impressum">Impressum</a> ·
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
<a href="https://tri-hub.de/agb">AGB</a>
</span>
</div>
</div>
</footer>
</main>
<dialog class="tb-confirm-dialog" id="anfrage-bestaetigung" aria-labelledby="bestaetigung-titel"
aria-describedby="bestaetigung-text">
<span class="kicker">Anfrage vorbereitet</span>
<h2 id="bestaetigung-titel">Terminanfrage</h2>
<p id="bestaetigung-text">Deine Terminanfrage wurde an dein E-Mail-Programm übergeben. Sende sie dort
bitte ab. Wir prüfen deinen Wunschtermin und schicken dir anschließend eine Bestätigung per E-Mail.</p>
<a class="tb-review-button tb-review-button--primary" href="../index.html">OK, zur Übersicht</a>
</dialog>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,30 +1,265 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ernährungsberater</title>
<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=Playfair+Display:wght@600;700&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- US-01-02 Leistungsübersicht -->
<section id="leistungen" class="pakete">
<div class="pakete__intro">
<h2 class="pakete__titel"></h2>
<p class="pakete__text"></p>
</div>
<div class="pakete__grid"></div>
</section>
<script src="js/script.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ernährungsberatung für Triathleten | tri-hub</title>
<!-- Schriften und Styles: gemeinsame Grundlage zuerst, danach die einzelnen Bereiche. -->
<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=DM+Sans:wght@400;500;600;700&amp;family=Inter:wght@400;500;600;700&amp;family=Sora:wght@400;600;700&amp;display=swap"
rel="stylesheet">
<link rel="stylesheet" href="css/site.css">
<link rel="stylesheet" href="css/navigation.css">
<link rel="stylesheet" href="css/produktuebersicht.css">
<link rel="stylesheet" href="css/leistungen.css">
<link rel="stylesheet" href="css/berater.css">
<link rel="stylesheet" href="css/beratung.css">
<link rel="stylesheet" href="css/terminbuchung.css">
<!-- Navigation und dynamische Inhalte werden nach dem Einlesen des HTML initialisiert. -->
<script src="js/navigation.js" defer></script>
<script src="js/leistungen.js" defer></script>
<script src="js/berater.js" defer></script>
<script src="js/beratung.js" defer></script>
<!-- Online-Terminbuchung: Kalender-Pop-up, das die "Trainingsplan buchen"-Buttons öffnen. -->
<script src="js/terminbuchung.js" defer></script>
</head>
<body>
<!-- Shahnam und Vincent: Gemeinsame Navigation außerhalb der einzelnen Bereiche. -->
<header class="site-navigation">
<div class="site-navigation__bar">
<div class="brand">
<a class="brand__logo" href="https://tri-hub.de/" aria-label="Zur Trihub-Startseite">
<img src="images/trihub-logo.jpg" alt="Trihub – fit und vital ab 50!" width="500" height="500">
</a>
<div class="brand__text">
<span class="brand__name">TRI-HUB</span>
<span class="brand__label">Triathlon als sportliches Zuhause ab 50</span>
</div>
</div>
<!-- Ein gemeinsamer Linksatz für Desktop und Mobil; navigation.js steuert den Menüknopf. -->
<button class="menu-toggle" type="button" aria-controls="navigation-links"
aria-expanded="false" aria-label="Menü öffnen">☰</button>
<nav class="site-navigation__links" id="navigation-links" aria-label="Hauptnavigation">
<ul>
<li><a href="#produktuebersicht">Übersicht</a></li>
<li><a href="#leistungen">Pakete</a></li>
<li><a href="#berater">Berater</a></li>
<li><a href="#beratung">Infos</a></li>
<li><a href="html/kontakt.html">Kontakt</a></li>
</ul>
</nav>
</div>
<div class="site-navigation__backdrop" aria-hidden="true"></div>
</header>
<main id="main-content">
<!-- Shahnam: Produktübersicht -->
<section class="produktuebersicht" id="produktuebersicht" aria-labelledby="hero-title">
<div class="produktuebersicht__main">
<div class="hero">
<img class="hero__image" src="images/hero-nutrition.png"
alt="KI-generierte Bildidee: Ernährungsberatung für einen älteren Radsportler, mit Müsli, Obst und Wasser am Tisch." width="1536"
height="1024" fetchpriority="high">
<div class="container hero__inner">
<div class="hero__content">
<p class="hero__eyebrow">Ernährungsberatung für Triathleten ab 50</p>
<h1 id="hero-title">
Dein Training hat einen Plan.
<span>Deine Ernährung auch?</span>
</h1>
<p class="hero__description">
Individuelle Ernährungsberatung für Triathletinnen und Triathleten ab 50. Gemeinsam finden wir heraus, was vor, während und nach dem Training zu dir passt – und wie du es im Alltag umsetzt.
</p>
</div>
<!-- Vorteile der Beratung und ergänzender Slogan. -->
<div class="hero__details">
<ul class="benefits" aria-label="Vorteile der Ernährungsberatung">
<li>
<span class="benefits__number" aria-hidden="true">01</span>
<p>Persönlich beraten <span>Deine Ziele und Gewohnheiten im Blick</span></p>
</li>
<li>
<span class="benefits__number" aria-hidden="true">02</span>
<p>Passend zum Training <span>Für Training, Wettkampf und Erholung</span></p>
</li>
<li>
<span class="benefits__number" aria-hidden="true">03</span>
<p>Machbar im Alltag <span>Konkrete Schritte, die zu dir passen</span></p>
</li>
</ul>
<div class="hero__caption">
<p class="hero__caption-label">Ernährung für deinen Sport</p>
<p class="hero__slogan">Gut versorgt. In deinem Rhythmus.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Vincent: Leistungsübersicht -->
<!-- Titel, Einleitung und Karten werden durch leistungen.js aus json/pakete.json erzeugt. -->
<section id="leistungen" class="pakete">
<div class="pakete__intro">
<h2 class="pakete__titel"></h2>
<p class="pakete__text"></p>
</div>
<div class="pakete__grid"></div>
</section>
<!-- Ahmad: Berater und Qualifikationen -->
<section class="berater-section" id="berater" aria-label="Berater und Qualifikationen">
<div class="section-header">
<h2 class="section-title"></h2>
<p class="section-subtitle"></p>
</div>
<!-- Container wird durch berater.js befüllt -->
<div class="berater-container" id="berater-container"></div>
</section>
<!-- Dennis: Beratungsablauf und Footer -->
<div class="beratung-bereich">
<section id="beratung" class="section">
<div class="container">
<div class="section-head reveal">
<span class="kicker">Info über die Beratung</span>
<h2>Gut zu wissen, bevor du startest</h2>
<p class="lead">Transparenz von Anfang an: So arbeitet die Beratung, für wen sie sich eignet und
was dich erwartet.</p>
</div>
<!-- Beratung im Überblick: Inhalte, Ablauf, Zielgruppe und weitere Informationen. -->
<div class="grid-2">
<div class="box reveal">
<h2>Was dich bei der Beratung erwartet</h2>
<p>Gemeinsam schauen wir uns deine individuellen Ziele, Gewohnheiten und Bedürfnisse an und
entwickeln daraus passende Strategien für deinen Alltag.</p>
<ul class="check-list">
<li>Individuelle Ernährungsberatung</li>
<li>Erstellung von Ernährungsplänen</li>
<li>Unterstützung bei Gewichtsmanagement</li>
<li>Beratung bei speziellen Ernährungsbedürfnissen</li>
<li>Analyse deiner aktuellen Ernährungsgewohnheiten</li>
<li>Praktische Tipps für die Umsetzung im Alltag</li>
</ul>
</div>
<div class="box reveal">
<h2>So läuft die Beratung ab</h2>
<ol class="beratung-ablauf">
<li>
<strong>Erstgespräch</strong>
<p>Wir besprechen deine Ziele, Wünsche und bisherigen Erfahrungen.</p>
</li>
<li>
<strong>Analyse</strong>
<p>Deine aktuelle Ernährung und deine individuellen Bedürfnisse werden betrachtet.
</p>
</li>
<li>
<strong>Planung</strong>
<p>Gemeinsam entwickeln wir konkrete und realistische Maßnahmen.</p>
</li>
<li>
<strong>Begleitung</strong>
<p>Bei Bedarf wird der Fortschritt regelmäßig überprüft und der Plan angepasst.</p>
</li>
</ol>
</div>
<div class="box reveal">
<h2>Für wen ist die Beratung geeignet?</h2>
<ul class="check-list">
<li>Menschen, die ihre Ernährung verbessern möchten</li>
<li>Sportler mit individuellen Ernährungszielen</li>
<li>Menschen mit speziellen Ernährungsbedürfnissen</li>
<li>Personen, die Unterstützung beim Gewichtsmanagement suchen</li>
</ul>
</div>
<div class="box reveal">
<h2>Weitere Informationen</h2>
<p class="muted">
Platzhalter für zukünftige Erweiterungen, beispielsweise Informationen zu Preisen,
Terminen, Kontaktmöglichkeiten oder zusätzlichen Beratungsangeboten.
</p>
</div>
</div>
</div>
<div class="container" id="kontakt">
<!-- Einstieg zur separaten Kontaktseite mit Formular. -->
<div class="cta-band reveal">
<h2>Bereit für deinen nächsten Schritt?</h2>
<p>Vereinbare ein unverbindliches Kennenlerngespräch – wir schauen gemeinsam, ob die Beratung zu
dir
passt.</p>
<a class="btn btn-accent" href="html/kontakt.html">Jetzt unverbindlich anfragen</a>
</div>
</div>
</section>
<!-- Seitenabschluss mit internen Links, Trihub-Angeboten und rechtlichen Informationen. -->
<footer class="site-footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a class="logo" href="https://tri-hub.de/">TRI-HUB</a>
<p>Triathlon ab 50 – Coaching, Wissen und Begleitung für Best Ager. Hier: der neue Bereich
Ernährungsberatung.</p>
</div>
<div class="footer-col">
<h3>Dieser Bereich</h3>
<ul>
<li><a href="#beratung">Ablauf der Beratung</a></li>
<li><a href="#kontakt">Kontakt</a></li>
</ul>
</div>
<div class="footer-col">
<h3>TRI-HUB</h3>
<ul>
<li><a href="https://tri-hub.de/">Start</a></li>
<li><a href="https://tri-hub.de/coaching-1zu1">1:1 Coaching</a></li>
<li><a href="https://tri-hub.de/camps-schwarzwald">Camps Schwarzwald</a></li>
<li><a href="https://tri-hub.de/webinare-workshops">Webinare &amp; Workshops</a></li>
<li><a href="https://tri-hub.de/wissen">Wissen</a></li>
<li><a href="https://tri-hub.de/kontakt">Kontakt</a></li>
</ul>
</div>
</div>
<div class="footer-legal">
<span>© 2026 Tri-hub GmbH</span>
<span>
<a href="https://tri-hub.de/impressum">Impressum</a> ·
<a href="https://tri-hub.de/datenschutz">Datenschutz</a> ·
<a href="https://tri-hub.de/agb">AGB</a>
</span>
</div>
</div>
</footer>
</div>
</main>
</body>
</html>

124
js/berater.js 100644
View File

@ -0,0 +1,124 @@
document.addEventListener('DOMContentLoaded', async () => {
// Container für die Berater-Karten auswählen
const container = document.getElementById('berater-container');
// Titel- und Untertitel-Elemente aus dem HTML abrufen
const titleEl = document.querySelector('.section-title');
const subtitleEl = document.querySelector('.section-subtitle');
let daten;
try {
// Daten aus der JSON-Datei asynchron laden
const response = await fetch('json/berater.json');
if (!response.ok) throw new Error(`HTTP Fehler: ${response.status}`);
daten = await response.json();
} catch (error) {
console.error('Fehler beim Laden der JSON-Datei:', error);
// Fehlermeldung auf der Webseite anzeigen, falls das Laden fehlschlägt
if (container) {
container.innerHTML = '<p style="color: #94A3B8;">Daten konnten nicht geladen werden.</p>';
}
return;
}
// 1. Titel und Einleitungstext (Intro) aus der JSON-Datei einfügen
if (titleEl && daten.intro) {
titleEl.textContent = daten.intro.titel;
}
if (subtitleEl && daten.intro) {
subtitleEl.textContent = daten.intro.text;
}
// 2. Berater-Karten verarbeiten und erstellen
// Wir iterieren über das Array "berater" innerhalb der JSON-Daten
daten.berater.forEach(berater => {
// Ein neues div-Element für die Karte erstellen
const card = document.createElement('div');
card.className = 'berater-card';
// Zertifikate verarbeiten (falls vorhanden)
let zertifikateHtml = '';
if (berater.zertifikate && berater.zertifikate.length > 0) {
zertifikateHtml = `<h4 class="bio-title" style="margin-top: 16px;">Zertifikate</h4><ul class="berater-liste">`;
berater.zertifikate.forEach(zert => {
zertifikateHtml += `<li>${zert}</li>`;
});
zertifikateHtml += `</ul>`;
}
// Links verarbeiten (falls vorhanden)
let linksHtml = '';
if (berater.links && berater.links.length > 0) {
linksHtml = `<div class="berater-links">`;
berater.links.forEach(link => {
if (link.aktion === 'terminbuchung') {
// Öffnet das Kalender-Pop-up (js/terminbuchung.js) mit dieser Berater-ID,
// statt eine externe Seite zu laden.
linksHtml += `<button type="button" class="berater-link-btn" data-terminbuchung="${berater.id}" data-url="${link.url}">${link.text}</button>`;
} else {
linksHtml += `<a href="${link.url}" target="_blank" class="berater-link-btn">${link.text}</a>`;
}
});
linksHtml += `</div>`;
}
// Zusatzinformationen verarbeiten (falls vorhanden)
let zusatzHtml = '';
if (berater.zusatzInfo) {
zusatzHtml = `<p class="berater-zusatz">${berater.zusatzInfo}</p>`;
}
// Eigene/unbekannte dynamische Felder verarbeiten
// Zuerst definieren wir, welche Felder Standard sind und ignoriert werden sollen
const standardFelder = ['id', 'name', 'role', 'image', 'bioTitle', 'bioText', 'zertifikate', 'links', 'zusatzInfo'];
let eigeneFelderHtml = '';
// Wir durchlaufen jeden Eintrag in der JSON für diesen spezifischen Berater
for (const [schluessel, wert] of Object.entries(berater)) {
// Wenn das Feld kein Standardfeld ist, generieren wir daraus eine Überschrift und den zugehörigen Text
if (!standardFelder.includes(schluessel)) {
eigeneFelderHtml += `
<h4 class="bio-title" style="margin-top: 16px; text-transform: capitalize;">${schluessel}</h4>
<p class="bio-text">${wert}</p>
`;
}
}
// Die Karte mit allen gesammelten HTML-Bausteinen zusammenbauen
card.innerHTML = `
<div class="berater-image-wrapper">
<img src="${berater.image}" alt="${berater.name} Profilbild">
</div>
<h2 class="berater-name">${berater.name}</h2>
<h3 class="berater-rolle">${berater.role}</h3>
<div class="berater-bio-box">
<h4 class="bio-title">${berater.bioTitle}</h4>
<p class="bio-text">${berater.bioText}</p>
${eigeneFelderHtml}
${zusatzHtml}
${zertifikateHtml}
${linksHtml}
</div>
`;
// Die fertige Karte zum Container auf der Seite hinzufügen
container.appendChild(card);
});
// "Trainingsplan buchen"-Buttons öffnen das Kalender-Pop-up (js/terminbuchung.js)
// mit der jeweiligen Berater-ID, statt eine externe Seite zu laden.
if (container) {
container.addEventListener('click', (event) => {
const button = event.target.closest('[data-terminbuchung]');
if (!button) return;
if (window.Terminbuchung && typeof window.Terminbuchung.oeffnen === 'function') {
window.Terminbuchung.oeffnen(Number(button.dataset.terminbuchung));
} else {
// Fallback, falls terminbuchung.js nicht geladen wurde: ursprüngliches Linkziel.
window.location.href = button.dataset.url || '#';
}
});
}
});

51
js/beratung.js 100644
View File

@ -0,0 +1,51 @@
// ----- Mobile Navigation -----
(function () {
const toggle = document.querySelector(".nav-toggle");
const nav = document.getElementById("main-nav");
if (!toggle || !nav) return;
toggle.addEventListener("click", function () {
const open = nav.classList.toggle("open");
toggle.setAttribute("aria-expanded", String(open));
toggle.setAttribute("aria-label", open ? "Menü schließen" : "Menü öffnen");
});
// Menü nach Klick auf einen Link automatisch schließen
nav.querySelectorAll("a").forEach(function (link) {
link.addEventListener("click", function () {
nav.classList.remove("open");
toggle.setAttribute("aria-expanded", "false");
});
});
})();
// ----- Scroll-Reveal -----
(function () {
const elements = document.querySelectorAll(".beratung-bereich .reveal");
if (!elements.length) return;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion || !("IntersectionObserver" in window)) {
elements.forEach(function (el) {
el.classList.add("visible");
});
return;
}
const observer = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
elements.forEach(function (el) {
observer.observe(el);
});
})();

198
js/kontakt.js 100644
View File

@ -0,0 +1,198 @@
// Dennis: Kontaktseite – Scroll-Reveal und Formular-Logik.
// ----- Scroll-Reveal -----
(function () {
const elements = document.querySelectorAll(".kontakt-seite .reveal");
if (!elements.length) return;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion || !("IntersectionObserver" in window)) {
elements.forEach(function (el) {
el.classList.add("visible");
});
return;
}
const observer = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
elements.forEach(function (el) {
observer.observe(el);
});
})();
// ----- Kontaktformular -----
(function () {
const form = document.getElementById("kontakt-formular");
if (!form) return;
const MIN_MESSAGE = 10;
const EMAIL_TARGET = "info@trihub.de";
const fields = {
name: form.querySelector("[name='name']"),
email: form.querySelector("[name='email']"),
phone: form.querySelector("[name='phone']"),
type: form.querySelector("[name='anfragetyp']"),
subject: form.querySelector("[name='betreff']"),
message: form.querySelector("[name='nachricht']")
};
const statusEl = document.getElementById("form-status");
function wrap(el) {
return el.closest(".field");
}
function setFieldState(el, state) {
const box = wrap(el);
if (!box) return;
box.classList.remove("has-error", "is-ok");
el.classList.remove("invalid", "valid");
if (state === "error") {
box.classList.add("has-error");
el.classList.add("invalid");
} else if (state === "ok") {
box.classList.add("is-ok");
el.classList.add("valid");
}
}
function validateName() {
const ok = fields.name.value.trim().length > 0;
setFieldState(fields.name, ok ? null : "error");
return ok;
}
function validateEmail() {
const value = fields.email.value.trim();
const ok = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value);
setFieldState(fields.email, ok ? null : "error");
return ok;
}
function validateType() {
const ok = fields.type.value !== "";
setFieldState(fields.type, ok ? null : "error");
return ok;
}
function validateSubject() {
const ok = fields.subject.value.trim().length > 0;
setFieldState(fields.subject, ok ? null : "error");
return ok;
}
function validateMessage() {
const len = fields.message.value.trim().length;
if (len === 0) {
setFieldState(fields.message, "error");
return false;
}
if (len >= MIN_MESSAGE) {
setFieldState(fields.message, "ok");
return true;
}
// Zu kurz: erst bei Submit als Fehler markieren, live nur grün bei erfüllt.
setFieldState(fields.message, null);
return false;
}
function allValid() {
return (
fields.name.value.trim().length > 0 &&
/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(fields.email.value.trim()) &&
fields.type.value !== "" &&
fields.subject.value.trim().length > 0 &&
fields.message.value.trim().length >= MIN_MESSAGE
);
}
function updateSubmitState() {
const submit = form.querySelector("[type='submit']");
if (submit) {
submit.disabled = !allValid();
}
}
// Der Button bleibt unabhängig von der Eingabereihenfolge mit allen Feldern synchron.
form.addEventListener("input", updateSubmitState);
form.addEventListener("change", updateSubmitState);
// Live-Feedback: Nachricht wird bei 10+ Zeichen grün markiert.
fields.message.addEventListener("input", function () {
validateMessage();
});
["name", "email", "subject"].forEach(function (key) {
fields[key].addEventListener("blur", function () {
if (fields[key].value.trim() !== "" || form.dataset.touched === "true") {
({ name: validateName, email: validateEmail, subject: validateSubject })[key]();
}
});
});
fields.type.addEventListener("change", validateType);
form.addEventListener("submit", function (event) {
event.preventDefault();
form.dataset.touched = "true";
const okName = validateName();
const okEmail = validateEmail();
const okType = validateType();
const okSubject = validateSubject();
const okMessage = validateMessage();
if (!(okName && okEmail && okType && okSubject && okMessage)) {
showStatus("error", "Bitte überprüfe die markierten Felder und versuche es erneut.");
return;
}
const subject = fields.subject.value.trim();
const typeLabel = fields.type.options[fields.type.selectedIndex].text;
const body = [
"Name: " + fields.name.value.trim(),
"E-Mail: " + fields.email.value.trim(),
"Telefon: " + (fields.phone.value.trim() || "–"),
"Anfrage-Typ: " + typeLabel,
"",
fields.message.value.trim()
].join("\n");
// Statische Seite ohne Backend: E-Mail-Programm wird mit allen Angaben geöffnet.
window.location.href =
"mailto:" + EMAIL_TARGET +
"?subject=" + encodeURIComponent("Ernährungsberatung – " + subject) +
"&body=" + encodeURIComponent(body);
showStatus(
"ok",
"Danke! Dein E-Mail-Programm wurde geöffnet. Sende die Nachricht ab – wir melden uns zeitnah bei dir."
);
form.reset();
Object.values(fields).forEach(function (el) {
setFieldState(el, null);
});
updateSubmitState();
});
function showStatus(kind, text) {
if (!statusEl) return;
statusEl.textContent = kind === "ok" ? "✓ " + text : "✗ " + text;
statusEl.className =
"form-status is-visible " + (kind === "ok" ? "form-status--ok" : "form-status--error");
}
updateSubmitState();
})();

132
js/leistungen.js 100644
View File

@ -0,0 +1,132 @@
// US-01-02 Leistungsübersicht
// Markiert das gewählte Paket visuell und stellt die Auswahl bereit,
// damit US-01-03 (Pakete online buchen) später darauf aufbauen kann.
// US-01-02 Leistungsübersicht
document.addEventListener("DOMContentLoaded", async () => {
const section = document.getElementById("leistungen");
if (!section) return;
let daten;
try {
const response = await fetch("json/pakete.json");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
daten = await response.json();
} catch (error) {
section.querySelector(".pakete__text").textContent = "Die Pakete konnten nicht geladen werden. Bitte lade die Seite erneut.";
console.error("Pakete konnten nicht geladen werden:", error);
return;
}
const titel = section.querySelector(".pakete__titel");
const text = section.querySelector(".pakete__text");
const grid = section.querySelector(".pakete__grid");
titel.textContent = daten.intro.titel;
text.textContent = daten.intro.text;
grid.innerHTML = "";
// Platzhalter-Pfade für die drei generierten Bilder
const bilderMap = {
"STARTER": "images/paket-starter.jpg",
"STANDARD": "images/paket-standard.jpg",
"PREMIUM": "images/paket-premium.jpg"
};
daten.pakete.forEach(paket => {
const karte = document.createElement("article");
karte.classList.add("paket-karte");
if (paket.empfohlen) {
karte.classList.add("paket-karte--empfohlen");
}
// 1. Bild oben (Platzhalter)
const imgWrapper = document.createElement("div");
imgWrapper.classList.add("paket-karte__image-wrapper");
const img = document.createElement("img");
img.src = bilderMap[paket.name] || "images/paket-placeholder.jpg";
img.alt = `Vorschau ${paket.name}`;
img.classList.add("paket-karte__image");
imgWrapper.appendChild(img);
karte.appendChild(imgWrapper);
// Content-Container für die Texte & Abstände
const body = document.createElement("div");
body.classList.add("paket-karte__body");
// 2. Header: Paketname + Badge nebeneinander
const header = document.createElement("div");
header.classList.add("paket-karte__header");
const name = document.createElement("h3");
name.classList.add("paket-karte__name");
name.textContent = paket.name;
header.appendChild(name);
if (paket.badge) {
const badge = document.createElement("span");
badge.classList.add("paket-karte__badge");
badge.textContent = paket.badge;
header.appendChild(badge);
}
body.appendChild(header);
// 3. Preis
const preis = document.createElement("p");
preis.classList.add("paket-karte__preis");
const betrag = document.createElement("span");
betrag.classList.add("paket-karte__betrag");
betrag.textContent = `${paket.preis} €`;
const zeitraum = document.createElement("span");
zeitraum.classList.add("paket-karte__zeitraum");
zeitraum.textContent = `/ ${paket.zeitraum}`;
preis.appendChild(betrag);
preis.appendChild(zeitraum);
body.appendChild(preis);
// 4. Beschreibung
const beschreibung = document.createElement("p");
beschreibung.classList.add("paket-karte__beschreibung");
beschreibung.textContent = paket.beschreibung;
body.appendChild(beschreibung);
// 5. Leistungen
const liste = document.createElement("ul");
liste.classList.add("paket-karte__leistungen");
paket.leistungen.forEach(leistung => {
const punkt = document.createElement("li");
punkt.textContent = leistung;
liste.appendChild(punkt);
});
body.appendChild(liste);
// 6. Button – Link zur Paketseite
const paketLinkMap = {
"starter": "html/paket-starter.html",
"standard": "html/paket-standard.html",
"premium": "html/paket-premium.html"
};
const paketLink = paketLinkMap[paket.dataPaket] || "#";
const button = document.createElement("a");
button.classList.add("paket-karte__cta");
button.href = paketLink;
button.textContent = "Paket ansehen";
button.setAttribute("role", "button");
body.appendChild(button);
karte.appendChild(body);
grid.appendChild(karte);
});
});

76
js/navigation.js 100644
View File

@ -0,0 +1,76 @@
(() => {
const navigation = document.querySelector('.site-navigation');
if (!navigation) return;
const button = navigation.querySelector('.menu-toggle');
const links = navigation.querySelector('#navigation-links');
const backdrop = navigation.querySelector('.site-navigation__backdrop');
if (!button || !links || !backdrop) return;
// Muss zum Grenzwert in navigation.css passen.
const mobileView = window.matchMedia('(max-width: 1100px)');
function setMenuOpen(isOpen) {
isOpen = isOpen && mobileView.matches;
navigation.classList.toggle('is-open', isOpen);
button.setAttribute('aria-expanded', String(isOpen));
button.setAttribute('aria-label', isOpen ? 'Menü schließen' : 'Menü öffnen');
button.textContent = isOpen ? '✕' : '☰';
}
button.addEventListener('click', () => {
setMenuOpen(button.getAttribute('aria-expanded') !== 'true');
});
// Nach einem Sprung bleibt der Tastaturfokus im gewählten Bereich.
links.addEventListener('click', (event) => {
const link = event.target.closest('a');
if (!link || !mobileView.matches) return;
setMenuOpen(false);
// Nur Anker-Sprünge innerhalb der Seite; Links zu anderen Seiten übernimmt der Browser.
const href = link.getAttribute('href');
if (!href || !href.startsWith('#')) return;
const target = document.querySelector(href);
if (target) {
const hadTabindex = target.hasAttribute('tabindex');
if (!hadTabindex) target.setAttribute('tabindex', '-1');
requestAnimationFrame(() => target.focus({ preventScroll: true }));
if (!hadTabindex) {
target.addEventListener('blur', () => target.removeAttribute('tabindex'), { once: true });
}
}
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && button.getAttribute('aria-expanded') === 'true') {
setMenuOpen(false);
button.focus();
}
});
backdrop.addEventListener('click', () => {
setMenuOpen(false);
button.focus();
});
// Das Menü bleibt beim Weitergehen mit Tab nicht über dem Seiteninhalt liegen.
navigation.addEventListener('focusout', (event) => {
if (!navigation.contains(event.relatedTarget)) setMenuOpen(false);
});
mobileView.addEventListener('change', () => {
const focusedElement = document.activeElement;
setMenuOpen(false);
if (mobileView.matches && links.contains(focusedElement)) button.focus();
if (!mobileView.matches && focusedElement === button) links.querySelector('a')?.focus();
});
// Berücksichtigt auch umgebrochene Texte und vergrößerte Schrift.
const headerObserver = new ResizeObserver(() => {
document.documentElement.style.setProperty(
'--navigation-offset', `${navigation.getBoundingClientRect().height}px`
);
});
headerObserver.observe(navigation);
setMenuOpen(false);
})();

View File

@ -1,95 +0,0 @@
// US-01-02 Leistungsübersicht
// Markiert das gewählte Paket visuell und stellt die Auswahl bereit,
// damit US-01-03 (Pakete online buchen) später darauf aufbauen kann.
document.addEventListener("DOMContentLoaded", async () => {
// JSON-Datei laden
const response = await fetch("data/pakete.json");
const daten = await response.json();
// Elemente aus dem HTML holen
const titel = document.querySelector(".pakete__titel");
const text = document.querySelector(".pakete__text");
const grid = document.querySelector(".pakete__grid");
// Intro mit Daten aus JSON füllen
titel.textContent = daten.intro.titel;
text.textContent = daten.intro.text;
// Vorhandenen Inhalt entfernen
grid.innerHTML = "";
// Für jedes Paket eine Karte erstellen
daten.pakete.forEach(paket => {
// Artikel erstellen
const karte = document.createElement("article");
karte.classList.add("paket-karte");
// Empfohlenes Paket bekommt zusätzliche Klasse
if (paket.empfohlen) {
karte.classList.add("paket-karte--empfohlen");
}
// Badge nur erstellen, wenn eines vorhanden ist
if (paket.badge) {
const badge = document.createElement("p");
badge.classList.add("paket-karte__badge");
badge.textContent = paket.badge;
karte.appendChild(badge);
}
// Name
const name = document.createElement("h3");
name.classList.add("paket-karte__name");
name.textContent = paket.name;
karte.appendChild(name);
// Preis
const preis = document.createElement("p");
preis.classList.add("paket-karte__preis");
const betrag = document.createElement("span");
betrag.classList.add("paket-karte__betrag");
betrag.textContent = `${paket.preis} €`;
const zeitraum = document.createElement("span");
zeitraum.classList.add("paket-karte__zeitraum");
zeitraum.textContent = `/ ${paket.zeitraum}`;
preis.appendChild(betrag);
preis.appendChild(zeitraum);
karte.appendChild(preis);
// Beschreibung
const beschreibung = document.createElement("p");
beschreibung.classList.add("paket-karte__beschreibung");
beschreibung.textContent = paket.beschreibung;
karte.appendChild(beschreibung);
// Leistungen
const liste = document.createElement("ul");
liste.classList.add("paket-karte__leistungen");
paket.leistungen.forEach(leistung => {
const punkt = document.createElement("li");
punkt.textContent = leistung;
liste.appendChild(punkt);
});
karte.appendChild(liste);
// Button
const button = document.createElement("button");
button.classList.add("paket-karte__cta");
button.type = "button";
button.textContent = "Paket wählen";
button.dataset.paket = paket.dataPaket;
karte.appendChild(button);
// Karte ins Grid einfügen
grid.appendChild(karte);
});
});

View File

@ -0,0 +1,382 @@
// Online-Terminbuchung – Buchungsseite (html/terminbuchung.html).
// Liest den gewählten Termin aus der URL (berater, datum, zeit), füllt die
// Zusammenfassung, verwaltet die Anamnese-Tags und prüft das Formular.
// Zum Absenden wird wie auf der Kontaktseite das E-Mail-Programm geöffnet.
// ----- Scroll-Reveal -----
(function () {
const elements = document.querySelectorAll(".terminbuchung-seite .reveal");
if (!elements.length) return;
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduceMotion || !("IntersectionObserver" in window)) {
elements.forEach(function (el) {
el.classList.add("visible");
});
return;
}
const observer = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
elements.forEach(function (el) {
observer.observe(el);
});
})();
// ----- Termin-Zusammenfassung aus der URL -----
(function () {
const params = new URLSearchParams(window.location.search);
const beraterId = params.get("berater");
const datum = params.get("datum");
const zeit = params.get("zeit");
const elBerater = document.getElementById("tb-berater");
const elDatum = document.getElementById("tb-datum");
const elZeit = document.getElementById("tb-zeit");
const zusammenfassung = document.getElementById("termin-zusammenfassung");
if (!zusammenfassung) return;
// Ohne Termin-Parameter (z. B. direkter Seitenaufruf): Hinweis statt Zusammenfassung.
if (!beraterId || !datum || !zeit) {
zusammenfassung.classList.add("tb-summary--fehlend");
zusammenfassung.innerHTML = `
<div class="tb-summary__item">
<span class="tb-summary__label">Kein Termin ausgewählt</span>
<p class="tb-summary__value" style="font-size: 0.95rem; font-weight: 400;">
Du hast noch keinen Termin im Kalender gewählt.
Gehe bitte zurück zur <a href="../index.html#berater">Startseite</a> und öffne dort über einen
Berater die Terminbuchung.
</p>
</div>
`;
return;
}
// Datum lesbar formatieren, z. B. "Montag, 05.10.2026"
const [jahr, monat, tag] = datum.split("-").map(Number);
if (!jahr || !monat || !tag) return;
const datumObjekt = new Date(jahr, monat - 1, tag);
const formatiert = new Intl.DateTimeFormat("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(datumObjekt);
elDatum.textContent = formatiert;
elZeit.textContent = zeit.replace(":", ".") + " Uhr";
// Berater-Namen aus json/berater.json auflösen (ID stammt aus dem Kalender).
fetch("../json/berater.json")
.then(function (response) {
if (!response.ok) throw new Error("HTTP " + response.status);
return response.json();
})
.then(function (daten) {
const treffer = (daten.berater || []).find(function (b) {
return String(b.id) === String(beraterId);
});
elBerater.textContent = treffer ? String(treffer.name).trim() : "Unbekannter Berater";
})
.catch(function () {
elBerater.textContent = "Unbekannter Berater";
});
})();
// ----- Anamnese-Tags (Allergien & Unverträglichkeiten) -----
(function () {
const input = document.getElementById("tag-input");
const addButton = document.getElementById("tag-add");
const liste = document.getElementById("tag-list");
if (!input || !addButton || !liste) return;
const tags = [];
const MAX_TAGS = 20;
function tagHinzufuegen() {
let wert = input.value.trim().replace(/^#+/, "").trim();
if (!wert) return;
if (tags.length >= MAX_TAGS) {
input.value = "";
return;
}
// Doppelte Einträge (groß/kleinschreibungsgleich) werden nicht aufgenommen.
const existiert = tags.some(function (t) {
return t.toLowerCase() === wert.toLowerCase();
});
if (!existiert) {
tags.push(wert);
}
input.value = "";
zeichnen();
}
function tagEntfernen(index) {
tags.splice(index, 1);
zeichnen();
}
function zeichnen() {
liste.innerHTML = "";
tags.forEach(function (wert, index) {
const li = document.createElement("li");
li.className = "tb-tag";
const text = document.createElement("span");
text.textContent = wert;
const entfernen = document.createElement("button");
entfernen.type = "button";
entfernen.setAttribute("aria-label", "Tag entfernen: " + wert);
entfernen.innerHTML = "&times;";
entfernen.addEventListener("click", function () {
tagEntfernen(index);
});
li.appendChild(text);
li.appendChild(entfernen);
liste.appendChild(li);
});
}
addButton.addEventListener("click", tagHinzufuegen);
input.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
event.preventDefault();
tagHinzufuegen();
}
});
// Für das E-Mail-Body: aktuelle Tag-Liste als Text.
window.TerminbuchungTags = {
holen: function () {
return tags.slice();
},
leeren: function () {
tags.length = 0;
zeichnen();
}
};
})();
// ----- Formularprüfung und Absenden -----
(function () {
const form = document.getElementById("terminbuchung-formular");
if (!form) return;
const EMAIL_TARGET = "info@trihub.de";
const bestaetigungDialog = document.getElementById("anfrage-bestaetigung");
const fields = {
art: form.querySelector("[name='art']"),
format: form.querySelector("[name='format']"),
gespraech: form.querySelector("[name='gespraech']"),
beschreibung: form.querySelector("[name='beschreibung']"),
name: form.querySelector("[name='name']"),
email: form.querySelector("[name='email']"),
phone: form.querySelector("[name='phone']")
};
const statusEl = document.getElementById("form-status");
const formularBereich = document.getElementById("formular");
const pruefBereich = document.getElementById("anfrage-pruefen");
const pruefDetails = document.getElementById("anfrage-pruefen-details");
const bearbeitenButton = document.getElementById("anfrage-bearbeiten");
const sendenLink = document.getElementById("anfrage-senden");
function wrap(el) {
return el.closest(".field");
}
function setFieldState(el, state) {
const box = wrap(el);
if (!box) return;
box.classList.remove("has-error", "is-ok");
el.classList.remove("invalid", "valid");
if (state === "error") {
box.classList.add("has-error");
el.classList.add("invalid");
} else if (state === "ok") {
box.classList.add("is-ok");
el.classList.add("valid");
}
}
function validateName() {
const ok = fields.name.value.trim().length > 0;
setFieldState(fields.name, ok ? null : "error");
return ok;
}
function validateEmail() {
const value = fields.email.value.trim();
const ok = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(value);
setFieldState(fields.email, ok ? null : "error");
return ok;
}
function validateSelect(key) {
const ok = fields[key].value !== "";
setFieldState(fields[key], ok ? null : "error");
return ok;
}
function allValid() {
return (
fields.art.value !== "" &&
fields.format.value !== "" &&
fields.gespraech.value !== "" &&
fields.name.value.trim().length > 0 &&
/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(fields.email.value.trim())
);
}
function updateSubmitState() {
const submit = form.querySelector("[type='submit']");
if (submit) {
submit.disabled = !allValid();
}
}
form.addEventListener("input", updateSubmitState);
form.addEventListener("change", updateSubmitState);
["name", "email"].forEach(function (key) {
fields[key].addEventListener("blur", function () {
if (fields[key].value.trim() !== "" || form.dataset.touched === "true") {
({ name: validateName, email: validateEmail })[key]();
}
});
});
["art", "format", "gespraech"].forEach(function (key) {
fields[key].addEventListener("change", function () {
validateSelect(key);
});
});
// Termin-Angaben aus der URL für das E-Mail übernehmen.
const params = new URLSearchParams(window.location.search);
const datumRoh = params.get("datum") || "";
const zeitRoh = params.get("zeit") || "";
let terminText = "–";
if (datumRoh && zeitRoh) {
const [jahr, monat, tag] = datumRoh.split("-").map(Number);
if (jahr && monat && tag) {
const formatiert = new Intl.DateTimeFormat("de-DE", {
weekday: "long",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(new Date(jahr, monat - 1, tag));
terminText = formatiert + " um " + zeitRoh.replace(":", ".") + " Uhr";
}
}
const elBerater = document.getElementById("tb-berater");
function labelVon(key) {
const select = fields[key];
return select.options[select.selectedIndex].text;
}
function werteAnzeigen(werte) {
pruefDetails.replaceChildren();
werte.forEach(function (eintrag) {
const label = document.createElement("dt");
label.textContent = eintrag[0];
const wert = document.createElement("dd");
wert.textContent = eintrag[1] || "–";
pruefDetails.append(label, wert);
});
}
bearbeitenButton.addEventListener("click", function () {
pruefBereich.hidden = true;
formularBereich.hidden = false;
formularBereich.scrollIntoView({ behavior: "smooth", block: "start" });
});
sendenLink.addEventListener("click", function (event) {
event.preventDefault();
bestaetigungDialog.showModal();
window.location.href = sendenLink.href;
});
form.addEventListener("submit", function (event) {
event.preventDefault();
form.dataset.touched = "true";
const okArt = validateSelect("art");
const okFormat = validateSelect("format");
const okGespraech = validateSelect("gespraech");
const okName = validateName();
const okEmail = validateEmail();
if (!(okArt && okFormat && okGespraech && okName && okEmail)) {
showStatus("error", "Bitte überprüfe die markierten Felder und versuche es erneut.");
return;
}
const tagListe = window.TerminbuchungTags ? window.TerminbuchungTags.holen() : [];
const beraterText = elBerater ? elBerater.textContent.trim() : "–";
const anfrageDaten = [
["Berater/in", beraterText],
["Termin", terminText],
["Art der Ernährungsberatung", labelVon("art")],
["Beratungsformat", labelVon("format")],
["Gesprächsart", labelVon("gespraech")],
["Kurzbeschreibung", fields.beschreibung.value.trim() || "–"],
["Anamnese (Allergien & Unverträglichkeiten)", tagListe.length ? tagListe.join(", ") : "–"],
["Name", fields.name.value.trim()],
["E-Mail", fields.email.value.trim()],
["Telefon", fields.phone.value.trim() || "–"]
];
const body = [
"Terminanfrage – Trainingsplan & Beratung",
"",
"Termin: " + terminText,
"Berater/in: " + beraterText,
"Art der Ernährungsberatung: " + labelVon("art"),
"Beratungsformat: " + labelVon("format"),
"Gesprächsart: " + labelVon("gespraech"),
"",
"Kurzbeschreibung: " + (fields.beschreibung.value.trim() || "–"),
"Anamnese (Allergien & Unverträglichkeiten): " + (tagListe.length ? tagListe.join(", ") : "–"),
"",
"Name: " + fields.name.value.trim(),
"E-Mail: " + fields.email.value.trim(),
"Telefon: " + (fields.phone.value.trim() || "–")
].join("\n");
werteAnzeigen(anfrageDaten);
sendenLink.href =
"mailto:" + EMAIL_TARGET +
"?subject=" + encodeURIComponent("Terminanfrage: " + terminText) +
"&body=" + encodeURIComponent(body);
formularBereich.hidden = true;
pruefBereich.hidden = false;
pruefBereich.scrollIntoView({ behavior: "smooth", block: "start" });
});
function showStatus(kind, text) {
if (!statusEl) return;
statusEl.textContent = kind === "ok" ? "✓ " + text : "✗ " + text;
statusEl.className =
"form-status is-visible " + (kind === "ok" ? "form-status--ok" : "form-status--error");
}
updateSubmitState();
})();

400
js/terminbuchung.js 100644
View File

@ -0,0 +1,400 @@
// Online-Terminbuchung – Kalender-Pop-up auf der Startseite (index.html).
// Daten: json/berater.json (Trainer mit ID) und json/terminbuchung.json
// (Verfügbarkeit pro Trainer-ID: Datum JJJJ-MM-TT → Array von Uhrzeiten HH:MM).
(function () {
"use strict";
const BERATER_URL = "json/berater.json";
const TERMINE_URL = "json/terminbuchung.json";
const ZIELSEITE = "html/terminbuchung.html";
const MONATE = [
"Januar", "Februar", "März", "April", "Mai", "Juni",
"Juli", "August", "September", "Oktober", "November", "Dezember"
];
const WOCHE = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
let beraterDaten = []; // [{id, name, ...}]
let terminDaten = {}; // { "1": { "2026-10-05": ["09:00", ...] }, ... }
let geladen = null; // Promise, die auf das Laden beider JSON-Dateien wartet
let overlay = null; // DOM: Pop-up (wird einmalig erzeugt)
let istGeoeffnet = false;
let fokusVorher = null; // Element, das vor dem Öffnen Fokus hatte
const zustand = {
beraterId: null,
jahr: 0,
monat: 0, // 0-basiert
datum: null, // "JJJJ-MM-TT"
zeit: null // "HH:MM"
};
// ----- Daten laden -----
function ladeDaten() {
if (geladen) return geladen;
geladen = Promise.all([
fetch(BERATER_URL).then((r) => (r.ok ? r.json() : Promise.reject(new Error("berater.json: HTTP " + r.status)))),
fetch(TERMINE_URL).then((r) => (r.ok ? r.json() : Promise.reject(new Error("terminbuchung.json: HTTP " + r.status))))
])
.then(([berater, termine]) => {
beraterDaten = Array.isArray(berater.berater) ? berater.berater : [];
terminDaten = termine && typeof termine === "object" ? termine : {};
})
.catch((error) => {
console.error("Terminbuchung: Daten konnten nicht geladen werden.", error);
});
return geladen;
}
// ----- Hilfsfunktionen -----
function pad(n) {
return String(n).padStart(2, "0");
}
function datumSchluessel(jahr, monat, tag) {
return `${jahr}-${pad(monat + 1)}-${pad(tag)}`;
}
function heutigerTag() {
const jetzt = new Date();
return { jahr: jetzt.getFullYear(), monat: jetzt.getMonth(), tag: jetzt.getDate() };
}
function istVergangen(jahr, monat, tag) {
const heute = heutigerTag();
return (
jahr < heute.jahr ||
(jahr === heute.jahr && monat < heute.monat) ||
(jahr === heute.jahr && monat === heute.monat && tag < heute.tag)
);
}
function zeitenFuer(beraterId, schluessel) {
const tabelle = terminDaten[String(beraterId)];
if (!tabelle || typeof tabelle !== "object") return [];
const zeiten = tabelle[schluessel];
return Array.isArray(zeiten) ? zeiten : [];
}
function hatFreieTermine(beraterId) {
const tabelle = terminDaten[String(beraterId)];
if (!tabelle || typeof tabelle !== "object") return false;
return Object.entries(tabelle).some(([schluessel, zeiten]) => {
const datum = /^(\d{4})-(\d{2})-(\d{2})$/.exec(schluessel);
if (!datum || !Array.isArray(zeiten) || zeiten.length === 0) return false;
return !istVergangen(Number(datum[1]), Number(datum[2]) - 1, Number(datum[3]));
});
}
function istVerfuegbar(jahr, monat, tag) {
if (istVergangen(jahr, monat, tag)) return false;
return zeitenFuer(zustand.beraterId, datumSchluessel(jahr, monat, tag)).length > 0;
}
function deutscherTagText(schluessel) {
const [jahr, monat, tag] = schluessel.split("-").map(Number);
const datum = new Date(jahr, monat - 1, tag);
return (
WOCHE[(datum.getDay() + 6) % 7] + ", " +
pad(tag) + "." + pad(monat) + "." + jahr
);
}
function beraterName(beraterId) {
const treffer = beraterDaten.find((b) => Number(b.id) === Number(beraterId));
return treffer ? String(treffer.name).trim() : "Berater/in";
}
// ----- Pop-up aufbauen (einmalig) -----
function bauPopUp() {
if (overlay) return overlay;
const div = document.createElement("div");
div.innerHTML = `
<div class="tb-overlay" hidden>
<div class="tb-dialog" role="dialog" aria-modal="true" aria-labelledby="tb-title">
<button type="button" class="tb-close" aria-label="Terminbuchung schließen">&times;</button>
<span class="tb-kicker">Online-Terminbuchung</span>
<h2 class="tb-title" id="tb-title">Trainingsplan &amp; Beratung – Termin wählen</h2>
<p class="tb-subtitle">Wähle deinen Berater, einen verfügbaren Tag und eine Uhrzeit.</p>
<span class="tb-trainer-label">Berater/in wählen:</span>
<div class="tb-trainer"></div>
<div class="tb-cal-head">
<button type="button" class="tb-cal-nav" data-richtung="-1" aria-label="Vorheriger Monat">&#8249;</button>
<span class="tb-cal-month" aria-live="polite"></span>
<button type="button" class="tb-cal-nav" data-richtung="1" aria-label="Nächster Monat">&#8250;</button>
</div>
<div class="tb-cal-weekdays" aria-hidden="true">
${WOCHE.map((w) => `<span>${w}</span>`).join("")}
</div>
<div class="tb-cal-grid"></div>
<ul class="tb-legend" aria-live="polite">
<li><span class="tb-dot" aria-hidden="true"></span><span class="tb-legend-label"></span></li>
</ul>
<div class="tb-times" hidden>
<p class="tb-times-label"></p>
<div class="tb-time-chips"></div>
</div>
<button type="button" class="tb-book" disabled>Weiter zur Buchung</button>
<p class="tb-hinweis">Nach der Auswahl wirst du zur Buchungsseite weitergeleitet, wo du die Details zu Beratung und Kontakt angibst.</p>
</div>
</div>
`;
overlay = div.firstElementChild;
document.body.appendChild(overlay);
// Trainer-Buttons füllen
const trainerBox = overlay.querySelector(".tb-trainer");
beraterDaten.forEach((berater) => {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "tb-trainer-btn";
btn.textContent = String(berater.name).trim();
btn.dataset.beraterId = berater.id;
btn.addEventListener("click", () => trainerWaehlen(Number(berater.id)));
trainerBox.appendChild(btn);
});
// Monatsnavigation
overlay.querySelectorAll(".tb-cal-nav").forEach((btn) => {
btn.addEventListener("click", () => {
verschiebeMonat(Number(btn.dataset.richtung));
});
});
// Schließen: Knopf, Klick auf den Schleier, Escape
overlay.querySelector(".tb-close").addEventListener("click", schliessen);
overlay.addEventListener("click", (event) => {
if (event.target === overlay) schliessen();
});
document.addEventListener("keydown", (event) => {
if (istGeoeffnet && event.key === "Escape") schliessen();
});
// Buchen
overlay.querySelector(".tb-book").addEventListener("click", buchen);
return overlay;
}
// ----- Öffnen / Schließen -----
async function oeffnen(beraterId) {
await ladeDaten();
// Ohne Berater-Daten gibt es nichts auszuwählen – Pop-up bleibt geschlossen.
if (!beraterDaten.length) {
console.error("Terminbuchung: Keine Berater geladen – Pop-up wird nicht geöffnet.");
return;
}
bauPopUp();
const heute = heutigerTag();
zustand.jahr = heute.jahr;
zustand.monat = heute.monat;
trainerWaehlen(beraterId != null ? Number(beraterId) : beraterDaten[0].id);
overlay.hidden = false;
istGeoeffnet = true;
fokusVorher = document.activeElement;
document.body.style.overflow = "hidden";
overlay.querySelector(".tb-close").focus();
}
function schliessen() {
if (!istGeoeffnet) return;
overlay.hidden = true;
istGeoeffnet = false;
document.body.style.overflow = "";
if (fokusVorher && typeof fokusVorher.focus === "function") {
fokusVorher.focus();
}
}
// ----- Auswahl-Logik -----
function trainerWaehlen(beraterId) {
zustand.beraterId = beraterId;
zustand.datum = null;
zustand.zeit = null;
overlay.querySelectorAll(".tb-trainer-btn").forEach((btn) => {
const aktiv = Number(btn.dataset.beraterId) === Number(beraterId);
btn.classList.toggle("is-active", aktiv);
btn.setAttribute("aria-pressed", aktiv ? "true" : "false");
});
legendeAktualisieren();
zeitenBereichZuruecksetzen();
kalenderZeichnen();
buchButtonAktualisieren();
}
function legendeAktualisieren() {
const dot = overlay.querySelector(".tb-legend .tb-dot");
const label = overlay.querySelector(".tb-legend-label");
const verfuegbar = hatFreieTermine(zustand.beraterId);
dot.className = "tb-dot " + (verfuegbar ? "tb-dot--verfuegbar" : "tb-dot--nicht");
label.textContent = verfuegbar ? "Verfügbar" : "Nicht verfügbar";
}
function verschiebeMonat(richtung) {
const heute = heutigerTag();
let ziel = zustand.jahr * 12 + zustand.monat + richtung;
if (ziel < heute.jahr * 12 + heute.monat) return; // nicht vor dem aktuellen Monat
zustand.jahr = Math.floor(ziel / 12);
zustand.monat = ziel % 12;
zustand.datum = null;
zustand.zeit = null;
zeitenBereichZuruecksetzen();
kalenderZeichnen();
buchButtonAktualisieren();
}
function tagKlicken(jahr, monat, tag) {
if (!istVerfuegbar(jahr, monat, tag)) return;
zustand.datum = datumSchluessel(jahr, monat, tag);
zustand.zeit = null;
kalenderZeichnen();
zeitenZeichnen();
buchButtonAktualisieren();
}
function zeitKlicken(zeit) {
zustand.zeit = zeit;
overlay.querySelectorAll(".tb-time-chip").forEach((chip) => {
chip.classList.toggle("is-active", chip.dataset.zeit === zeit);
});
buchButtonAktualisieren();
}
function buchen() {
if (!zustand.datum || !zustand.zeit || zustand.beraterId == null) return;
const ziel =
ZIELSEITE +
"?berater=" + encodeURIComponent(zustand.beraterId) +
"&datum=" + encodeURIComponent(zustand.datum) +
"&zeit=" + encodeURIComponent(zustand.zeit);
window.location.href = ziel;
}
// ----- Zeichnen -----
function kalenderZeichnen() {
const grid = overlay.querySelector(".tb-cal-grid");
const monatsLabel = overlay.querySelector(".tb-cal-month");
const navVorher = overlay.querySelector('.tb-cal-nav[data-richtung="-1"]');
monatsLabel.textContent = `${MONATE[zustand.monat]} ${zustand.jahr}`;
// Vor dem aktuellen Monat kann nicht zurückgeblättert werden.
const heute = heutigerTag();
navVorher.disabled = zustand.jahr * 12 + zustand.monat <= heute.jahr * 12 + heute.monat;
const ersterTag = new Date(zustand.jahr, zustand.monat, 1);
const versatz = (ersterTag.getDay() + 6) % 7; // Montag als erste Spalte
const tageImMonat = new Date(zustand.jahr, zustand.monat + 1, 0).getDate();
const heuteInfo = heutigerTag();
grid.innerHTML = "";
for (let i = 0; i < versatz; i++) {
const leer = document.createElement("span");
leer.className = "tb-day tb-day--leer";
grid.appendChild(leer);
}
for (let tag = 1; tag <= tageImMonat; tag++) {
const verfuegbar = istVerfuegbar(zustand.jahr, zustand.monat, tag);
const schluessel = datumSchluessel(zustand.jahr, zustand.monat, tag);
const vergangen = istVergangen(zustand.jahr, zustand.monat, tag);
const btn = document.createElement("button");
btn.type = "button";
let klassen = "tb-day ";
if (verfuegbar) {
klassen += "tb-day--verfuegbar";
} else {
klassen += "tb-day--neutral";
}
if (
zustand.jahr === heuteInfo.jahr &&
zustand.monat === heuteInfo.monat &&
tag === heuteInfo.tag
) {
klassen += " tb-day--heute";
}
if (zustand.datum === schluessel) {
klassen += " tb-day--ausgewaehlt";
}
btn.className = klassen;
btn.textContent = tag;
btn.disabled = !verfuegbar;
const status = verfuegbar ? "verfügbar" : vergangen ? "vergangen" : "keine Termine eingetragen";
btn.setAttribute("aria-label", `${deutscherTagText(schluessel)} – ${status}`);
if (verfuegbar) {
btn.addEventListener("click", () => tagKlicken(zustand.jahr, zustand.monat, tag));
}
grid.appendChild(btn);
}
}
function zeitenBereichZuruecksetzen() {
const bereich = overlay.querySelector(".tb-times");
bereich.hidden = true;
overlay.querySelector(".tb-time-chips").innerHTML = "";
overlay.querySelector(".tb-times-label").textContent = "";
}
function zeitenZeichnen() {
const bereich = overlay.querySelector(".tb-times");
const label = overlay.querySelector(".tb-times-label");
const chips = overlay.querySelector(".tb-time-chips");
const zeiten = zeitenFuer(zustand.beraterId, zustand.datum);
if (!zeiten.length) {
bereich.hidden = true;
return;
}
label.innerHTML = `Wähle eine Uhrzeit für <strong>${deutscherTagText(zustand.datum)}</strong> bei ${beraterName(zustand.beraterId)}:`;
chips.innerHTML = "";
zeiten.forEach((zeit) => {
const chip = document.createElement("button");
chip.type = "button";
chip.className = "tb-time-chip" + (zustand.zeit === zeit ? " is-active" : "");
chip.dataset.zeit = zeit;
chip.textContent = zeit.replace(":", ".") + " Uhr";
chip.addEventListener("click", () => zeitKlicken(zeit));
chips.appendChild(chip);
});
bereich.hidden = false;
}
function buchButtonAktualisieren() {
const btn = overlay.querySelector(".tb-book");
btn.disabled = !(zustand.datum && zustand.zeit && zustand.beraterId != null);
}
// ----- Öffentliche Schnittstelle (wird von berater.js genutzt) -----
window.Terminbuchung = {
oeffnen,
schliessen,
istGeoeffnet: () => istGeoeffnet
};
})();

54
json/berater.json 100644
View File

@ -0,0 +1,54 @@
{
"intro": {
"titel": "Deine Ernährungsberater und Coaches",
"text": "Erfahrene Triathleten und zertifizierte Coaches, die dich auf dem Weg zu deinen Zielen begleiten und dir helfen, deine Leistung zu optimieren."
},
"berater": [
{
"id": 1,
"name": "Relindis Agethen ",
"role": "TRIATHLET UND ERNÄHRUNGSBERATERIN",
"image": "images/relindis_agethen.png",
"bioTitle": "Kurzbiografie",
"bioText": "Relindis Agethen, genannt Lilli, ist seit 16 Jahren im Triathlon aktiv und bringt starke sportliche Erfolge mit: Vize-Europameisterin, Deutsche Meisterin und mehrfache Baden-Württembergische Meisterin. Als zertifizierte Ernährungsberaterin für Sportler B-Lizenz ergänzt sie das Team um Leistungsentwicklung und Ernährungswissen.",
"zertifikate": [
"Zertifizierter Triathlon-Trainer",
"Ernährungscoach B-Lizenz"
],
"links": [
{
"text": "Instagram Profil",
"url": "https://instagram.com"
},
{
"text": "Trainingsplan buchen",
"url": "https://tri-hub.de/booking",
"aktion": "terminbuchung"
}
]
},
{
"id": 2,
"name": "Relindis Agethen ",
"role": "TRIATHLET UND ERNÄHRUNGSBERATERIN",
"image": "images/relindis_agethen_1.jpg",
"bioTitle": "Kurzbiografie",
"bioText": "Relindis Agethen, genannt Lilli, ist seit 16 Jahren im Triathlon aktiv und bringt starke sportliche Erfolge mit: Vize-Europameisterin, Deutsche Meisterin und mehrfache Baden-Württembergische Meisterin. Als zertifizierte Ernährungsberaterin für Sportler B-Lizenz ergänzt sie das Team um Leistungsentwicklung und Ernährungswissen.",
"zertifikate": [
"Zertifizierter Triathlon-Trainer",
"Ernährungscoach B-Lizenz"
],
"links": [
{
"text": "Instagram Profil",
"url": "https://instagram.com"
},
{
"text": "Trainingsplan buchen",
"url": "https://tri-hub.de/booking",
"aktion": "terminbuchung"
}
]
}
]
}

View File

@ -5,7 +5,7 @@
},
"pakete": [
{
"name": "Starter",
"name": "STARTER",
"preis": 79,
"zeitraum": "Monat",
"beschreibung": "Für den Einstieg in eine bewusste Ernährung neben dem Training.",
@ -17,10 +17,10 @@
],
"badge": null,
"empfohlen": false,
"dataPaket": "Starter"
"dataPaket": "starter"
},
{
"name": "STANDART",
"name": "STANDARD",
"preis": 149,
"zeitraum": "Monat",
"beschreibung": "Laufende Begleitung für alle, die regelmäßig für Wettkämpfe trainieren.",
@ -33,15 +33,15 @@
],
"badge": "Beliebteste Wahl",
"empfohlen": true,
"dataPaket": "Ausdauer"
"dataPaket": "standard"
},
{
"name": "Rundum Sorglos Paket",
"name": "PREMIUM",
"preis": 249,
"zeitraum": "Monat",
"beschreibung": "Volle Betreuung für ambitionierte Athletinnen und Athleten.",
"leistungen": [
"Alles aus Ausdauer",
"Alles aus STANDARD",
"Wöchentliche Beratungsgespräche",
"Individuelle Supplement-Empfehlung",
"Betreuung am Wettkampftag",
@ -49,7 +49,7 @@
],
"badge": null,
"empfohlen": false,
"dataPaket": "Wettkampf"
"dataPaket": "premium"
}
]
}

View File

@ -0,0 +1,93 @@
{
"_hinweis": "Verfügbare Termine pro Trainer. Schlüssel = Berater-ID aus json/berater.json (z. B. \"1\"). Wert = Objekt: Datum im Format JJJJ-MM-TT als Schlüssel, Array mit Uhrzeiten (HH:MM) als Wert. Tage ohne Eintrag werden im Kalender als 'Nicht verfügbar' angezeigt. Schüssel, die mit '_' beginnen, werden von terminbuchung.js ignoriert.",
"1": {
"2026-10-05": [
"09:00",
"11:00",
"14:30"
],
"2026-10-07": [
"10:00",
"15:00"
],
"2026-10-08": [
"09:30",
"11:30",
"16:00"
],
"2026-10-12": [
"10:00",
"14:00"
],
"2026-10-14": [
"09:00",
"10:30",
"15:30"
],
"2026-10-15": [
"11:00",
"16:00"
],
"2026-10-19": [
"09:30",
"14:30"
],
"2026-10-21": [
"10:00",
"11:30"
],
"2026-10-22": [
"15:00",
"16:30"
],
"2026-10-26": [
"09:00",
"10:30",
"14:00"
],
"2026-10-28": [
"11:00",
"15:00"
],
"2026-10-29": [
"09:30",
"16:00"
]
},
"2": {
"2026-10-06": [
"10:00",
"14:00",
"16:00"
],
"2026-10-09": [
"09:00",
"11:00"
],
"2026-10-13": [
"10:30",
"15:00"
],
"2026-10-16": [
"09:30",
"14:30"
],
"2026-10-20": [
"10:00",
"11:30",
"16:00"
],
"2026-10-23": [
"09:00",
"15:30"
],
"2026-10-27": [
"10:30",
"14:00"
],
"2026-10-30": [
"11:00",
"16:00"
]
}
}