feat: Integrate Directus CMS, add i18n with next-intl, and configure project tooling with pnpm, husky, and commitlint.**

This commit is contained in:
2026-02-05 01:18:06 +01:00
parent 765cfd4c69
commit e80140f7cf
65 changed files with 12793 additions and 5879 deletions

View File

@@ -1,18 +1,38 @@
'use client';
"use client";
import React from 'react';
import Image from 'next/image';
import { Award, Clock, Lightbulb, Linkedin, MessageSquare, ShieldCheck, Truck } from 'lucide-react';
import { Reveal } from './Reveal';
import { TechBackground } from './TechBackground';
import { Counter } from './Counter';
import { Button } from './Button';
import React from "react";
import Image from "next/image";
import {
Award,
Clock,
Lightbulb,
Linkedin,
MessageSquare,
ShieldCheck,
Truck,
} from "lucide-react";
import { Reveal } from "./Reveal";
import { TechBackground } from "./TechBackground";
import { Counter } from "./Counter";
import { Button } from "./Button";
import { useTranslations } from "next-intl";
export default function About() {
const t = useTranslations("About");
const manifestIcons = [
Award,
Clock,
Lightbulb,
Truck,
MessageSquare,
ShieldCheck,
];
return (
<div className="overflow-hidden relative">
{/* Hero Section */}
<section className="relative min-h-[60vh] flex items-center pt-32 pb-20 overflow-hidden">
<section className="relative min-h-[60vh] flex items-center pt-44 pb-20 overflow-hidden">
<div className="absolute inset-0 z-0">
<Image
src="/media/drums/about-hero.jpg"
@@ -30,17 +50,21 @@ export default function About() {
<Counter value={1} className="section-number" />
<Reveal delay={0.1}>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
Über uns
{t("hero.tagline")}
</span>
</Reveal>
<Reveal delay={0.2}>
<h1 className="text-4xl sm:text-5xl md:text-6xl font-extrabold text-primary mb-6 md:mb-8 leading-tight">
Wir gestalten die <span className="text-accent">Infrastruktur</span> der Zukunft
{t.rich("hero.title", {
accent: (chunks) => (
<span className="text-accent">{chunks}</span>
),
})}
</h1>
</Reveal>
<Reveal delay={0.3}>
<p className="text-slate-600 text-lg md:text-2xl leading-relaxed mb-8">
MB Grid Solution steht for technische Exzellenz in der Energiekabeltechnologie. Wir verstehen uns als Ihr technischer Lotse.
{t("hero.subtitle")}
</p>
</Reveal>
</div>
@@ -56,28 +80,43 @@ export default function About() {
<Reveal direction="right">
<div className="space-y-6 text-lg text-slate-600 leading-relaxed relative">
<div className="absolute -left-4 top-0 w-1 h-full bg-accent/10" />
<p>
Unsere Wurzeln liegen in der tiefen praktischen Erfahrung unserer technischen Berater und unserer Netzwerke im globalem Kabelmarkt. Wir vereinen Tradition mit modernster Innovation, um zuverlässige Energielösungen für Projekte bis 110 kV zu realisieren.
</p>
<p>
Wir verstehen die Herausforderungen der Energiewende und bieten herstellerneutrale Beratung, die auf Fakten, Normen und jahrzehntelanger Erfahrung basiert.
</p>
<p>{t("intro.p1")}</p>
<p>{t("intro.p2")}</p>
</div>
</Reveal>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{[
{ name: 'Michael Bodemer', role: 'Geschäftsführung & Inhaber', linkedin: 'https://www.linkedin.com/in/michael-bodemer-33b493122/' },
{ name: 'Klaus Mintel', role: 'Geschäftsführung', linkedin: 'https://www.linkedin.com/in/klaus-mintel-b80a8b193/' }
{
name: "Michael Bodemer",
role: t("team.bodemer"),
linkedin:
"https://www.linkedin.com/in/michael-bodemer-33b493122/",
},
{
name: "Klaus Mintel",
role: t("team.mintel"),
linkedin:
"https://www.linkedin.com/in/klaus-mintel-b80a8b193/",
},
].map((person, i) => (
<Reveal key={i} delay={i * 0.1}>
<div className="card-modern !p-6 hover:-translate-y-1 transition-[box-shadow,transform] duration-300 relative overflow-hidden tech-card-border">
<div className="flex justify-between items-start mb-4 relative z-10">
<h3 className="text-xl font-bold text-primary">{person.name}</h3>
<a href={person.linkedin} target="_blank" rel="noopener noreferrer" className="text-[#0077b5] hover:scale-110 transition-transform">
<h3 className="text-xl font-bold text-primary">
{person.name}
</h3>
<a
href={person.linkedin}
target="_blank"
rel="noopener noreferrer"
className="text-[#0077b5] hover:scale-110 transition-transform"
>
<Linkedin size={20} />
</a>
</div>
<p className="text-accent text-sm font-bold uppercase tracking-wider relative z-10">{person.role}</p>
<p className="text-accent text-sm font-bold uppercase tracking-wider relative z-10">
{person.role}
</p>
</div>
</Reveal>
))}
@@ -92,31 +131,37 @@ export default function About() {
<div className="container-custom relative z-10">
<Counter value={3} className="section-number !text-white/5" />
<Reveal className="mb-20">
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">Werte</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">Unser Manifest</h2>
<p className="text-slate-400 text-base md:text-lg">Werte, die unsere tägliche Arbeit leiten und den Erfolg Ihrer Projekte sichern.</p>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("manifest.tagline")}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{t("manifest.title")}
</h2>
<p className="text-slate-400 text-base md:text-lg">
{t("manifest.subtitle")}
</p>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{[
{ icon: Award, title: 'Kompetenz', desc: 'Jahrzehntelange Erfahrung kombiniert mit europaweitem Know-how in modernsten Anlagen.' },
{ icon: Clock, title: 'Verfügbarkeit', desc: 'Schnelle und verlässliche Unterstützung ohne unnötige Verzögerungen.' },
{ icon: Lightbulb, title: 'Lösungen', desc: 'Wir stellen die richtigen Fragen, um die technisch und wirtschaftlich beste Lösung zu finden.' },
{ icon: Truck, title: 'Logistik', desc: 'Von der Fertigungsüberwachung bis zum termingerechten Fracht-Tracking.' },
{ icon: MessageSquare, title: 'Offenheit', desc: 'Wir hören zu und passen unsere Prozesse kontinuierlich an Ihren Erfolg an.' },
{ icon: ShieldCheck, title: 'Zuverlässigkeit', desc: 'Wir halten, was wir versprechen ohne Ausnahme. Verbindlichkeit ist unser Fundament.' }
].map((item, i) => (
<Reveal key={i} delay={i * 0.1}>
<div className="bg-white/5 p-10 rounded-3xl border border-white/10 group hover:-translate-y-1 transition-[box-shadow,transform] duration-300 h-full motion-fix relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-1 bg-accent/0 group-hover:bg-accent/50 transition-all duration-500" />
<div className="text-accent mb-6">
<item.icon size={32} />
{t.raw("manifest.items").map((item: any, i: number) => {
const Icon = manifestIcons[i];
return (
<Reveal key={i} delay={i * 0.1}>
<div className="bg-white/5 p-10 rounded-3xl border border-white/10 group hover:-translate-y-1 transition-[box-shadow,transform] duration-300 h-full motion-fix relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-1 bg-accent/0 group-hover:bg-accent/50 transition-all duration-500" />
<div className="text-accent mb-6">
<Icon size={32} />
</div>
<h4 className="text-xl font-bold text-white mb-4">
{i + 1}. {item.title}
</h4>
<p className="text-slate-400 leading-relaxed">
{item.desc}
</p>
</div>
<h4 className="text-xl font-bold text-white mb-4">{i + 1}. {item.title}</h4>
<p className="text-slate-400 leading-relaxed">{item.desc}</p>
</div>
</Reveal>
))}
</Reveal>
);
})}
</div>
</div>
</section>
@@ -132,13 +177,18 @@ export default function About() {
<div className="tech-corner bottom-8 right-8 border-b-2 border-r-2" />
<div className="relative z-10 max-w-2xl">
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6 md:mb-8">
Bereit für Ihr nächstes Projekt?
{t("cta.title")}
</h2>
<p className="text-slate-400 text-lg md:text-xl mb-8 md:mb-12">
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden.
{t("cta.subtitle")}
</p>
<Button href="/kontakt" variant="accent" showArrow className="w-full sm:w-auto !px-10 !py-5 text-lg">
Jetzt Kontakt aufnehmen
<Button
href="/kontakt"
variant="accent"
showArrow
className="w-full sm:w-auto !px-10 !py-5 text-lg"
>
{t("cta.button")}
</Button>
</div>
</div>

View File

@@ -1,30 +1,30 @@
'use client';
"use client";
import React, { useRef, useState } from 'react';
import { m, LazyMotion, domAnimation } from 'framer-motion';
import Link from 'next/link';
import { ArrowRight } from 'lucide-react';
import React, { useRef, useState } from "react";
import { m, LazyMotion, domAnimation } from "framer-motion";
import Link from "next/link";
import { ArrowRight } from "lucide-react";
interface ButtonProps {
children: React.ReactNode;
href?: string;
onClick?: () => void;
variant?: 'primary' | 'accent' | 'outline' | 'ghost';
variant?: "primary" | "accent" | "outline" | "ghost";
className?: string;
showArrow?: boolean;
type?: 'button' | 'submit' | 'reset';
type?: "button" | "submit" | "reset";
disabled?: boolean;
}
export const Button = ({
children,
href,
onClick,
variant = 'primary',
className = '',
export const Button = ({
children,
href,
onClick,
variant = "primary",
className = "",
showArrow = false,
type = 'button',
disabled = false
type = "button",
disabled = false,
}: ButtonProps) => {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
const [isHovered, setIsHovered] = useState(false);
@@ -37,23 +37,25 @@ export const Button = ({
});
};
const baseStyles = "inline-flex items-center justify-center px-6 py-4 md:px-10 md:py-5 rounded-xl md:rounded-2xl font-bold uppercase tracking-[0.2em] text-[10px] transition-all duration-500 relative group disabled:opacity-50 disabled:cursor-not-allowed select-none overflow-hidden";
const baseStyles =
"inline-flex items-center justify-center px-6 py-4 md:px-10 md:py-5 rounded-xl md:rounded-2xl font-bold uppercase tracking-[0.2em] text-[10px] transition-all duration-500 relative group cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed select-none overflow-hidden";
const variants = {
primary: "bg-primary text-white shadow-lg",
accent: "bg-accent text-white shadow-lg",
outline: "border-2 border-primary text-primary hover:bg-primary hover:text-white",
ghost: "bg-slate-100 text-primary hover:bg-slate-200"
outline:
"border-2 border-primary text-primary hover:bg-primary hover:text-white",
ghost: "bg-slate-100 text-primary hover:bg-slate-200",
};
const content = (
<span className="relative z-10 flex items-center gap-3">
{children}
{showArrow && (
<ArrowRight
size={14}
strokeWidth={3}
className="group-hover:translate-x-1 transition-transform duration-300"
<ArrowRight
size={14}
strokeWidth={3}
className="group-hover:translate-x-1 transition-transform duration-300"
/>
)}
</span>
@@ -61,13 +63,13 @@ export const Button = ({
const spotlight = (
<LazyMotion features={domAnimation}>
<m.div
className="absolute inset-0 z-0 pointer-events-none transition-opacity duration-500"
style={{
opacity: isHovered ? 1 : 0,
background: `radial-gradient(600px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(255,255,255,0.15), transparent 40%)`,
}}
/>
<m.div
className="absolute inset-0 z-0 pointer-events-none transition-opacity duration-500"
style={{
opacity: isHovered ? 1 : 0,
background: `radial-gradient(600px circle at ${mousePosition.x}px ${mousePosition.y}px, rgba(255,255,255,0.15), transparent 40%)`,
}}
/>
</LazyMotion>
);

View File

@@ -1,37 +1,70 @@
'use client';
"use client";
import React, { useState } from 'react';
import Image from 'next/image';
import { Mail, MapPin, CheckCircle } from 'lucide-react';
import { Button } from './Button';
import { Counter } from './Counter';
import { Reveal } from './Reveal';
import { TechBackground } from './TechBackground';
import React, { useState } from "react";
import Image from "next/image";
import { Mail, MapPin, CheckCircle } from "lucide-react";
import { Button } from "./Button";
import { Counter } from "./Counter";
import { Reveal } from "./Reveal";
import { TechBackground } from "./TechBackground";
import { StatusModal } from "./StatusModal";
import { useTranslations } from "next-intl";
export default function Contact() {
const t = useTranslations("Contact");
const [submitted, setSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [statusModal, setStatusModal] = useState({
isOpen: false,
type: "success" as "success" | "error",
title: "",
message: "",
buttonText: "",
});
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);
const formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData.entries());
try {
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (response.ok) {
setSubmitted(true);
setStatusModal({
isOpen: true,
type: "success",
title: t("form.successTitle"),
message: t("form.successMessage"),
buttonText: t("form.close") || "Schließen",
});
} else {
const err = await response.json();
alert(`Fehler: ${err.error || 'Es gab einen Fehler beim Senden Ihrer Nachricht.'}`);
const errorMsg = t.has(`form.${err.error}`)
? t(`form.${err.error}`)
: err.error || t("form.errorMessage");
setStatusModal({
isOpen: true,
type: "error",
title: t("form.errorTitle"),
message: errorMsg,
buttonText: t("form.tryAgain") || "Erneut versuchen",
});
}
} catch (error) {
alert('Es gab einen Fehler beim Senden Ihrer Nachricht.');
setStatusModal({
isOpen: true,
type: "error",
title: t("form.errorTitle"),
message: t("form.errorMessage"),
buttonText: t("form.tryAgain") || "Erneut versuchen",
});
} finally {
setLoading(false);
}
@@ -40,7 +73,7 @@ export default function Contact() {
return (
<div className="overflow-hidden relative">
{/* Hero Section */}
<section className="relative min-h-[40vh] flex items-center pt-32 pb-20 overflow-hidden">
<section className="relative min-h-[40vh] flex items-center pt-44 pb-20 overflow-hidden">
<div className="absolute inset-0 z-0">
<Image
src="/media/laying/contact-hero.jpg"
@@ -57,16 +90,22 @@ export default function Contact() {
<div className="text-left relative">
<div className="section-number">01</div>
<Reveal delay={0.1}>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">Kontakt</span>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("hero.tagline")}
</span>
</Reveal>
<Reveal delay={0.2}>
<h1 className="text-4xl sm:text-5xl md:text-6xl font-extrabold text-primary mb-6 md:mb-8 leading-tight">
Lassen Sie uns <span className="text-accent">sprechen</span>
{t.rich("hero.title", {
accent: (chunks) => (
<span className="text-accent">{chunks}</span>
),
})}
</h1>
</Reveal>
<Reveal delay={0.3}>
<p className="text-slate-600 text-lg md:text-2xl leading-relaxed">
Haben Sie Fragen zu einem Projekt oder benötigen Sie technische Beratung? Wir freuen uns auf Ihre Nachricht.
{t("hero.subtitle")}
</p>
</Reveal>
</div>
@@ -85,8 +124,13 @@ export default function Contact() {
<Mail size={24} />
</div>
<div className="relative z-10">
<h4 className="text-slate-400 font-bold text-xs uppercase tracking-widest mb-1 md:mb-2">E-Mail</h4>
<a href="mailto:info@mb-grid-solutions.com" className="text-white text-lg md:text-xl font-bold hover:text-accent transition-colors break-all">
<h4 className="text-slate-400 font-bold text-xs uppercase tracking-widest mb-1 md:mb-2">
{t("info.email")}
</h4>
<a
href="mailto:info@mb-grid-solutions.com"
className="text-white text-lg md:text-xl font-bold hover:text-accent transition-colors break-all"
>
info@mb-grid-solutions.com
</a>
</div>
@@ -99,10 +143,14 @@ export default function Contact() {
<MapPin size={24} />
</div>
<div className="relative z-10">
<h4 className="text-slate-400 font-bold text-xs uppercase tracking-widest mb-1 md:mb-2">Anschrift</h4>
<h4 className="text-slate-400 font-bold text-xs uppercase tracking-widest mb-1 md:mb-2">
{t("info.address")}
</h4>
<p className="text-white text-lg md:text-xl font-bold leading-relaxed">
MB Grid Solutions & Services GmbH<br />
Raiffeisenstraße 22<br />
{t("info.company")}
<br />
Raiffeisenstraße 22
<br />
73630 Remshalden
</p>
</div>
@@ -112,13 +160,13 @@ export default function Contact() {
<Reveal delay={0.3}>
<div className="w-full h-[300px] rounded-[2.5rem] overflow-hidden border border-white/10 shadow-sm grayscale hover:grayscale-0 transition-all duration-700 relative group">
<div className="absolute inset-0 border-2 border-accent/0 group-hover:border-accent/20 transition-all duration-500 z-10 pointer-events-none rounded-[2.5rem]" />
<iframe
width="100%"
height="100%"
frameBorder="0"
scrolling="no"
marginHeight={0}
marginWidth={0}
<iframe
width="100%"
height="100%"
frameBorder="0"
scrolling="no"
marginHeight={0}
marginWidth={0}
src="https://www.openstreetmap.org/export/embed.html?bbox=9.445,48.815,9.465,48.825&layer=mapnik&marker=48.8198,9.4552"
></iframe>
</div>
@@ -129,80 +177,116 @@ export default function Contact() {
<div className="bg-white p-6 md:p-12 rounded-3xl md:rounded-[2.5rem] border border-slate-100 shadow-xl relative overflow-hidden group">
<div className="tech-corner top-6 left-6 border-t-2 border-l-2 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="tech-corner bottom-6 right-6 border-b-2 border-r-2 opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
{submitted ? (
<div className="text-center py-12">
<div className="w-20 h-20 rounded-full bg-accent/10 text-accent flex items-center justify-center mx-auto mb-8">
<CheckCircle size={40} />
</div>
<h3 className="text-3xl font-bold text-primary mb-4">Nachricht gesendet</h3>
<h3 className="text-3xl font-bold text-primary mb-4">
{t("form.successTitle")}
</h3>
<p className="text-slate-600 text-lg mb-10">
Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.
{t("form.successMessage")}
</p>
<Button onClick={() => setSubmitted(false)}>
Weitere Nachricht
{t("form.moreMessages")}
</Button>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-6 relative z-10">
<form
onSubmit={handleSubmit}
className="space-y-6 relative z-10"
>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label htmlFor="name" className="text-sm font-bold text-slate-700 ml-1">Name *</label>
<label
htmlFor="name"
className="text-sm font-bold text-slate-700 ml-1"
>
{t("form.name")}
</label>
<input
type="text"
id="name"
name="name"
required
placeholder="Ihr Name"
placeholder={t("form.namePlaceholder")}
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:outline-none focus:border-accent focus:ring-4 focus:ring-accent/5 transition-all text-slate-900"
/>
</div>
<div className="space-y-2">
<label htmlFor="company" className="text-sm font-bold text-slate-700 ml-1">Firma</label>
<label
htmlFor="company"
className="text-sm font-bold text-slate-700 ml-1"
>
{t("form.company")}
</label>
<input
type="text"
id="company"
name="company"
placeholder="Ihr Unternehmen"
placeholder={t("form.companyPlaceholder")}
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:outline-none focus:border-accent focus:ring-4 focus:ring-accent/5 transition-all text-slate-900"
/>
</div>
</div>
<div className="space-y-2">
<label htmlFor="email" className="text-sm font-bold text-slate-700 ml-1">E-Mail *</label>
<label
htmlFor="email"
className="text-sm font-bold text-slate-700 ml-1"
>
{t("form.email")}
</label>
<input
type="email"
id="email"
name="email"
required
placeholder="ihre@email.de"
placeholder={t("form.emailPlaceholder")}
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:outline-none focus:border-accent focus:ring-4 focus:ring-accent/5 transition-all text-slate-900"
/>
</div>
<div className="space-y-2">
<label htmlFor="message" className="text-sm font-bold text-slate-700 ml-1">Nachricht *</label>
<label
htmlFor="message"
className="text-sm font-bold text-slate-700 ml-1"
>
{t("form.message")}
</label>
<textarea
id="message"
name="message"
required
rows={5}
placeholder="Wie können wir Ihnen helfen?"
placeholder={t("form.messagePlaceholder")}
className="w-full px-5 py-4 bg-slate-50 border border-slate-200 rounded-2xl focus:outline-none focus:border-accent focus:ring-4 focus:ring-accent/5 transition-all resize-none text-slate-900"
></textarea>
</div>
<Button type="submit" variant="accent" disabled={loading} className="w-full py-5 text-lg" showArrow>
{loading ? 'Wird gesendet...' : 'Nachricht senden'}
<Button
type="submit"
variant="accent"
disabled={loading}
className="w-full py-5 text-lg"
showArrow
>
{loading ? t("form.submitting") : t("form.submit")}
</Button>
<p className="text-xs text-slate-400 text-center">
* Pflichtfelder. Mit dem Absenden erklären Sie sich mit unserer{' '}
<a href="/datenschutz" className="text-accent hover:underline font-semibold">
Datenschutzerklärung
</a>{' '}
einverstanden.
{t.rich("form.privacyNote", {
link: (chunks) => (
<a
href="/datenschutz"
className="text-accent hover:underline font-semibold"
>
{chunks}
</a>
),
})}
</p>
</form>
)}
@@ -211,6 +295,15 @@ export default function Contact() {
</div>
</div>
</section>
<StatusModal
isOpen={statusModal.isOpen}
onClose={() => setStatusModal({ ...statusModal, isOpen: false })}
type={statusModal.type}
title={statusModal.title}
message={statusModal.message}
buttonText={statusModal.buttonText}
/>
</div>
);
}

View File

@@ -1,53 +1,62 @@
'use client';
"use client";
import { m, LazyMotion, domAnimation } from 'framer-motion';
import { BarChart3, CheckCircle2, ChevronRight, Shield, Zap } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import { Button } from './Button';
import { Counter } from './Counter';
import { Reveal } from './Reveal';
import { TechBackground } from './TechBackground';
import { TileGrid } from './TileGrid';
import { m, LazyMotion, domAnimation } from "framer-motion";
import {
BarChart3,
CheckCircle2,
ChevronRight,
Shield,
Zap,
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { Button } from "./Button";
import { Counter } from "./Counter";
import { Reveal } from "./Reveal";
import { TechBackground } from "./TechBackground";
import { TileGrid } from "./TileGrid";
import { useTranslations } from "next-intl";
export default function Home() {
const t = useTranslations("Index");
const serviceJsonLd = {
"@context": "https://schema.org",
"@type": "Service",
"name": "Technische Beratung für Energiekabelprojekte",
"provider": {
name: t("portfolio.items.beratung.title"),
provider: {
"@type": "Organization",
"name": "MB Grid Solutions & Services GmbH"
name: "MB Grid Solutions & Services GmbH",
},
"description": "Herstellerneutrale technische Beratung für Ihre Projekte in Mittel- und Hochspannungsnetzen bis zu 110 kV.",
"areaServed": "Europe",
"hasOfferCatalog": {
description: t("portfolio.description"),
areaServed: "Europe",
hasOfferCatalog: {
"@type": "OfferCatalog",
"name": "Dienstleistungen",
"itemListElement": [
name: t("portfolio.title"),
itemListElement: [
{
"@type": "Offer",
"itemOffered": {
itemOffered: {
"@type": "Service",
"name": "Technische Beratung"
}
name: t("portfolio.items.beratung.title"),
},
},
{
"@type": "Offer",
"itemOffered": {
itemOffered: {
"@type": "Service",
"name": "Projektbegleitung"
}
name: t("portfolio.items.begleitung.title"),
},
},
{
"@type": "Offer",
"itemOffered": {
itemOffered: {
"@type": "Service",
"name": "Produktbeschaffung"
}
}
]
}
name: t("portfolio.items.beschaffung.title"),
},
},
],
},
};
return (
@@ -56,9 +65,9 @@ export default function Home() {
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(serviceJsonLd) }}
/>
{/* Hero Section */}
<section className="relative min-h-[90vh] flex items-center pt-32 pb-20 overflow-hidden">
<section className="relative min-h-[90vh] flex items-center pt-44 pb-20 overflow-hidden">
<div className="absolute inset-0 z-0">
<Image
src="/media/business/hero-bg.jpg"
@@ -82,29 +91,37 @@ export default function Home() {
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-accent opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-accent"></span>
</span>
Engineering Excellence
{t("hero.tag")}
</span>
</Reveal>
<Reveal delay={0.2}>
<h1 className="text-4xl sm:text-5xl md:text-7xl font-extrabold text-primary mb-6 md:mb-8 leading-[1.1]">
Spezialisierter Partner für <span className="text-accent">Energiekabelprojekte</span>
{t("hero.title") ===
"Spezialisierter Partner für Energiekabelprojekte" ? (
<>
Spezialisierter Partner für{" "}
<span className="text-accent">Energiekabelprojekte</span>
</>
) : (
t("hero.title")
)}
</h1>
</Reveal>
<Reveal delay={0.3}>
<p className="text-slate-600 text-lg md:text-2xl leading-relaxed mb-8 md:mb-12 max-w-2xl">
Herstellerneutrale technische Beratung für Ihre Projekte in Mittel- und Hochspannungsnetzen bis zu 110 kV.
{t("hero.subtitle")}
</p>
</Reveal>
<Reveal delay={0.4}>
<div className="flex flex-wrap gap-4">
<Button href="/kontakt" variant="accent" showArrow>
Projekt anfragen
{t("hero.ctaPrimary")}
</Button>
<Button href="/ueber-uns" variant="ghost">
Mehr erfahren
{t("hero.ctaSecondary")}
</Button>
</div>
</Reveal>
@@ -119,34 +136,45 @@ export default function Home() {
<Counter value={2} className="section-number !text-white/5" />
<Reveal className="flex flex-col md:flex-row md:items-end justify-between gap-8 mb-16">
<div>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">Portfolio</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">Unsere Leistungen</h2>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("portfolio.tag")}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{t("portfolio.title")}
</h2>
<p className="text-slate-400 text-base md:text-xl">
Beratung durch unabhängige Experten mit jahrzehntelanger Erfahrung aus Engineering, Normengremien, Planung und Produktion.
{t("portfolio.description")}
</p>
</div>
<Link href="/ueber-uns" className="text-accent font-bold flex items-center gap-2 hover:text-white transition-colors group">
Alle Details ansehen <ChevronRight className="transition-transform group-hover:translate-x-1" size={20} />
<Link
href="/ueber-uns"
className="text-accent font-bold flex items-center gap-2 hover:text-white transition-colors group"
>
{t("portfolio.link")}{" "}
<ChevronRight
className="transition-transform group-hover:translate-x-1"
size={20}
/>
</Link>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{[
{
icon: <Zap size={32} />,
title: 'Technische Beratung',
desc: 'Individuelle Konzepte, Vergleiche, Risikobetrachtung und Empfehlungen für Ihre Kabelinfrastruktur.'
{
icon: <Zap size={32} />,
title: t("portfolio.items.beratung.title"),
desc: t("portfolio.items.beratung.desc"),
},
{
icon: <Shield size={32} />,
title: 'Projektbegleitung',
desc: 'Wir begleiten Sie bei der Verlegung und Installation, um Herausforderungen proaktiv zu lösen.'
{
icon: <Shield size={32} />,
title: t("portfolio.items.begleitung.title"),
desc: t("portfolio.items.begleitung.desc"),
},
{
icon: <BarChart3 size={32} />,
title: t("portfolio.items.beschaffung.title"),
desc: t("portfolio.items.beschaffung.desc"),
},
{
icon: <BarChart3 size={32} />,
title: 'Produktbeschaffung',
desc: 'Herstellerneutrale Marktanalyse und Unterstützung bei der Komponentenwahl hinsichtlich Qualität und Preis.'
}
].map((item, i) => (
<Reveal key={i} delay={i * 0.1}>
<div className="bg-white/5 p-8 rounded-2xl border border-white/10 group hover:-translate-y-2 transition-[box-shadow,transform] duration-300 h-full relative overflow-hidden">
@@ -154,7 +182,9 @@ export default function Home() {
<div className="w-16 h-16 rounded-2xl bg-accent/10 text-accent flex items-center justify-center mb-8 group-hover:bg-accent group-hover:text-white transition-colors relative z-10">
{item.icon}
</div>
<h3 className="text-2xl font-bold text-white mb-4 relative z-10">{item.title}</h3>
<h3 className="text-2xl font-bold text-white mb-4 relative z-10">
{item.title}
</h3>
<p className="text-slate-400 leading-relaxed relative z-10">
{item.desc}
</p>
@@ -187,21 +217,18 @@ export default function Home() {
</Reveal>
<div>
<Reveal>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">Expertise</span>
<h2 className="text-3xl md:text-5xl font-bold text-primary mb-6 md:mb-8">Anwendungen & Zielgruppen</h2>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("expertise.tag")}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-primary mb-6 md:mb-8">
{t("expertise.title")}
</h2>
<p className="text-slate-600 text-base md:text-xl mb-8 md:mb-12">
Wir unterstützen Akteure der Energiewende bei der Realisierung komplexer Kabelprojekte mit höchster Präzision.
{t("expertise.description")}
</p>
</Reveal>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{[
'Energieversorger',
'Ingenieurbüros',
'Tiefbauunternehmen',
'Industrie',
'Projektierer EE',
'Planungsbüros'
].map((item, i) => (
{t.raw("expertise.groups").map((item: string, i: number) => (
<Reveal key={i} delay={i * 0.05}>
<div className="flex items-center gap-4 p-4 bg-slate-50 rounded-xl border border-slate-100 hover:border-accent/30 transition-colors group relative overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-accent/0 group-hover:bg-accent/100 transition-all duration-300" />
@@ -230,7 +257,7 @@ export default function Home() {
<div className="absolute inset-0 bg-gradient-to-b from-slate-900 via-slate-900/80 to-slate-900" />
</div>
<TechBackground />
<div className="container-custom relative z-10">
<Counter value={4} className="section-number !text-white/5" />
{/* Data Stream Effect */}
@@ -238,15 +265,31 @@ export default function Home() {
<div className="absolute -bottom-10 left-10 w-px h-64 bg-gradient-to-b from-transparent via-accent/30 to-transparent animate-pulse delay-700" />
<Reveal className="mb-20">
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">Spezifikationen</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">Technische Expertise</h2>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("specs.tag")}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{t("specs.title")}
</h2>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8">
{[
{ label: 'Kabeltypen', value: 'N2XS(FL)2Y, N2X(F)KLD2Y...', desc: 'Umfassende Expertise im Design gängiger Hochspannungskabel.' },
{ label: 'Spannungsebenen', value: '64/110 kV & Mittelspannung', desc: 'Spezialisierte Beratung für komplexe Infrastrukturprojekte.' },
{ label: 'Leitertechnologie', value: 'Massiv-, Mehrdraht- & Milliken', desc: 'Optimierung des Leiterdesigns hinsichtlich Stromtragfähigkeit.' }
{
label: t("specs.items.kabel.label"),
value: t("specs.items.kabel.value"),
desc: t("specs.items.kabel.desc"),
},
{
label: t("specs.items.spannung.label"),
value: t("specs.items.spannung.value"),
desc: t("specs.items.spannung.desc"),
},
{
label: t("specs.items.technologie.label"),
value: t("specs.items.technologie.value"),
desc: t("specs.items.technologie.desc"),
},
].map((item, i) => (
<Reveal key={i} delay={i * 0.1}>
<div className="p-10 rounded-3xl bg-white/5 border border-white/10 backdrop-blur-sm hover:bg-white/10 transition-colors h-full relative group overflow-hidden">
@@ -257,9 +300,7 @@ export default function Home() {
<p className="text-2xl font-bold text-white mb-4 leading-tight">
{item.value}
</p>
<p className="text-slate-400 leading-relaxed">
{item.desc}
</p>
<p className="text-slate-400 leading-relaxed">{item.desc}</p>
</div>
</Reveal>
))}
@@ -283,38 +324,73 @@ export default function Home() {
<div className="tech-corner top-8 right-8 border-t-2 border-r-2" />
<div className="tech-corner bottom-8 left-8 border-b-2 border-l-2" />
<div className="tech-corner bottom-8 right-8 border-b-2 border-r-2" />
<div className="absolute top-0 right-0 w-1/2 h-full opacity-10 pointer-events-none">
<LazyMotion features={domAnimation}>
<svg viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<m.circle
animate={{ r: [400, 410, 400], opacity: [0.1, 0.2, 0.1] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut" }}
cx="400" cy="0" r="400" stroke="white" strokeWidth="2"
/>
<m.circle
animate={{ r: [300, 310, 300], opacity: [0.1, 0.2, 0.1] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut", delay: 0.5 }}
cx="400" cy="0" r="300" stroke="white" strokeWidth="2"
/>
<m.circle
animate={{ r: [200, 210, 200], opacity: [0.1, 0.2, 0.1] }}
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut", delay: 1 }}
cx="400" cy="0" r="200" stroke="white" strokeWidth="2"
/>
</svg>
<svg
viewBox="0 0 400 400"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<m.circle
animate={{ r: [400, 410, 400], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 5,
repeat: Infinity,
ease: "easeInOut",
}}
cx="400"
cy="0"
r="400"
stroke="white"
strokeWidth="2"
/>
<m.circle
animate={{ r: [300, 310, 300], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 4,
repeat: Infinity,
ease: "easeInOut",
delay: 0.5,
}}
cx="400"
cy="0"
r="300"
stroke="white"
strokeWidth="2"
/>
<m.circle
animate={{ r: [200, 210, 200], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 3,
repeat: Infinity,
ease: "easeInOut",
delay: 1,
}}
cx="400"
cy="0"
r="200"
stroke="white"
strokeWidth="2"
/>
</svg>
</LazyMotion>
</div>
<div className="relative z-10">
<h2 className="text-3xl md:text-6xl font-bold text-white mb-6 md:mb-8 leading-tight">
Bereit für Ihr nächstes Projekt?
{t("cta.title")}
</h2>
<p className="text-slate-300 text-lg md:text-xl mb-8 md:mb-12 leading-relaxed">
Lassen Sie uns gemeinsam die optimale Lösung für Ihre Energieinfrastruktur finden. Wir beraten Sie herstellerneutral und kompetent.
{t("cta.subtitle")}
</p>
<Button href="/kontakt" variant="accent" showArrow className="w-full sm:w-auto !px-10 !py-5 text-lg">
Jetzt Kontakt aufnehmen
<Button
href="/kontakt"
variant="accent"
showArrow
className="w-full sm:w-auto !px-10 !py-5 text-lg"
>
{t("cta.button")}
</Button>
</div>
</div>

View File

@@ -1,15 +1,17 @@
'use client';
"use client";
import { AnimatePresence, m, LazyMotion, domAnimation } from 'framer-motion';
import { ArrowUp, Home, Info, Menu, X } from 'lucide-react';
import Image from 'next/image';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { Button } from './Button';
import { Reveal } from './Reveal';
import { AnimatePresence, m, LazyMotion, domAnimation } from "framer-motion";
import { ArrowUp, Home, Info, Menu, X } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { usePathname } from "next/navigation";
import React, { useEffect, useState } from "react";
import { Button } from "./Button";
import { Reveal } from "./Reveal";
import { useTranslations } from "next-intl";
const Layout = ({ children }: { children: React.ReactNode }) => {
const t = useTranslations("Layout");
const pathname = usePathname();
const [showScrollTop, setShowScrollTop] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
@@ -20,9 +22,9 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
setShowScrollTop(window.scrollY > 400);
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
window.addEventListener("scroll", handleScroll, { passive: true });
return () => window.removeEventListener("scroll", handleScroll);
}, []);
useEffect(() => {
@@ -30,124 +32,131 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
}, [pathname]);
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
window.scrollTo({ top: 0, behavior: "smooth" });
};
const isActive = (path: string) => pathname === path;
const isActive = (path: string) =>
pathname === path || pathname === `/en${path}` || pathname === `/de${path}`;
const navLinks = [
{ href: '/', label: 'Startseite', icon: Home },
{ href: '/ueber-uns', label: 'Über uns', icon: Info },
{ href: "/", label: t("nav.home"), icon: Home },
{ href: "/ueber-uns", label: t("nav.about"), icon: Info },
];
return (
<div className="min-h-screen flex flex-col font-sans">
<Reveal direction="down" fullWidth trigger="mount" className="fixed top-0 left-0 right-0 z-[100]">
<Reveal
direction="down"
fullWidth
trigger="mount"
className="fixed top-0 left-0 right-0 z-[100]"
>
<header
className={`transition-all duration-300 flex items-center py-1 ${
isScrolled
? 'bg-white/90 backdrop-blur-lg border-b border-slate-200 shadow-sm'
: 'bg-gradient-to-b from-white/80 via-white/40 to-transparent'
? "bg-white/90 backdrop-blur-lg border-b border-slate-200 shadow-sm"
: "bg-gradient-to-b from-white/80 via-white/40 to-transparent"
}`}
>
<div className="container-custom flex justify-between items-center w-full relative z-10">
<Link
href="/"
className="relative z-10 flex items-center group"
aria-label="MB Grid Solutions - Zur Startseite"
>
<div className={`relative transition-all duration-300 ${isScrolled ? 'h-[50px] md:h-[80px] w-[120px] md:w-[200px] mt-0 mb-[-10px]' : 'h-[80px] md:h-[140px] w-[180px] md:w-[320px] mt-2 md:mt-4 mb-[-20px] md:mb-[-40px]'}`}>
<Image
src="/assets/logo.png"
alt="MB Grid Solutions"
fill
className="object-contain"
priority
/>
</div>
</Link>
{/* Desktop Navigation */}
<nav className="hidden md:flex items-center gap-8" aria-label="Hauptnavigation">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`relative px-2 py-1 text-sm font-bold tracking-tight transition-all group ${
isActive(link.href)
? 'text-primary'
: `${isScrolled ? 'text-slate-600' : 'text-slate-900'} hover:text-primary`
}`}
>
{link.label}
<span className={`absolute -bottom-1 left-0 w-full h-0.5 bg-accent transition-transform duration-300 origin-left ${isActive(link.href) ? 'scale-x-100' : 'scale-x-0 group-hover:scale-x-100'}`} />
</Link>
))}
<Button
href="/kontakt"
className="ml-4 !py-2 !px-5 !text-[10px]"
<div className="container-custom flex justify-between items-center w-full relative z-10">
<Link
href="/"
className="relative z-10 flex items-center group"
aria-label={`${t("nav.home")} - Zur Startseite`}
>
Projekt anfragen
</Button>
</nav>
<div
className={`relative transition-all duration-300 ${isScrolled ? "h-[50px] md:h-[80px] w-[120px] md:w-[200px] mt-0 mb-[-10px]" : "h-[80px] md:h-[140px] w-[180px] md:w-[320px] mt-2 md:mt-4 mb-[-20px] md:mb-[-40px]"}`}
>
<Image
src="/assets/logo.png"
alt="MB Grid Solutions"
fill
className="object-contain"
priority
/>
</div>
</Link>
{/* Mobile Menu Toggle */}
<button
className="md:hidden p-2 text-slate-600 hover:text-primary transition-colors"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-label="Menü öffnen"
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Desktop Navigation */}
<nav
className="hidden md:flex items-center gap-8"
aria-label="Hauptnavigation"
>
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`relative px-2 py-1 text-sm font-bold tracking-tight transition-all group ${
isActive(link.href)
? "text-primary"
: `${isScrolled ? "text-slate-600" : "text-slate-900"} hover:text-primary`
}`}
>
{link.label}
<span
className={`absolute -bottom-1 left-0 w-full h-0.5 bg-accent transition-transform duration-300 origin-left ${isActive(link.href) ? "scale-x-100" : "scale-x-0 group-hover:scale-x-100"}`}
/>
</Link>
))}
<Button href="/kontakt" className="ml-4 !py-2 !px-5 !text-[10px]">
{t("nav.cta")}
</Button>
</nav>
{/* Mobile Menu Toggle */}
<button
className="md:hidden p-2 text-slate-600 hover:text-primary transition-colors"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-label="Menü öffnen"
>
{isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</header>
</Reveal>
{/* Mobile Menu Overlay */}
<LazyMotion features={domAnimation}>
<AnimatePresence>
{isMobileMenuOpen && (
<m.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="fixed inset-0 z-[90] bg-white pt-32 px-6 md:hidden"
>
<nav className="flex flex-col gap-4">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`flex items-center gap-4 p-4 rounded-xl text-lg font-semibold transition-all ${
isActive(link.href)
? 'text-accent bg-accent/5'
: 'text-slate-600 hover:text-primary hover:bg-slate-50'
}`}
>
<link.icon size={24} />
{link.label}
</Link>
))}
<Button
href="/kontakt"
className="mt-4 w-full"
>
Projekt anfragen
</Button>
</nav>
</m.div>
)}
</AnimatePresence>
<AnimatePresence>
{isMobileMenuOpen && (
<m.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="fixed inset-0 z-[90] bg-white pt-32 px-6 md:hidden"
>
<nav className="flex flex-col gap-4">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className={`flex items-center gap-4 p-4 rounded-xl text-lg font-semibold transition-all ${
isActive(link.href)
? "text-accent bg-accent/5"
: "text-slate-600 hover:text-primary hover:bg-slate-50"
}`}
>
<link.icon size={24} />
{link.label}
</Link>
))}
<Button href="/kontakt" className="mt-4 w-full">
{t("nav.cta")}
</Button>
</nav>
</m.div>
)}
</AnimatePresence>
</LazyMotion>
<main className="flex-grow">
{children}
</main>
<main className="flex-grow">{children}</main>
<button
onClick={scrollToTop}
className={`fixed bottom-8 right-8 w-12 h-12 bg-primary text-white rounded-full flex items-center justify-center cursor-pointer z-[80] shadow-xl transition-all duration-300 hover:-translate-y-1 hover:bg-accent ${
showScrollTop ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4 pointer-events-none'
showScrollTop
? "opacity-100 translate-y-0"
: "opacity-0 translate-y-4 pointer-events-none"
}`}
aria-label="Nach oben scrollen"
>
@@ -157,87 +166,128 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
<Reveal fullWidth>
<footer className="bg-slate-900 text-slate-300 py-16 md:py-24 relative overflow-hidden group">
<div className="absolute inset-0 grid-pattern opacity-[0.08] pointer-events-none" />
{/* Animated Tech Lines */}
<LazyMotion features={domAnimation}>
<m.div
animate={{ x: ['-100%', '100%'] }}
transition={{ duration: 15, repeat: Infinity, ease: "linear" }}
className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-accent/30 to-transparent"
/>
<m.div
animate={{ x: ['100%', '-100%'] }}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-accent/20 to-transparent"
/>
<m.div
animate={{ x: ["-100%", "100%"] }}
transition={{ duration: 15, repeat: Infinity, ease: "linear" }}
className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-accent/30 to-transparent"
/>
<m.div
animate={{ x: ["100%", "-100%"] }}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
className="absolute bottom-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-accent/20 to-transparent"
/>
</LazyMotion>
{/* Corner Accents */}
<div className="tech-corner top-8 left-8 border-t border-l border-white/10 group-hover:border-accent/30 transition-colors duration-700" />
<div className="tech-corner bottom-8 right-8 border-b border-r border-white/10 group-hover:border-accent/30 transition-colors duration-700" />
<div className="container-custom relative z-10">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10 md:gap-12 mb-12 md:mb-16">
<div className="lg:col-span-2">
<Link href="/" className="inline-block mb-6 md:mb-8 group">
<div className="relative h-16 md:h-20 w-48 brightness-0 invert opacity-80 group-hover:opacity-100 transition-opacity">
<Image
src="/assets/logo.png"
alt="MB Grid Solutions"
fill
className="object-contain object-left"
/>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-10 md:gap-12 mb-12 md:mb-16">
<div className="lg:col-span-2">
<Link href="/" className="inline-block mb-6 md:mb-8 group">
<div className="relative h-16 md:h-20 w-48 brightness-0 invert opacity-80 group-hover:opacity-100 transition-opacity">
<Image
src="/assets/logo.png"
alt="MB Grid Solutions"
fill
className="object-contain object-left"
/>
</div>
</Link>
<p className="text-slate-400 max-w-md leading-relaxed mb-8">
{t("footer.description")}
</p>
<div className="flex gap-4">
{/* Social links could go here */}
</div>
</Link>
<p className="text-slate-400 max-w-md leading-relaxed mb-8">
Ihr spezialisierter Partner für herstellerneutrale technische Beratung und Projektbegleitung bei Energiekabelprojekten bis 110 kV.
</p>
<div className="flex gap-4">
{/* Social links could go here */}
</div>
<div>
<h4 className="text-white font-bold mb-6">
{t("footer.navigation")}
</h4>
<nav className="flex flex-col gap-4">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="hover:text-accent transition-colors"
>
{link.label}
</Link>
))}
<Link
href="/kontakt"
className="hover:text-accent transition-colors"
>
{t("nav.contact")}
</Link>
</nav>
</div>
<div>
<h4 className="text-white font-bold mb-6">
{t("footer.legal")}
</h4>
<nav className="flex flex-col gap-4">
<Link
href="/impressum"
className="hover:text-accent transition-colors"
>
{t("footer.impressum")}
</Link>
<Link
href="/datenschutz"
className="hover:text-accent transition-colors"
>
{t("footer.datenschutz")}
</Link>
<Link
href="/agb"
className="hover:text-accent transition-colors"
>
{t("footer.agb")}
</Link>
</nav>
</div>
</div>
<div>
<h4 className="text-white font-bold mb-6">Navigation</h4>
<nav className="flex flex-col gap-4">
{navLinks.map((link) => (
<Link key={link.href} href={link.href} className="hover:text-accent transition-colors">
{link.label}
</Link>
))}
<Link href="/kontakt" className="hover:text-accent transition-colors">Kontakt</Link>
</nav>
</div>
<div>
<h4 className="text-white font-bold mb-6">Rechtliches</h4>
<nav className="flex flex-col gap-4">
<Link href="/impressum" className="hover:text-accent transition-colors">Impressum</Link>
<Link href="/datenschutz" className="hover:text-accent transition-colors">Datenschutz</Link>
<Link href="/agb" className="hover:text-accent transition-colors">AGB</Link>
</nav>
<div className="pt-8 border-t border-slate-800 flex flex-col md:flex-row justify-between items-center gap-6 md:gap-4 text-sm text-slate-500 relative text-center md:text-left">
<div className="absolute -top-px left-1/2 -translate-x-1/2 md:left-0 md:translate-x-0 w-12 h-px bg-accent/50" />
<p>
&copy; {new Date().getFullYear()} MB Grid Solutions & Services
GmbH. <br className="md:hidden" /> {t("footer.rights")}
</p>
<p className="flex items-center gap-2">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-accent opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-accent"></span>
</span>
{t("footer.madeWith")}{" "}
<span className="text-accent">{t("footer.precision")}</span>{" "}
{t("footer.inGermany")}
</p>
</div>
</div>
<div className="pt-8 border-t border-slate-800 flex flex-col md:flex-row justify-between items-center gap-6 md:gap-4 text-sm text-slate-500 relative text-center md:text-left">
<div className="absolute -top-px left-1/2 -translate-x-1/2 md:left-0 md:translate-x-0 w-12 h-px bg-accent/50" />
<p>&copy; {new Date().getFullYear()} MB Grid Solutions & Services GmbH. <br className="md:hidden" /> Alle Rechte vorbehalten.</p>
<p className="flex items-center gap-2">
<span className="relative flex h-2 w-2">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-accent opacity-75"></span>
<span className="relative inline-flex rounded-full h-2 w-2 bg-accent"></span>
</span>
Made with <span className="text-accent">precision</span> in Germany
</p>
</div>
</div>
</footer>
</Reveal>
<div className="bg-slate-950 py-6 border-t border-white/5">
<div className="container-custom">
<p className="text-[10px] uppercase tracking-[0.2em] text-slate-600 text-center md:text-left">
Website developed by <a href="https://mintel.me" target="_blank" rel="noopener noreferrer" className="text-slate-500 hover:text-accent transition-colors duration-300">mintel.me</a>
Website developed by{" "}
<a
href="https://mintel.me"
target="_blank"
rel="noopener noreferrer"
className="text-slate-500 hover:text-accent transition-colors duration-300"
>
mintel.me
</a>
</p>
</div>
</div>

104
components/StatusModal.tsx Normal file
View File

@@ -0,0 +1,104 @@
"use client";
import React from "react";
import { m, AnimatePresence, LazyMotion, domAnimation } from "framer-motion";
import { CheckCircle, AlertCircle, X } from "lucide-react";
import { Button } from "./Button";
interface StatusModalProps {
isOpen: boolean;
onClose: () => void;
type: "success" | "error";
title: string;
message: string;
buttonText: string;
}
export const StatusModal = ({
isOpen,
onClose,
type,
title,
message,
buttonText,
}: StatusModalProps) => {
return (
<LazyMotion features={domAnimation}>
<AnimatePresence>
{isOpen && (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-6">
{/* Backdrop */}
<m.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="absolute inset-0 bg-slate-950/80 backdrop-blur-sm"
/>
{/* Modal Content */}
<m.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ type: "spring", damping: 25, stiffness: 300 }}
className="relative w-full max-w-lg bg-white rounded-[2.5rem] border border-slate-100 shadow-2xl overflow-hidden group"
>
{/* Tech Decoration */}
<div className="absolute top-0 left-0 w-full h-2 bg-slate-100 overflow-hidden">
<m.div
initial={{ x: "-100%" }}
animate={{ x: "100%" }}
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
className={`absolute inset-0 w-1/2 ${type === "success" ? "bg-accent" : "bg-red-500"} opacity-30`}
/>
</div>
<button
onClick={onClose}
className="absolute top-6 right-6 p-2 text-slate-400 hover:text-primary transition-colors hover:bg-slate-50 rounded-xl"
>
<X size={20} />
</button>
<div className="p-8 md:p-12 text-center">
<div
className={`w-20 h-20 rounded-full ${type === "success" ? "bg-accent/10 text-accent" : "bg-red-50 text-red-500"} flex items-center justify-center mx-auto mb-8 relative`}
>
<div
className={`absolute inset-0 ${type === "success" ? "bg-accent/20" : "bg-red-500/20"} rounded-full animate-ping opacity-20`}
/>
{type === "success" ? (
<CheckCircle size={40} />
) : (
<AlertCircle size={40} />
)}
</div>
<h3 className="text-3xl font-extrabold text-primary mb-4 leading-tight">
{title}
</h3>
<p className="text-slate-600 text-lg mb-10 leading-relaxed">
{message}
</p>
<Button
onClick={onClose}
variant={type === "success" ? "accent" : "primary"}
className="w-full py-5 text-lg"
showArrow
>
{buttonText}
</Button>
</div>
{/* Decorative Corners */}
<div className="tech-corner top-4 left-4 border-t-2 border-l-2 opacity-20" />
<div className="tech-corner bottom-4 right-4 border-b-2 border-r-2 opacity-20" />
</m.div>
</div>
)}
</AnimatePresence>
</LazyMotion>
);
};