feat: complete migration to static MDX, stabilize local infrastructure and fix i18n issues
Some checks failed
🚀 Build & Deploy / 🔍 Prepare (push) Successful in 4s
🚀 Build & Deploy / 🚀 Deploy (push) Has been cancelled
🚀 Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
🚀 Build & Deploy / 🏗️ Build (push) Has been cancelled
🚀 Build & Deploy / 🔔 Notify (push) Has been cancelled
🚀 Build & Deploy / 🧪 QA (push) Has been cancelled

This commit is contained in:
2026-05-04 10:26:45 +02:00
parent 41ef9092d6
commit 5fb73d57dd
43 changed files with 2304 additions and 532 deletions

View File

@@ -0,0 +1,52 @@
"use client";
import React from "react";
import Image from "next/image";
import { Reveal } from "@/components/Reveal";
import { TechBackground } from "@/components/TechBackground";
import { Counter } from "@/components/Counter";
interface AboutHeroProps {
tagline: string;
title: React.ReactNode;
subtitle: string;
}
export function AboutHero({ tagline, title, subtitle }: AboutHeroProps) {
return (
<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"
alt="About MB Grid Solutions"
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-r from-white via-white/95 to-white/40" />
<TechBackground />
</div>
<div className="container-custom relative z-10">
<div className="text-left relative">
<Counter value={1} className="section-number" />
<Reveal delay={0.1}>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{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">
{title}
</h1>
</Reveal>
<Reveal delay={0.3}>
<p className="text-slate-600 text-lg md:text-2xl leading-relaxed mb-8">
{subtitle}
</p>
</Reveal>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,63 @@
"use client";
import React from "react";
import { Linkedin } from "lucide-react";
import { Reveal } from "@/components/Reveal";
import { TechBackground } from "@/components/TechBackground";
interface Person {
name: string;
role: string;
linkedin: string;
}
interface AboutIntroProps {
paragraphs: string[];
team: Person[];
}
export function AboutIntro({ paragraphs, team }: AboutIntroProps) {
return (
<section className="bg-white relative overflow-hidden">
<TechBackground />
<div className="container-custom relative z-10">
<div className="section-number">02</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center">
<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" />
{paragraphs.map((p, i) => (
<p key={i}>{p}</p>
))}
</div>
</Reveal>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
{team.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">
<h2 className="text-xl font-bold text-primary">
{person.name}
</h2>
<a
href={person.linkedin}
target="_blank"
rel="noopener noreferrer"
className="text-[#0077b5] hover:scale-110 transition-transform"
aria-label={`LinkedIn Profile of ${person.name}`}
>
<Linkedin size={20} />
</a>
</div>
<p className="text-accent text-sm font-bold uppercase tracking-wider relative z-10">
{person.role}
</p>
</div>
</Reveal>
))}
</div>
</div>
</div>
</section>
);
}

View File

@@ -1,16 +1,30 @@
"use client";
import React from "react";
import { m } from "framer-motion";
import { Reveal } from "../Reveal";
import { Counter } from "../Counter";
import { TechBackground } from "../TechBackground";
import { Button } from "../Button";
import { motion } from "framer-motion";
import { Reveal } from "@/components/Reveal";
import { Counter } from "@/components/Counter";
import { TechBackground } from "@/components/TechBackground";
import { Button } from "@/components/Button";
import { useTranslations } from "next-intl";
export const CTASection = () => {
interface CTASectionProps {
title?: string;
subtitle?: string;
buttonText?: string;
}
export const CTASection = ({
title,
subtitle,
buttonText,
}: CTASectionProps) => {
const t = useTranslations("Index");
const displayTitle = title || t("cta.title");
const displaySubtitle = subtitle || t("cta.subtitle");
const displayButtonText = buttonText || t("cta.button");
return (
<section className="bg-white relative overflow-hidden">
<TechBackground />
@@ -34,7 +48,7 @@ export const CTASection = () => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<m.circle
<motion.circle
animate={{ r: [400, 410, 400], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 5,
@@ -47,7 +61,7 @@ export const CTASection = () => {
stroke="white"
strokeWidth="2"
/>
<m.circle
<motion.circle
animate={{ r: [300, 310, 300], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 4,
@@ -61,7 +75,7 @@ export const CTASection = () => {
stroke="white"
strokeWidth="2"
/>
<m.circle
<motion.circle
animate={{ r: [200, 210, 200], opacity: [0.1, 0.2, 0.1] }}
transition={{
duration: 3,
@@ -80,10 +94,10 @@ export const CTASection = () => {
<div className="relative z-10">
<h2 className="text-3xl md:text-6xl font-bold text-white mb-6 md:mb-8 leading-tight">
{t("cta.title")}
{displayTitle}
</h2>
<p className="text-slate-300 text-lg md:text-xl mb-8 md:mb-12 leading-relaxed">
{t("cta.subtitle")}
{displaySubtitle}
</p>
<Button
href="/kontakt"
@@ -91,7 +105,7 @@ export const CTASection = () => {
showArrow
className="w-full sm:w-auto !px-10 !py-5 text-lg"
>
{t("cta.button")}
{displayButtonText}
</Button>
</div>
</div>

View File

@@ -3,14 +3,31 @@
import React from "react";
import Image from "next/image";
import { CheckCircle2 } from "lucide-react";
import { Reveal } from "../Reveal";
import { Counter } from "../Counter";
import { TechBackground } from "../TechBackground";
import { Reveal } from "@/components/Reveal";
import { Counter } from "@/components/Counter";
import { TechBackground } from "@/components/TechBackground";
import { useTranslations } from "next-intl";
export const ExpertiseSection = () => {
interface ExpertiseSectionProps {
tag?: string;
title?: string;
description?: string;
groups?: string[];
}
export const ExpertiseSection = ({
tag,
title,
description,
groups,
}: ExpertiseSectionProps) => {
const t = useTranslations("Index");
const displayTag = tag || t("expertise.tag");
const displayTitle = title || t("expertise.title");
const displayDescription = description || t("expertise.description");
const displayGroups = groups || t.raw("expertise.groups");
return (
<section className="bg-white relative overflow-hidden">
<TechBackground />
@@ -34,17 +51,17 @@ export const ExpertiseSection = () => {
<div>
<Reveal>
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("expertise.tag")}
{displayTag}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-primary mb-6 md:mb-8">
{t("expertise.title")}
{displayTitle}
</h2>
<p className="text-slate-600 text-base md:text-xl mb-8 md:mb-12">
{t("expertise.description")}
{displayDescription}
</p>
</Reveal>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{t.raw("expertise.groups").map((item: string, i: number) => (
{displayGroups.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" />

View File

@@ -0,0 +1,79 @@
"use client";
import React from "react";
import Image from "next/image";
import { Button } from "@/components/Button";
import { Counter } from "@/components/Counter";
import { Reveal } from "@/components/Reveal";
import { TechBackground } from "@/components/TechBackground";
interface HeroProps {
tag: string;
title: React.ReactNode;
subtitle: string;
ctaPrimary: string;
ctaSecondary: string;
}
export function Hero({
tag,
title,
subtitle,
ctaPrimary,
ctaSecondary,
}: HeroProps) {
return (
<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"
alt="MB Grid Solutions Hero"
fill
className="object-cover"
priority
quality={75}
/>
<div className="absolute inset-0 bg-gradient-to-r from-slate-100/80 via-white/90 to-white/40 md:to-transparent" />
<TechBackground />
</div>
<div className="container-custom relative z-10">
<div className="text-left relative">
<Counter value={1} className="section-number" />
<Reveal delay={0.1}>
<span className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent/10 text-accent text-xs font-bold uppercase tracking-wider mb-6">
<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>
{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]">
{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">
{subtitle}
</p>
</Reveal>
<Reveal delay={0.4}>
<div className="flex flex-wrap gap-4">
<Button href="/kontakt" variant="accent" showArrow>
{ctaPrimary}
</Button>
<Button href="/ueber-uns" variant="ghost">
{ctaSecondary}
</Button>
</div>
</Reveal>
</div>
</div>
</section>
);
}

View File

@@ -0,0 +1,80 @@
"use client";
import React from "react";
import {
Award,
Clock,
Lightbulb,
Truck,
MessageSquare,
ShieldCheck,
} from "lucide-react";
import { Reveal } from "@/components/Reveal";
import { TechBackground } from "@/components/TechBackground";
import { Counter } from "@/components/Counter";
const manifestIcons = [
Award,
Clock,
Lightbulb,
Truck,
MessageSquare,
ShieldCheck,
];
interface ManifestItem {
title: string;
desc: string;
}
interface ManifestSectionProps {
tagline: string;
title: string;
subtitle: string;
items: ManifestItem[];
}
export function ManifestSection({
tagline,
title,
subtitle,
items,
}: ManifestSectionProps) {
return (
<section className="bg-slate-950 text-accent relative overflow-hidden">
<TechBackground />
<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">
{tagline}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{title}
</h2>
<p className="text-slate-400 text-base md:text-lg">{subtitle}</p>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{items.map((item, i) => {
const Icon = manifestIcons[i % manifestIcons.length];
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>
<h3 className="text-xl font-bold text-white mb-4">
{i + 1}. {item.title}
</h3>
<p className="text-slate-400 leading-relaxed">{item.desc}</p>
</div>
</Reveal>
);
})}
</div>
</div>
</section>
);
}

View File

@@ -3,14 +3,59 @@
import React from "react";
import Link from "next/link";
import { ChevronRight, Zap, Shield, BarChart3 } from "lucide-react";
import { Reveal } from "../Reveal";
import { Counter } from "../Counter";
import { TechBackground } from "../TechBackground";
import { Reveal } from "@/components/Reveal";
import { Counter } from "@/components/Counter";
import { TechBackground } from "@/components/TechBackground";
import { useTranslations } from "next-intl";
export const PortfolioSection = () => {
interface PortfolioItem {
icon: React.ReactNode;
title: string;
desc: string;
}
interface PortfolioSectionProps {
tag?: string;
title?: string;
description?: string;
linkText?: string;
items?: PortfolioItem[];
}
export const PortfolioSection = ({
tag,
title,
description,
linkText,
items,
}: PortfolioSectionProps) => {
const t = useTranslations("Index");
const displayTag = tag || t("portfolio.tag");
const displayTitle = title || t("portfolio.title");
const displayDescription = description || t("portfolio.description");
const displayLinkText = linkText || t("portfolio.link");
const defaultItems = [
{
icon: <Zap size={32} />,
title: t("portfolio.items.beratung.title"),
desc: t("portfolio.items.beratung.desc"),
},
{
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"),
},
];
const displayItems = items || defaultItems;
return (
<section className="bg-slate-950 text-accent relative overflow-hidden">
<TechBackground />
@@ -19,20 +64,20 @@ export const PortfolioSection = () => {
<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">
{t("portfolio.tag")}
{displayTag}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{t("portfolio.title")}
{displayTitle}
</h2>
<p className="text-slate-400 text-base md:text-xl">
{t("portfolio.description")}
{displayDescription}
</p>
</div>
<Link
href="/ueber-uns"
className="text-accent font-bold flex items-center gap-2 hover:text-white transition-colors group"
>
{t("portfolio.link")}{" "}
{displayLinkText}{" "}
<ChevronRight
className="transition-transform group-hover:translate-x-1"
size={20}
@@ -41,23 +86,7 @@ export const PortfolioSection = () => {
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{[
{
icon: <Zap size={32} />,
title: t("portfolio.items.beratung.title"),
desc: t("portfolio.items.beratung.desc"),
},
{
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"),
},
].map((item, i) => (
{displayItems.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">
<div className="absolute top-0 right-0 w-16 h-16 bg-accent/5 -mr-8 -mt-8 rounded-full group-hover:bg-accent/10 transition-colors" />

View File

@@ -2,14 +2,53 @@
import React from "react";
import Image from "next/image";
import { Reveal } from "../Reveal";
import { Counter } from "../Counter";
import { TechBackground } from "../TechBackground";
import { Reveal } from "@/components/Reveal";
import { Counter } from "@/components/Counter";
import { TechBackground } from "@/components/TechBackground";
import { useTranslations } from "next-intl";
export const TechnicalSpecsSection = () => {
interface SpecItem {
label: string;
value: string;
desc: string;
}
interface TechnicalSpecsSectionProps {
tag?: string;
title?: string;
items?: SpecItem[];
}
export const TechnicalSpecsSection = ({
tag,
title,
items,
}: TechnicalSpecsSectionProps) => {
const t = useTranslations("Index");
const displayTag = tag || t("specs.tag");
const displayTitle = title || t("specs.title");
const defaultItems = [
{
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"),
},
];
const displayItems = items || defaultItems;
return (
<section className="relative py-24 md:py-32 text-white overflow-hidden bg-slate-900">
<div className="absolute inset-0 opacity-20">
@@ -31,31 +70,15 @@ export const TechnicalSpecsSection = () => {
<Reveal className="mb-20">
<span className="text-accent font-bold uppercase tracking-widest text-sm mb-4 block">
{t("specs.tag")}
{displayTag}
</span>
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6">
{t("specs.title")}
{displayTitle}
</h2>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-8">
{[
{
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) => (
{displayItems.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">
<div className="absolute top-0 left-0 w-full h-1 bg-accent/0 group-hover:bg-accent/50 transition-all duration-500" />