feat: add restaurant and about us pages with animated sections

- Implemented RestaurantPage with features, atmosphere, gallery, and contact CTA.
- Created UeberUnsPage detailing the history, values, and team of Storfwirt.
- Added AnimatedSection component for smooth animations on scroll.
- Introduced ContactCTA component for event inquiries.
- Developed ContactForm component for user inquiries with validation.
- Created Footer and Header components for site navigation and branding.
- Added PlaceholderImage and Section components for consistent layout.
- Configured TypeScript settings in tsconfig.json for improved development experience.
This commit is contained in:
1elle1
2026-02-23 17:29:53 +01:00
parent c97ebe5888
commit c36bc49cdd
35 changed files with 9515 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

41
website/.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

36
website/README.md Normal file
View File

@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

18
website/eslint.config.mjs Normal file
View File

@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;

7
website/next.config.ts Normal file
View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

6657
website/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

28
website/package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "website",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"framer-motion": "^12.34.3",
"lucide-react": "^0.575.0",
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.1.6",
"tailwindcss": "^4",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

1
website/public/file.svg Normal file
View File

@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

1
website/public/globe.svg Normal file
View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
website/public/next.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,210 @@
import type { Metadata } from "next";
import {
ArrowRight,
Users,
Heart,
Trophy,
PartyPopper,
CheckCircle2,
MessageSquareQuote,
} from "lucide-react";
import Link from "next/link";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { ContactCTA } from "@/components/ContactCTA";
import { PlaceholderImage } from "@/components/PlaceholderImage";
export const metadata: Metadata = {
title: "Catering",
description:
"Catering in Reutte und Umgebung für Firmenfeiern, Hochzeiten, Vereinsveranstaltungen und private Feste. Regionale Küche, verlässliche Organisation.",
};
const cateringTypes = [
{
icon: Trophy,
title: "Firmenfeiern & Seminare",
description:
"Vom Stehempfang beim Firmenjubiläum bis zum Mittagsbuffet bei der Klausurtagung. Wir kennen die Anforderungen und liefern zuverlässig.",
},
{
icon: Heart,
title: "Hochzeiten & Familienfeste",
description:
"Ihr Fest soll besonders sein das Essen auch. Wir stellen gemeinsam ein Menü zusammen, das zu Ihnen passt.",
},
{
icon: Users,
title: "Vereinsveranstaltungen",
description:
"Ob Jahreshauptversammlung oder Vereinsfest: Wir liefern für jede Größe. Unkompliziert und zu fairen Konditionen.",
},
{
icon: PartyPopper,
title: "Private Feiern",
description:
"Geburtstag, Taufe, Jubiläum was auch immer der Anlass ist, wir sorgen für gutes Essen und einen reibungslosen Ablauf.",
},
];
const steps = [
{
number: "01",
title: "Anfragen",
description:
"Sagen Sie uns, was Sie planen. Anlass, Datum, ungefähre Gästezahl mehr brauchen wir zunächst nicht.",
},
{
number: "02",
title: "Planen",
description:
"Wir besprechen mit Ihnen Menü, Ablauf und Details. Sie bekommen ein Angebot, das passt.",
},
{
number: "03",
title: "Genießen",
description:
"Am Tag Ihres Events kümmern wir uns um alles. Sie können sich auf Ihre Gäste konzentrieren.",
},
];
export default function CateringPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Catering im Außerfern
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Catering für jeden Anlass
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Sie planen ein Event? Wir bringen das Essen. Frisch gekocht,
regional beschafft, pünktlich geliefert egal ob 20 oder 200
Gäste.
</p>
<Link
href="/kontakt"
className="inline-flex items-center gap-2 mt-8 rounded-lg bg-white px-6 py-3.5 text-base font-semibold text-primary hover:bg-sand transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary-dark"
>
Catering anfragen
<ArrowRight className="h-4 w-4" />
</Link>
</AnimatedSection>
</div>
</section>
{/* Catering Types */}
<Section className="bg-cream">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-4 text-center">
Für wen wir kochen
</h2>
<p className="text-bark-light text-lg max-w-2xl mx-auto text-center mb-12">
Jedes Event ist anders. Die Qualität bleibt gleich.
</p>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{cateringTypes.map((type, index) => (
<AnimatedSection key={type.title} delay={index * 0.1}>
<div className="rounded-xl border border-driftwood bg-cream p-6 sm:p-8 h-full">
<type.icon className="h-8 w-8 text-primary mb-4" />
<h3 className="font-heading text-xl font-semibold text-bark mb-2">
{type.title}
</h3>
<p className="text-bark-light leading-relaxed">
{type.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* How it works */}
<Section className="bg-sand">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-12 text-center">
So läuft&apos;s ab
</h2>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{steps.map((step, index) => (
<AnimatedSection key={step.number} delay={index * 0.15}>
<div className="text-center">
<span className="inline-block font-heading text-5xl font-bold text-primary/20 mb-4">
{step.number}
</span>
<h3 className="font-heading text-xl font-semibold text-bark mb-2">
{step.title}
</h3>
<p className="text-bark-light leading-relaxed">
{step.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* What you get */}
<Section className="bg-cream">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<AnimatedSection>
<PlaceholderImage
icon={Users}
label="Bild: Catering bei einer Veranstaltung"
/>
</AnimatedSection>
<AnimatedSection delay={0.1}>
<h2 className="font-heading text-3xl font-bold text-bark mb-6">
Was Sie von uns erwarten können
</h2>
<ul className="space-y-4">
{[
"Frische, regionale Zutaten aus dem Außerfern",
"Individuelle Menüplanung nach Ihren Wünschen",
"Pünktliche Lieferung und professioneller Auf-/Abbau",
"Erfahrenes Servicepersonal auf Wunsch",
"Flexible Lösungen für jede Gruppengröße",
"Transparente Preise ohne versteckte Kosten",
].map((item) => (
<li key={item} className="flex items-start gap-3">
<CheckCircle2 className="h-5 w-5 text-primary shrink-0 mt-0.5" />
<span className="text-bark-light">{item}</span>
</li>
))}
</ul>
</AnimatedSection>
</div>
</Section>
{/* Testimonial */}
<Section className="bg-sand">
<AnimatedSection>
<div className="max-w-3xl mx-auto text-center">
<MessageSquareQuote className="h-10 w-10 text-primary/30 mx-auto mb-6" />
<blockquote className="font-heading text-2xl sm:text-3xl text-bark leading-snug mb-6">
&ldquo;Wir lassen seit drei Jahren jede Firmenveranstaltung vom
Storfwirt betreuen. Das Essen ist immer herausragend, die
Organisation stimmt, und wir müssen uns um nichts kümmern.&rdquo;
</blockquote>
<p className="text-bark-light">
<span className="font-medium text-bark">Christian M.</span>
{" "} Geschäftsführer, Reutte
</p>
</div>
</AnimatedSection>
</Section>
{/* CTA */}
<ContactCTA
headline="Ihr Event verdient gutes Essen"
text="Schreiben Sie uns, was Sie planen. Wir melden uns mit einem Vorschlag."
/>
</>
);
}

View File

@@ -0,0 +1,173 @@
import type { Metadata } from "next";
import { Section } from "@/components/Section";
export const metadata: Metadata = {
title: "Datenschutzerklärung",
description:
"Datenschutzerklärung des Storfwirt Reutte Informationen zur Verarbeitung personenbezogener Daten.",
robots: { index: false, follow: false },
};
export default function DatenschutzPage() {
return (
<>
<section className="bg-primary-dark pt-32 pb-12 sm:pt-40 sm:pb-16">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1 className="font-heading text-3xl sm:text-4xl font-bold text-white">
Datenschutzerklärung
</h1>
</div>
</section>
<Section className="bg-cream">
<div className="max-w-3xl mx-auto">
<div className="space-y-8 text-bark-light leading-relaxed">
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
1. Verantwortlicher
</h2>
<p>
Storfwirt Reutte
<br />
Musterstraße 1
<br />
6600 Reutte, Österreich
<br />
E-Mail:{" "}
<a
href="mailto:info@storfwirt-reutte.at"
className="text-primary hover:underline"
>
info@storfwirt-reutte.at
</a>
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
2. Erhebung und Verarbeitung personenbezogener Daten
</h2>
<p>
Beim Besuch unserer Website werden keine personenbezogenen Daten
automatisch erfasst. Wir verwenden keine Cookies, Tracking-Tools
oder Analysedienste.
</p>
<p className="mt-3">
Personenbezogene Daten werden nur erhoben, wenn Sie uns diese im
Rahmen einer Kontaktanfrage freiwillig mitteilen (z.B. Name,
E-Mail-Adresse, Telefonnummer).
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
3. Kontaktformular
</h2>
<p>
Wenn Sie unser Kontaktformular nutzen, werden die von Ihnen
angegebenen Daten (Name, E-Mail-Adresse, Telefonnummer,
Nachricht) zum Zweck der Bearbeitung Ihrer Anfrage verarbeitet.
</p>
<p className="mt-3">
Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO
(vorvertragliche Maßnahmen) bzw. Art. 6 Abs. 1 lit. a DSGVO
(Ihre Einwilligung). Die Daten werden nach Abschluss der Anfrage
und Ablauf der gesetzlichen Aufbewahrungsfristen gelöscht.
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
4. Keine Weitergabe an Dritte
</h2>
<p>
Ihre personenbezogenen Daten werden nicht an Dritte
weitergegeben, es sei denn, dies ist zur Erfüllung Ihrer Anfrage
erforderlich oder gesetzlich vorgeschrieben.
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
5. Hosting
</h2>
<p>
Unsere Website wird bei einem externen Dienstleister gehostet.
Der Hosting-Anbieter verarbeitet die technisch notwendigen Daten
(z.B. IP-Adresse, Browsertyp, Zeitpunkt des Zugriffs) im Rahmen
der Bereitstellung der Website. Rechtsgrundlage ist Art. 6
Abs. 1 lit. f DSGVO (berechtigtes Interesse an der
Bereitstellung der Website).
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
6. Ihre Rechte
</h2>
<p>Sie haben das Recht auf:</p>
<ul className="list-disc list-inside mt-2 space-y-1">
<li>Auskunft über Ihre gespeicherten Daten</li>
<li>Berichtigung unrichtiger Daten</li>
<li>Löschung Ihrer Daten</li>
<li>Einschränkung der Verarbeitung</li>
<li>Datenübertragbarkeit</li>
<li>Widerruf einer erteilten Einwilligung</li>
</ul>
<p className="mt-3">
Zur Ausübung Ihrer Rechte wenden Sie sich bitte an die oben
genannte E-Mail-Adresse.
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
7. Beschwerderecht
</h2>
<p>
Sie haben das Recht, sich bei der zuständigen Aufsichtsbehörde
zu beschweren:
</p>
<p className="mt-2">
Österreichische Datenschutzbehörde
<br />
Barichgasse 40-42
<br />
1030 Wien
<br />
<a
href="https://www.dsb.gv.at"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
www.dsb.gv.at
</a>
</p>
</div>
<div>
<h2 className="font-heading text-xl font-semibold text-bark mb-3">
8. Änderungen
</h2>
<p>
Wir behalten uns vor, diese Datenschutzerklärung bei Bedarf
anzupassen, um sie an geänderte Rechtslagen oder bei Änderungen
unserer Datenverarbeitungen anzupassen.
</p>
</div>
<div className="border-t border-driftwood pt-6 mt-8">
<p className="text-sm text-bark-light/70">
<strong className="text-bark">Hinweis:</strong> Diese
Datenschutzerklärung ist ein Entwurf und muss vor der
Veröffentlichung von einem Rechtsanwalt oder Datenschutzexperten
geprüft und an die tatsächlichen Gegebenheiten angepasst werden.
</p>
</div>
</div>
</div>
</Section>
</>
);
}

BIN
website/src/app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,159 @@
import type { Metadata } from "next";
import {
ShoppingBag,
Gift,
ArrowRight,
Milk,
Beef,
CakeSlice,
Wine,
} from "lucide-react";
import Link from "next/link";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { PlaceholderImage } from "@/components/PlaceholderImage";
export const metadata: Metadata = {
title: "Feinkost",
description:
"Regionale Feinkost und Spezialitäten aus dem Außerfern Käse, Speck, Marmeladen, Geschenkkörbe und mehr. Direkt beim Storfwirt in Reutte.",
};
const categories = [
{
icon: Milk,
title: "Käse & Milchprodukte",
description:
"Bergkäse, Almkäse, Frischkäse von Sennereien aus dem Außerfern und dem Allgäu.",
},
{
icon: Beef,
title: "Speck & Wurst",
description:
"Tiroler Speck, Kaminwurzen und Hauswürste. Geräuchert und gereift nach traditioneller Art.",
},
{
icon: CakeSlice,
title: "Marmeladen & Aufstriche",
description:
"Selbstgemacht oder von kleinen Produzenten. Saisonale Sorten, je nachdem, was die Region gerade hergibt.",
},
{
icon: Wine,
title: "Getränke & Spirituosen",
description:
"Ausgewählte Weine, Edelbrände und Liköre. Viele davon aus Tirol und dem angrenzenden Allgäu.",
},
];
export default function FeinkostPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Regionale Spezialitäten
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Feinkost aus der Region
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Gute Produkte von Menschen, die wir kennen. Käse, Speck,
Marmeladen und mehr zum Mitnehmen oder als Geschenk.
</p>
</AnimatedSection>
</div>
</section>
{/* Categories */}
<Section className="bg-cream">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-12 text-center">
Unser Sortiment
</h2>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{categories.map((category, index) => (
<AnimatedSection key={category.title} delay={index * 0.1}>
<div className="rounded-xl border border-driftwood bg-sand p-6 sm:p-8 h-full">
<category.icon className="h-8 w-8 text-accent mb-4" />
<h3 className="font-heading text-xl font-semibold text-bark mb-2">
{category.title}
</h3>
<p className="text-bark-light leading-relaxed">
{category.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* Gift Baskets */}
<Section className="bg-sand">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<AnimatedSection>
<PlaceholderImage
icon={Gift}
label="Bild: Geschenkkorb mit regionalen Produkten"
/>
</AnimatedSection>
<AnimatedSection delay={0.1}>
<Gift className="h-8 w-8 text-accent mb-4" />
<h2 className="font-heading text-3xl font-bold text-bark mb-6">
Geschenkkörbe nach Wunsch
</h2>
<div className="space-y-4 text-bark-light leading-relaxed">
<p>
Ob für Geschäftspartner, Mitarbeiter oder privat
unsere Geschenkkörbe stellen wir individuell zusammen.
Sagen Sie uns, was Ihnen vorschwebt, und wir kümmern
uns um den Rest.
</p>
<p>
Beliebt sind unsere Körbe besonders zu Weihnachten, als
Dankeschön an Kunden oder als Mitbringsel für Geburtstage.
Die Zusammenstellung variiert je nach Saison und Verfügbarkeit.
</p>
</div>
<Link
href="/kontakt"
className="inline-flex items-center gap-2 mt-6 rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-dark transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
Geschenkkorb anfragen
<ArrowRight className="h-4 w-4" />
</Link>
</AnimatedSection>
</div>
</Section>
{/* Visit us */}
<Section className="bg-cream">
<AnimatedSection>
<div className="max-w-2xl mx-auto text-center">
<ShoppingBag className="h-10 w-10 text-primary/30 mx-auto mb-4" />
<h2 className="font-heading text-3xl font-bold text-bark mb-4">
Am besten selbst vorbeikommen
</h2>
<p className="text-bark-light leading-relaxed mb-6">
Unsere Feinkost gibt es direkt bei uns im Haus. Schauen Sie vorbei,
probieren Sie und nehmen Sie mit, was Ihnen schmeckt. Wir beraten
Sie gerne persönlich.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<Link
href="/kontakt"
className="inline-flex items-center gap-2 text-primary font-medium hover:gap-3 transition-all"
>
So finden Sie uns
<ArrowRight className="h-4 w-4" />
</Link>
</div>
</div>
</AnimatedSection>
</Section>
</>
);
}

View File

@@ -0,0 +1,58 @@
@import "tailwindcss";
@theme inline {
/* Brand Colors Storfwirt Reutte */
--color-primary: #8F2E34;
--color-primary-dark: #6E1F24;
--color-primary-light: #B34D54;
--color-accent: #8F2E34;
--color-accent-dark: #6E1F24;
--color-accent-light: #F0D0D2;
/* Surfaces clean white base */
--color-sand: #F5F5F5;
--color-sand-dark: #EBEBEB;
--color-cream: #FFFFFF;
/* Text black base */
--color-bark: #000000;
--color-bark-light: #555555;
/* Borders */
--color-driftwood: #E0E0E0;
/* Fonts */
--font-heading: var(--font-lora);
--font-body: var(--font-inter);
}
html {
scroll-behavior: smooth;
}
body {
background-color: var(--color-cream);
color: var(--color-bark);
font-family: var(--font-body), system-ui, sans-serif;
}
::selection {
background-color: var(--color-primary);
color: white;
}
.skip-link {
position: absolute;
top: -100%;
left: 0;
z-index: 100;
padding: 1rem;
background: var(--color-primary);
color: white;
font-weight: 500;
text-decoration: none;
}
.skip-link:focus {
top: 0;
}

View File

@@ -0,0 +1,135 @@
import type { Metadata } from "next";
import { Section } from "@/components/Section";
export const metadata: Metadata = {
title: "Impressum",
description: "Impressum und rechtliche Informationen des Storfwirt Reutte.",
robots: { index: false, follow: false },
};
export default function ImpressumPage() {
return (
<>
<section className="bg-primary-dark pt-32 pb-12 sm:pt-40 sm:pb-16">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<h1 className="font-heading text-3xl sm:text-4xl font-bold text-white">
Impressum
</h1>
</div>
</section>
<Section className="bg-cream">
<div className="max-w-3xl mx-auto prose prose-bark">
<h2 className="font-heading text-xl font-semibold text-bark mt-0">
Angaben gemäß § 5 ECG / § 25 MedienG
</h2>
<div className="space-y-6 text-bark-light leading-relaxed">
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Unternehmen
</h3>
<p>
Storfwirt Reutte
<br />
Musterstraße 1
<br />
6600 Reutte
<br />
Österreich
</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Kontakt
</h3>
<p>
Telefon:{" "}
<a
href="tel:+43567200000"
className="text-primary hover:underline"
>
+43 5672 00000
</a>
<br />
E-Mail:{" "}
<a
href="mailto:info@storfwirt-reutte.at"
className="text-primary hover:underline"
>
info@storfwirt-reutte.at
</a>
</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Unternehmensgegenstand
</h3>
<p>Gastronomie, Catering, Eventlocation</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Aufsichtsbehörde
</h3>
<p>Bezirkshauptmannschaft Reutte</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Berufsrecht
</h3>
<p>
Gewerbeordnung:{" "}
<a
href="https://www.ris.bka.gv.at"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
www.ris.bka.gv.at
</a>
</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Haftungsausschluss
</h3>
<p>
Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine
Haftung für die Inhalte externer Links. Für den Inhalt der
verlinkten Seiten sind ausschließlich deren Betreiber
verantwortlich.
</p>
</div>
<div>
<h3 className="font-heading text-lg font-semibold text-bark mb-2">
Urheberrecht
</h3>
<p>
Die durch den Seitenbetreiber erstellten Inhalte und Werke auf
diesen Seiten unterliegen dem österreichischen Urheberrecht.
Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der
Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der
schriftlichen Zustimmung des Erstellers.
</p>
</div>
<div className="border-t border-driftwood pt-6 mt-8">
<p className="text-sm text-bark-light/70">
<strong className="text-bark">Hinweis:</strong> Die
Kontaktdaten und Angaben auf dieser Seite sind Platzhalter und
müssen vor der Veröffentlichung mit den tatsächlichen Daten des
Unternehmens ersetzt werden.
</p>
</div>
</div>
</div>
</Section>
</>
);
}

View File

@@ -0,0 +1,139 @@
import type { Metadata } from "next";
import { Phone, Mail, MapPin, Clock } from "lucide-react";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { ContactForm } from "@/components/ContactForm";
export const metadata: Metadata = {
title: "Kontakt",
description:
"Kontaktieren Sie den Storfwirt Reutte für Catering-Anfragen, Eventplanung oder allgemeine Fragen. Per Formular, Telefon oder E-Mail.",
};
const contactInfo = [
{
icon: Phone,
label: "Telefon",
value: "+43 5672 00000",
href: "tel:+43567200000",
},
{
icon: Mail,
label: "E-Mail",
value: "info@storfwirt-reutte.at",
href: "mailto:info@storfwirt-reutte.at",
},
{
icon: MapPin,
label: "Adresse",
value: "Musterstraße 1, 6600 Reutte, Tirol",
href: null,
},
];
export default function KontaktPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">Kontakt</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Sprechen Sie mit uns
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Egal ob Anfrage, Frage oder einfach nur ein Hallo wir freuen uns
auf Ihre Nachricht.
</p>
</AnimatedSection>
</div>
</section>
{/* Contact Section */}
<Section className="bg-cream">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12">
{/* Form */}
<div className="lg:col-span-2">
<AnimatedSection>
<h2 className="font-heading text-2xl font-bold text-bark mb-6">
Anfrage senden
</h2>
<ContactForm />
</AnimatedSection>
</div>
{/* Sidebar */}
<div>
<AnimatedSection delay={0.1}>
<div className="rounded-xl border border-driftwood bg-sand p-6 sm:p-8 space-y-6">
<h3 className="font-heading text-lg font-semibold text-bark">
Direkt erreichen
</h3>
{contactInfo.map((info) => (
<div key={info.label} className="flex items-start gap-3">
<info.icon className="h-5 w-5 text-primary shrink-0 mt-0.5" />
<div>
<p className="text-sm font-medium text-bark">
{info.label}
</p>
{info.href ? (
<a
href={info.href}
className="text-sm text-bark-light hover:text-primary transition-colors"
>
{info.value}
</a>
) : (
<p className="text-sm text-bark-light">{info.value}</p>
)}
</div>
</div>
))}
<div className="pt-4 border-t border-driftwood">
<div className="flex items-start gap-3">
<Clock className="h-5 w-5 text-primary shrink-0 mt-0.5" />
<div>
<p className="text-sm font-medium text-bark">
Öffnungszeiten
</p>
<div className="text-sm text-bark-light space-y-0.5 mt-1">
<p>Mo Fr: 11:00 14:00</p>
<p>Sa: nach Vereinbarung</p>
<p>So: Ruhetag</p>
</div>
</div>
</div>
</div>
<div className="pt-4 border-t border-driftwood">
<p className="text-sm text-bark-light leading-relaxed">
Antworten dauern in der Regel nicht länger als 24 Stunden.
Bei dringenden Anfragen rufen Sie uns einfach an.
</p>
</div>
</div>
</AnimatedSection>
</div>
</div>
</Section>
{/* Map Placeholder */}
<section className="bg-sand-dark">
<div className="aspect-[21/9] sm:aspect-[3/1] flex items-center justify-center">
<div className="text-center p-8">
<MapPin className="h-12 w-12 text-primary/25 mx-auto mb-3" />
<p className="text-bark-light text-sm">
Kartenansicht Musterstraße 1, 6600 Reutte
</p>
<p className="text-bark-light/50 text-xs mt-1">
Hier kann eine interaktive Karte (z.B. OpenStreetMap) eingebunden werden
</p>
</div>
</div>
</section>
</>
);
}

View File

@@ -0,0 +1,97 @@
import type { Metadata } from "next";
import { Lora, Inter } from "next/font/google";
import "./globals.css";
import { Header } from "@/components/Header";
import { Footer } from "@/components/Footer";
const lora = Lora({
variable: "--font-lora",
subsets: ["latin"],
display: "swap",
});
const inter = Inter({
variable: "--font-inter",
subsets: ["latin"],
display: "swap",
});
export const metadata: Metadata = {
title: {
default: "Storfwirt Reutte Catering & Eventlocation in Tirol",
template: "%s | Storfwirt Reutte",
},
description:
"Catering, Eventlocation, Mittagstisch und Feinkost in Reutte. Regionale Küche, professionelle Organisation Ihr Partner für Events im Außerfern.",
keywords: [
"Catering Reutte",
"Eventlocation Tirol",
"Mittagstisch Reutte",
"Feinkost Außerfern",
"Restaurant Reutte",
"Firmenfeiern Tirol",
"Hochzeit Catering Tirol",
],
authors: [{ name: "Storfwirt Reutte" }],
openGraph: {
type: "website",
locale: "de_AT",
siteName: "Storfwirt Reutte",
title: "Storfwirt Reutte Catering & Eventlocation",
description:
"Regionale Küche, professionelle Event-Organisation. Ihr Partner im Außerfern.",
},
robots: {
index: true,
follow: true,
},
};
const structuredData = {
"@context": "https://schema.org",
"@type": "Restaurant",
name: "Storfwirt Reutte",
description:
"Catering, Eventlocation und Restaurant in Reutte, Tirol. Regionale Küche für Events und den täglichen Genuss.",
address: {
"@type": "PostalAddress",
streetAddress: "Musterstraße 1",
addressLocality: "Reutte",
addressRegion: "Tirol",
postalCode: "6600",
addressCountry: "AT",
},
geo: {
"@type": "GeoCoordinates",
latitude: 47.4833,
longitude: 10.7167,
},
servesCuisine: "Regional, Österreichisch",
priceRange: "€€",
url: "https://www.storfwirt-reutte.at",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="de">
<body
className={`${lora.variable} ${inter.variable} font-body antialiased`}
>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/>
<a href="#main" className="skip-link">
Zum Inhalt springen
</a>
<Header />
<main id="main">{children}</main>
<Footer />
</body>
</html>
);
}

View File

@@ -0,0 +1,194 @@
import type { Metadata } from "next";
import {
UtensilsCrossed,
Building2,
Clock,
ShoppingBag,
CheckCircle2,
ArrowRight,
Shield,
Leaf,
Handshake,
} from "lucide-react";
import Link from "next/link";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { ContactCTA } from "@/components/ContactCTA";
export const metadata: Metadata = {
title: "Leistungen",
description:
"Alle Leistungen des Storfwirt Reutte im Überblick: Catering, Eventlocation, Mittagstisch und Feinkost. Regional, verlässlich, aus einer Hand.",
};
const services = [
{
icon: UtensilsCrossed,
title: "Catering für Events & Feiern",
description:
"Firmenfeiern, Hochzeiten, Vereinsveranstaltungen, private Feste. Wir liefern frisch gekochtes Essen ob Buffet, Menü oder Fingerfood. Ab 20 bis über 200 Gäste.",
href: "/catering",
features: [
"Individuelle Menüplanung",
"Regionale Zutaten",
"Service-Personal auf Wunsch",
"Lieferung im gesamten Außerfern",
],
},
{
icon: Building2,
title: "Eventlocation & Restaurant",
description:
"Unsere Räume in Reutte bieten Platz für bis zu 120 Gäste. Flexibel einrichtbar, modern ausgestattet, mit eigener Küche direkt im Haus.",
href: "/restaurant",
features: [
"Flexible Raumgestaltung",
"Eigene Küche im Haus",
"Parkplätze vorhanden",
"Zentrale Lage in Reutte",
],
},
{
icon: Clock,
title: "Mittagstisch",
description:
"Jeden Werktag frisch gekocht. Wechselnde Menüs mit regionalen Zutaten, schnell serviert. Perfekt für die Mittagspause.",
href: "/mittagstisch",
features: [
"Mo Fr, 11:00 14:00 Uhr",
"Wöchentlich wechselnde Karte",
"Frisch & regional",
"Faire Preise",
],
},
{
icon: ShoppingBag,
title: "Feinkost & regionale Spezialitäten",
description:
"Ausgewählte Produkte aus der Region Käse, Speck, Marmeladen, Aufstriche und mehr. Auch als individuelle Geschenkkörbe.",
href: "/feinkost",
features: [
"Regionale Produzenten",
"Geschenkkörbe nach Wunsch",
"Saisonale Spezialitäten",
"Direktverkauf im Haus",
],
},
];
const advantages = [
{
icon: Shield,
title: "Verlässlichkeit",
description:
"Wenn wir sagen, dass es klappt, dann klappt es. Pünktlich, in der abgesprochenen Qualität, ohne Überraschungen.",
},
{
icon: Leaf,
title: "Regionale Qualität",
description:
"Unsere Zutaten kommen aus dem Außerfern und dem südlichen Allgäu. Kurze Wege, frische Produkte, ehrliche Küche.",
},
{
icon: Handshake,
title: "Persönliche Betreuung",
description:
"Bei uns haben Sie einen Ansprechpartner, der Ihr Event kennt. Keine Callcenter, keine Weiterleitungen.",
},
];
export default function LeistungenPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Storfwirt Reutte
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Unsere Leistungen
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Vom Catering über die Eventlocation bis zum täglichen Mittagstisch.
Alles aus einer Küche, alles aus der Region.
</p>
</AnimatedSection>
</div>
</section>
{/* Services Detail */}
<Section className="bg-cream">
<div className="space-y-16">
{services.map((service, index) => (
<AnimatedSection key={service.title}>
<div
className={`grid grid-cols-1 lg:grid-cols-2 gap-8 items-start ${
index % 2 === 1 ? "lg:direction-rtl" : ""
}`}
>
<div className="rounded-xl border border-driftwood bg-sand p-8 sm:p-10">
<service.icon className="h-10 w-10 text-primary mb-4" />
<h2 className="font-heading text-2xl sm:text-3xl font-bold text-bark mb-4">
{service.title}
</h2>
<p className="text-bark-light leading-relaxed mb-6">
{service.description}
</p>
<Link
href={service.href}
className="inline-flex items-center gap-2 text-primary font-medium hover:gap-3 transition-all"
>
Details ansehen
<ArrowRight className="h-4 w-4" />
</Link>
</div>
<div className="space-y-3 lg:pt-4">
<h3 className="font-heading text-lg font-semibold text-bark mb-3">
Auf einen Blick
</h3>
{service.features.map((feature) => (
<div key={feature} className="flex items-center gap-3">
<CheckCircle2 className="h-5 w-5 text-primary shrink-0" />
<span className="text-bark-light">{feature}</span>
</div>
))}
</div>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* Advantages */}
<Section className="bg-sand">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-12 text-center">
Warum Storfwirt?
</h2>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{advantages.map((advantage, index) => (
<AnimatedSection key={advantage.title} delay={index * 0.1}>
<div className="text-center">
<div className="inline-flex items-center justify-center w-14 h-14 rounded-full bg-primary/10 mb-4">
<advantage.icon className="h-7 w-7 text-primary" />
</div>
<h3 className="font-heading text-xl font-semibold text-bark mb-2">
{advantage.title}
</h3>
<p className="text-bark-light leading-relaxed">
{advantage.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* CTA */}
<ContactCTA />
</>
);
}

View File

@@ -0,0 +1,239 @@
import type { Metadata } from "next";
import { Clock, MapPin, Phone, Leaf } from "lucide-react";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
export const metadata: Metadata = {
title: "Mittagstisch",
description:
"Mittagstisch in Reutte jeden Werktag frisch gekocht mit regionalen Zutaten. Wechselnde Menüs, faire Preise, schnell serviert.",
};
interface MenuItem {
label: string;
dish: string;
price: string;
}
interface DayMenu {
day: string;
menus: MenuItem[];
}
const weeklyMenu: DayMenu[] = [
{
day: "Montag",
menus: [
{ label: "Menü 1", dish: "Tiroler Gröstl mit Spiegelei und Krautsalat", price: "€ 9,90" },
{ label: "Menü 2", dish: "Gemüsecremesuppe mit Brot", price: "€ 7,50" },
],
},
{
day: "Dienstag",
menus: [
{ label: "Menü 1", dish: "Rindsgulasch mit Semmelknödel", price: "€ 10,50" },
{ label: "Menü 2", dish: "Spinatknödel mit Parmesan und Salat", price: "€ 9,50" },
],
},
{
day: "Mittwoch",
menus: [
{ label: "Menü 1", dish: "Gebratene Forelle mit Petersilkartoffeln", price: "€ 11,90" },
{ label: "Menü 2", dish: "Kartoffelsuppe mit Einlage", price: "€ 7,90" },
{ label: "Menü 3", dish: "Salatteller mit gebratenen Pilzen", price: "€ 9,90" },
],
},
{
day: "Donnerstag",
menus: [
{ label: "Menü 1", dish: "Kässpätzle mit Röstzwiebeln und Salat", price: "€ 9,50" },
{ label: "Menü 2", dish: "Wiener Schnitzel mit Erdäpfelsalat", price: "€ 12,90" },
],
},
{
day: "Freitag",
menus: [
{ label: "Menü 1", dish: "Schweinsbraten mit Knödel und Blaukraut", price: "€ 10,90" },
{ label: "Menü 2", dish: "Fischstäbchen mit Reis und Tartarsauce", price: "€ 9,90" },
],
},
];
export default function MittagstischPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Montag bis Freitag
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Jeden Tag frisch auf dem Tisch
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Unser Mittagstisch wechselt wöchentlich. Regionale Zutaten, ehrliche
Portionen, faire Preise. Ohne Reservierung, einfach vorbeikommen.
</p>
</AnimatedSection>
</div>
</section>
{/* Info Bar */}
<section className="bg-sand-dark py-6">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex flex-col sm:flex-row items-center justify-center gap-6 sm:gap-10 text-sm text-bark-light">
<div className="flex items-center gap-2">
<Clock className="h-4 w-4 text-primary" />
<span>Mo Fr: 11:00 14:00 Uhr</span>
</div>
<div className="flex items-center gap-2">
<MapPin className="h-4 w-4 text-primary" />
<span>Musterstraße 1, 6600 Reutte</span>
</div>
<div className="flex items-center gap-2">
<Phone className="h-4 w-4 text-primary" />
<a
href="tel:+43567200000"
className="hover:text-primary transition-colors"
>
+43 5672 00000
</a>
</div>
</div>
</div>
</section>
{/* Weekly Menu */}
<Section className="bg-white">
<AnimatedSection>
<div className="max-w-4xl mx-auto">
<div className="text-center mb-10">
<div className="inline-flex items-center gap-2 rounded-full bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary mb-4">
<Leaf className="h-4 w-4" />
Beispiel-Wochenkarte
</div>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-black mb-3">
Diese Woche bei uns
</h2>
<p className="text-bark-light">
Die Karte wechselt wöchentlich. Am besten einfach vorbeikommen
oder kurz anrufen.
</p>
</div>
{/* Desktop Table */}
<div className="hidden sm:block rounded-xl border border-driftwood overflow-hidden">
{weeklyMenu.map((dayEntry, dayIndex) => (
<div key={dayEntry.day}>
<table className="w-full">
<thead>
<tr className="bg-primary text-white">
<th
colSpan={3}
className="px-5 py-3 text-left text-sm font-semibold tracking-wide"
>
{dayEntry.day}
</th>
</tr>
</thead>
<tbody>
{dayEntry.menus.map((menu, menuIndex) => (
<tr
key={menu.label}
className={`${
menuIndex % 2 === 0 ? "bg-white" : "bg-sand"
} ${
dayIndex < weeklyMenu.length - 1 ||
menuIndex < dayEntry.menus.length - 1
? "border-b border-driftwood"
: ""
}`}
>
<td className="px-5 py-3 text-sm font-medium text-primary w-28">
{menu.label}
</td>
<td className="px-5 py-3 text-sm text-black">
{menu.dish}
</td>
<td className="px-5 py-3 text-sm font-semibold text-black text-right w-24 whitespace-nowrap">
{menu.price}
</td>
</tr>
))}
</tbody>
</table>
</div>
))}
</div>
{/* Mobile Stacked Layout */}
<div className="sm:hidden space-y-4">
{weeklyMenu.map((dayEntry) => (
<div
key={dayEntry.day}
className="rounded-xl border border-driftwood overflow-hidden"
>
<div className="bg-primary px-4 py-2.5">
<h3 className="text-white font-semibold text-sm">
{dayEntry.day}
</h3>
</div>
<div className="divide-y divide-driftwood">
{dayEntry.menus.map((menu) => (
<div key={menu.label} className="px-4 py-3 bg-white">
<div className="flex items-center justify-between mb-1">
<span className="text-xs font-medium text-primary">
{menu.label}
</span>
<span className="text-sm font-semibold text-black">
{menu.price}
</span>
</div>
<p className="text-sm text-black">{menu.dish}</p>
</div>
))}
</div>
</div>
))}
</div>
<p className="text-center text-bark-light text-sm mt-6">
Alle Gerichte inkl. Salat oder Suppe. Änderungen vorbehalten.
Allergene auf Anfrage.
</p>
</div>
</AnimatedSection>
</Section>
{/* Info Section */}
<Section className="bg-sand">
<AnimatedSection>
<div className="max-w-2xl mx-auto text-center">
<h2 className="font-heading text-3xl font-bold text-bark mb-6">
Gut zu wissen
</h2>
<div className="space-y-4 text-bark-light leading-relaxed text-left">
<p>
Unseren Mittagstisch gibt es seit Jahren. Angefangen hat es mit
ein paar Stammgästen aus der Nachbarschaft mittlerweile kommen
jeden Tag Berufstätige, Handwerker und Pensionisten gleichermaßen.
</p>
<p>
Reservieren müssen Sie nicht. Kommen Sie einfach vorbei.
Wenn Sie für eine größere Gruppe bestellen möchten, rufen Sie
kurz vorher an.
</p>
<p>
Übrigens: Unser Mittagstisch eignet sich auch als tägliches
Mitarbeiter-Essen für Firmen in der Umgebung. Sprechen Sie uns
an, wir finden eine Lösung.
</p>
</div>
</div>
</AnimatedSection>
</Section>
</>
);
}

238
website/src/app/page.tsx Normal file
View File

@@ -0,0 +1,238 @@
import Link from "next/link";
import {
ArrowRight,
UtensilsCrossed,
Building2,
Clock,
ShoppingBag,
Star,
} from "lucide-react";
import { AnimatedSection } from "@/components/AnimatedSection";
import { ContactCTA } from "@/components/ContactCTA";
import { Section } from "@/components/Section";
import { PlaceholderImage } from "@/components/PlaceholderImage";
const services = [
{
icon: UtensilsCrossed,
title: "Catering",
description:
"Von der Firmenfeier bis zur Hochzeit wir bringen gutes Essen dahin, wo gefeiert wird.",
href: "/catering",
},
{
icon: Building2,
title: "Eventlocation",
description:
"Unsere Räume bieten Platz für Ihre Veranstaltung. Flexibel einrichtbar, zentral in Reutte.",
href: "/restaurant",
},
{
icon: Clock,
title: "Mittagstisch",
description:
"Jeden Tag frisch gekocht. Wechselnde Menüs aus regionalen Zutaten schnell und gut.",
href: "/mittagstisch",
},
{
icon: ShoppingBag,
title: "Feinkost",
description:
"Regionale Spezialitäten, Geschenkkörbe und mehr direkt bei uns im Haus.",
href: "/feinkost",
},
];
const testimonials = [
{
quote:
"Die haben unser Firmenjubiläum betreut über 120 Gäste, und alles hat reibungslos geklappt. Von der Vorspeise bis zum Dessert war alles perfekt.",
author: "Markus H.",
role: "Unternehmer aus Reutte",
},
{
quote:
"Wir bestellen regelmäßig den Mittagstisch. Schmeckt wie selbst gekocht, nur besser.",
author: "Sandra K.",
role: "Reutte",
},
{
quote:
"Unsere Hochzeitsfeier im Storfwirt war genau so, wie wir sie uns vorgestellt haben. Unkompliziert und einfach gut.",
author: "Petra & Thomas W.",
role: "",
},
];
export default function Home() {
return (
<>
{/* Hero Section */}
<section className="relative min-h-[90vh] flex items-center bg-gradient-to-br from-primary-dark via-primary to-primary-light overflow-hidden">
<div className="absolute inset-0 bg-[radial-gradient(circle_at_30%_40%,rgba(255,255,255,0.08)_0%,transparent_60%)]" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_80%_80%,rgba(143,46,52,0.15)_0%,transparent_50%)]" />
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-32 sm:py-40">
<AnimatedSection>
<p className="text-accent-light font-medium text-base sm:text-lg mb-4 tracking-wide">
Catering & Eventlocation in Reutte
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl xl:text-7xl font-bold text-white leading-tight tracking-tight max-w-4xl">
Gut essen.
<br />
Gut feiern.
</h1>
<p className="mt-6 text-lg sm:text-xl text-white/80 max-w-2xl leading-relaxed">
Wir kochen, organisieren und sorgen dafür, dass Ihr Event so wird,
wie Sie es sich vorstellen. In Reutte und der ganzen Region.
</p>
<div className="mt-10 flex flex-col sm:flex-row gap-4">
<Link
href="/kontakt"
className="inline-flex items-center justify-center gap-2 rounded-lg bg-white px-6 py-3.5 text-base font-semibold text-primary hover:bg-sand transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary-dark"
>
Event anfragen
<ArrowRight className="h-4 w-4" />
</Link>
<Link
href="/leistungen"
className="inline-flex items-center justify-center gap-2 rounded-lg border border-white/30 px-6 py-3.5 text-base font-semibold text-white hover:bg-white/10 transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary-dark"
>
Unsere Leistungen
</Link>
</div>
</AnimatedSection>
</div>
<div className="absolute bottom-0 left-0 right-0">
<svg
viewBox="0 0 1440 60"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="w-full h-auto block"
aria-hidden="true"
>
<path
d="M0 60V30C240 0 480 0 720 30C960 60 1200 60 1440 30V60H0Z"
fill="var(--color-cream)"
/>
</svg>
</div>
</section>
{/* Services Section */}
<Section className="bg-cream">
<AnimatedSection>
<div className="text-center mb-12 sm:mb-16">
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-4">
Was wir für Sie tun
</h2>
<p className="text-bark-light text-lg max-w-2xl mx-auto">
Ob großes Event oder schneller Mittagstisch bei uns bekommen Sie
regionale Qualität aus einer Hand.
</p>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{services.map((service, index) => (
<AnimatedSection key={service.title} delay={index * 0.1}>
<Link
href={service.href}
className="group block rounded-xl border border-driftwood bg-cream p-6 hover:bg-sand hover:border-primary/20 transition-all duration-300 h-full"
>
<service.icon className="h-8 w-8 text-primary mb-4 group-hover:text-primary-dark transition-colors" />
<h3 className="font-heading text-xl font-semibold text-bark mb-2">
{service.title}
</h3>
<p className="text-bark-light text-sm leading-relaxed">
{service.description}
</p>
<span className="inline-flex items-center gap-1 mt-4 text-sm font-medium text-primary group-hover:gap-2 transition-all">
Mehr erfahren
<ArrowRight className="h-3.5 w-3.5" />
</span>
</Link>
</AnimatedSection>
))}
</div>
</Section>
{/* About Teaser */}
<Section className="bg-sand">
<AnimatedSection>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<div>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-6">
Verwurzelt in Reutte. Seit Jahren.
</h2>
<div className="space-y-4 text-bark-light leading-relaxed">
<p>
Was als kleiner Gasthof angefangen hat, ist heute die erste
Adresse für Catering und Events im Außerfern. Aber eines ist
geblieben: Wir kochen noch selbst.
</p>
<p>
Regionale Zutaten, ehrliche Küche, persönliche Betreuung.
Kein Schnickschnack, dafür Verlässlichkeit. Das schätzen
unsere Gäste und darauf sind wir stolz.
</p>
</div>
<Link
href="/ueber-uns"
className="inline-flex items-center gap-2 mt-6 text-primary font-medium hover:gap-3 transition-all"
>
Mehr über uns
<ArrowRight className="h-4 w-4" />
</Link>
</div>
<PlaceholderImage
icon={Building2}
label="Bild: Der Storfwirt in Reutte"
/>
</div>
</AnimatedSection>
</Section>
{/* Testimonials */}
<Section className="bg-cream">
<AnimatedSection>
<div className="text-center mb-12">
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-4">
Das sagen unsere Gäste
</h2>
</div>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{testimonials.map((testimonial, index) => (
<AnimatedSection key={testimonial.author} delay={index * 0.1}>
<div className="rounded-xl bg-sand p-6 sm:p-8 h-full flex flex-col">
<div className="flex gap-1 mb-4" aria-label="5 von 5 Sternen">
{Array.from({ length: 5 }).map((_, i) => (
<Star
key={i}
className="h-4 w-4 fill-accent text-accent"
/>
))}
</div>
<blockquote className="text-bark leading-relaxed flex-1">
&ldquo;{testimonial.quote}&rdquo;
</blockquote>
<div className="mt-4 pt-4 border-t border-driftwood">
<p className="font-medium text-bark text-sm">
{testimonial.author}
</p>
{testimonial.role && (
<p className="text-bark-light text-xs mt-0.5">
{testimonial.role}
</p>
)}
</div>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* Contact CTA */}
<ContactCTA />
</>
);
}

View File

@@ -0,0 +1,181 @@
import type { Metadata } from "next";
import {
ArrowRight,
Users,
Maximize2,
MapPin,
Wifi,
Car,
Music,
Building2,
} from "lucide-react";
import Link from "next/link";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { ContactCTA } from "@/components/ContactCTA";
import { PlaceholderImage } from "@/components/PlaceholderImage";
export const metadata: Metadata = {
title: "Restaurant & Eventlocation",
description:
"Eventlocation in Reutte flexibel einrichtbare Räume für Firmenfeiern, Hochzeiten und private Veranstaltungen. Zentral gelegen im Herzen von Reutte.",
};
const features = [
{
icon: Users,
title: "Bis zu 120 Gäste",
description: "Genug Platz für große Feiern, gemütlich genug für kleine Runden.",
},
{
icon: Maximize2,
title: "Flexible Raumgestaltung",
description: "Bestuhlung, Tischanordnung und Deko wir richten den Raum nach Ihren Wünschen ein.",
},
{
icon: MapPin,
title: "Zentrale Lage",
description: "Mitten in Reutte, gut erreichbar aus dem gesamten Außerfern und dem Allgäu.",
},
{
icon: Car,
title: "Parkplätze vorhanden",
description: "Ihre Gäste finden direkt vor dem Haus genügend Parkmöglichkeiten.",
},
{
icon: Wifi,
title: "Moderne Ausstattung",
description: "WLAN, Beamer und Tonanlage für Präsentationen und Feiern gleichermaßen.",
},
{
icon: Music,
title: "Musik & Unterhaltung",
description: "Eigene Musikanlage vorhanden. Live-Musik oder DJ? Organisieren wir gerne.",
},
];
export default function RestaurantPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Eventlocation in Reutte
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Räume, die zu Ihrem Event passen
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Nicht jeder Raum passt zu jedem Anlass. Unsere Location lässt sich
so einrichten, wie Sie es brauchen ob Seminar, Hochzeit oder
Vereinsabend.
</p>
<Link
href="/kontakt"
className="inline-flex items-center gap-2 mt-8 rounded-lg bg-white px-6 py-3.5 text-base font-semibold text-primary hover:bg-sand transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary-dark"
>
Location anfragen
<ArrowRight className="h-4 w-4" />
</Link>
</AnimatedSection>
</div>
</section>
{/* Features */}
<Section className="bg-cream">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-4 text-center">
Was unsere Location bietet
</h2>
<p className="text-bark-light text-lg max-w-2xl mx-auto text-center mb-12">
Alles unter einem Dach: Raum, Küche, Service.
</p>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{features.map((feature, index) => (
<AnimatedSection key={feature.title} delay={index * 0.08}>
<div className="rounded-xl border border-driftwood bg-cream p-6 h-full">
<feature.icon className="h-7 w-7 text-primary mb-3" />
<h3 className="font-heading text-lg font-semibold text-bark mb-1.5">
{feature.title}
</h3>
<p className="text-bark-light text-sm leading-relaxed">
{feature.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* Atmosphere */}
<Section className="bg-sand">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<AnimatedSection>
<h2 className="font-heading text-3xl font-bold text-bark mb-6">
Einladend, nicht steif
</h2>
<div className="space-y-4 text-bark-light leading-relaxed">
<p>
Unsere Räume sind warm und einladend eingerichtet. Hier fühlen
sich Ihre Gäste willkommen ohne dass es nach Konferenzraum
aussieht.
</p>
<p>
Die Kombination aus gutem Essen direkt aus unserer Küche und einer
Location, die sich anpassen lässt, macht den Unterschied. Sie
müssen keinen externen Caterer organisieren bei uns kommt alles
aus einer Hand.
</p>
<p>
Und wenn Sie nach dem Event noch zusammensitzen wollen? Der
Storfwirt schließt nicht, solange Sie feiern.
</p>
</div>
</AnimatedSection>
<AnimatedSection delay={0.1}>
<PlaceholderImage
icon={Building2}
label="Bild: Innenansicht der Eventlocation"
/>
</AnimatedSection>
</div>
</Section>
{/* Gallery placeholder */}
<Section className="bg-cream">
<AnimatedSection>
<h2 className="font-heading text-3xl font-bold text-bark mb-8 text-center">
Eindrücke
</h2>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{[
"Saal mit Tischgedeck",
"Stehempfang",
"Buffet-Aufbau",
"Festliche Dekoration",
"Außenbereich",
"Bar & Getränke",
].map((label, index) => (
<AnimatedSection key={label} delay={index * 0.08}>
<PlaceholderImage
icon={Building2}
label={`Bild: ${label}`}
aspectRatio="aspect-square"
/>
</AnimatedSection>
))}
</div>
</Section>
{/* CTA */}
<ContactCTA
headline="Ihre Feier, unsere Räume"
text="Beschreiben Sie uns Ihr Event wir prüfen die Verfügbarkeit und melden uns."
/>
</>
);
}

View File

@@ -0,0 +1,193 @@
import type { Metadata } from "next";
import { Leaf, Heart, Users, ArrowRight } from "lucide-react";
import Link from "next/link";
import { AnimatedSection } from "@/components/AnimatedSection";
import { Section } from "@/components/Section";
import { PlaceholderImage } from "@/components/PlaceholderImage";
import { ContactCTA } from "@/components/ContactCTA";
export const metadata: Metadata = {
title: "Über uns",
description:
"Lernen Sie den Storfwirt Reutte kennen unsere Geschichte, unsere Werte und unser Team. Seit Jahren verwurzelt in Reutte und dem Außerfern.",
};
const values = [
{
icon: Leaf,
title: "Regional & frisch",
description:
"Unsere Zutaten kommen aus der Umgebung. Kurze Wege, frische Produkte, weniger Umweltbelastung. Nicht, weil es im Trend liegt sondern weil wir das schon immer so gemacht haben.",
},
{
icon: Heart,
title: "Mit Herz & Handwerk",
description:
"Bei uns wird noch selbst gekocht. Keine Fertigprodukte, keine Abkürzungen. Das schmeckt man, und das soll auch so bleiben.",
},
{
icon: Users,
title: "Persönlich & direkt",
description:
"Wenn Sie bei uns anrufen, sprechen Sie mit jemandem, der Ihr Event auch betreut. Keine Weiterleitungen, kein Callcenter. So arbeiten wir.",
},
];
const team = [
{
name: "Vorname Nachname",
role: "Geschäftsführung & Küche",
description: "Seit über 20 Jahren in der Gastronomie und der Kopf hinter dem Storfwirt.",
},
{
name: "Vorname Nachname",
role: "Eventorganisation",
description: "Zuständig für die Planung und Betreuung aller Veranstaltungen.",
},
{
name: "Vorname Nachname",
role: "Küche & Catering",
description: "Sorgt dafür, dass jeden Tag frisch und auf den Punkt gekocht wird.",
},
];
export default function UeberUnsPage() {
return (
<>
{/* Hero */}
<section className="bg-gradient-to-br from-primary-dark to-primary pt-32 pb-20 sm:pt-40 sm:pb-28">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<AnimatedSection>
<p className="text-accent-light font-medium mb-3">
Über uns
</p>
<h1 className="font-heading text-4xl sm:text-5xl lg:text-6xl font-bold text-white leading-tight tracking-tight max-w-3xl">
Gastfreundschaft ist bei uns kein Slogan
</h1>
<p className="mt-6 text-lg text-white/80 max-w-2xl leading-relaxed">
Wir sind ein Familienbetrieb aus Reutte. Was wir machen, machen wir
seit Jahren und wir machen es gerne.
</p>
</AnimatedSection>
</div>
</section>
{/* Story */}
<Section className="bg-cream">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-6">
Unsere Geschichte
</h2>
<div className="space-y-4 text-bark-light leading-relaxed">
<p>
Der Storfwirt hat als klassischer Gasthof in Reutte angefangen.
Gut bürgerliche Küche, Stammtisch, Sonntagsbraten. So wie man
sich das vorstellt.
</p>
<p>
Mit der Zeit kamen die Anfragen: Ob wir nicht auch für die
Firmenfeier kochen könnten. Ob die Räume für eine Hochzeit
gehen. Ob wir vielleicht auch Geschenkkörbe zusammenstellen.
Jede Anfrage haben wir als Einladung verstanden, besser zu
werden.
</p>
<p>
Heute sind wir die erste Adresse für Catering und Events
im Außerfern. Aber wir machen immer noch, was wir am Anfang
gemacht haben: kochen, was gut ist. Und uns um unsere Gäste
kümmern.
</p>
</div>
</AnimatedSection>
<AnimatedSection delay={0.1}>
<PlaceholderImage
icon={Users}
label="Bild: Das Team vom Storfwirt"
/>
</AnimatedSection>
</div>
</Section>
{/* Values */}
<Section className="bg-sand">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-12 text-center">
Wofür wir stehen
</h2>
</AnimatedSection>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{values.map((value, index) => (
<AnimatedSection key={value.title} delay={index * 0.1}>
<div className="text-center">
<div className="inline-flex items-center justify-center w-14 h-14 rounded-full bg-primary/10 mb-4">
<value.icon className="h-7 w-7 text-primary" />
</div>
<h3 className="font-heading text-xl font-semibold text-bark mb-3">
{value.title}
</h3>
<p className="text-bark-light leading-relaxed">
{value.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* Team */}
<Section className="bg-cream">
<AnimatedSection>
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-bark mb-4 text-center">
Unser Team
</h2>
<p className="text-bark-light text-center max-w-xl mx-auto mb-12">
Klein, eingespielt, verlässlich. Die Menschen hinter dem Storfwirt.
</p>
</AnimatedSection>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6">
{team.map((member, index) => (
<AnimatedSection key={member.role} delay={index * 0.1}>
<div className="rounded-xl border border-driftwood bg-sand p-6 text-center h-full">
<div className="w-20 h-20 rounded-full bg-gradient-to-br from-primary/15 to-accent/15 mx-auto mb-4 flex items-center justify-center">
<Users className="h-8 w-8 text-primary/40" />
</div>
<h3 className="font-heading text-lg font-semibold text-bark">
{member.name}
</h3>
<p className="text-primary text-sm font-medium mt-1">
{member.role}
</p>
<p className="text-bark-light text-sm mt-3 leading-relaxed">
{member.description}
</p>
</div>
</AnimatedSection>
))}
</div>
</Section>
{/* CTA */}
<Section className="bg-sand">
<AnimatedSection>
<div className="max-w-2xl mx-auto text-center">
<h2 className="font-heading text-3xl font-bold text-bark mb-4">
Lernen Sie uns kennen
</h2>
<p className="text-bark-light leading-relaxed mb-6">
Am besten persönlich. Schauen Sie vorbei, rufen Sie an
oder schreiben Sie uns. Wir freuen uns auf Sie.
</p>
<Link
href="/kontakt"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-6 py-3 text-base font-medium text-white hover:bg-primary-dark transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
Kontakt aufnehmen
<ArrowRight className="h-4 w-4" />
</Link>
</div>
</AnimatedSection>
</Section>
</>
);
}

View File

@@ -0,0 +1,31 @@
"use client";
import { useRef } from "react";
import { motion, useInView } from "framer-motion";
interface AnimatedSectionProps {
children: React.ReactNode;
className?: string;
delay?: number;
}
export function AnimatedSection({
children,
className = "",
delay = 0,
}: AnimatedSectionProps) {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: "-80px" });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 24 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 24 }}
transition={{ duration: 0.6, ease: "easeOut", delay }}
className={className}
>
{children}
</motion.div>
);
}

View File

@@ -0,0 +1,41 @@
import Link from "next/link";
import { Phone, ArrowRight } from "lucide-react";
interface ContactCTAProps {
headline?: string;
text?: string;
}
export function ContactCTA({
headline = "Wir freuen uns auf Ihre Anfrage",
text = "Erzählen Sie uns von Ihrem Event wir melden uns zeitnah.",
}: ContactCTAProps) {
return (
<section className="bg-primary py-16 sm:py-20">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 text-center">
<h2 className="font-heading text-3xl sm:text-4xl font-bold text-white mb-4">
{headline}
</h2>
<p className="text-lg text-white/80 max-w-2xl mx-auto mb-8">
{text}
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<Link
href="/kontakt"
className="inline-flex items-center gap-2 rounded-lg bg-white px-6 py-3 text-base font-medium text-primary hover:bg-sand transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary"
>
Event anfragen
<ArrowRight className="h-4 w-4" />
</Link>
<a
href="tel:+43567200000"
className="inline-flex items-center gap-2 rounded-lg border border-white/30 px-6 py-3 text-base font-medium text-white hover:bg-white/10 transition-colors focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-primary"
>
<Phone className="h-4 w-4" />
+43 5672 00000
</a>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,271 @@
"use client";
import { useState, type FormEvent } from "react";
import { Send, CheckCircle2 } from "lucide-react";
interface FormData {
name: string;
email: string;
phone: string;
eventType: string;
date: string;
guests: string;
message: string;
privacy: boolean;
}
const initialFormData: FormData = {
name: "",
email: "",
phone: "",
eventType: "",
date: "",
guests: "",
message: "",
privacy: false,
};
const eventTypes = [
"Firmenfeier",
"Hochzeit",
"Vereinsveranstaltung",
"Private Feier",
"Catering-Anfrage",
"Geschenkkorb",
"Sonstiges",
];
export function ContactForm() {
const [formData, setFormData] = useState<FormData>(initialFormData);
const [isSubmitted, setIsSubmitted] = useState(false);
const [errors, setErrors] = useState<Partial<Record<keyof FormData, string>>>({});
function validate(): boolean {
const newErrors: Partial<Record<keyof FormData, string>> = {};
if (!formData.name.trim()) {
newErrors.name = "Bitte geben Sie Ihren Namen ein.";
}
if (!formData.email.trim()) {
newErrors.email = "Bitte geben Sie Ihre E-Mail-Adresse ein.";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = "Bitte geben Sie eine gültige E-Mail-Adresse ein.";
}
if (!formData.message.trim()) {
newErrors.message = "Bitte beschreiben Sie kurz Ihr Anliegen.";
}
if (!formData.privacy) {
newErrors.privacy = "Bitte bestätigen Sie die Datenschutzerklärung.";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
}
function handleSubmit(e: FormEvent) {
e.preventDefault();
if (!validate()) return;
// TODO: Connect to actual form submission endpoint
// Options: API route, FormSubmit.co, Netlify Forms, etc.
// For now, we simulate a successful submission.
setIsSubmitted(true);
}
function handleChange(
field: keyof FormData,
value: string | boolean
) {
setFormData((prev) => ({ ...prev, [field]: value }));
if (errors[field]) {
setErrors((prev) => ({ ...prev, [field]: undefined }));
}
}
if (isSubmitted) {
return (
<div className="rounded-xl border border-driftwood bg-sand p-8 sm:p-12 text-center">
<CheckCircle2 className="h-12 w-12 text-primary mx-auto mb-4" />
<h3 className="font-heading text-2xl font-bold text-bark mb-2">
Vielen Dank für Ihre Anfrage
</h3>
<p className="text-bark-light leading-relaxed max-w-md mx-auto">
Wir haben Ihre Nachricht erhalten und melden uns zeitnah bei Ihnen.
In der Regel innerhalb von 24 Stunden.
</p>
<button
onClick={() => {
setIsSubmitted(false);
setFormData(initialFormData);
}}
className="mt-6 text-sm text-primary font-medium hover:underline"
>
Weitere Anfrage senden
</button>
</div>
);
}
const inputClasses =
"w-full rounded-lg border border-driftwood bg-cream px-4 py-3 text-bark placeholder:text-bark-light/50 focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary transition-colors";
const labelClasses = "block text-sm font-medium text-bark mb-1.5";
const errorClasses = "text-red-600 text-xs mt-1";
return (
<form onSubmit={handleSubmit} noValidate className="space-y-5">
{/* Name */}
<div>
<label htmlFor="contact-name" className={labelClasses}>
Name <span className="text-red-500">*</span>
</label>
<input
id="contact-name"
type="text"
autoComplete="name"
value={formData.name}
onChange={(e) => handleChange("name", e.target.value)}
className={inputClasses}
placeholder="Ihr Name"
/>
{errors.name && <p className={errorClasses}>{errors.name}</p>}
</div>
{/* Email + Phone */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
<div>
<label htmlFor="contact-email" className={labelClasses}>
E-Mail <span className="text-red-500">*</span>
</label>
<input
id="contact-email"
type="email"
autoComplete="email"
value={formData.email}
onChange={(e) => handleChange("email", e.target.value)}
className={inputClasses}
placeholder="ihre@email.at"
/>
{errors.email && <p className={errorClasses}>{errors.email}</p>}
</div>
<div>
<label htmlFor="contact-phone" className={labelClasses}>
Telefon
</label>
<input
id="contact-phone"
type="tel"
autoComplete="tel"
value={formData.phone}
onChange={(e) => handleChange("phone", e.target.value)}
className={inputClasses}
placeholder="+43 ..."
/>
</div>
</div>
{/* Event Type + Date */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-5">
<div>
<label htmlFor="contact-event-type" className={labelClasses}>
Art der Veranstaltung
</label>
<select
id="contact-event-type"
value={formData.eventType}
onChange={(e) => handleChange("eventType", e.target.value)}
className={inputClasses}
>
<option value="">Bitte wählen</option>
{eventTypes.map((type) => (
<option key={type} value={type}>
{type}
</option>
))}
</select>
</div>
<div>
<label htmlFor="contact-date" className={labelClasses}>
Gewünschtes Datum
</label>
<input
id="contact-date"
type="date"
value={formData.date}
onChange={(e) => handleChange("date", e.target.value)}
className={inputClasses}
/>
</div>
</div>
{/* Guests */}
<div>
<label htmlFor="contact-guests" className={labelClasses}>
Ungefähre Gästeanzahl
</label>
<input
id="contact-guests"
type="text"
value={formData.guests}
onChange={(e) => handleChange("guests", e.target.value)}
className={inputClasses}
placeholder="z.B. ca. 50 Personen"
/>
</div>
{/* Message */}
<div>
<label htmlFor="contact-message" className={labelClasses}>
Ihre Nachricht <span className="text-red-500">*</span>
</label>
<textarea
id="contact-message"
rows={5}
value={formData.message}
onChange={(e) => handleChange("message", e.target.value)}
className={inputClasses}
placeholder="Beschreiben Sie kurz, was Sie planen..."
/>
{errors.message && <p className={errorClasses}>{errors.message}</p>}
</div>
{/* Privacy */}
<div>
<label className="flex items-start gap-3 cursor-pointer">
<input
type="checkbox"
checked={formData.privacy}
onChange={(e) => handleChange("privacy", e.target.checked)}
className="mt-1 h-4 w-4 rounded border-driftwood text-primary focus:ring-primary"
/>
<span className="text-sm text-bark-light">
Ich habe die{" "}
<a
href="/datenschutz"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline hover:text-primary-dark"
>
Datenschutzerklärung
</a>{" "}
gelesen und stimme der Verarbeitung meiner Daten zu.{" "}
<span className="text-red-500">*</span>
</span>
</label>
{errors.privacy && <p className={errorClasses}>{errors.privacy}</p>}
</div>
{/* Submit */}
<button
type="submit"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-6 py-3 text-base font-medium text-white hover:bg-primary-dark transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 w-full sm:w-auto justify-center"
>
<Send className="h-4 w-4" />
Anfrage senden
</button>
</form>
);
}

View File

@@ -0,0 +1,134 @@
import Link from "next/link";
import Image from "next/image";
import { Phone, Mail, MapPin, Clock } from "lucide-react";
const footerNavLinks = [
{ href: "/catering", label: "Catering" },
{ href: "/restaurant", label: "Restaurant" },
{ href: "/leistungen", label: "Leistungen" },
{ href: "/mittagstisch", label: "Mittagstisch" },
{ href: "/feinkost", label: "Feinkost" },
{ href: "/ueber-uns", label: "Über uns" },
{ href: "/kontakt", label: "Kontakt" },
];
const legalLinks = [
{ href: "/impressum", label: "Impressum" },
{ href: "/datenschutz", label: "Datenschutz" },
];
export function Footer() {
const currentYear = new Date().getFullYear();
return (
<footer className="bg-primary-dark text-white" role="contentinfo">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-16">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12">
{/* Brand */}
<div>
<Link href="/" className="inline-block">
<Image
src="/assets/logo.png"
alt="Storfwirt Reutte Logo"
width={160}
height={50}
className="h-10 w-auto object-contain brightness-0 invert"
/>
</Link>
<p className="mt-4 text-sm text-white/70 leading-relaxed">
Catering, Eventlocation und regionale Küche in Reutte.
Seit Jahren die erste Adresse im Außerfern.
</p>
</div>
{/* Navigation */}
<div>
<h3 className="font-heading text-sm font-semibold uppercase tracking-wider mb-4">
Navigation
</h3>
<ul className="space-y-2">
{footerNavLinks.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-sm text-white/70 hover:text-white transition-colors"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
{/* Contact */}
<div>
<h3 className="font-heading text-sm font-semibold uppercase tracking-wider mb-4">
Kontakt
</h3>
<ul className="space-y-3">
<li className="flex items-start gap-3 text-sm text-white/70">
<MapPin className="h-4 w-4 mt-0.5 shrink-0" />
<span>
Musterstraße 1
<br />
6600 Reutte, Tirol
</span>
</li>
<li>
<a
href="tel:+43567200000"
className="flex items-center gap-3 text-sm text-white/70 hover:text-white transition-colors"
>
<Phone className="h-4 w-4 shrink-0" />
+43 5672 00000
</a>
</li>
<li>
<a
href="mailto:info@storfwirt-reutte.at"
className="flex items-center gap-3 text-sm text-white/70 hover:text-white transition-colors"
>
<Mail className="h-4 w-4 shrink-0" />
info@storfwirt-reutte.at
</a>
</li>
</ul>
</div>
{/* Hours */}
<div>
<h3 className="font-heading text-sm font-semibold uppercase tracking-wider mb-4">
Öffnungszeiten
</h3>
<div className="flex items-start gap-3 text-sm text-white/70">
<Clock className="h-4 w-4 mt-0.5 shrink-0" />
<div className="space-y-1">
<p>Mo Fr: 11:00 14:00</p>
<p>Sa: nach Vereinbarung</p>
<p>So: Ruhetag</p>
</div>
</div>
</div>
</div>
{/* Bottom */}
<div className="mt-12 pt-8 border-t border-white/10 flex flex-col sm:flex-row items-center justify-between gap-4">
<p className="text-xs text-white/50">
© {currentYear} Storfwirt Reutte. Alle Rechte vorbehalten.
</p>
<div className="flex gap-6">
{legalLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-xs text-white/50 hover:text-white/70 transition-colors"
>
{link.label}
</Link>
))}
</div>
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,150 @@
"use client";
import { useState, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { usePathname } from "next/navigation";
import { Menu, X, Phone } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
const navLinks = [
{ href: "/catering", label: "Catering" },
{ href: "/restaurant", label: "Restaurant" },
{ href: "/leistungen", label: "Leistungen" },
{ href: "/mittagstisch", label: "Mittagstisch" },
{ href: "/feinkost", label: "Feinkost" },
{ href: "/ueber-uns", label: "Über uns" },
{ href: "/kontakt", label: "Kontakt" },
];
export function Header() {
const [isScrolled, setIsScrolled] = useState(false);
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const pathname = usePathname();
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
if (isMobileMenuOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
return () => {
document.body.style.overflow = "";
};
}, [isMobileMenuOpen]);
// Close mobile menu on route change
useEffect(() => {
setIsMobileMenuOpen(false);
}, [pathname]);
return (
<header
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-300 ${
isScrolled
? "bg-white/95 backdrop-blur-sm shadow-sm py-3"
: "bg-transparent py-5"
}`}
>
<nav
className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 flex items-center justify-between"
aria-label="Hauptnavigation"
>
<Link href="/" className="shrink-0">
<Image
src="/assets/logo.png"
alt="Storfwirt Reutte Logo"
width={180}
height={56}
className="h-10 w-auto sm:h-12 lg:h-14 object-contain"
priority
/>
</Link>
{/* Desktop Navigation */}
<div className="hidden lg:flex items-center gap-7">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`text-sm font-medium transition-colors ${
pathname === link.href
? "text-primary"
: "text-bark-light hover:text-primary"
}`}
>
{link.label}
</Link>
))}
<Link
href="/kontakt"
className="inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-dark transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
>
<Phone className="h-4 w-4" />
Event anfragen
</Link>
</div>
{/* Mobile Menu Button */}
<button
className="lg:hidden p-2 text-bark hover:text-primary transition-colors"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-label={isMobileMenuOpen ? "Menü schließen" : "Menü öffnen"}
aria-expanded={isMobileMenuOpen}
>
{isMobileMenuOpen ? (
<X className="h-6 w-6" />
) : (
<Menu className="h-6 w-6" />
)}
</button>
</nav>
{/* Mobile Menu */}
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
className="lg:hidden fixed inset-x-0 top-[60px] bottom-0 bg-white z-40 overflow-y-auto"
>
<div className="flex flex-col p-6 gap-1">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`py-3 px-4 text-lg font-medium rounded-lg transition-colors ${
pathname === link.href
? "text-primary bg-sand"
: "text-bark hover:text-primary hover:bg-sand"
}`}
>
{link.label}
</Link>
))}
<div className="mt-4 pt-4 border-t border-driftwood">
<Link
href="/kontakt"
className="inline-flex items-center justify-center gap-2 w-full rounded-lg bg-primary px-5 py-3 text-base font-medium text-white hover:bg-primary-dark transition-colors"
>
<Phone className="h-5 w-5" />
Event anfragen
</Link>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</header>
);
}

View File

@@ -0,0 +1,26 @@
import type { LucideIcon } from "lucide-react";
interface PlaceholderImageProps {
icon: LucideIcon;
label: string;
aspectRatio?: string;
className?: string;
}
export function PlaceholderImage({
icon: Icon,
label,
aspectRatio = "aspect-[4/3]",
className = "",
}: PlaceholderImageProps) {
return (
<div
className={`relative ${aspectRatio} rounded-xl bg-gradient-to-br from-primary/8 via-sand-dark to-accent/8 flex items-center justify-center overflow-hidden ${className}`}
>
<div className="text-center p-8">
<Icon className="h-16 w-16 text-primary/25 mx-auto mb-3" />
<p className="text-bark-light/50 text-sm">{label}</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,13 @@
interface SectionProps {
children: React.ReactNode;
className?: string;
id?: string;
}
export function Section({ children, className = "", id }: SectionProps) {
return (
<section id={id} className={`py-16 sm:py-20 lg:py-24 ${className}`}>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">{children}</div>
</section>
);
}

34
website/tsconfig.json Normal file
View File

@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}