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
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:
@@ -11,10 +11,10 @@ import {
|
||||
ShieldCheck,
|
||||
Truck,
|
||||
} from "lucide-react";
|
||||
import { Reveal } from "./Reveal";
|
||||
import { TechBackground } from "./TechBackground";
|
||||
import { Counter } from "./Counter";
|
||||
import { Button } from "./Button";
|
||||
import { Reveal } from "@/components/Reveal";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
import { Counter } from "@/components/Counter";
|
||||
import { Button } from "@/components/Button";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function About() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { m } from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
import Link from "next/link";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
|
||||
@@ -62,7 +62,7 @@ export const Button = ({
|
||||
);
|
||||
|
||||
const spotlight = (
|
||||
<m.div
|
||||
<motion.div
|
||||
className="absolute inset-0 z-0 pointer-events-none transition-opacity duration-500"
|
||||
style={{
|
||||
opacity: isHovered ? 1 : 0,
|
||||
|
||||
@@ -4,11 +4,11 @@ import React, { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
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 { Button } from "@/components/Button";
|
||||
import { Counter } from "@/components/Counter";
|
||||
import { Reveal } from "@/components/Reveal";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
import { StatusModal } from "@/components/StatusModal";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function Contact() {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { Button } from "./Button";
|
||||
import { Counter } from "./Counter";
|
||||
import { Reveal } from "./Reveal";
|
||||
import { TechBackground } from "./TechBackground";
|
||||
import { Button } from "@/components/Button";
|
||||
import { Counter } from "@/components/Counter";
|
||||
import { Reveal } from "@/components/Reveal";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { AnimatePresence, m } from "framer-motion";
|
||||
import { AnimatePresence, motion } 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 { Button } from "@/components/Button";
|
||||
import { Reveal } from "@/components/Reveal";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
@@ -118,7 +118,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
{/* Mobile Menu Overlay */}
|
||||
<AnimatePresence>
|
||||
{isMobileMenuOpen && (
|
||||
<m.div
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
@@ -143,7 +143,7 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
{t("nav.cta")}
|
||||
</Button>
|
||||
</nav>
|
||||
</m.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
@@ -166,12 +166,12 @@ const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
<div className="absolute inset-0 grid-pattern opacity-[0.08] pointer-events-none" />
|
||||
|
||||
{/* Animated Tech Lines */}
|
||||
<m.div
|
||||
<motion.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
|
||||
<motion.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"
|
||||
|
||||
39
components/LegalLayout.tsx
Normal file
39
components/LegalLayout.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { TechBackground } from "@/components/TechBackground";
|
||||
|
||||
interface LegalLayoutProps {
|
||||
title: string;
|
||||
heroAction?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function LegalLayout({ title, heroAction, children }: LegalLayoutProps) {
|
||||
return (
|
||||
<div className="bg-slate-50 min-h-screen pt-40 pb-20 relative overflow-hidden">
|
||||
<TechBackground />
|
||||
<div className="container-custom relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", stiffness: 50, damping: 20 }}
|
||||
className="max-w-4xl mx-auto bg-white p-8 md:p-12 rounded-[2.5rem] shadow-sm border border-slate-100 relative overflow-hidden group"
|
||||
>
|
||||
<div className="tech-corner top-8 left-8 border-t-2 border-l-2 opacity-20" />
|
||||
<div className="tech-corner bottom-8 right-8 border-b-2 border-r-2 opacity-20" />
|
||||
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8 relative z-10">
|
||||
<h1 className="text-4xl font-extrabold text-primary">{title}</h1>
|
||||
{heroAction}
|
||||
</div>
|
||||
|
||||
<div className="space-y-8 text-slate-600 leading-relaxed relative z-10 prose prose-slate max-w-none prose-h1:hidden prose-h2:text-xl prose-h2:font-bold prose-h2:text-primary prose-h2:mb-4">
|
||||
{children}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { m } from "framer-motion";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
interface RevealProps {
|
||||
children: React.ReactNode;
|
||||
@@ -30,7 +30,7 @@ export const Reveal = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<m.div
|
||||
<motion.div
|
||||
initial={{
|
||||
opacity: 0,
|
||||
...directions[direction],
|
||||
@@ -68,7 +68,7 @@ export const Reveal = ({
|
||||
className={`${fullWidth ? "w-full" : ""} ${className} motion-fix will-change-[transform,opacity]`}
|
||||
>
|
||||
{children}
|
||||
</m.div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -84,7 +84,7 @@ export const Stagger = ({
|
||||
staggerDelay = 0.1,
|
||||
}: StaggerProps) => {
|
||||
return (
|
||||
<m.div
|
||||
<motion.div
|
||||
initial="initial"
|
||||
whileInView="animate"
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
@@ -98,6 +98,6 @@ export const Stagger = ({
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</m.div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { m, AnimatePresence, LazyMotion, domAnimation } from "framer-motion";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { CheckCircle, AlertCircle, X } from "lucide-react";
|
||||
import { Button } from "./Button";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
interface StatusModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -23,82 +23,80 @@ export const StatusModal = ({
|
||||
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 }}
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-6">
|
||||
{/* Backdrop */}
|
||||
<motion.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 */}
|
||||
<motion.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">
|
||||
<motion.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 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"
|
||||
className="absolute top-6 right-6 p-2 text-slate-400 hover:text-primary transition-colors hover:bg-slate-50 rounded-xl"
|
||||
>
|
||||
{/* 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>
|
||||
<X size={20} />
|
||||
</button>
|
||||
|
||||
<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"
|
||||
<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`}
|
||||
>
|
||||
<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>
|
||||
|
||||
<h2 className="text-3xl font-extrabold text-primary mb-4 leading-tight">
|
||||
{title}
|
||||
</h2>
|
||||
<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>
|
||||
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>
|
||||
|
||||
{/* 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>
|
||||
<h2 className="text-3xl font-extrabold text-primary mb-4 leading-tight">
|
||||
{title}
|
||||
</h2>
|
||||
<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" />
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
52
components/sections/AboutHero.tsx
Normal file
52
components/sections/AboutHero.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
63
components/sections/AboutIntro.tsx
Normal file
63
components/sections/AboutIntro.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
79
components/sections/Hero.tsx
Normal file
79
components/sections/Hero.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
80
components/sections/ManifestSection.tsx
Normal file
80
components/sections/ManifestSection.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user