feat: Implement initial website structure with core pages, layout, and reusable UI components, alongside ESLint configuration and SEO setup.
This commit is contained in:
242
app/aktuelles/page.tsx
Normal file
242
app/aktuelles/page.tsx
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import { Container } from "@/components/ui/Container";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
|
import { Card } from "@/components/ui/Card";
|
||||||
|
import { FadeIn } from "@/components/ui/FadeIn";
|
||||||
|
import { Calendar, ArrowRight } from "lucide-react";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Aktuelles",
|
||||||
|
description:
|
||||||
|
"Bleiben Sie auf dem Laufenden über Neuigkeiten, Events und Angebote bei Sportbox Reutte.",
|
||||||
|
openGraph: {
|
||||||
|
title: "Aktuelles | Sportbox Reutte",
|
||||||
|
description:
|
||||||
|
"Neuigkeiten, Events und Angebote bei Sportbox Reutte – Ihrem Fitnessstudio in Reutte, Tirol.",
|
||||||
|
url: "https://sportbox-reutte.at/aktuelles",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const BLOG_POSTS = [
|
||||||
|
{
|
||||||
|
title: "5 Tipps für den perfekten Trainingsstart",
|
||||||
|
excerpt:
|
||||||
|
"Sie möchten mit dem Training beginnen, wissen aber nicht wo? Unsere Experten teilen ihre besten Tipps für Einsteiger.",
|
||||||
|
date: "2026-01-15",
|
||||||
|
category: "Tipps",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Neue Gruppenkurse ab Februar",
|
||||||
|
excerpt:
|
||||||
|
"Ab Februar erweitern wir unser Kursangebot: HIIT, Functional Training und Pilates neu im Programm.",
|
||||||
|
date: "2026-01-10",
|
||||||
|
category: "Neuigkeiten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Ernährung und Training: Was wirklich zählt",
|
||||||
|
excerpt:
|
||||||
|
"Unsere Ernährungsberaterin Lisa Mayer erklärt, wie Sie Ihre Ernährung optimal auf Ihr Training abstimmen.",
|
||||||
|
date: "2026-01-05",
|
||||||
|
category: "Ernährung",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Rückblick: Community Workout im Dezember",
|
||||||
|
excerpt:
|
||||||
|
"Über 40 Teilnehmer beim letzten Community Workout – ein Rückblick auf einen großartigen Abend.",
|
||||||
|
date: "2025-12-20",
|
||||||
|
category: "Events",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Krafttraining für Anfänger: Der richtige Einstieg",
|
||||||
|
excerpt:
|
||||||
|
"Thomas Huber zeigt die wichtigsten Grundübungen und erklärt, worauf Anfänger achten sollten.",
|
||||||
|
date: "2025-12-15",
|
||||||
|
category: "Training",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Yoga und Fitness: Die perfekte Kombination",
|
||||||
|
excerpt:
|
||||||
|
"Warum Yoga die ideale Ergänzung zu Ihrem Krafttraining ist und wie Sie beide Disziplinen verbinden können.",
|
||||||
|
date: "2025-12-10",
|
||||||
|
category: "Yoga",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const EVENTS = [
|
||||||
|
{
|
||||||
|
title: "Community Workout – Open Air",
|
||||||
|
date: "15. März 2026",
|
||||||
|
time: "10:00 – 12:00 Uhr",
|
||||||
|
description: "Gemeinsames Training im Freien für alle Fitnesslevel.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Ernährungs-Workshop",
|
||||||
|
date: "22. März 2026",
|
||||||
|
time: "14:00 – 16:00 Uhr",
|
||||||
|
description:
|
||||||
|
"Lernen Sie, wie Sie Ihre Mahlzeiten optimal für Ihre Ziele planen.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fitness-Challenge: 30 Tage",
|
||||||
|
date: "1. April 2026",
|
||||||
|
time: "Start: jederzeit",
|
||||||
|
description:
|
||||||
|
"30 Tage, 30 Challenges – machen Sie mit und erreichen Sie neue Bestleistungen.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
function formatDate(dateString: string): string {
|
||||||
|
return new Date(dateString).toLocaleDateString("de-AT", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AktuellesPage() {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="pt-32 pb-[var(--spacing-section)] md:pt-40 md:pb-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-secondary/60 mb-4">
|
||||||
|
Aktuelles
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<h1
|
||||||
|
className="font-bold max-w-2xl"
|
||||||
|
style={{
|
||||||
|
fontSize: "clamp(var(--text-3xl), 4vw, var(--text-5xl))",
|
||||||
|
lineHeight: "1.1",
|
||||||
|
letterSpacing: "var(--text-5xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Neuigkeiten & Events
|
||||||
|
</h1>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.2}>
|
||||||
|
<p className="mt-6 max-w-xl text-secondary/70" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
Bleiben Sie auf dem Laufenden über Neuigkeiten, Events und
|
||||||
|
Angebote in unserem Fitnessstudio.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Blog Posts */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-2"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-2xl)",
|
||||||
|
lineHeight: "var(--text-2xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Beiträge
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted mb-10">
|
||||||
|
Tipps, Neuigkeiten und Wissenswertes rund um Fitness und Gesundheit.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{BLOG_POSTS.map((post, index) => (
|
||||||
|
<FadeIn key={post.title} delay={index * 0.05}>
|
||||||
|
<article>
|
||||||
|
<Card className="h-full flex flex-col">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<span className="text-xs uppercase tracking-widest text-muted font-medium">
|
||||||
|
{post.category}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted">
|
||||||
|
{formatDate(post.date)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold mb-2">{post.title}</h3>
|
||||||
|
<p className="text-sm text-muted flex-1">{post.excerpt}</p>
|
||||||
|
<div className="mt-4 flex items-center gap-1 text-sm font-medium text-primary">
|
||||||
|
<span>Weiterlesen</span>
|
||||||
|
<ArrowRight size={14} aria-hidden="true" />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</article>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Events */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-neutral">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-2"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-2xl)",
|
||||||
|
lineHeight: "var(--text-2xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Kommende Events
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted mb-10">
|
||||||
|
Workshops, Community-Events und mehr – seien Sie dabei.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{EVENTS.map((event, index) => (
|
||||||
|
<FadeIn key={event.title} delay={index * 0.1}>
|
||||||
|
<Card className="bg-background">
|
||||||
|
<div className="flex flex-col md:flex-row md:items-center gap-4 md:gap-8">
|
||||||
|
<div className="flex items-center gap-3 md:min-w-48">
|
||||||
|
<Calendar size={18} className="text-muted" aria-hidden="true" />
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-bold">{event.date}</p>
|
||||||
|
<p className="text-xs text-muted">{event.time}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-base font-bold">{event.title}</h3>
|
||||||
|
<p className="text-sm text-muted mt-1">{event.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container className="text-center">
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-4"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Nichts verpassen
|
||||||
|
</h2>
|
||||||
|
<p className="text-secondary/70 mb-8 max-w-lg mx-auto">
|
||||||
|
Kontaktieren Sie uns für aktuelle Informationen zu Events und
|
||||||
|
Angeboten.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
variant="secondary"
|
||||||
|
className="border-secondary text-secondary hover:bg-secondary hover:text-primary"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Button>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,33 @@
|
|||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
import "@/theme/globals.css";
|
import "@/theme/globals.css";
|
||||||
|
import "@/theme/stylesheet.css";
|
||||||
|
import { Header } from "@/components/layout/Header";
|
||||||
|
import { Footer } from "@/components/layout/Footer";
|
||||||
|
|
||||||
|
const inter = Inter({
|
||||||
|
subsets: ["latin"],
|
||||||
|
display: "swap",
|
||||||
|
variable: "--font-inter",
|
||||||
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Website",
|
title: {
|
||||||
description: "Generated website",
|
default: "Sportbox Reutte – Dein Fitnessstudio in Reutte",
|
||||||
|
template: "%s | Sportbox Reutte",
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
"Willkommen bei Sportbox Reutte – Ihr Fitnessstudio für individuelle Trainingspläne und persönliche Betreuung in Reutte, Tirol.",
|
||||||
|
metadataBase: new URL("https://sportbox-reutte.at"),
|
||||||
|
openGraph: {
|
||||||
|
type: "website",
|
||||||
|
locale: "de_AT",
|
||||||
|
url: "https://sportbox-reutte.at",
|
||||||
|
siteName: "Sportbox Reutte",
|
||||||
|
title: "Sportbox Reutte – Dein Fitnessstudio in Reutte",
|
||||||
|
description:
|
||||||
|
"Ihr Fitnessstudio für individuelle Trainingspläne und persönliche Betreuung in Reutte, Tirol.",
|
||||||
|
},
|
||||||
robots: {
|
robots: {
|
||||||
index: true,
|
index: true,
|
||||||
follow: true,
|
follow: true,
|
||||||
@@ -13,7 +37,7 @@ export const metadata: Metadata = {
|
|||||||
export const viewport: Viewport = {
|
export const viewport: Viewport = {
|
||||||
width: "device-width",
|
width: "device-width",
|
||||||
initialScale: 1,
|
initialScale: 1,
|
||||||
themeColor: "#ffffff",
|
themeColor: "#000000",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -22,8 +46,12 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="de">
|
<html lang="de" className={inter.variable}>
|
||||||
<body>{children}</body>
|
<body>
|
||||||
|
<Header />
|
||||||
|
{children}
|
||||||
|
<Footer />
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
227
app/leistungen/page.tsx
Normal file
227
app/leistungen/page.tsx
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
import { Container } from "@/components/ui/Container";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
||||||
|
import { Card } from "@/components/ui/Card";
|
||||||
|
import { FadeIn } from "@/components/ui/FadeIn";
|
||||||
|
import { ContactForm } from "@/components/ui/ContactForm";
|
||||||
|
import { FaqSection } from "@/components/sections/FaqSection";
|
||||||
|
import { Dumbbell, Users, Apple, Flower2, Zap, HeartPulse } from "lucide-react";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Leistungen",
|
||||||
|
description:
|
||||||
|
"Informieren Sie sich über unser vielfältiges Kursangebot und individuelle Trainingsmöglichkeiten bei Sportbox Reutte.",
|
||||||
|
openGraph: {
|
||||||
|
title: "Leistungen | Sportbox Reutte",
|
||||||
|
description:
|
||||||
|
"Vielfältiges Kursangebot und individuelle Trainingsmöglichkeiten bei Sportbox Reutte in Reutte, Tirol.",
|
||||||
|
url: "https://sportbox-reutte.at/leistungen",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SERVICES = [
|
||||||
|
{
|
||||||
|
icon: Dumbbell,
|
||||||
|
title: "Personal Training",
|
||||||
|
description:
|
||||||
|
"Individuell abgestimmte Trainingspläne mit persönlicher Betreuung durch zertifizierte Trainer. Ideal für gezielte Ziele wie Muskelaufbau, Gewichtsreduktion oder Rehabilitation.",
|
||||||
|
features: [
|
||||||
|
"1:1 Betreuung",
|
||||||
|
"Individueller Trainingsplan",
|
||||||
|
"Regelmäßige Fortschrittskontrolle",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Users,
|
||||||
|
title: "Gruppenkurse",
|
||||||
|
description:
|
||||||
|
"Vielfältige Kurse für jedes Fitnesslevel – von HIIT über Functional Training bis Pilates. Gemeinsam trainieren motiviert und macht Spaß.",
|
||||||
|
features: [
|
||||||
|
"Verschiedene Kursformate",
|
||||||
|
"Für alle Levels",
|
||||||
|
"Motivierende Gruppenatmosphäre",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Apple,
|
||||||
|
title: "Ernährungsberatung",
|
||||||
|
description:
|
||||||
|
"Professionelle Beratung durch unsere diplomierte Ernährungsberaterin. Wir helfen Ihnen, Ihre Ernährung optimal auf Ihre Trainingsziele abzustimmen.",
|
||||||
|
features: [
|
||||||
|
"Persönlicher Ernährungsplan",
|
||||||
|
"Sportgerechte Ernährung",
|
||||||
|
"Nachhaltige Umstellung",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Flower2,
|
||||||
|
title: "Yoga & Mobility",
|
||||||
|
description:
|
||||||
|
"Finden Sie Balance zwischen Kraft und Flexibilität. Unsere Yoga- und Mobility-Kurse verbessern Ihre Beweglichkeit und reduzieren Stress.",
|
||||||
|
features: [
|
||||||
|
"Verschiedene Yoga-Stile",
|
||||||
|
"Stretching & Mobility",
|
||||||
|
"Atemtechniken",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Zap,
|
||||||
|
title: "Krafttraining",
|
||||||
|
description:
|
||||||
|
"Strukturiertes Krafttraining an modernen Geräten und mit freien Gewichten. Von Grundlagen bis Wettkampfvorbereitung.",
|
||||||
|
features: [
|
||||||
|
"Moderne Geräte",
|
||||||
|
"Freie Gewichte",
|
||||||
|
"Technikschulung",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: HeartPulse,
|
||||||
|
title: "Gesundheitstraining",
|
||||||
|
description:
|
||||||
|
"Präventives und rehabilitatives Training für mehr Wohlbefinden im Alltag. Ideal für Rückengesundheit, Haltungsverbesserung und allgemeine Fitness.",
|
||||||
|
features: [
|
||||||
|
"Rückentraining",
|
||||||
|
"Haltungsanalyse",
|
||||||
|
"Präventives Training",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function LeistungenPage() {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="pt-32 pb-[var(--spacing-section)] md:pt-40 md:pb-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-secondary/60 mb-4">
|
||||||
|
Leistungen
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<h1
|
||||||
|
className="font-bold max-w-2xl"
|
||||||
|
style={{
|
||||||
|
fontSize: "clamp(var(--text-3xl), 4vw, var(--text-5xl))",
|
||||||
|
lineHeight: "1.1",
|
||||||
|
letterSpacing: "var(--text-5xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Was wir für Sie tun können.
|
||||||
|
</h1>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.2}>
|
||||||
|
<p className="mt-6 max-w-xl text-secondary/70" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
Vielfältige Angebote für Ihre individuellen Fitnessziele –
|
||||||
|
professionell, persönlich und auf Sie abgestimmt.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Services */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Unser Angebot"
|
||||||
|
subtitle="Von Personal Training bis Ernährungsberatung – wir haben für jeden das Richtige."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{SERVICES.map((service, index) => (
|
||||||
|
<FadeIn key={service.title} delay={index * 0.08}>
|
||||||
|
<Card className="h-full flex flex-col">
|
||||||
|
<service.icon
|
||||||
|
size={28}
|
||||||
|
className="mb-4 text-primary"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<h3 className="text-lg font-bold mb-2">{service.title}</h3>
|
||||||
|
<p className="text-sm text-muted mb-4 flex-1">
|
||||||
|
{service.description}
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-1.5">
|
||||||
|
{service.features.map((feature) => (
|
||||||
|
<li
|
||||||
|
key={feature}
|
||||||
|
className="text-sm text-foreground flex items-start gap-2"
|
||||||
|
>
|
||||||
|
<span className="mt-1.5 w-1 h-1 bg-primary rounded-full shrink-0" aria-hidden="true" />
|
||||||
|
{feature}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* FAQ */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-neutral">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Häufige Fragen"
|
||||||
|
subtitle="Antworten auf die häufigsten Fragen rund um Sportbox Reutte."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<FaqSection />
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Contact Form */}
|
||||||
|
<section id="kontakt" className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||||
|
<FadeIn>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-muted mb-4">
|
||||||
|
Kontakt
|
||||||
|
</p>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-6"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
letterSpacing: "var(--text-3xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Jetzt Termin vereinbaren.
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted leading-relaxed mb-6">
|
||||||
|
Haben Sie Fragen oder möchten Sie ein Probetraining
|
||||||
|
vereinbaren? Schreiben Sie uns – wir melden uns schnellstmöglich
|
||||||
|
bei Ihnen.
|
||||||
|
</p>
|
||||||
|
<div className="space-y-3 text-sm text-muted">
|
||||||
|
<p>
|
||||||
|
<span className="font-medium text-foreground">E-Mail:</span>{" "}
|
||||||
|
kontakt@sportbox-reutte.at
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="font-medium text-foreground">Telefon:</span>{" "}
|
||||||
|
+43 123 456 789
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span className="font-medium text-foreground">Adresse:</span>{" "}
|
||||||
|
Reutte, Tirol, Österreich
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.15}>
|
||||||
|
<ContactForm />
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
253
app/page.tsx
253
app/page.tsx
@@ -1,7 +1,256 @@
|
|||||||
export default function Page() {
|
import { Container } from "@/components/ui/Container";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
||||||
|
import { Card } from "@/components/ui/Card";
|
||||||
|
import { FadeIn } from "@/components/ui/FadeIn";
|
||||||
|
import { Dumbbell, Users, Clock, Heart } from "lucide-react";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Sportbox Reutte – Dein Fitnessstudio in Reutte",
|
||||||
|
description:
|
||||||
|
"Willkommen bei Sportbox Reutte – Ihr Fitnessstudio für individuelle Trainingspläne und persönliche Betreuung in Reutte, Tirol.",
|
||||||
|
openGraph: {
|
||||||
|
title: "Sportbox Reutte – Dein Fitnessstudio in Reutte",
|
||||||
|
description:
|
||||||
|
"Ihr Fitnessstudio für individuelle Trainingspläne und persönliche Betreuung in Reutte, Tirol.",
|
||||||
|
url: "https://sportbox-reutte.at",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const FEATURES = [
|
||||||
|
{
|
||||||
|
icon: Dumbbell,
|
||||||
|
title: "Individuelle Betreuung",
|
||||||
|
description:
|
||||||
|
"Persönliche Trainingspläne, abgestimmt auf Ihre Ziele und Ihr Fitnesslevel.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Users,
|
||||||
|
title: "Starke Community",
|
||||||
|
description:
|
||||||
|
"Trainieren Sie in einer motivierenden Gemeinschaft, die Sie unterstützt und inspiriert.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Clock,
|
||||||
|
title: "Flexible Zeiten",
|
||||||
|
description:
|
||||||
|
"Großzügige Öffnungszeiten, damit Fitness in Ihren Alltag passt.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Heart,
|
||||||
|
title: "Ganzheitlicher Ansatz",
|
||||||
|
description:
|
||||||
|
"Körperliche Fitness, mentale Gesundheit und Ernährung – alles unter einem Dach.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const SERVICES_PREVIEW = [
|
||||||
|
{
|
||||||
|
title: "Personal Training",
|
||||||
|
description:
|
||||||
|
"Eins-zu-Eins Betreuung durch zertifizierte Trainer für maximale Ergebnisse.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Gruppenkurse",
|
||||||
|
description:
|
||||||
|
"Von Yoga bis Krafttraining – vielfältige Kurse für jedes Fitnesslevel.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Ernährungsberatung",
|
||||||
|
description:
|
||||||
|
"Professionelle Beratung für eine Ernährung, die Ihre Trainingsziele unterstützt.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<p>Ready</p>
|
{/* Hero Section */}
|
||||||
|
<section className="relative flex items-center min-h-screen bg-primary text-secondary">
|
||||||
|
<Container className="py-32 md:py-40">
|
||||||
|
<FadeIn>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-secondary/60 mb-6">
|
||||||
|
Fitnessstudio in Reutte, Tirol
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<h1
|
||||||
|
className="font-bold max-w-3xl"
|
||||||
|
style={{
|
||||||
|
fontSize: "clamp(var(--text-4xl), 5vw, var(--text-6xl))",
|
||||||
|
lineHeight: "1.1",
|
||||||
|
letterSpacing: "var(--text-5xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Dein Weg zu mehr
|
||||||
|
<br />
|
||||||
|
Fitness beginnt hier.
|
||||||
|
</h1>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.2}>
|
||||||
|
<p className="mt-6 max-w-xl text-secondary/70" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
Individuelle Trainingspläne, persönliche Betreuung und eine
|
||||||
|
motivierende Community. Willkommen bei Sportbox Reutte.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.3}>
|
||||||
|
<div className="mt-10 flex flex-col sm:flex-row gap-4">
|
||||||
|
<Button href="/leistungen#kontakt" variant="secondary" className="border-secondary text-secondary hover:bg-secondary hover:text-primary">
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Button>
|
||||||
|
<Button href="/über-uns" variant="ghost" className="text-secondary/80 hover:text-secondary">
|
||||||
|
Mehr über uns erfahren
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Features Section */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Warum Sportbox Reutte?"
|
||||||
|
subtitle="Wir bieten mehr als nur ein Fitnessstudio – wir sind Ihr Partner auf dem Weg zu einem gesünderen Leben."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{FEATURES.map((feature, index) => (
|
||||||
|
<FadeIn key={feature.title} delay={index * 0.1}>
|
||||||
|
<Card className="text-center h-full">
|
||||||
|
<feature.icon
|
||||||
|
size={32}
|
||||||
|
className="mx-auto mb-4 text-primary"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<h3 className="text-base font-bold mb-2">{feature.title}</h3>
|
||||||
|
<p className="text-sm text-muted">{feature.description}</p>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* About Teaser Section */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-neutral">
|
||||||
|
<Container>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||||
|
<FadeIn>
|
||||||
|
<div
|
||||||
|
className="aspect-[4/3] bg-accent/40 flex items-center justify-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<p className="text-muted text-sm">PLACEHOLDER: Studio-Bild</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.15}>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-muted mb-4">
|
||||||
|
Über uns
|
||||||
|
</p>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-6"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
letterSpacing: "var(--text-3xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Mehr als nur Training – eine Gemeinschaft.
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted mb-6 leading-relaxed">
|
||||||
|
Bei Sportbox Reutte stehen Sie im Mittelpunkt. Unser erfahrenes
|
||||||
|
Team begleitet Sie auf Ihrem individuellen Weg – ob Einsteiger
|
||||||
|
oder Fortgeschrittener. Gemeinsam erreichen wir Ihre Ziele.
|
||||||
|
</p>
|
||||||
|
<Button href="/über-uns" variant="secondary">
|
||||||
|
Mehr über uns erfahren
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Services Preview Section */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Unsere Leistungen"
|
||||||
|
subtitle="Entdecken Sie unser vielfältiges Angebot für Ihre persönlichen Fitnessziele."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{SERVICES_PREVIEW.map((service, index) => (
|
||||||
|
<FadeIn key={service.title} delay={index * 0.1}>
|
||||||
|
<Card className="h-full">
|
||||||
|
<h3 className="text-lg font-bold mb-3">{service.title}</h3>
|
||||||
|
<p className="text-sm text-muted mb-4">{service.description}</p>
|
||||||
|
<Button href="/leistungen" variant="ghost" className="text-sm">
|
||||||
|
Mehr erfahren
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA Section */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container className="text-center">
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-4"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Bereit für den ersten Schritt?
|
||||||
|
</h2>
|
||||||
|
<p className="text-secondary/70 mb-8 max-w-lg mx-auto">
|
||||||
|
Vereinbaren Sie jetzt einen unverbindlichen Termin und lernen Sie
|
||||||
|
Sportbox Reutte kennen.
|
||||||
|
</p>
|
||||||
|
<Button
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
variant="secondary"
|
||||||
|
className="border-secondary text-secondary hover:bg-secondary hover:text-primary"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Button>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* JSON-LD Structured Data */}
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: JSON.stringify({
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "LocalBusiness",
|
||||||
|
name: "Sportbox Reutte",
|
||||||
|
description:
|
||||||
|
"Fitnessstudio für individuelle Trainingspläne und persönliche Betreuung in Reutte, Tirol.",
|
||||||
|
url: "https://sportbox-reutte.at",
|
||||||
|
telephone: "+43123456789",
|
||||||
|
email: "kontakt@sportbox-reutte.at",
|
||||||
|
address: {
|
||||||
|
"@type": "PostalAddress",
|
||||||
|
addressLocality: "Reutte",
|
||||||
|
addressRegion: "Tirol",
|
||||||
|
addressCountry: "AT",
|
||||||
|
},
|
||||||
|
sameAs: [],
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
222
app/studio/page.tsx
Normal file
222
app/studio/page.tsx
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import { Container } from "@/components/ui/Container";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
||||||
|
import { Card } from "@/components/ui/Card";
|
||||||
|
import { FadeIn } from "@/components/ui/FadeIn";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Studio",
|
||||||
|
description:
|
||||||
|
"Entdecken Sie unsere modernen Räumlichkeiten und hochwertigen Fitnessgeräte bei Sportbox Reutte.",
|
||||||
|
openGraph: {
|
||||||
|
title: "Studio | Sportbox Reutte",
|
||||||
|
description:
|
||||||
|
"Moderne Räumlichkeiten und hochwertige Fitnessgeräte bei Sportbox Reutte in Reutte, Tirol.",
|
||||||
|
url: "https://sportbox-reutte.at/studio",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const AREAS = [
|
||||||
|
{
|
||||||
|
title: "Kraftbereich",
|
||||||
|
description:
|
||||||
|
"Freie Gewichte, Kabelzüge und Kraftgeräte der neuesten Generation für effektives Krafttraining.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cardio-Zone",
|
||||||
|
description:
|
||||||
|
"Laufbänder, Ergometer und Rudergeräte für Ihr Ausdauertraining in angenehmer Atmosphäre.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Functional Area",
|
||||||
|
description:
|
||||||
|
"Offener Trainingsbereich mit Rig, Kettlebells, Medizinbällen und TRX für funktionelles Training.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Kursraum",
|
||||||
|
description:
|
||||||
|
"Großer, heller Kursraum für Yoga, Pilates, HIIT und weitere Gruppenkurse.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Umkleiden & Duschen",
|
||||||
|
description:
|
||||||
|
"Moderne Umkleidebereiche mit Schließfächern, Duschen und allem, was Sie brauchen.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Lounge",
|
||||||
|
description:
|
||||||
|
"Gemütlicher Aufenthaltsbereich zum Ankommen, Austauschen und Entspannen nach dem Training.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const GALLERY_ITEMS = [
|
||||||
|
"Trainingsbereich",
|
||||||
|
"Cardio-Zone",
|
||||||
|
"Kursraum",
|
||||||
|
"Eingangsbereich",
|
||||||
|
"Functional Area",
|
||||||
|
"Lounge",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function StudioPage() {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="pt-32 pb-[var(--spacing-section)] md:pt-40 md:pb-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-secondary/60 mb-4">
|
||||||
|
Unser Studio
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<h1
|
||||||
|
className="font-bold max-w-2xl"
|
||||||
|
style={{
|
||||||
|
fontSize: "clamp(var(--text-3xl), 4vw, var(--text-5xl))",
|
||||||
|
lineHeight: "1.1",
|
||||||
|
letterSpacing: "var(--text-5xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Ihr Trainingsraum in Reutte.
|
||||||
|
</h1>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.2}>
|
||||||
|
<p className="mt-6 max-w-xl text-secondary/70" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
Moderne Geräte, durchdachte Räumlichkeiten und eine Atmosphäre,
|
||||||
|
die zum Training motiviert.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Studio Overview */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||||
|
<FadeIn>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-muted mb-4">
|
||||||
|
Räumlichkeiten
|
||||||
|
</p>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-6"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
letterSpacing: "var(--text-3xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Trainieren mit Blick auf das Wesentliche.
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted leading-relaxed mb-4">
|
||||||
|
Unser Studio verbindet Funktionalität mit Atmosphäre. Auf
|
||||||
|
großzügiger Fläche finden Sie alles, was Sie für ein
|
||||||
|
effektives Training brauchen – ohne Ablenkung, aber mit Stil.
|
||||||
|
</p>
|
||||||
|
<p className="text-muted leading-relaxed">
|
||||||
|
Hochwertige Geräte namhafter Hersteller, durchdachte
|
||||||
|
Trainingszonen und eine klare Raumaufteilung sorgen dafür,
|
||||||
|
dass Sie sich auf das konzentrieren können, was zählt: Ihr
|
||||||
|
Training.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.15}>
|
||||||
|
<div
|
||||||
|
className="aspect-[4/3] bg-neutral flex items-center justify-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<p className="text-muted text-sm">PLACEHOLDER: Studio-Übersicht</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Areas */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-neutral">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Unsere Bereiche"
|
||||||
|
subtitle="Jeder Bereich ist darauf ausgelegt, Ihnen das bestmögliche Trainingserlebnis zu bieten."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{AREAS.map((area, index) => (
|
||||||
|
<FadeIn key={area.title} delay={index * 0.08}>
|
||||||
|
<Card className="h-full bg-background">
|
||||||
|
<h3 className="text-base font-bold mb-2">{area.title}</h3>
|
||||||
|
<p className="text-sm text-muted">{area.description}</p>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Gallery */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Einblicke"
|
||||||
|
subtitle="Werfen Sie einen Blick in unsere Räumlichkeiten."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{GALLERY_ITEMS.map((item, index) => (
|
||||||
|
<FadeIn key={item} delay={index * 0.05}>
|
||||||
|
<div
|
||||||
|
className="aspect-[4/3] bg-neutral flex items-center justify-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<p className="text-muted text-sm">PLACEHOLDER: {item}</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container className="text-center">
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-4"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Überzeugen Sie sich selbst.
|
||||||
|
</h2>
|
||||||
|
<p className="text-secondary/70 mb-8 max-w-lg mx-auto">
|
||||||
|
Vereinbaren Sie einen Termin für ein unverbindliches Probetraining
|
||||||
|
in unserem Studio.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||||
|
<Button
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
variant="secondary"
|
||||||
|
className="border-secondary text-secondary hover:bg-secondary hover:text-primary"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
href="/leistungen"
|
||||||
|
variant="ghost"
|
||||||
|
className="text-secondary/80 hover:text-secondary"
|
||||||
|
>
|
||||||
|
Unsere Leistungen
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
231
app/über-uns/page.tsx
Normal file
231
app/über-uns/page.tsx
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
import { Container } from "@/components/ui/Container";
|
||||||
|
import { Button } from "@/components/ui/Button";
|
||||||
|
import { SectionHeading } from "@/components/ui/SectionHeading";
|
||||||
|
import { Card } from "@/components/ui/Card";
|
||||||
|
import { FadeIn } from "@/components/ui/FadeIn";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Über Uns",
|
||||||
|
description:
|
||||||
|
"Erfahren Sie mehr über unsere Philosophie, unser Team und unsere Community bei Sportbox Reutte.",
|
||||||
|
openGraph: {
|
||||||
|
title: "Über Uns | Sportbox Reutte",
|
||||||
|
description:
|
||||||
|
"Erfahren Sie mehr über unsere Philosophie, unser Team und unsere Community bei Sportbox Reutte.",
|
||||||
|
url: "https://sportbox-reutte.at/über-uns",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const TEAM_MEMBERS = [
|
||||||
|
{
|
||||||
|
name: "Max Mustermann",
|
||||||
|
role: "Gründer & Head Coach",
|
||||||
|
description:
|
||||||
|
"Zertifizierter Personal Trainer mit über 10 Jahren Erfahrung in der Fitnessbranche.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Anna Berger",
|
||||||
|
role: "Yoga & Mobility Coach",
|
||||||
|
description:
|
||||||
|
"Spezialisiert auf Yoga, Stretching und ganzheitliche Bewegungskonzepte.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Thomas Huber",
|
||||||
|
role: "Kraft- & Konditionstrainer",
|
||||||
|
description:
|
||||||
|
"Experte für Krafttraining, Athletiktraining und Wettkampfvorbereitung.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Lisa Mayer",
|
||||||
|
role: "Ernährungsberaterin",
|
||||||
|
description:
|
||||||
|
"Diplomierte Ernährungsberaterin mit Fokus auf sportgerechte Ernährung.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const VALUES = [
|
||||||
|
{
|
||||||
|
title: "Individualität",
|
||||||
|
description:
|
||||||
|
"Jeder Mensch ist einzigartig. Unsere Trainingspläne werden individuell auf Sie abgestimmt.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Gemeinschaft",
|
||||||
|
description:
|
||||||
|
"Gemeinsam erreichen wir mehr. Unsere Community motiviert und unterstützt sich gegenseitig.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Qualität",
|
||||||
|
description:
|
||||||
|
"Von der Ausbildung unserer Trainer bis zur Auswahl der Geräte – wir setzen auf höchste Qualität.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export default function UeberUnsPage() {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
{/* Hero */}
|
||||||
|
<section className="pt-32 pb-[var(--spacing-section)] md:pt-40 md:pb-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-secondary/60 mb-4">
|
||||||
|
Über uns
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.1}>
|
||||||
|
<h1
|
||||||
|
className="font-bold max-w-2xl"
|
||||||
|
style={{
|
||||||
|
fontSize: "clamp(var(--text-3xl), 4vw, var(--text-5xl))",
|
||||||
|
lineHeight: "1.1",
|
||||||
|
letterSpacing: "var(--text-5xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Wir glauben an die Kraft der Bewegung.
|
||||||
|
</h1>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.2}>
|
||||||
|
<p className="mt-6 max-w-xl text-secondary/70" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
Seit der Gründung von Sportbox Reutte verfolgen wir eine klare
|
||||||
|
Mission: Fitness für jeden zugänglich und persönlich zu gestalten.
|
||||||
|
</p>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Philosophy */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
|
||||||
|
<FadeIn>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm uppercase tracking-[0.2em] text-muted mb-4">
|
||||||
|
Unsere Philosophie
|
||||||
|
</p>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-6"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
letterSpacing: "var(--text-3xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Fitness ist kein Ziel – es ist ein Weg.
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted leading-relaxed mb-4">
|
||||||
|
Bei Sportbox Reutte geht es nicht um kurzfristige Ergebnisse.
|
||||||
|
Wir begleiten Sie auf einem nachhaltigen Weg zu mehr
|
||||||
|
Wohlbefinden, Kraft und Lebensqualität.
|
||||||
|
</p>
|
||||||
|
<p className="text-muted leading-relaxed">
|
||||||
|
Unser ganzheitlicher Ansatz verbindet körperliches Training,
|
||||||
|
mentale Gesundheit und ausgewogene Ernährung. Denn wahre
|
||||||
|
Fitness entsteht, wenn Körper und Geist im Einklang sind.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
<FadeIn delay={0.15}>
|
||||||
|
<div
|
||||||
|
className="aspect-[4/3] bg-neutral flex items-center justify-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<p className="text-muted text-sm">PLACEHOLDER: Team-Bild</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Values */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-neutral">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Unsere Werte"
|
||||||
|
subtitle="Was uns antreibt und was Sie von uns erwarten können."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{VALUES.map((value, index) => (
|
||||||
|
<FadeIn key={value.title} delay={index * 0.1}>
|
||||||
|
<Card className="h-full bg-background">
|
||||||
|
<h3 className="text-lg font-bold mb-3">{value.title}</h3>
|
||||||
|
<p className="text-sm text-muted">{value.description}</p>
|
||||||
|
</Card>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Team */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)]">
|
||||||
|
<Container>
|
||||||
|
<FadeIn>
|
||||||
|
<SectionHeading
|
||||||
|
title="Unser Team"
|
||||||
|
subtitle="Lernen Sie die Menschen kennen, die Sportbox Reutte ausmachen."
|
||||||
|
/>
|
||||||
|
</FadeIn>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{TEAM_MEMBERS.map((member, index) => (
|
||||||
|
<FadeIn key={member.name} delay={index * 0.1}>
|
||||||
|
<div className="text-center">
|
||||||
|
<div
|
||||||
|
className="w-full aspect-square bg-neutral mb-4 flex items-center justify-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<p className="text-muted text-xs">PLACEHOLDER: Foto</p>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-base font-bold">{member.name}</h3>
|
||||||
|
<p className="text-sm text-muted mt-1">{member.role}</p>
|
||||||
|
<p className="text-sm text-muted mt-2">
|
||||||
|
{member.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<section className="py-[var(--spacing-section)] md:py-[var(--spacing-4xl)] bg-primary text-secondary">
|
||||||
|
<Container className="text-center">
|
||||||
|
<FadeIn>
|
||||||
|
<h2
|
||||||
|
className="font-bold mb-4"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Werden Sie Teil unserer Community.
|
||||||
|
</h2>
|
||||||
|
<p className="text-secondary/70 mb-8 max-w-lg mx-auto">
|
||||||
|
Kommen Sie vorbei und überzeugen Sie sich selbst von der
|
||||||
|
Sportbox-Atmosphäre.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||||
|
<Button
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
variant="secondary"
|
||||||
|
className="border-secondary text-secondary hover:bg-secondary hover:text-primary"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
href="/studio"
|
||||||
|
variant="ghost"
|
||||||
|
className="text-secondary/80 hover:text-secondary"
|
||||||
|
>
|
||||||
|
Unser Studio entdecken
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FadeIn>
|
||||||
|
</Container>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
97
components/layout/Footer.tsx
Normal file
97
components/layout/Footer.tsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { Mail, Phone, MapPin } from "lucide-react";
|
||||||
|
|
||||||
|
const NAV_LINKS = [
|
||||||
|
{ href: "/", label: "Startseite" },
|
||||||
|
{ href: "/über-uns", label: "Über Uns" },
|
||||||
|
{ href: "/leistungen", label: "Leistungen" },
|
||||||
|
{ href: "/studio", label: "Studio" },
|
||||||
|
{ href: "/aktuelles", label: "Aktuelles" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="bg-primary text-secondary" role="contentinfo">
|
||||||
|
<div
|
||||||
|
className="mx-auto px-[var(--spacing-container-padding)] py-16"
|
||||||
|
style={{ maxWidth: "var(--spacing-container)" }}
|
||||||
|
>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-12">
|
||||||
|
<div>
|
||||||
|
<p className="text-xl font-bold tracking-tight mb-4">SPORTBOX</p>
|
||||||
|
<p className="text-sm text-secondary/70 leading-relaxed">
|
||||||
|
Ihr Fitnessstudio in Reutte für individuelle Trainingspläne
|
||||||
|
und persönliche Betreuung.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-bold uppercase tracking-widest mb-4">
|
||||||
|
Navigation
|
||||||
|
</p>
|
||||||
|
<nav aria-label="Footer-Navigation">
|
||||||
|
<ul className="space-y-2">
|
||||||
|
{NAV_LINKS.map((link) => (
|
||||||
|
<li key={link.href}>
|
||||||
|
<Link
|
||||||
|
href={link.href}
|
||||||
|
className="text-sm text-secondary/70 transition-colors hover:text-secondary"
|
||||||
|
>
|
||||||
|
{link.label}
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-bold uppercase tracking-widest mb-4">
|
||||||
|
Kontakt
|
||||||
|
</p>
|
||||||
|
<address className="not-italic space-y-3">
|
||||||
|
<a
|
||||||
|
href="mailto:kontakt@sportbox-reutte.at"
|
||||||
|
className="flex items-center gap-2 text-sm text-secondary/70 transition-colors hover:text-secondary"
|
||||||
|
>
|
||||||
|
<Mail size={16} aria-hidden="true" />
|
||||||
|
kontakt@sportbox-reutte.at
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="tel:+43123456789"
|
||||||
|
className="flex items-center gap-2 text-sm text-secondary/70 transition-colors hover:text-secondary"
|
||||||
|
>
|
||||||
|
<Phone size={16} aria-hidden="true" />
|
||||||
|
+43 123 456 789
|
||||||
|
</a>
|
||||||
|
<p className="flex items-center gap-2 text-sm text-secondary/70">
|
||||||
|
<MapPin size={16} aria-hidden="true" />
|
||||||
|
Reutte, Tirol, Österreich
|
||||||
|
</p>
|
||||||
|
</address>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 pt-8 border-t border-secondary/20 flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||||
|
<p className="text-xs text-secondary/50">
|
||||||
|
© {new Date().getFullYear()} Sportbox Reutte. Alle Rechte vorbehalten.
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<Link
|
||||||
|
href="/impressum"
|
||||||
|
className="text-xs text-secondary/50 transition-colors hover:text-secondary"
|
||||||
|
>
|
||||||
|
Impressum
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
href="/datenschutz"
|
||||||
|
className="text-xs text-secondary/50 transition-colors hover:text-secondary"
|
||||||
|
>
|
||||||
|
Datenschutz
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
97
components/layout/Header.tsx
Normal file
97
components/layout/Header.tsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { Menu, X } from "lucide-react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
|
const NAV_ITEMS = [
|
||||||
|
{ href: "/", label: "Startseite" },
|
||||||
|
{ href: "/über-uns", label: "Über Uns" },
|
||||||
|
{ href: "/leistungen", label: "Leistungen" },
|
||||||
|
{ href: "/studio", label: "Studio" },
|
||||||
|
{ href: "/aktuelles", label: "Aktuelles" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="fixed top-0 left-0 right-0 z-50 bg-background/95 backdrop-blur-sm border-b border-border">
|
||||||
|
<div className="mx-auto flex items-center justify-between px-[var(--spacing-container-padding)] py-4" style={{ maxWidth: "var(--spacing-container)" }}>
|
||||||
|
<Link href="/" className="text-xl font-bold tracking-tight" aria-label="Sportbox Reutte – Zur Startseite">
|
||||||
|
SPORTBOX
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<nav className="hidden lg:flex items-center gap-8" aria-label="Hauptnavigation">
|
||||||
|
{NAV_ITEMS.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className={`text-sm tracking-wide transition-colors hover:text-muted ${
|
||||||
|
pathname === item.href ? "font-bold" : "font-normal"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<Link
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
className="bg-primary text-secondary px-5 py-2.5 text-sm font-medium transition-opacity hover:opacity-80"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="lg:hidden p-2 -mr-2"
|
||||||
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||||
|
aria-expanded={mobileMenuOpen}
|
||||||
|
aria-controls="mobile-menu"
|
||||||
|
aria-label={mobileMenuOpen ? "Menü schließen" : "Menü öffnen"}
|
||||||
|
>
|
||||||
|
{mobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AnimatePresence>
|
||||||
|
{mobileMenuOpen && (
|
||||||
|
<motion.nav
|
||||||
|
id="mobile-menu"
|
||||||
|
initial={{ height: 0, opacity: 0 }}
|
||||||
|
animate={{ height: "auto", opacity: 1 }}
|
||||||
|
exit={{ height: 0, opacity: 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="lg:hidden overflow-hidden border-t border-border bg-background"
|
||||||
|
aria-label="Mobile Navigation"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col px-[var(--spacing-container-padding)] py-4 gap-1">
|
||||||
|
{NAV_ITEMS.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
|
className={`py-3 text-base transition-colors hover:text-muted ${
|
||||||
|
pathname === item.href ? "font-bold" : "font-normal"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<Link
|
||||||
|
href="/leistungen#kontakt"
|
||||||
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
|
className="mt-2 bg-primary text-secondary px-5 py-3 text-center text-sm font-medium transition-opacity hover:opacity-80"
|
||||||
|
>
|
||||||
|
Jetzt Termin buchen
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</motion.nav>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
99
components/sections/FaqSection.tsx
Normal file
99
components/sections/FaqSection.tsx
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
|
|
||||||
|
const FAQ_ITEMS = [
|
||||||
|
{
|
||||||
|
question: "Brauche ich Vorkenntnisse, um bei Sportbox zu trainieren?",
|
||||||
|
answer:
|
||||||
|
"Nein, bei uns sind alle willkommen – vom absoluten Anfänger bis zum erfahrenen Sportler. Unsere Trainer erstellen individuelle Trainingspläne und begleiten Sie in Ihrem Tempo.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Bieten Sie ein Probetraining an?",
|
||||||
|
answer:
|
||||||
|
"Ja, wir bieten ein unverbindliches Probetraining an. Kontaktieren Sie uns einfach über das Kontaktformular oder telefonisch, um einen Termin zu vereinbaren.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Welche Öffnungszeiten haben Sie?",
|
||||||
|
answer:
|
||||||
|
"Wir haben großzügige Öffnungszeiten, damit Fitness in Ihren Alltag passt. Die aktuellen Öffnungszeiten erfahren Sie bei uns vor Ort oder telefonisch.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Welche Kurse bieten Sie an?",
|
||||||
|
answer:
|
||||||
|
"Unser Kursangebot umfasst unter anderem Yoga, Pilates, HIIT, Functional Training und Krafttraining. Die Kurse sind für verschiedene Fitnesslevel geeignet. Schauen Sie sich unsere Leistungen an oder fragen Sie direkt bei uns an.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Kann ich meinen Trainingsplan anpassen lassen?",
|
||||||
|
answer:
|
||||||
|
"Selbstverständlich. Wir passen Ihren Trainingsplan regelmäßig an Ihre Fortschritte und sich verändernde Ziele an. Sprechen Sie einfach Ihren Trainer an.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
question: "Bieten Sie auch Online-Training an?",
|
||||||
|
answer:
|
||||||
|
"Wir arbeiten daran, unser Angebot um Online-Trainingsmöglichkeiten zu erweitern. Kontaktieren Sie uns für aktuelle Informationen.",
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export function FaqSection() {
|
||||||
|
const [openIndex, setOpenIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
function toggle(index: number) {
|
||||||
|
setOpenIndex(openIndex === index ? null : index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-3xl mx-auto space-y-2">
|
||||||
|
{FAQ_ITEMS.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="border border-border bg-background"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => toggle(index)}
|
||||||
|
aria-expanded={openIndex === index}
|
||||||
|
className="w-full flex items-center justify-between px-6 py-4 text-left text-sm font-medium"
|
||||||
|
>
|
||||||
|
<span>{item.question}</span>
|
||||||
|
<ChevronDown
|
||||||
|
size={18}
|
||||||
|
className={`shrink-0 ml-4 transition-transform duration-200 ${
|
||||||
|
openIndex === index ? "rotate-180" : ""
|
||||||
|
}`}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{openIndex === index && (
|
||||||
|
<div className="px-6 pb-4">
|
||||||
|
<p className="text-sm text-muted leading-relaxed">
|
||||||
|
{item.answer}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* FAQ Schema Markup */}
|
||||||
|
<script
|
||||||
|
type="application/ld+json"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: JSON.stringify({
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "FAQPage",
|
||||||
|
mainEntity: FAQ_ITEMS.map((item) => ({
|
||||||
|
"@type": "Question",
|
||||||
|
name: item.question,
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: item.answer,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
components/ui/Button.tsx
Normal file
45
components/ui/Button.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
href?: string;
|
||||||
|
variant?: "primary" | "secondary" | "ghost";
|
||||||
|
className?: string;
|
||||||
|
type?: "button" | "submit" | "reset";
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Button({
|
||||||
|
children,
|
||||||
|
href,
|
||||||
|
variant = "primary",
|
||||||
|
className = "",
|
||||||
|
type = "button",
|
||||||
|
onClick,
|
||||||
|
}: ButtonProps) {
|
||||||
|
const base =
|
||||||
|
"inline-flex items-center justify-center px-6 py-3 text-sm font-medium transition-all duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary";
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
primary: "bg-primary text-secondary hover:opacity-80",
|
||||||
|
secondary:
|
||||||
|
"border border-primary text-primary hover:bg-primary hover:text-secondary",
|
||||||
|
ghost: "text-primary underline-offset-4 hover:underline",
|
||||||
|
};
|
||||||
|
|
||||||
|
const classes = `${base} ${variants[variant]} ${className}`;
|
||||||
|
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<Link href={href} className={classes}>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type={type} className={classes} onClick={onClick}>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
components/ui/Card.tsx
Normal file
15
components/ui/Card.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
interface CardProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Card({ children, className = "" }: CardProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`border border-border p-6 md:p-8 transition-shadow duration-200 hover:shadow-lg ${className}`}
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
171
components/ui/ContactForm.tsx
Normal file
171
components/ui/ContactForm.tsx
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Button } from "./Button";
|
||||||
|
|
||||||
|
interface FormState {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
phone: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormErrors {
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContactForm() {
|
||||||
|
const [form, setForm] = useState<FormState>({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
message: "",
|
||||||
|
});
|
||||||
|
const [errors, setErrors] = useState<FormErrors>({});
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
function validate(): FormErrors {
|
||||||
|
const newErrors: FormErrors = {};
|
||||||
|
if (!form.name.trim()) newErrors.name = "Bitte geben Sie Ihren Namen ein.";
|
||||||
|
if (!form.email.trim()) {
|
||||||
|
newErrors.email = "Bitte geben Sie Ihre E-Mail-Adresse ein.";
|
||||||
|
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.email)) {
|
||||||
|
newErrors.email = "Bitte geben Sie eine gültige E-Mail-Adresse ein.";
|
||||||
|
}
|
||||||
|
if (!form.message.trim())
|
||||||
|
newErrors.message = "Bitte geben Sie eine Nachricht ein.";
|
||||||
|
return newErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit(e: React.FormEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
const newErrors = validate();
|
||||||
|
setErrors(newErrors);
|
||||||
|
if (Object.keys(newErrors).length === 0) {
|
||||||
|
setSubmitted(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(
|
||||||
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
|
) {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setForm((prev) => ({ ...prev, [name]: value }));
|
||||||
|
if (errors[name as keyof FormErrors]) {
|
||||||
|
setErrors((prev) => ({ ...prev, [name]: undefined }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submitted) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="border border-success p-8 text-center"
|
||||||
|
style={{ borderRadius: "var(--radius-md)" }}
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
<p className="text-lg font-bold mb-2">Vielen Dank für Ihre Nachricht!</p>
|
||||||
|
<p className="text-muted">
|
||||||
|
Wir werden uns so schnell wie möglich bei Ihnen melden.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} noValidate className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="contact-name" className="block text-sm font-medium mb-1.5">
|
||||||
|
Name <span aria-hidden="true">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="contact-name"
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
autoComplete="name"
|
||||||
|
aria-required="true"
|
||||||
|
aria-invalid={!!errors.name}
|
||||||
|
aria-describedby={errors.name ? "name-error" : undefined}
|
||||||
|
value={form.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
|
||||||
|
style={{ borderRadius: "var(--radius-sm)" }}
|
||||||
|
/>
|
||||||
|
{errors.name && (
|
||||||
|
<p id="name-error" className="mt-1.5 text-sm text-error" role="alert">
|
||||||
|
{errors.name}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="contact-email" className="block text-sm font-medium mb-1.5">
|
||||||
|
E-Mail <span aria-hidden="true">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="contact-email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
autoComplete="email"
|
||||||
|
aria-required="true"
|
||||||
|
aria-invalid={!!errors.email}
|
||||||
|
aria-describedby={errors.email ? "email-error" : undefined}
|
||||||
|
value={form.email}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
|
||||||
|
style={{ borderRadius: "var(--radius-sm)" }}
|
||||||
|
/>
|
||||||
|
{errors.email && (
|
||||||
|
<p id="email-error" className="mt-1.5 text-sm text-error" role="alert">
|
||||||
|
{errors.email}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="contact-phone" className="block text-sm font-medium mb-1.5">
|
||||||
|
Telefon
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="contact-phone"
|
||||||
|
name="phone"
|
||||||
|
type="tel"
|
||||||
|
autoComplete="tel"
|
||||||
|
value={form.phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none"
|
||||||
|
style={{ borderRadius: "var(--radius-sm)" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="contact-message" className="block text-sm font-medium mb-1.5">
|
||||||
|
Nachricht <span aria-hidden="true">*</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="contact-message"
|
||||||
|
name="message"
|
||||||
|
rows={5}
|
||||||
|
aria-required="true"
|
||||||
|
aria-invalid={!!errors.message}
|
||||||
|
aria-describedby={errors.message ? "message-error" : undefined}
|
||||||
|
value={form.message}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full border border-border px-4 py-3 text-sm bg-background text-foreground transition-colors focus:border-primary focus:outline-none resize-y"
|
||||||
|
style={{ borderRadius: "var(--radius-sm)" }}
|
||||||
|
/>
|
||||||
|
{errors.message && (
|
||||||
|
<p id="message-error" className="mt-1.5 text-sm text-error" role="alert">
|
||||||
|
{errors.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" variant="primary">
|
||||||
|
Nachricht senden
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
components/ui/Container.tsx
Normal file
20
components/ui/Container.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
interface ContainerProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
as?: "div" | "section" | "article";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Container({
|
||||||
|
children,
|
||||||
|
className = "",
|
||||||
|
as: Component = "div",
|
||||||
|
}: ContainerProps) {
|
||||||
|
return (
|
||||||
|
<Component
|
||||||
|
className={`mx-auto px-[var(--spacing-container-padding)] ${className}`}
|
||||||
|
style={{ maxWidth: "var(--spacing-container)" }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
components/ui/FadeIn.tsx
Normal file
23
components/ui/FadeIn.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
interface FadeInProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
delay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FadeIn({ children, className = "", delay = 0 }: FadeInProps) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true, margin: "-50px" }}
|
||||||
|
transition={{ duration: 0.5, delay, ease: "easeOut" }}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
components/ui/SectionHeading.tsx
Normal file
31
components/ui/SectionHeading.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
interface SectionHeadingProps {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
|
align?: "left" | "center";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SectionHeading({
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
align = "center",
|
||||||
|
}: SectionHeadingProps) {
|
||||||
|
return (
|
||||||
|
<div className={`mb-12 ${align === "center" ? "text-center" : "text-left"}`}>
|
||||||
|
<h2
|
||||||
|
className="text-3xl md:text-4xl font-bold tracking-tight"
|
||||||
|
style={{
|
||||||
|
fontSize: "var(--text-3xl)",
|
||||||
|
lineHeight: "var(--text-3xl-line-height)",
|
||||||
|
letterSpacing: "var(--text-3xl-letter-spacing)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
{subtitle && (
|
||||||
|
<p className="mt-4 text-muted max-w-2xl mx-auto" style={{ fontSize: "var(--text-lg)" }}>
|
||||||
|
{subtitle}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
eslint.config.mjs
Normal file
22
eslint.config.mjs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import typescriptParser from "@typescript-eslint/parser";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: [".next/**", "node_modules/**"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["**/*.ts", "**/*.tsx"],
|
||||||
|
languageOptions: {
|
||||||
|
parser: typescriptParser,
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: { jsx: true },
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
4904
package-lock.json
generated
4904
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -10,20 +10,24 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^15.1.0",
|
|
||||||
"react": "^19.0.0",
|
|
||||||
"react-dom": "^19.0.0",
|
|
||||||
"framer-motion": "^11.15.0",
|
"framer-motion": "^11.15.0",
|
||||||
"lenis": "^1.1.18",
|
"lenis": "^1.1.18",
|
||||||
"lucide-react": "^0.469.0"
|
"lucide-react": "^0.469.0",
|
||||||
|
"next": "^15.1.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.3",
|
||||||
|
"@tailwindcss/postcss": "^4.0.0",
|
||||||
"@types/node": "^22.10.0",
|
"@types/node": "^22.10.0",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
"typescript": "^5.7.0",
|
"@typescript-eslint/parser": "^8.54.0",
|
||||||
|
"eslint": "^9.39.2",
|
||||||
|
"eslint-config-next": "^16.1.6",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"@tailwindcss/postcss": "^4.0.0"
|
"typescript": "^5.7.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
|
|||||||
4
public/robots.txt
Normal file
4
public/robots.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://sportbox-reutte.at/sitemap.xml
|
||||||
@@ -6,6 +6,50 @@
|
|||||||
|
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
/* ── Tailwind v4 Theme ───────────────────────────────── */
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
/* Colors */
|
||||||
|
--color-primary: #000000;
|
||||||
|
--color-secondary: #FFFFFF;
|
||||||
|
--color-accent: #d6d6d6;
|
||||||
|
--color-neutral: #F5F5F5;
|
||||||
|
--color-background: #FFFFFF;
|
||||||
|
--color-foreground: #000000;
|
||||||
|
--color-muted: #B0B0B0;
|
||||||
|
--color-muted-foreground: #ffffff;
|
||||||
|
--color-border: #E0E0E0;
|
||||||
|
--color-success: #4CAF50;
|
||||||
|
--color-warning: #FF9800;
|
||||||
|
--color-error: #F44336;
|
||||||
|
|
||||||
|
/* Fonts */
|
||||||
|
--font-heading: 'Inter', sans-serif;
|
||||||
|
--font-body: 'Inter', sans-serif;
|
||||||
|
|
||||||
|
/* Typography Scale – override Tailwind defaults to match design tokens */
|
||||||
|
--text-xl: 1.5rem;
|
||||||
|
--text-xl--line-height: 2rem;
|
||||||
|
--text-2xl: 1.75rem;
|
||||||
|
--text-2xl--line-height: 2.25rem;
|
||||||
|
--text-3xl: 2rem;
|
||||||
|
--text-3xl--line-height: 2.5rem;
|
||||||
|
--text-4xl: 2.5rem;
|
||||||
|
--text-4xl--line-height: 2.75rem;
|
||||||
|
--text-5xl: 3rem;
|
||||||
|
--text-5xl--line-height: 1.1;
|
||||||
|
--text-6xl: 3.75rem;
|
||||||
|
--text-6xl--line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Font Override (connect next/font to CSS vars) ──── */
|
||||||
|
/* --font-inter is injected by next/font/google on <html> */
|
||||||
|
|
||||||
|
html {
|
||||||
|
--font-heading: var(--font-inter, 'Inter', sans-serif);
|
||||||
|
--font-body: var(--font-inter, 'Inter', sans-serif);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Base Resets ───────────────────────────────────── */
|
/* ── Base Resets ───────────────────────────────────── */
|
||||||
|
|
||||||
*, *::before, *::after {
|
*, *::before, *::after {
|
||||||
@@ -39,14 +83,10 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
/* ── Links ───────────────────────────────────────── */
|
/* ── Links ───────────────────────────────────────── */
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-primary);
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Images ──────────────────────────────────────── */
|
/* ── Images ──────────────────────────────────────── */
|
||||||
|
|
||||||
img, video, svg {
|
img, video, svg {
|
||||||
@@ -68,3 +108,15 @@ img, video, svg {
|
|||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
color: var(--color-background);
|
color: var(--color-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Reduced Motion ─────────────────────────────── */
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user