diff --git a/app/components/age-groups.php b/app/components/age-groups.php new file mode 100644 index 0000000..4c1f033 --- /dev/null +++ b/app/components/age-groups.php @@ -0,0 +1,47 @@ + +
+
+

+ +
+
diff --git a/app/components/cta-band.php b/app/components/cta-band.php new file mode 100644 index 0000000..d374936 --- /dev/null +++ b/app/components/cta-band.php @@ -0,0 +1,30 @@ + +
> +
+ +

+ + +

+ +
+ + $cta]); ?> + +
+
+
diff --git a/app/components/faq-contact.php b/app/components/faq-contact.php new file mode 100644 index 0000000..2d3a286 --- /dev/null +++ b/app/components/faq-contact.php @@ -0,0 +1,76 @@ +/ (ohne JS bedienbar); FAQPage-Schema liefert die Seite. + */ +$faqTitle = $faqTitle ?? 'Häufige Fragen'; +$faqIntro = $faqIntro ?? ''; +$contactTitle = $contactTitle ?? 'Dein Ansprechpartner'; +$anchor = $anchor ?? 'kontakt'; +$faq = array_values(array_filter( + $faq ?? [], + static fn (array $i): bool => !empty($i['q']) && !empty($i['a']) +)); +$persons = array_values(array_filter($persons ?? [], static fn ($p): bool => !empty($p))); +$hasPerson = $persons !== []; +$isSlider = count($persons) > 1; +if ($faq === [] && !$hasPerson) { + return; +} +?> +
+
+

+ +

+ +
+
+
+ +
+ +

+
+ +
+
+ + + +
+
+
diff --git a/app/components/faq.php b/app/components/faq.php new file mode 100644 index 0000000..64d7af5 --- /dev/null +++ b/app/components/faq.php @@ -0,0 +1,35 @@ +/ → ohne JS bedienbar. Das FAQPage-Schema liefert + * die Seite über $meta['schema'] (helpers: faq_schema/team_schema), nicht diese Komponente. + */ +$title = $title ?? 'Häufige Fragen'; +$anchor = $anchor ?? 'faq'; +$faq = array_values(array_filter( + $faq ?? [], + static fn (array $i): bool => !empty($i['q']) && !empty($i['a']) +)); +if ($faq === []) { + return; +} +?> +
+
+

+
+ +
+ +

+
+ +
+
+
diff --git a/app/components/key-facts.php b/app/components/key-facts.php new file mode 100644 index 0000000..13de6fd --- /dev/null +++ b/app/components/key-facts.php @@ -0,0 +1,41 @@ + isset($f['value']) && $f['value'] !== '' +)); +if ($facts === []) { + return; +} +?> +
> +
+
    + +
  • + + + + + + + + + +
  • + +
+
+
diff --git a/app/components/partner-grid.php b/app/components/partner-grid.php index c42f674..169eba7 100644 --- a/app/components/partner-grid.php +++ b/app/components/partner-grid.php @@ -12,7 +12,7 @@ $partners = json_load('partners')['partners'] ?? [];

-

+

  • diff --git a/app/components/person-grid.php b/app/components/person-grid.php index e77743f..f3e7582 100644 --- a/app/components/person-grid.php +++ b/app/components/person-grid.php @@ -6,6 +6,7 @@ declare(strict_types=1); * Ansprechpartner-Grid aus data/ansprechpartner.json (Single Source of Truth). * Alle Props optional: * $departments array — nur diese Abteilungen zeigen (z. B. ['Herrenfußball','Damenfußball']). + * $names array — zusätzlich auf diese Namen einschränken (z. B. ['Benedikt Höfner']). * $title string — Überschrift überschreiben (Default aus JSON). * $intro string — Intro-Text überschreiben (Default aus JSON; '' = ausblenden). * $anchor string — Sektions-ID (Default 'ansprechpartner'). @@ -24,6 +25,12 @@ if (!empty($departments)) { static fn (array $p): bool => in_array($p['department'] ?? '', $departments, true) )); } +if (!empty($names)) { + $persons = array_values(array_filter( + $persons, + static fn (array $p): bool => in_array($p['name'] ?? '', $names, true) + )); +} if ($persons === []) { return; diff --git a/app/components/team-bento.php b/app/components/team-bento.php new file mode 100644 index 0000000..dc76475 --- /dev/null +++ b/app/components/team-bento.php @@ -0,0 +1,82 @@ + 'Spielklasse', 'value' => $team['league']] + : ['label' => 'Bereich', 'value' => $team['category'] ?? '']; + +$club = json_load('club'); +$addr = $club['address'] ?? []; +$mapsQuery = !empty($venueOverride['maps_query']) + ? rawurlencode($venueOverride['maps_query']) + : rawurlencode(trim(($addr['venue'] ?? '') . ', ' . ($addr['street'] ?? '') . ', ' . ($addr['zip'] ?? '') . ' ' . ($addr['city'] ?? ''), ', ')); +?> +
    +
    +
    +
    +

    +

    +
    + +
    +

    Trainingszeiten

    + +
      + +
    • + + +
    • + +
    + +

    Die genauen Trainingszeiten erfährst du direkt bei unserem Ansprechpartner.

    + + +

    + + +
    + + +
    + + +
    + + + + + +
    +
    +
    diff --git a/app/components/team-cards.php b/app/components/team-cards.php new file mode 100644 index 0000000..4daa272 --- /dev/null +++ b/app/components/team-cards.php @@ -0,0 +1,55 @@ + +
    + +
    diff --git a/app/components/team-photo.php b/app/components/team-photo.php new file mode 100644 index 0000000..25b8d08 --- /dev/null +++ b/app/components/team-photo.php @@ -0,0 +1,27 @@ + +
    +
    +
    + $image, 'sizes' => '(max-width: 1100px) 100vw, 1040px', 'class' => 'team-photo__img']); ?> + +
    + +
    +
    +
    diff --git a/app/components/team-roster.php b/app/components/team-roster.php new file mode 100644 index 0000000..fabb4ef --- /dev/null +++ b/app/components/team-roster.php @@ -0,0 +1,78 @@ + '', 'members' => $flat]]; +} +// Leere Gruppen rauswerfen. +$groups = array_values(array_filter($groups, static fn ($g): bool => !empty($g['members']))); +if ($groups === []) { + return; +} + +$variant = ($roster['variant'] ?? 'below') === 'overlay' ? 'overlay' : 'below'; +$title = $roster['title'] ?? 'Kader'; +$intro = $roster['intro'] ?? ''; +$cardMod = $variant === 'overlay' ? ' roster-card--overlay' : ''; +?> +
    +
    +

    + +

    + + + +

    + +
      + + + + +
    • + + <?= $alt ?> +
      + + + +

      +
      + +
      + <?= $alt ?> +
      +
      + + + +

      +
      + +
    • + +
    + +
    +
    diff --git a/app/components/team.php b/app/components/team.php new file mode 100644 index 0000000..699b404 --- /dev/null +++ b/app/components/team.php @@ -0,0 +1,46 @@ + $team['hero']]); + +if (!empty($team['team_photo']['image'])) { + component('team-photo', ['photo' => $team['team_photo'], 'anchor' => 'mannschaftsfoto']); +} + +component('team-bento', ['team' => $team, 'anchor' => 'team-info']); + +if (!empty($team['roster']['members']) || !empty($team['roster']['groups'])) { + component('team-roster', ['roster' => $team['roster'], 'anchor' => 'kader']); +} + +component('faq-contact', [ + 'faq' => $team['faq'] ?? [], + 'faqIntro' => 'Die wichtigsten Fragen rund um die ' . ($team['name'] ?? 'Mannschaft') . ' – kurz beantwortet.', + 'persons' => $contacts, + 'contactTitle' => '', + 'anchor' => 'kontakt', +]); diff --git a/app/components/training-location.php b/app/components/training-location.php new file mode 100644 index 0000000..6bf0323 --- /dev/null +++ b/app/components/training-location.php @@ -0,0 +1,73 @@ + +
    +
    +
    +
    +

    Trainingszeiten

    + +
      + +
    • + + +
    • + +
    + +

    Die genauen Trainingszeiten erfährst du direkt bei unserem Ansprechpartner.

    + + +

    + + +
    + + +
    +

    Spielort

    +

    +
    +
    + +

    + +
    + + + + + +
    + + +
    + +
    +
    +
    diff --git a/app/pages/datenschutz.php b/app/pages/datenschutz.php new file mode 100644 index 0000000..3e683df --- /dev/null +++ b/app/pages/datenschutz.php @@ -0,0 +1,261 @@ + 'Datenschutzerklärung', + 'description' => 'Datenschutzerklärung des TSV 1908 Kulmbach e. V. – welche Daten beim Besuch unserer Website verarbeitet werden. Komplett self-hosted, ohne Tracking und ohne Drittanbieter-Dienste im Browser.', + 'schema' => page_schema([ + ['name' => 'Startseite', 'slug' => ''], + ['name' => 'Datenschutz', 'slug' => 'datenschutz'], + ]), +]; +?> + diff --git a/app/pages/fussball.php b/app/pages/fussball.php new file mode 100644 index 0000000..0d7bad4 --- /dev/null +++ b/app/pages/fussball.php @@ -0,0 +1,53 @@ + '1. Mannschaft', 'slug' => 'fussball/erste-mannschaft', 'category' => 'Herren', 'league' => 'Kreisklasse', 'image' => $teams['erste-mannschaft']['team_photo']['image'] ?? $teams['erste-mannschaft']['hero']['image']], + ['name' => '2. Mannschaft', 'slug' => 'fussball/zweite-mannschaft', 'category' => 'Herren', 'image' => $teams['zweite-mannschaft']['hero']['image']], + ['name' => 'Damenmannschaft', 'slug' => 'fussball/damen', 'category' => 'Damen', 'image' => $teams['damen']['hero']['image']], + ['name' => 'Jugendfußball', 'slug' => 'jugendfussball', 'category' => 'G- bis C-Jugend', 'image' => [ + 'base' => 'img/pages/jugendfussball-hero', 'widths' => [640, 1000, 1440], 'alt' => 'Jugendfußball des TSV 08 Kulmbach', + ]], +]; + +$meta = [ + 'title' => 'Fußball in Kulmbach', + 'description' => 'Herren-, Damen- und Jugendfußball beim TSV 08 Kulmbach: erste und zweite Mannschaft auf Kreisklasse-Niveau, Damenmannschaft und Nachwuchs – aktiv werden im Katzbachtal.', + 'og_image' => 'img/pages/fussball-hero-1600.jpg', + 'schema' => page_schema( + [ + ['name' => 'Startseite', 'slug' => ''], + ['name' => 'Fußball', 'slug' => 'fussball'], + ], + $page['faq'] ?? [] + ), +]; + +component('hero', ['hero' => $page['hero']]); +component('team-cards', [ + 'teams' => $cards, + 'intro' => $page['mannschaften_intro'] ?? '', + 'title_hidden' => true, + 'intro_below' => true, +]); + +component('person-grid', [ + 'departments' => ['Herrenfußball', 'Damenfußball'], + // Auf dem Hub bewusst nur die beiden Haupt-Ansprechpartner (mannschaftsspezifische + // Zusatzkontakte wie Rustem erscheinen auf der jeweiligen Team-Seite). + 'names' => ['Benedikt Höfner', 'Sam Kern'], + 'title' => 'Ansprechpartner Fußball', + 'intro' => 'Fragen zu Training, Probetraining oder einem Wechsel? Sprich direkt unsere Verantwortlichen an.', + 'anchor' => 'kontakt', +]); + +component('faq', ['faq' => $page['faq'] ?? []]); diff --git a/app/pages/home.php b/app/pages/home.php index b9b55ca..f7b63f9 100644 --- a/app/pages/home.php +++ b/app/pages/home.php @@ -26,4 +26,5 @@ foreach ($home['sections'] as $section) { component('stats', ['stats' => $home['stats']]); component('partner-grid', ['intro' => $home['partners']]); +component('person-grid'); component('contact-form', ['intro' => $home['contact']]); diff --git a/app/pages/impressum.php b/app/pages/impressum.php new file mode 100644 index 0000000..9a53968 --- /dev/null +++ b/app/pages/impressum.php @@ -0,0 +1,139 @@ + 'Impressum', + 'description' => 'Impressum des TSV 1908 Kulmbach e. V. – Anbieterkennzeichnung, vertretungsberechtigter Vorstand und Kontaktdaten.', + 'schema' => page_schema([ + ['name' => 'Startseite', 'slug' => ''], + ['name' => 'Impressum', 'slug' => 'impressum'], + ]), +]; +?> + diff --git a/app/pages/jugendfussball.php b/app/pages/jugendfussball.php new file mode 100644 index 0000000..6367fc4 --- /dev/null +++ b/app/pages/jugendfussball.php @@ -0,0 +1,80 @@ + 'Jugendfußball in Kulmbach', + 'description' => 'Jugendfußball in Kulmbach – entdecke unsere Mannschaften von der G- bis zur C-Jugend, Betreuer und Trainingszeiten. Probiere Fußball in familiärer Atmosphäre aus.', + 'og_image' => 'img/pages/jugendfussball-hero-1440.jpg', + 'schema' => page_schema( + [ + ['name' => 'Startseite', 'slug' => ''], + ['name' => 'Fußball', 'slug' => 'fussball'], + ['name' => 'Jugendfußball', 'slug' => 'jugendfussball'], + ], + $page['faq'] ?? [] + ), +]; + +component('hero', ['hero' => $page['hero']]); +?> +
    +
    +
    +
    + $page['hero']['image'], 'sizes' => '(max-width: 900px) 100vw, 40vw', 'class' => 'intro-bento__img']); ?> +
    + +
    +

    +

    +
    + + + +
    + Altersklassen + G – C +
    +
    +
    +
    + $page['groups'] ?? []]); + +component('person-grid', [ + 'departments' => ['Jugendfußball'], + 'title' => 'Ansprechpartner Jugendfußball', + 'intro' => 'Fragen zum Schnuppertraining oder zur passenden Altersgruppe? Melde dich direkt.', + 'anchor' => 'kontakt', +]); + +component('cta-band', ['band' => [ + 'title' => 'Neugierig geworden?', + 'text' => 'Komm mit deinem Kind zum kostenlosen Schnuppertraining – ganz unverbindlich.', + 'ctas' => [ + ['label' => 'Zum Ansprechpartner', 'slug' => 'jugendfussball#kontakt', 'style' => 'primary'], + ['label' => 'Mitglied werden', 'slug' => 'mitglied-werden', 'style' => 'outline'], + ], +]]); + +component('faq', ['faq' => $page['faq'] ?? []]); diff --git a/app/pages/team-damen.php b/app/pages/team-damen.php new file mode 100644 index 0000000..ce68c04 --- /dev/null +++ b/app/pages/team-damen.php @@ -0,0 +1,18 @@ + 'Damenmannschaft – Damenfußball Kulmbach', + 'description' => $team['hero']['text'], + 'og_image' => $team['hero']['image']['base'] . '-1600.jpg', + 'schema' => team_schema($team, $slug), +]; + +component('team', ['team' => $team, 'slug' => $slug]); diff --git a/app/pages/team-erste.php b/app/pages/team-erste.php new file mode 100644 index 0000000..b8cd5ed --- /dev/null +++ b/app/pages/team-erste.php @@ -0,0 +1,18 @@ + '1. Mannschaft – Herrenfußball Kreisklasse Kulmbach', + 'description' => $team['hero']['text'], + 'og_image' => $team['hero']['image']['base'] . '-1600.jpg', + 'schema' => team_schema($team, $slug), +]; + +component('team', ['team' => $team, 'slug' => $slug]); diff --git a/app/pages/team-zwei.php b/app/pages/team-zwei.php new file mode 100644 index 0000000..d2d3240 --- /dev/null +++ b/app/pages/team-zwei.php @@ -0,0 +1,19 @@ + '2. Mannschaft – Herrenfußball Kulmbach', + 'description' => $team['hero']['text'], + 'og_image' => $team['hero']['image']['base'] . '-1600.jpg', + 'scripts' => (count($team['contact_names'] ?? []) > 1) ? ['contact-slider.js'] : [], + 'schema' => team_schema($team, $slug), +]; + +component('team', ['team' => $team, 'slug' => $slug]); diff --git a/app/pages/verein.php b/app/pages/verein.php new file mode 100644 index 0000000..f22f5b9 --- /dev/null +++ b/app/pages/verein.php @@ -0,0 +1,84 @@ + 'Fußball', + 'slug' => 'fussball', + 'category' => 'Herren · Damen · Jugend', + 'image' => ['base' => 'img/pages/fussball-hero', 'widths' => [640, 1000, 1600], 'alt' => 'Fußball beim TSV 08 Kulmbach'], + ], + [ + 'name' => 'Turnabteilung', + 'slug' => 'turnen', + 'category' => 'Turnen · Trampolin', + 'image' => ['base' => 'img/pages/turnen', 'widths' => [640, 1000], 'alt' => 'Turnabteilung des TSV 08 Kulmbach'], + ], +]; + +$meta = [ + 'title' => 'Der Verein', + 'description' => 'Der TSV 1908 Kulmbach e. V. – seit 1908 dein Sportverein im Katzbachtal in Kulmbach. Über 650 Mitglieder, die Abteilungen Fußball und Turnen, zuhause auf der Hans-Rausch-Sportanlage.', + 'og_image' => 'img/pages/vereinsgelaende-1600.jpg', + 'schema' => page_schema( + [ + ['name' => 'Startseite', 'slug' => ''], + ['name' => 'Der Verein', 'slug' => 'verein'], + ], + $page['faq'] ?? [] + ), +]; + +component('hero', ['hero' => $page['hero']]); + +component('key-facts', ['facts' => $page['facts'] ?? [], 'anchor' => 'eckdaten']); + +component('section', ['section' => $page['about']]); + +component('team-cards', [ + 'teams' => $cards, + 'title' => 'Unsere Abteilungen', + 'intro' => $page['abteilungen_intro'] ?? '', + 'intro_below' => true, + 'anchor' => 'abteilungen', +]); +?> +
    +
    +

    +

    +
    + $page['gelaende']['image'], 'sizes' => '(max-width: 1200px) 100vw, 1200px', 'class' => 'facility__img']); ?> + +
    + +
    +

    + Route planen (öffnet Google Maps in neuem Tab) +

    +
    +
    + [ + 'title' => 'Werde Teil des TSV 08', + 'text' => 'Ob Fußball oder Turnen – komm unverbindlich vorbei oder schreib uns. Wir finden gemeinsam die passende Gruppe für dich.', + 'ctas' => [ + ['label' => 'Mitglied werden', 'slug' => 'mitglied-werden', 'style' => 'primary'], + ['label' => 'Kontakt aufnehmen', 'slug' => '#kontakt', 'style' => 'outline'], + ], +]]); + +component('faq', ['faq' => $page['faq'] ?? []]); diff --git a/app/routes.php b/app/routes.php index 70e1251..94c208c 100644 --- a/app/routes.php +++ b/app/routes.php @@ -10,8 +10,15 @@ declare(strict_types=1); * Canonical und Sitemap folgen automatisch. */ return [ - '' => ['file' => 'home.php', 'sitemap' => true], - 'fussball' => ['file' => 'fussball.php', 'sitemap' => true], - 'historie' => ['file' => 'historie.php', 'sitemap' => true], - '404' => ['file' => '404.php', 'sitemap' => false], + '' => ['file' => 'home.php', 'sitemap' => true], + 'fussball' => ['file' => 'fussball.php', 'sitemap' => true], + 'fussball/erste-mannschaft' => ['file' => 'team-erste.php', 'sitemap' => true], + 'fussball/zweite-mannschaft' => ['file' => 'team-zwei.php', 'sitemap' => true], + 'fussball/damen' => ['file' => 'team-damen.php', 'sitemap' => true], + 'jugendfussball' => ['file' => 'jugendfussball.php', 'sitemap' => true], + 'verein' => ['file' => 'verein.php', 'sitemap' => true], + 'historie' => ['file' => 'historie.php', 'sitemap' => true], + 'impressum' => ['file' => 'impressum.php', 'sitemap' => true], + 'datenschutz' => ['file' => 'datenschutz.php', 'sitemap' => true], + '404' => ['file' => '404.php', 'sitemap' => false], ]; diff --git a/data/ansprechpartner.json b/data/ansprechpartner.json index c8745a9..f6ac4da 100644 --- a/data/ansprechpartner.json +++ b/data/ansprechpartner.json @@ -8,6 +8,11 @@ "phone": "+49 160 97516404", "image": "img/persons/benedikt-hoefner.jpg" }, + { + "department": "Herrenfußball", + "name": "Rustem Hajzeraj", + "image": "img/persons/rustem-hajzeraj.jpg" + }, { "department": "Damenfußball", "name": "Sam Kern", diff --git a/data/club.json b/data/club.json index 46260c1..0cff863 100644 --- a/data/club.json +++ b/data/club.json @@ -13,6 +13,22 @@ }, "email": "info@tsv08kulmbach.de", "email_turnen": "turnabteilung.tsv08kulmbach@gmail.com", + "board": { + "note": "Vertretungsberechtigter Vorstand gemäß § 26 BGB", + "members": [ + { "name": "Kai van Avondt", "role": "1. Vorsitzender" }, + { "name": "Marco Früh", "role": "" }, + { "name": "Fabian Früh", "role": "" }, + { "name": "Matthias Korzonek", "role": "" }, + { "name": "Michael Senar", "role": "" } + ] + }, + "banking": [ + { "bank": "Sparkasse Kulmbach-Kronach", "iban": "DE11 7715 0000 0000 1542 52", "bic": "BYLADEM1KUB" }, + { "bank": "VR Bank Oberfranken Mitte", "iban": "DE90 7719 0000 0000 0362 18", "bic": "GENODEF1KU1" } + ], + "data_protection_contact": "Fabian Früh", + "webdesign": "Felix Schneider", "memberships": [ "Bayerischer Landes-Sportverband", "Bayerischer Fußball-Verband", diff --git a/data/fussball.json b/data/fussball.json new file mode 100644 index 0000000..a461ac4 --- /dev/null +++ b/data/fussball.json @@ -0,0 +1,23 @@ +{ + "hero": { + "align": "bottom-left", + "compact": true, + "title": "Fußball", + "text": "Zwei Herrenmannschaften (1. Mannschaft in der Kreisklasse), eine Damenmannschaft und Jugendteams von der G- bis zur C-Jugend – alle zuhause auf der Hans-Rausch-Sportanlage im Katzbachtal in Kulmbach.", + "image": { + "base": "img/pages/fussball-hero", + "widths": [640, 1000, 1600], + "alt": "Fußballplatz des TSV 08 Kulmbach im Katzbachtal" + }, + "ctas": [] + }, + "mannschaften_intro": "Vom Kreisklasse-Fußball der Herren über die Damenmannschaft bis zum Nachwuchs – wähle deine Mannschaft.", + "faq": [ + { "q": "Wo kann ich in Kulmbach Fußball spielen?", "a": "Beim TSV 08 Kulmbach im Katzbachtal – mit erster und zweiter Herrenmannschaft, einer Damenmannschaft und Jugendmannschaften von der G- bis zur C-Jugend." }, + { "q": "Nehmt ihr neue Spieler und Spielerinnen auf?", "a": "Ja, jederzeit. Komm unverbindlich zum Probetraining der passenden Mannschaft – sprich einfach den jeweiligen Ansprechpartner an." }, + { "q": "Auf welchem Niveau spielt der Herrenbereich?", "a": "Die erste Herrenmannschaft spielt auf Kreisklasse-Niveau, dazu kommt die zweite Mannschaft für alle, die mit Spaß regelmäßig kicken." }, + { "q": "Wann ist Fußballtraining beim TSV 08 Kulmbach?", "a": "Die erste Herrenmannschaft trainiert dienstags und donnerstags um 18:30 Uhr auf der Hans-Rausch-Sportanlage. Die Trainingszeiten der zweiten Mannschaft, der Damen und der Jugend erfährst du direkt beim jeweiligen Ansprechpartner." }, + { "q": "Ab welchem Alter kann mein Kind bei euch Fußball spielen?", "a": "Schon die Kleinsten sind willkommen: Unsere Jugendabteilung reicht von der G-Jugend (Bambini) bis zur C-Jugend. Mehr dazu auf unserer Seite zum Jugendfußball." }, + { "q": "Wo ist der Sportplatz?", "a": "Auf der Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach, mitten im Katzbachtal." } + ] +} diff --git a/data/jugend.json b/data/jugend.json new file mode 100644 index 0000000..a5156f4 --- /dev/null +++ b/data/jugend.json @@ -0,0 +1,32 @@ +{ + "hero": { + "compact": true, + "align": "bottom-left", + "title": "Jugendfußball in Kulmbach", + "tagline": "G- bis C-Jugend im Katzbachtal", + "text": "Entdecke unsere Jugendmannschaften, Spielerinnen und Spieler sowie engagierte Betreuer – und lerne den Katzbachtaler Weg im Jugendfußball kennen.", + "image": { + "base": "img/pages/jugendfussball-hero", + "widths": [640, 1000, 1440], + "alt": "Kinder beim Jugendfußball-Training des TSV 08 Kulmbach" + }, + "ctas": [] + }, + "about": { + "title": "Mitmachen leicht gemacht", + "text": "Wenn deine Kinder Lust haben, bei uns Fußball auszuprobieren oder mitzuspielen, meldet euch gerne ganz unkompliziert. Wir freuen uns über jedes neue Gesicht und bieten Fußball in familiärer Atmosphäre – vom ersten Ballkontakt bis zum echten Mannschaftsspiel." + }, + "groups": [ + { "code": "G", "name": "G-Jugend (Bambini)", "training": "Freitag 16:30 Uhr", "photo": { "base": "img/teams/jugend-g", "widths": [400, 800], "alt": "G-Jugend des TSV 08 Kulmbach" } }, + { "code": "F", "name": "F-Jugend", "training": "Freitag 16:30 Uhr", "photo": { "base": "img/teams/jugend-f", "widths": [400, 800], "alt": "F-Jugend des TSV 08 Kulmbach" } }, + { "code": "E", "name": "E-Jugend", "note": "Trainingszeit auf Anfrage", "photo": { "base": "img/teams/jugend-e", "widths": [400, 800], "alt": "E-Jugend des TSV 08 Kulmbach" } }, + { "code": "D", "name": "D-Jugend", "note": "Trainingszeit auf Anfrage", "photo": { "base": "img/teams/jugend-d", "widths": [400, 800], "alt": "D-Jugend des TSV 08 Kulmbach" } }, + { "code": "C", "name": "C-Jugend", "note": "Trainingszeit auf Anfrage", "photo": { "base": "img/teams/jugend-c", "widths": [400, 800], "alt": "C-Jugend des TSV 08 Kulmbach" } } + ], + "faq": [ + { "q": "Ab welchem Alter können Kinder mitspielen?", "a": "Vom Bambini-Alter (G-Jugend) bis zur C-Jugend ist bei uns alles dabei. Am besten kommt ihr einfach zum Schnuppertraining der passenden Altersgruppe vorbei." }, + { "q": "Wie melde ich mein Kind an?", "a": "Ganz unkompliziert: Ruf unseren Ansprechpartner an oder schreib eine WhatsApp-Nachricht. Wir beantworten alle Fragen rund um den Jugendfußball und sagen euch, wann das nächste Training ist." }, + { "q": "Was kostet ein Schnuppertraining?", "a": "Das Schnuppertraining ist kostenlos und unverbindlich – ihr schaut euch in Ruhe an, ob es passt." }, + { "q": "Wo trainiert die Jugend?", "a": "Auf der Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach, mitten im Katzbachtal." } + ] +} diff --git a/data/teams.json b/data/teams.json new file mode 100644 index 0000000..33728be --- /dev/null +++ b/data/teams.json @@ -0,0 +1,209 @@ +{ + "erste-mannschaft": { + "name": "1. Mannschaft", + "department": "Herrenfußball", + "category": "Herren", + "league": "Kreisklasse", + "hero": { + "compact": true, + "align": "bottom-left", + "title": "1. Mannschaft", + "tagline": "Herrenfußball · Kreisklasse", + "text": "Unsere erste Herrenmannschaft spielt auf Kreisklasse-Niveau im Katzbachtal – ambitioniert, aber mit echtem Vereinsgeist. Komm vorbei und spiel mit.", + "image": { + "base": "img/teams/herren-1", + "widths": [640, 1000, 1600], + "alt": "Spielszene der 1. Mannschaft des TSV 08 Kulmbach" + }, + "ctas": [] + }, + "facts": [ + { "icon": "people-fill", "label": "Bereich", "value": "Herren" }, + { "icon": "trophy", "label": "Liga", "value": "Kreisklasse" }, + { "icon": "geo-alt", "label": "Heimat", "value": "Katzbachtal, Kulmbach" } + ], + "about": { + "title": "Über uns", + "text": "Bei der ersten Mannschaft treffen Ehrgeiz und Spaß am Spiel aufeinander. Wir trainieren regelmäßig im Katzbachtal und freuen uns über ambitionierte Hobbyfußballer, die Lust auf Kreisklasse-Fußball in einer eingeschworenen Truppe haben." + }, + "team_photo": { + "image": { + "base": "img/teams/herren-1-team", + "widths": [640, 1000, 1600], + "alt": "Mannschaftsfoto der 1. Mannschaft des TSV 08 Kulmbach" + }, + "caption": "Die 1. Mannschaft des TSV 08 Kulmbach" + }, + "roster": { + "title": "Mannschaft & Trainerstab", + "intro": "Trainerstab und Kader der ersten Herrenmannschaft in der Saison.", + "variant": "overlay", + "groups": [ + { + "title": "Trainer & Betreuer", + "members": [ + { "name": "Ali Kilic", "role": "Trainer", "image": "img/persons/ali-kilic.jpg" }, + { "name": "Benedikt Höfner", "role": "Sportlicher Leiter", "image": "img/persons/benedikt-hoefner.jpg" }, + { "name": "Bastian Werner", "role": "Spielleiter", "image": "img/persons/bastian-werner.jpg" } + ] + }, + { + "title": "Torwart", + "members": [ + { "name": "Janick Martin", "image": "img/persons/janick-martin.jpg" }, + { "name": "Sascha Zimmerer", "image": "img/persons/sascha-zimmerer.jpg" } + ] + }, + { + "title": "Abwehr", + "members": [ + { "name": "Ali Kilic", "image": "img/persons/ali-kilic-spieler.jpg" }, + { "name": "Alexander Hatter", "image": "img/persons/alexander-hatter.jpg" }, + { "name": "Alex Schütz", "image": "img/persons/alex-schuetz.jpg" }, + { "name": "Rustem Hajzeraj", "image": "img/persons/rustem-hajzeraj.jpg" }, + { "name": "Güray Kosuva" }, + { "name": "Maximilian Koenen" } + ] + }, + { + "title": "Mittelfeld", + "members": [ + { "name": "Fabian Früh", "image": "img/persons/fabian-frueh.jpg" }, + { "name": "Christian Kästner", "image": "img/persons/christian-kaestner.jpg" }, + { "name": "Sebastiano Gross", "image": "img/persons/sebastiano-gross.jpg" }, + { "name": "Tim Hofmann", "image": "img/persons/tim-hofmann.jpg" }, + { "name": "Felix Schneider", "image": "img/persons/felix-schneider.jpg" }, + { "name": "Alexander Vogel", "image": "img/persons/alexander-vogel.jpg" }, + { "name": "Hannes Buchmeier" }, + { "name": "Hakan Yilmaz", "image": "img/persons/hakan-yilmaz.jpg" }, + { "name": "Dominik Schulz", "image": "img/persons/dominik-schulz.jpg" } + ] + }, + { + "title": "Angriff", + "members": [ + { "name": "Christian Ohnemüller", "image": "img/persons/christian-ohnemueller.jpg" }, + { "name": "Marek Szabelski", "image": "img/persons/marek-szabelski.jpg" } + ] + } + ] + }, + "training": { + "venue_from_club": true, + "sessions": [ + { "day": "Dienstag", "time": "18:30 Uhr" }, + { "day": "Donnerstag", "time": "18:30 Uhr" } + ] + }, + "contact_department": "Herrenfußball", + "contact_name": "Benedikt Höfner", + "faq": [ + { "q": "Kann ich zum Probetraining kommen?", "a": "Klar – melde dich kurz bei unserem Ansprechpartner und komm einfach zum Training vorbei. Ein Probetraining ist jederzeit unverbindlich möglich." }, + { "q": "Auf welchem Niveau spielt die 1. Mannschaft?", "a": "Die erste Herrenmannschaft des TSV 08 Kulmbach spielt auf Kreisklasse-Niveau." }, + { "q": "Wann trainiert die 1. Mannschaft?", "a": "Die erste Mannschaft trainiert dienstags und donnerstags um 18:30 Uhr auf der Hans-Rausch-Sportanlage im Katzbachtal." }, + { "q": "Wer ist der Trainer der 1. Mannschaft?", "a": "Trainer ist Ali Kilic. Sportlicher Leiter ist Benedikt Höfner, Spielleiter Bastian Werner." }, + { "q": "Wo trainiert und spielt die Mannschaft?", "a": "Heimat ist die Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach, mitten im Katzbachtal." } + ] + }, + "zweite-mannschaft": { + "name": "2. Mannschaft", + "department": "Herrenfußball", + "category": "Herren", + "league": null, + "hero": { + "compact": true, + "align": "bottom-left", + "title": "2. Mannschaft", + "tagline": "Herrenfußball · SG mit TSV Melkendorf", + "text": "Unsere zweite Herrenmannschaft spielt in einer Spielgemeinschaft mit dem TSV Melkendorf. Hier steht der Spaß am Fußball im Vordergrund – ideal für alle, die regelmäßig kicken und Teil der TSV-Familie werden wollen.", + "image": { + "base": "img/teams/herren-2", + "widths": [640, 1000, 1600], + "alt": "Einlaufen der Mannschaften beim TSV 08 Kulmbach" + }, + "ctas": [] + }, + "facts": [ + { "icon": "people-fill", "label": "Bereich", "value": "Herren" }, + { "icon": "geo-alt", "label": "Heimat", "value": "Katzbachtal, Kulmbach" } + ], + "about": { + "title": "Über uns", + "text": "Unsere zweite Mannschaft ist die ideale Anlaufstelle für alle, die mit Freude am Spiel regelmäßig auf dem Platz stehen wollen – vom Wiedereinsteiger bis zum ambitionierten Hobbyfußballer. Gemeinsam mit dem TSV Melkendorf bilden wir eine Spielgemeinschaft: Breiter Kader, gute Stimmung, echtes Teamgefühl – über beide Vereine hinweg." + }, + "team_photo": { + "image": { + "base": "img/teams/herren-2", + "widths": [640, 1000, 1600], + "alt": "Die 2. Mannschaft des TSV 08 Kulmbach" + }, + "caption": "Die 2. Mannschaft des TSV 08 Kulmbach" + }, + "training": { + "venue_from_club": true, + "venue": { + "name": "Sportgelände TSV Melkendorf", + "locality": "Melkendorf · 95326 Kulmbach", + "maps_query": "Sportgelände TSV Melkendorf Kulmbach" + }, + "sessions": [], + "note": "Als Spielgemeinschaft mit dem TSV Melkendorf finden die Heimspiele meistens in Melkendorf statt; trainiert wird abwechselnd in Melkendorf und bei uns im Katzbachtal." + }, + "contact_department": "Herrenfußball", + "contact_names": ["Benedikt Höfner", "Rustem Hajzeraj"], + "faq": [ + { "q": "Kann ich zum Probetraining kommen?", "a": "Jederzeit – sprich kurz unseren Ansprechpartner an und komm unverbindlich zum Training vorbei." }, + { "q": "Für wen ist die 2. Mannschaft geeignet?", "a": "Für alle, die mit Freude am Spiel regelmäßig auf dem Platz stehen wollen – vom Wiedereinsteiger bis zum ambitionierten Hobbyfußballer. Vereinserfahrung ist keine Voraussetzung." }, + { "q": "Bildet die 2. Mannschaft eine Spielgemeinschaft?", "a": "Ja, die zweite Mannschaft ist eine Spielgemeinschaft mit dem TSV Melkendorf. Trainiert wird abwechselnd auf der Hans-Rausch-Sportanlage im Katzbachtal und in Melkendorf." }, + { "q": "Wer ist Ansprechpartner der 2. Mannschaft?", "a": "Benedikt Höfner – sprich ihn einfach direkt an, wenn du mitspielen oder zum Probetraining kommen möchtest." }, + { "q": "Wo trainiert und spielt die 2. Mannschaft?", "a": "Heimspiele und Training finden auf der Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach im Katzbachtal sowie – im Rahmen der Spielgemeinschaft – beim TSV Melkendorf statt." } + ] + }, + "damen": { + "name": "Damenmannschaft", + "department": "Damenfußball", + "category": "Damen", + "league": null, + "hero": { + "compact": true, + "align": "bottom-left", + "title": "Damenmannschaft", + "tagline": "Damenfußball", + "text": "Frauen- und Mädchenfußball hat beim TSV 08 Kulmbach einen festen Platz. Ob erfahren oder Wiedereinsteigerin – bei uns findest du dein Team im Katzbachtal.", + "image": { + "base": "img/teams/damen", + "widths": [640, 1000, 1600], + "alt": "Damenmannschaft des TSV 08 Kulmbach" + }, + "ctas": [] + }, + "facts": [ + { "icon": "people-fill", "label": "Bereich", "value": "Damen" }, + { "icon": "geo-alt", "label": "Heimat", "value": "Katzbachtal, Kulmbach" } + ], + "about": { + "title": "Über uns", + "text": "Unsere Damenmannschaft lebt vom Zusammenhalt und der Freude am Fußball. Wir freuen uns über jede neue Spielerin – egal ob mit Vereinserfahrung oder als Wiedereinsteigerin. Trau dich und komm zum Probetraining." + }, + "team_photo": { + "image": { + "base": "img/teams/damen", + "widths": [640, 1000, 1600], + "alt": "Die Damenmannschaft des TSV 08 Kulmbach" + }, + "caption": "Die Damenmannschaft des TSV 08 Kulmbach" + }, + "training": { + "venue_from_club": true, + "sessions": [] + }, + "contact_department": "Damenfußball", + "contact_name": "Sam Kern", + "faq": [ + { "q": "Kann ich als Wiedereinsteigerin mitmachen?", "a": "Unbedingt – bei uns sind Spielerinnen mit und ohne Vereinserfahrung willkommen. Komm einfach zum Probetraining." }, + { "q": "Für wen ist die Damenmannschaft?", "a": "Für Frauen und Mädchen, die Fußball spielen wollen – ob ambitioniert oder als Wiedereinsteigerin, mit oder ohne Vereinserfahrung." }, + { "q": "Wer ist Ansprechpartner für die Damenmannschaft?", "a": "Sam Kern – melde dich einfach direkt für Fragen oder ein unverbindliches Probetraining." }, + { "q": "Wo trainiert und spielt die Damenmannschaft?", "a": "Heimat ist die Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach, mitten im Katzbachtal." } + ] + } +} diff --git a/data/verein.json b/data/verein.json new file mode 100644 index 0000000..b7e9c84 --- /dev/null +++ b/data/verein.json @@ -0,0 +1,49 @@ +{ + "hero": { + "align": "bottom-left", + "compact": true, + "title": "Der Verein", + "text": "Seit über 100 Jahren dein Sportverein im Katzbachtal – Fußball und Turnen unter einem Dach, getragen vom Ehrenamt. Made in Kulmbach.", + "image": { + "base": "img/pages/vereinsgelaende", + "widths": [1000, 1600], + "alt": "Luftaufnahme der Hans-Rausch-Sportanlage des TSV 08 Kulmbach im Katzbachtal" + }, + "ctas": [] + }, + "facts": [ + { "icon": "clock", "label": "Gegründet", "value": "1908" }, + { "icon": "people-fill", "label": "Mitglieder", "value": "650+" }, + { "icon": "trophy", "label": "Mannschaften", "value": "10+" }, + { "icon": "geo-alt", "label": "Zuhause", "value": "Katzbachtal" } + ], + "about": { + "id": "ueber-uns", + "title": "Im Katzbachtal zuhause", + "text": "Im Katzbachtal zuhause sind wir dein Verein für Turnen und Fußball. Schwerpunkt ist dabei die kulturelle und sportliche Förderung von Jugendlichen. Bei uns wird das Ehrenamt leidenschaftlich gelebt – so entstehen Jahr für Jahr neue erfolgreiche Sportgeschichten, made in Kulmbach.", + "tinted": true, + "image": { + "base": "img/pages/sportheim", + "widths": [640, 1000], + "alt": "Sportheim des TSV 08 Kulmbach mit Pokalen und Vereinswappen" + } + }, + "abteilungen_intro": "Zwei Abteilungen, ein Verein – wähle deinen Sport.", + "gelaende": { + "title": "Unser Gelände im Katzbachtal", + "text": "Mach dir einen Eindruck von unseren Gegebenheiten: Auf der Hans-Rausch-Sportanlage liegen Rasenplatz, Hartplatz, Turnhalle und Sportheim direkt beieinander – mitten im Katzbachtal in Kulmbach. Wir freuen uns, dich schon bald hier begrüßen zu dürfen.", + "caption": "Hans-Rausch-Sportanlage aus der Luft – Rasenplatz, Hartplatz, Turnhalle und Sportheim auf einen Blick.", + "image": { + "base": "img/pages/verein", + "widths": [640, 1000], + "alt": "Beschriftete Luftaufnahme des Vereinsgeländes: Rasenplatz, Hartplatz, Turnhalle und Sportheim" + } + }, + "faq": [ + { "q": "Welche Abteilungen hat der TSV 08 Kulmbach?", "a": "Der TSV 1908 Kulmbach e. V. hat zwei Abteilungen: Fußball (Herren, Damen und Jugend) sowie Turnen, zu dem auch das Trampolinturnen gehört." }, + { "q": "Seit wann gibt es den TSV 08 Kulmbach?", "a": "Der Verein wurde 1908 gegründet und ist damit seit über 100 Jahren in Kulmbach aktiv." }, + { "q": "Wo ist der TSV 08 Kulmbach zuhause?", "a": "Auf der Hans-Rausch-Sportanlage in der Thurnauer Str. 51 in Kulmbach, mitten im Katzbachtal." }, + { "q": "Wie viele Mitglieder hat der Verein?", "a": "Der TSV 08 Kulmbach zählt über 650 Mitglieder in mehr als zehn Mannschaften." }, + { "q": "Wie kann ich Mitglied werden?", "a": "Komm unverbindlich vorbei oder nimm über das Kontaktformular Kontakt mit uns auf – wir helfen dir, die passende Abteilung und Gruppe zu finden." } + ] +} diff --git a/public/assets/css/components.css b/public/assets/css/components.css index d5b90e8..db08414 100644 --- a/public/assets/css/components.css +++ b/public/assets/css/components.css @@ -158,6 +158,17 @@ } /* === instagram === */ +/* Sektions-Kopf zentriert (einheitlich mit partners/persons/…) */ +.instagram > .container > h2, +.instagram > .container > .text-muted { + text-align: center; +} + +.instagram > .container > .text-muted { + max-width: 60ch; + margin-inline: auto; +} + .instagram__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -276,6 +287,13 @@ box-shadow: var(--shadow-card); } +/* Mannschafts-„Über"-Sektion: ruhiges 4:3-Bild (nicht zu hoch), mit dem Text ausbalanciert. */ +#ueber .split__img { + aspect-ratio: 4 / 3; + object-fit: cover; + object-position: center top; +} + @media (max-width: 992px) { .split__inner { grid-template-columns: 1fr; @@ -545,6 +563,11 @@ margin-inline: auto; } +/* Intro-Abstand zur Überschrift einheitlich mit den anderen Sektionen */ +.partners__intro { + margin-top: 1rem; +} + .partners__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 220px)); @@ -620,6 +643,8 @@ height: auto; aspect-ratio: 4 / 5; object-fit: cover; + /* Ausschnitt nach oben → Gesicht bleibt im Bild, nicht der Bauch */ + object-position: center top; } .person-card__body { @@ -847,6 +872,12 @@ --banner-interval: 6s; } +/* Sektions-Kopf zentriert (einheitlich mit partners/persons/…) */ +.ad-banner > .container > h2, +.ad-banner__subline { + text-align: center; +} + .ad-banner__subline { margin-top: 0.75rem; /* Volle Breite, damit der Dank in einer Zeile steht; auf Mobil bricht er natürlich um. */ @@ -1111,7 +1142,8 @@ .key-facts__value { font-family: var(--ff-heading); - font-size: var(--fs-600); + font-size: var(--fs-650); + line-height: var(--lh-heading); } /* === team-about === */ @@ -1119,55 +1151,160 @@ max-width: 68ch; } -/* === training-location === */ -.training__inner { +/* === training-location (Bento-Grid) === */ +.bento { display: grid; - grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); - gap: 3rem; - align-items: start; + grid-template-columns: repeat(4, 1fr); + grid-auto-rows: minmax(0, auto); + gap: 1.25rem; } -@media (max-width: 768px) { - .training__inner { - grid-template-columns: 1fr; - gap: 2.5rem; - } +/* Tile-Größen: Trainingszeiten = großer Anker, Spielort = breit, dazu zwei kleine. */ +.bento__tile--times { grid-column: span 2; grid-row: span 2; } +.bento__tile--venue { grid-column: span 2; } +.bento__tile--accent { grid-column: span 1; } +.bento__tile--route { grid-column: span 1; } + +.bento__tile { + position: relative; + display: flex; + flex-direction: column; + padding: 1.75rem; + background: var(--clr-glass-bg); + border: 1px solid var(--clr-glass-border); + border-radius: var(--radius-card); + overflow: hidden; } -.training__table { - width: 100%; - margin-top: 1rem; - border-collapse: collapse; +/* Akzent-Tile: gefüllt, weiße Schrift (gut lesbar), zentrierte Kennzahl. */ +.bento__tile--accent { + justify-content: center; + gap: 0.35rem; + background: var(--clr-accent); + border-color: var(--clr-accent); + text-align: center; } -.training__table th, -.training__table td { - padding: 0.65rem 0; +.bento__tile--route { + justify-content: center; + align-items: center; + gap: 0.9rem; + text-align: center; +} + +/* Inhalt über dem Deko-Icon halten. */ +.bento__heading, +.bento__sessions, +.bento__address, +.bento__note { + position: relative; + z-index: 1; +} + +.bento__heading { + display: flex; + align-items: center; + gap: 0.7rem; + margin-bottom: 1.5rem; + font-size: clamp(2rem, 1.6rem + 1.2vw, 2.6rem); + line-height: var(--lh-heading); +} + +.bento__hicon { + flex: none; + font-size: 1.6rem; + color: var(--clr-accent); +} + +.bento__sessions { + margin: 0; + padding: 0; + list-style: none; +} + +.bento__sessions li { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 1rem; + padding: 1rem 0; border-bottom: 1px solid var(--clr-glass-border); } -.training__table th { - font-weight: var(--fw-regular); - text-align: left; +.bento__sessions li:first-child { padding-top: 0; } +.bento__sessions li:last-child { padding-bottom: 0; border-bottom: 0; } + +.bento__day { + font-size: var(--fs-600); } -.training__table td { - color: var(--clr-text-muted); - text-align: right; +.bento__time { + font-family: var(--ff-heading); + font-size: var(--fs-650); + color: var(--clr-text); } -.training__address { - display: flex; - gap: 0.6rem; - align-items: start; - margin: 1rem 0 1.5rem; +.bento__note { + margin-top: 1rem; } -.training__pin { - flex: none; - margin-top: 0.15rem; - font-size: 1.3rem; - color: var(--clr-accent); +.bento__address { + margin: 0; + font-size: var(--fs-600); + line-height: 1.5; +} + +/* Akzent-Tile-Typo. */ +.bento__stat-label { + font-size: var(--fs-500); + text-transform: uppercase; + letter-spacing: 0.08em; + color: rgb(255 255 255 / 0.85); +} + +.bento__stat-value { + font-family: var(--ff-heading); + font-size: clamp(2rem, 1.5rem + 2vw, 2.8rem); + line-height: var(--lh-heading); + color: #fff; +} + +.bento__route-label { + font-family: var(--ff-heading); + font-size: var(--fs-650); +} + +/* Großes, angeschnittenes Deko-Icon unten rechts. */ +.bento__deco { + position: absolute; + right: -1rem; + bottom: -1.75rem; + z-index: 0; + font-size: 9rem; + line-height: 1; + color: var(--clr-text); + opacity: 0.06; + pointer-events: none; +} + +@media (max-width: 900px) { + .bento { + grid-template-columns: repeat(2, 1fr); + } + .bento__tile--times { grid-column: span 2; grid-row: auto; } + .bento__tile--venue { grid-column: span 2; } +} + +@media (max-width: 560px) { + .bento { + grid-template-columns: 1fr; + } + .bento__tile--times, + .bento__tile--venue, + .bento__tile--accent, + .bento__tile--route { + grid-column: span 1; + } } /* === cta-band === */ @@ -1200,28 +1337,14 @@ text-align: center; } +/* Schlichte Trennlinien-Liste statt Kästchen. */ .faq__list { - display: grid; - gap: 0.75rem; margin-top: 2.5rem; + border-top: 1px solid var(--clr-glass-border); } .faq__item { - background: var(--clr-glass-bg); - border: 1px solid var(--clr-glass-border); - border-radius: var(--radius-card); - overflow: hidden; - transition: border-color var(--transition), background var(--transition); -} - -.faq__item:hover { - border-color: rgb(255 255 255 / 0.35); -} - -/* Offener Eintrag klar hervorgehoben. */ -.faq__item[open] { - border-color: rgb(var(--clr-accent-rgb) / 0.55); - background: rgb(var(--clr-accent-rgb) / 0.06); + border-bottom: 1px solid var(--clr-glass-border); } .faq__q { @@ -1229,49 +1352,50 @@ align-items: center; justify-content: space-between; gap: 1rem; - padding: 1.15rem 1.4rem; + padding: 1.35rem 0.25rem; font-size: var(--fs-600); line-height: 1.3; cursor: pointer; list-style: none; + transition: color var(--transition); } .faq__q::-webkit-details-marker { display: none; } -.faq__q:focus-visible { - outline: 2px solid var(--clr-text); - outline-offset: -2px; +.faq__q:hover { + color: var(--clr-text-muted); +} + +.faq__q:focus-visible { + outline: 2px solid var(--clr-text); + outline-offset: 2px; } -/* Chevron als runder Akzent-Badge. */ .faq__chevron { flex: none; - display: grid; - place-items: center; - width: 2rem; - height: 2rem; - font-size: 0.8rem; - color: var(--clr-accent); - background: rgb(var(--clr-accent-rgb) / 0.14); - border-radius: 50%; - transition: transform var(--transition), background var(--transition), color var(--transition); + font-size: 0.9rem; + color: var(--clr-text-muted); + transition: transform var(--transition), color var(--transition); +} + +.faq__item[open] .faq__q { + color: var(--clr-text); } .faq__item[open] .faq__chevron { transform: rotate(180deg); - background: var(--clr-accent); - color: #fff; + color: var(--clr-text); } .faq__a { - padding: 0 1.4rem 1.3rem; + padding: 0 0.25rem 1.4rem; color: var(--clr-text-muted); } .faq__a p { - max-width: 62ch; + max-width: 64ch; margin: 0; } @@ -1307,6 +1431,17 @@ border: 1px solid var(--clr-glass-border); border-radius: var(--radius-card); overflow: hidden; + transition: transform var(--transition), box-shadow var(--transition); +} + +.age-group:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-card); +} + +.age-group__media { + position: relative; + overflow: hidden; } .age-group__img { @@ -1315,17 +1450,41 @@ height: auto; aspect-ratio: 4 / 3; object-fit: cover; + transition: transform var(--transition); +} + +.age-group:hover .age-group__img { + transform: scale(1.05); +} + +/* Klassen-Badge (G/F/E/D/C) auf dem Bild. */ +.age-group__badge { + position: absolute; + top: 0.75rem; + left: 0.75rem; + display: grid; + place-items: center; + width: 2.6rem; + height: 2.6rem; + font-family: var(--ff-heading); + font-size: var(--fs-650); + line-height: 1; + color: #fff; + background: var(--clr-accent); + border-radius: 50%; + box-shadow: 0 2px 10px rgb(0 0 0 / 0.35); } .age-group__body { display: grid; - gap: 0.35rem; + gap: 0.4rem; align-content: start; - padding: 1rem 1.2rem 1.2rem; + padding: 1.1rem 1.3rem 1.3rem; } .age-group__name { - font-size: var(--fs-600); + font-size: var(--fs-650); + line-height: var(--lh-heading); } .age-group__training { @@ -1352,9 +1511,15 @@ margin: 1rem auto 0; } +/* Intro als „Klärtext" unter den Kacheln → mehr Luft nach oben zur Kartenreihe */ +.team-cards__intro--below { + margin-top: 2.5rem; +} + .team-cards__grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + /* Zwei Karten pro Reihe (50/50) — bei vier Mannschaften ergibt das ein 2×2-Raster */ + grid-template-columns: repeat(2, 1fr); gap: 1.5rem; margin: 2.5rem 0 0; padding: 0; @@ -1362,6 +1527,12 @@ text-align: left; } +@media (max-width: 600px) { + .team-cards__grid { + grid-template-columns: 1fr; + } +} + .team-card { display: grid; grid-template-rows: auto 1fr; @@ -1434,3 +1605,581 @@ .team-card:hover .team-card__arrow { transform: translateX(3px); } + +/* === team-roster (Kader + Trainerstab) === */ +.team-roster > .container { + text-align: center; +} + +.team-roster__intro { + max-width: 60ch; + margin: 1rem auto 0; +} + +/* Positions-Zwischenüberschrift (Trainer & Betreuer, Torwart, Abwehr, …) */ +.team-roster__group { + margin-top: 2.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--clr-glass-border); + text-align: left; + font-size: var(--fs-700); + line-height: var(--lh-heading); +} + +.team-roster__grid { + display: grid; + /* Feste 5 pro Reihe → größere Cards/Bilder; runter gestuft auf schmaleren Screens */ + grid-template-columns: repeat(5, 1fr); + gap: 1.25rem; + margin: 1.5rem 0 0; + padding: 0; + list-style: none; + text-align: left; +} + +@media (max-width: 992px) { + .team-roster__grid { + grid-template-columns: repeat(4, 1fr); + } +} + +@media (max-width: 768px) { + .team-roster__grid { + grid-template-columns: repeat(3, 1fr); + } +} + +@media (max-width: 500px) { + .team-roster__grid { + grid-template-columns: repeat(2, 1fr); + } +} + +.roster-card { + display: grid; + grid-template-rows: auto 1fr; + height: 100%; + background: var(--clr-glass-bg); + border: 1px solid var(--clr-glass-border); + border-radius: var(--radius-card); + overflow: hidden; + transition: transform var(--transition), box-shadow var(--transition); +} + +.roster-card:hover { + transform: translateY(-4px); + box-shadow: var(--shadow-card); +} + +/* Trainerstab dezent mit Akzentkante abgesetzt */ +.roster-card--staff { + border-color: var(--clr-accent); +} + +.roster-card__media img { + display: block; + width: 100%; + height: auto; + aspect-ratio: 4 / 5; + object-fit: cover; + object-position: center top; +} + +.roster-card__body { + display: flex; + flex-direction: column; + gap: 0.1rem; + padding: 0.85rem 1rem 1rem; + background: linear-gradient(180deg, rgb(20 20 20 / 0) 0%, rgb(20 20 20 / 0.35) 100%); +} + +.roster-card__role { + font-size: var(--fs-400); + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--clr-accent); +} + +/* Name in Abel (Body) — Coolvetica wäre unter 28px unzulässig (siehe CLAUDE.md) */ +.roster-card__name { + font-size: var(--fs-500); +} + +/* --- Variante: Name als Overlay im Foto, unten links, dunkler Verlauf von unten --- */ +.roster-card--overlay { + position: relative; + display: block; +} + +.roster-card--overlay .roster-card__img { + display: block; + width: 100%; + height: auto; + aspect-ratio: 4 / 5; + object-fit: cover; + object-position: center top; +} + +.roster-card__caption { + position: absolute; + inset: auto 0 0 0; + display: flex; + flex-direction: column; + gap: 0.15rem; + padding: 2.5rem 0.9rem 0.85rem; + /* dunkler Fade von unten → Name bleibt auf jedem Foto lesbar */ + background: linear-gradient(180deg, rgb(15 15 15 / 0) 0%, rgb(15 15 15 / 0.55) 45%, rgb(15 15 15 / 0.9) 100%); +} + +/* Name hier in Coolvetica (≥28px = fs-650, Mindestgröße eingehalten) */ +.roster-card--overlay .roster-card__name { + font-family: var(--ff-heading); + font-size: var(--fs-650); + line-height: var(--lh-heading); + text-transform: uppercase; + color: #fff; +} + +.roster-card--overlay .roster-card__role { + /* Auf dem dunklen Foto-Verlauf: klein und weiß statt rot → besser lesbar */ + font-size: var(--fs-300); + color: #fff; +} + +/* === faq-contact (FAQ 2/3 + Ansprechpartner 1/3) === */ +.faq-contact__inner { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); + gap: 3rem; + align-items: start; +} + +/* Ohne Kontakt: FAQ allein, lesbar zentriert. */ +.faq-contact--solo .faq-contact__inner { + grid-template-columns: minmax(0, 760px); + justify-content: center; +} + +/* Sektions-Kopf zentriert über dem 2-Spalten-Inhalt (FAQ + Kontakt) */ +.faq-contact__title { + text-align: center; + margin: 0; +} + +/* Gedämpfte Subline unter der Überschrift (einheitlich mit den anderen Sektionen) */ +.faq-contact__intro { + max-width: 60ch; + margin-inline: auto; + margin-bottom: 2.25rem; + text-align: center; +} + +/* Abstände je nachdem, ob eine Subline folgt */ +.faq-contact__title + .faq-contact__intro { + margin-top: 1rem; +} + +.faq-contact__title + .faq-contact__inner { + margin-top: 2.25rem; +} + +.faq-contact__aside > h2 { + margin-bottom: 1.5rem; +} + +/* --- Ansprechpartner-Mini-Slider (mehrere Kontakte) --- + Ohne JS: alle Karten gestapelt sichtbar. Mit JS (--ready): nur eine sichtbar, + wechselt automatisch; Punkte zur manuellen Auswahl. */ +.contact-slider { + /* Auf Kartenbreite begrenzt und zentriert → Karte und Punkte teilen dieselbe Mittelachse */ + max-width: 320px; + margin-inline: auto; +} + +.contact-slider__track { + display: flex; + flex-direction: column; + gap: 1.5rem; + margin: 0; + padding: 0; + list-style: none; +} + +.contact-slider__dots { + /* Ohne JS funktionslos → ausgeblendet; erst mit aktivem Slider sichtbar. */ + display: none; +} + +.contact-slider--ready .contact-slider__dots { + display: flex; + justify-content: center; + gap: 0.4rem; + margin-top: 1rem; +} + +.contact-slider__dot { + /* 24px-Ziel (WCAG 2.5.8), sichtbarer Punkt 10px */ + display: grid; + place-items: center; + width: 24px; + height: 24px; + padding: 0; + background: none; + border: 0; + cursor: pointer; +} + +.contact-slider__dot::before { + content: ''; + width: 10px; + height: 10px; + border-radius: 50%; + border: 1px solid var(--clr-glass-border-strong); + transition: background var(--transition), border-color var(--transition); +} + +.contact-slider__dot[aria-current="true"]::before { + background: var(--clr-accent); + border-color: var(--clr-accent); +} + +@keyframes contact-slide-fade { + from { opacity: 0; } + to { opacity: 1; } +} + +.contact-slider--ready .contact-slider__slide.is-active { + animation: contact-slide-fade 0.4s ease; +} + +.faq-contact__main .faq__list { + margin-top: 0; +} + +.faq-contact__aside .person-card { + max-width: 320px; + margin-inline: auto; +} + +/* Flacheres Foto im Kontakt-Block (Gesicht bleibt oben erhalten). */ +.faq-contact__aside .person-card__media img { + aspect-ratio: 4 / 3; +} + +@media (max-width: 900px) { + .faq-contact__inner { + grid-template-columns: 1fr; + gap: 2.5rem; + } +} + +/* === team-photo (Mannschaftsfoto in voller Breite) === */ +.team-photo__figure { + margin: 0; +} + +.team-photo__img { + display: block; + width: 100%; + height: auto; + border-radius: var(--radius-card); + border: 1px solid var(--clr-glass-border); +} + +.team-photo__caption { + margin-top: 0.75rem; + text-align: center; + color: var(--clr-text-muted); + font-size: var(--fs-400); +} + +/* === team-bento (Bild + Einleitung + Training + Spielort als Dashboard) === */ +.team-bento { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.25rem; + grid-template-areas: + "about about about" + "train train klasse" + "venue venue venue"; +} + +.team-bento__about { grid-area: about; } +.team-bento__training { grid-area: train; } +.team-bento__klasse { grid-area: klasse; } +.team-bento__venue { grid-area: venue; } + +.team-bento__about { + justify-content: center; +} + +.team-bento__about > h2 { + margin-bottom: 0.75rem; +} + +.team-bento__about p { + color: var(--clr-text-muted); + max-width: 60ch; +} + +/* Spielort: breite untere Kachel — Adresse links, Button rechts. */ +.team-bento__venue { + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 1.25rem; +} + +.team-bento__venue-main { + position: relative; + z-index: 1; +} + +.team-bento__venue .bento__heading { + margin-bottom: 0.6rem; +} + +.team-bento__route { + position: relative; + z-index: 1; + flex: none; +} + +@media (max-width: 900px) { + .team-bento { + grid-template-columns: 1fr; + grid-template-areas: + "about" + "train" + "klasse" + "venue"; + } +} + +/* === intro-bento (Jugend-Einleitung: Bild + Text + Spielort + Stat) === */ +.intro-bento { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.25rem; + grid-template-areas: + "image about about" + "image venue stat"; +} + +.intro-bento__image { + grid-area: image; + position: relative; + min-height: 260px; + padding: 0; + overflow: hidden; +} + +.intro-bento__img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + object-position: center top; +} + +.intro-bento__about { + grid-area: about; + justify-content: center; +} + +.intro-bento__about > h2 { + margin-bottom: 0.75rem; +} + +.intro-bento__about p { + max-width: 60ch; + color: var(--clr-text-muted); +} + +.intro-bento__venue { + grid-area: venue; +} + +.intro-bento__venue .bento__address { + margin-bottom: 1.25rem; +} + +.intro-bento__route { + position: relative; + z-index: 1; + align-self: start; + margin-top: auto; +} + +.intro-bento__stat { + grid-area: stat; +} + +@media (max-width: 900px) { + .intro-bento { + grid-template-columns: 1fr; + grid-template-areas: + "image" + "about" + "venue" + "stat"; + } + .intro-bento__image { + aspect-ratio: 16 / 10; + min-height: 0; + } +} + +/* === legal (Impressum, Datenschutz) === + Schmale, gut lesbare Fließtext-Seite. Top-Padding klärt den absolut + positionierten Header (analog zur Hero-Logik). */ +.legal { + padding-block: calc(var(--header-h) + 80px) var(--space-section); +} + +.legal__head { + max-width: 60ch; + margin-bottom: clamp(2.5rem, 1.5rem + 3vw, 4rem); +} + +.legal__kicker { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.25rem; + font-size: var(--fs-500); + text-transform: uppercase; + letter-spacing: 0.18em; +} + +.legal__kicker::before { + content: ''; + width: 3rem; + height: 4px; + background: var(--clr-accent); + border-radius: 2px; +} + +.legal__lead { + margin-top: 1.25rem; + color: var(--clr-text-muted); +} + +/* Lesespalte: angenehme Zeilenlänge für lange Rechtstexte */ +.legal__body { + max-width: 72ch; +} + +.legal__body > section + section { + margin-top: clamp(2.5rem, 1.8rem + 2vw, 3.5rem); +} + +.legal__body h2 { + font-size: var(--fs-700); + margin-bottom: 1rem; +} + +/* Coolvetica nie unter --fs-650 (28px) — Unterüberschriften daher in Abel, + per Laufweite/Uppercase ausgezeichnet statt in der Display-Schrift. */ +.legal__body h3 { + font-family: var(--ff-body); + font-size: var(--fs-600); + font-weight: var(--fw-bold); + text-transform: none; + line-height: var(--lh-body); + margin-top: 1.75rem; + margin-bottom: 0.5rem; +} + +.legal__body p, +.legal__body ul { + margin-bottom: 1rem; +} + +.legal__body a { + color: var(--clr-text); + text-decoration: underline; + text-underline-offset: 0.2em; +} + +.legal__body a:hover { + color: var(--clr-text-muted); +} + +.legal__body ul { + padding-left: 1.25rem; + max-width: 70ch; +} + +.legal__body li { + margin-bottom: 0.35rem; +} + +.legal__body li::marker { + color: var(--clr-accent); +} + +/* Adress-/Kontaktblöcke (Verantwortlicher, Bankverbindung) als abgesetzte Karte */ +.legal__card { + margin-bottom: 1rem; + padding: clamp(1.25rem, 1rem + 1.5vw, 1.75rem); + background: var(--clr-glass-bg); + border: 1px solid var(--clr-glass-border); + border-radius: var(--radius-card); +} + +.legal__card :last-child { + margin-bottom: 0; +} + +.legal__card address { + font-style: normal; + line-height: var(--lh-body); +} + +.legal__cards { + display: grid; + gap: 1rem; + margin-bottom: 1rem; +} + +@media (min-width: 600px) { + .legal__cards--2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +.legal__muted { + color: var(--clr-text-muted); +} + +/* === facility (Gelände / Drohnen-Luftbild auf der Vereinsseite) === */ +.facility__lead { + max-width: 70ch; + margin-top: 1rem; + color: var(--clr-text-muted); +} + +.facility__figure { + margin: 2.5rem 0 0; +} + +.facility__img { + display: block; + width: 100%; + height: auto; + border-radius: var(--radius-card); + box-shadow: var(--shadow-card); +} + +.facility__caption { + margin-top: 0.85rem; + color: var(--clr-text-faint); + font-size: var(--fs-500); +} + +.facility__actions { + margin-top: 1.75rem; +} diff --git a/public/assets/img/pages/fussball-hero-1000.jpg b/public/assets/img/pages/fussball-hero-1000.jpg new file mode 100644 index 0000000..1b8d5fb Binary files /dev/null and b/public/assets/img/pages/fussball-hero-1000.jpg differ diff --git a/public/assets/img/pages/fussball-hero-1600.jpg b/public/assets/img/pages/fussball-hero-1600.jpg new file mode 100644 index 0000000..c77ea09 Binary files /dev/null and b/public/assets/img/pages/fussball-hero-1600.jpg differ diff --git a/public/assets/img/pages/fussball-hero-640.jpg b/public/assets/img/pages/fussball-hero-640.jpg new file mode 100644 index 0000000..f9b3452 Binary files /dev/null and b/public/assets/img/pages/fussball-hero-640.jpg differ diff --git a/public/assets/img/pages/fussball-mannschaften-1000.jpg b/public/assets/img/pages/fussball-mannschaften-1000.jpg new file mode 100644 index 0000000..4901cf5 Binary files /dev/null and b/public/assets/img/pages/fussball-mannschaften-1000.jpg differ diff --git a/public/assets/img/pages/fussball-mannschaften-640.jpg b/public/assets/img/pages/fussball-mannschaften-640.jpg new file mode 100644 index 0000000..358d50c Binary files /dev/null and b/public/assets/img/pages/fussball-mannschaften-640.jpg differ diff --git a/public/assets/img/pages/jugendfussball-hero-1000.jpg b/public/assets/img/pages/jugendfussball-hero-1000.jpg new file mode 100644 index 0000000..f91849a Binary files /dev/null and b/public/assets/img/pages/jugendfussball-hero-1000.jpg differ diff --git a/public/assets/img/pages/jugendfussball-hero-1440.jpg b/public/assets/img/pages/jugendfussball-hero-1440.jpg new file mode 100644 index 0000000..bddec5a Binary files /dev/null and b/public/assets/img/pages/jugendfussball-hero-1440.jpg differ diff --git a/public/assets/img/pages/jugendfussball-hero-640.jpg b/public/assets/img/pages/jugendfussball-hero-640.jpg new file mode 100644 index 0000000..b26df5d Binary files /dev/null and b/public/assets/img/pages/jugendfussball-hero-640.jpg differ diff --git a/public/assets/img/pages/turnen-1000.jpg b/public/assets/img/pages/turnen-1000.jpg new file mode 100644 index 0000000..d85ff68 Binary files /dev/null and b/public/assets/img/pages/turnen-1000.jpg differ diff --git a/public/assets/img/pages/turnen-640.jpg b/public/assets/img/pages/turnen-640.jpg new file mode 100644 index 0000000..01eea98 Binary files /dev/null and b/public/assets/img/pages/turnen-640.jpg differ diff --git a/public/assets/img/persons/alex-schuetz.jpg b/public/assets/img/persons/alex-schuetz.jpg new file mode 100644 index 0000000..0dd3674 Binary files /dev/null and b/public/assets/img/persons/alex-schuetz.jpg differ diff --git a/public/assets/img/persons/alexander-hatter.jpg b/public/assets/img/persons/alexander-hatter.jpg new file mode 100644 index 0000000..db3718b Binary files /dev/null and b/public/assets/img/persons/alexander-hatter.jpg differ diff --git a/public/assets/img/persons/alexander-vogel.jpg b/public/assets/img/persons/alexander-vogel.jpg new file mode 100644 index 0000000..3558868 Binary files /dev/null and b/public/assets/img/persons/alexander-vogel.jpg differ diff --git a/public/assets/img/persons/ali-kilic-spieler.jpg b/public/assets/img/persons/ali-kilic-spieler.jpg new file mode 100644 index 0000000..30f1705 Binary files /dev/null and b/public/assets/img/persons/ali-kilic-spieler.jpg differ diff --git a/public/assets/img/persons/ali-kilic.jpg b/public/assets/img/persons/ali-kilic.jpg new file mode 100644 index 0000000..71f4480 Binary files /dev/null and b/public/assets/img/persons/ali-kilic.jpg differ diff --git a/public/assets/img/persons/bastian-werner.jpg b/public/assets/img/persons/bastian-werner.jpg new file mode 100644 index 0000000..b169078 Binary files /dev/null and b/public/assets/img/persons/bastian-werner.jpg differ diff --git a/public/assets/img/persons/christian-kaestner.jpg b/public/assets/img/persons/christian-kaestner.jpg new file mode 100644 index 0000000..42a553e Binary files /dev/null and b/public/assets/img/persons/christian-kaestner.jpg differ diff --git a/public/assets/img/persons/christian-ohnemueller.jpg b/public/assets/img/persons/christian-ohnemueller.jpg new file mode 100644 index 0000000..b44c70b Binary files /dev/null and b/public/assets/img/persons/christian-ohnemueller.jpg differ diff --git a/public/assets/img/persons/dominik-schulz.jpg b/public/assets/img/persons/dominik-schulz.jpg new file mode 100644 index 0000000..5cca54f Binary files /dev/null and b/public/assets/img/persons/dominik-schulz.jpg differ diff --git a/public/assets/img/persons/felix-schneider.jpg b/public/assets/img/persons/felix-schneider.jpg new file mode 100644 index 0000000..b98b0ff Binary files /dev/null and b/public/assets/img/persons/felix-schneider.jpg differ diff --git a/public/assets/img/persons/hakan-yilmaz.jpg b/public/assets/img/persons/hakan-yilmaz.jpg new file mode 100644 index 0000000..16e1d11 Binary files /dev/null and b/public/assets/img/persons/hakan-yilmaz.jpg differ diff --git a/public/assets/img/persons/janick-martin.jpg b/public/assets/img/persons/janick-martin.jpg new file mode 100644 index 0000000..f0be878 Binary files /dev/null and b/public/assets/img/persons/janick-martin.jpg differ diff --git a/public/assets/img/persons/marek-szabelski.jpg b/public/assets/img/persons/marek-szabelski.jpg new file mode 100644 index 0000000..df0a1a3 Binary files /dev/null and b/public/assets/img/persons/marek-szabelski.jpg differ diff --git a/public/assets/img/persons/platzhalter-spieler.jpg b/public/assets/img/persons/platzhalter-spieler.jpg new file mode 100644 index 0000000..38251a8 Binary files /dev/null and b/public/assets/img/persons/platzhalter-spieler.jpg differ diff --git a/public/assets/img/persons/rustem-hajzeraj.jpg b/public/assets/img/persons/rustem-hajzeraj.jpg new file mode 100644 index 0000000..b68ce1c Binary files /dev/null and b/public/assets/img/persons/rustem-hajzeraj.jpg differ diff --git a/public/assets/img/persons/sascha-zimmerer.jpg b/public/assets/img/persons/sascha-zimmerer.jpg new file mode 100644 index 0000000..e9a607d Binary files /dev/null and b/public/assets/img/persons/sascha-zimmerer.jpg differ diff --git a/public/assets/img/persons/sebastiano-gross.jpg b/public/assets/img/persons/sebastiano-gross.jpg new file mode 100644 index 0000000..f0a6410 Binary files /dev/null and b/public/assets/img/persons/sebastiano-gross.jpg differ diff --git a/public/assets/img/persons/tim-hofmann.jpg b/public/assets/img/persons/tim-hofmann.jpg new file mode 100644 index 0000000..ecaf376 Binary files /dev/null and b/public/assets/img/persons/tim-hofmann.jpg differ diff --git a/public/assets/img/teams/damen-1000.jpg b/public/assets/img/teams/damen-1000.jpg new file mode 100644 index 0000000..cdb4801 Binary files /dev/null and b/public/assets/img/teams/damen-1000.jpg differ diff --git a/public/assets/img/teams/damen-1600.jpg b/public/assets/img/teams/damen-1600.jpg new file mode 100644 index 0000000..4f5df01 Binary files /dev/null and b/public/assets/img/teams/damen-1600.jpg differ diff --git a/public/assets/img/teams/damen-640.jpg b/public/assets/img/teams/damen-640.jpg new file mode 100644 index 0000000..cf7f500 Binary files /dev/null and b/public/assets/img/teams/damen-640.jpg differ diff --git a/public/assets/img/teams/herren-1-1000.jpg b/public/assets/img/teams/herren-1-1000.jpg new file mode 100644 index 0000000..b2a3858 Binary files /dev/null and b/public/assets/img/teams/herren-1-1000.jpg differ diff --git a/public/assets/img/teams/herren-1-1600.jpg b/public/assets/img/teams/herren-1-1600.jpg new file mode 100644 index 0000000..9ad1a65 Binary files /dev/null and b/public/assets/img/teams/herren-1-1600.jpg differ diff --git a/public/assets/img/teams/herren-1-640.jpg b/public/assets/img/teams/herren-1-640.jpg new file mode 100644 index 0000000..0a39bbc Binary files /dev/null and b/public/assets/img/teams/herren-1-640.jpg differ diff --git a/public/assets/img/teams/herren-1-team-1000.jpg b/public/assets/img/teams/herren-1-team-1000.jpg new file mode 100644 index 0000000..f3e0849 Binary files /dev/null and b/public/assets/img/teams/herren-1-team-1000.jpg differ diff --git a/public/assets/img/teams/herren-1-team-1600.jpg b/public/assets/img/teams/herren-1-team-1600.jpg new file mode 100644 index 0000000..cdaf90d Binary files /dev/null and b/public/assets/img/teams/herren-1-team-1600.jpg differ diff --git a/public/assets/img/teams/herren-1-team-640.jpg b/public/assets/img/teams/herren-1-team-640.jpg new file mode 100644 index 0000000..d79efaa Binary files /dev/null and b/public/assets/img/teams/herren-1-team-640.jpg differ diff --git a/public/assets/img/teams/herren-2-1000.jpg b/public/assets/img/teams/herren-2-1000.jpg new file mode 100644 index 0000000..e06099b Binary files /dev/null and b/public/assets/img/teams/herren-2-1000.jpg differ diff --git a/public/assets/img/teams/herren-2-1600.jpg b/public/assets/img/teams/herren-2-1600.jpg new file mode 100644 index 0000000..99c1f2e Binary files /dev/null and b/public/assets/img/teams/herren-2-1600.jpg differ diff --git a/public/assets/img/teams/herren-2-640.jpg b/public/assets/img/teams/herren-2-640.jpg new file mode 100644 index 0000000..5c6ad88 Binary files /dev/null and b/public/assets/img/teams/herren-2-640.jpg differ diff --git a/public/assets/img/teams/jugend-c-400.jpg b/public/assets/img/teams/jugend-c-400.jpg new file mode 100644 index 0000000..ca9237e Binary files /dev/null and b/public/assets/img/teams/jugend-c-400.jpg differ diff --git a/public/assets/img/teams/jugend-c-800.jpg b/public/assets/img/teams/jugend-c-800.jpg new file mode 100644 index 0000000..48bc445 Binary files /dev/null and b/public/assets/img/teams/jugend-c-800.jpg differ diff --git a/public/assets/img/teams/jugend-d-400.jpg b/public/assets/img/teams/jugend-d-400.jpg new file mode 100644 index 0000000..dcf1743 Binary files /dev/null and b/public/assets/img/teams/jugend-d-400.jpg differ diff --git a/public/assets/img/teams/jugend-d-800.jpg b/public/assets/img/teams/jugend-d-800.jpg new file mode 100644 index 0000000..f749e6b Binary files /dev/null and b/public/assets/img/teams/jugend-d-800.jpg differ diff --git a/public/assets/img/teams/jugend-e-400.jpg b/public/assets/img/teams/jugend-e-400.jpg new file mode 100644 index 0000000..9e8cc29 Binary files /dev/null and b/public/assets/img/teams/jugend-e-400.jpg differ diff --git a/public/assets/img/teams/jugend-e-800.jpg b/public/assets/img/teams/jugend-e-800.jpg new file mode 100644 index 0000000..cb4def1 Binary files /dev/null and b/public/assets/img/teams/jugend-e-800.jpg differ diff --git a/public/assets/img/teams/jugend-f-400.jpg b/public/assets/img/teams/jugend-f-400.jpg new file mode 100644 index 0000000..002469c Binary files /dev/null and b/public/assets/img/teams/jugend-f-400.jpg differ diff --git a/public/assets/img/teams/jugend-f-800.jpg b/public/assets/img/teams/jugend-f-800.jpg new file mode 100644 index 0000000..29bd096 Binary files /dev/null and b/public/assets/img/teams/jugend-f-800.jpg differ diff --git a/public/assets/img/teams/jugend-g-400.jpg b/public/assets/img/teams/jugend-g-400.jpg new file mode 100644 index 0000000..9bc2c9b Binary files /dev/null and b/public/assets/img/teams/jugend-g-400.jpg differ diff --git a/public/assets/img/teams/jugend-g-800.jpg b/public/assets/img/teams/jugend-g-800.jpg new file mode 100644 index 0000000..dfff24c Binary files /dev/null and b/public/assets/img/teams/jugend-g-800.jpg differ diff --git a/public/assets/js/contact-slider.js b/public/assets/js/contact-slider.js new file mode 100644 index 0000000..14fe628 --- /dev/null +++ b/public/assets/js/contact-slider.js @@ -0,0 +1,75 @@ +/* Ansprechpartner-Mini-Slider: zeigt bei mehreren Kontakten nur eine Karte und + wechselt automatisch durch. Progressive Enhancement: + - ohne JS bleiben alle Karten gestapelt sichtbar (kein Info-Verlust), + - bei prefers-reduced-motion findet keine Enhancement/Auto-Rotation statt + (alle Karten bleiben sichtbar), + - Pause bei Hover/Fokus, Punkte zur manuellen Auswahl. */ +(function () { + 'use strict'; + + var root = document.querySelector('[data-contact-slider]'); + if (!root) { + return; + } + + var slides = Array.prototype.slice.call(root.querySelectorAll('[data-contact-slide]')); + if (slides.length < 2) { + return; + } + + // Bewegungsreduzierung: nicht enhancen → alle Karten bleiben sichtbar. + if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + return; + } + + var dots = Array.prototype.slice.call(root.querySelectorAll('[data-contact-dot]')); + var INTERVAL = 5000; + var current = 0; + var timer = null; + + function show(n) { + current = (n + slides.length) % slides.length; + slides.forEach(function (slide, idx) { + var active = idx === current; + slide.hidden = !active; + slide.classList.toggle('is-active', active); + }); + dots.forEach(function (dot, idx) { + if (idx === current) { + dot.setAttribute('aria-current', 'true'); + } else { + dot.removeAttribute('aria-current'); + } + }); + } + + function stop() { + if (timer) { + window.clearInterval(timer); + timer = null; + } + } + + function start() { + stop(); + timer = window.setInterval(function () { + show(current + 1); + }, INTERVAL); + } + + dots.forEach(function (dot, idx) { + dot.addEventListener('click', function () { + show(idx); + start(); + }); + }); + + root.addEventListener('mouseenter', stop); + root.addEventListener('mouseleave', start); + root.addEventListener('focusin', stop); + root.addEventListener('focusout', start); + + root.classList.add('contact-slider--ready'); + show(0); + start(); +})();