animations

This commit is contained in:
2026-01-17 13:17:10 +01:00
parent 021d23ab93
commit 29168a9778
19 changed files with 1353 additions and 252 deletions

View File

@@ -20,7 +20,7 @@ export default async function LocaleLayout({
<body className="flex flex-col min-h-screen font-sans selection:bg-accent selection:text-primary-dark">
<NextIntlClientProvider messages={messages} locale={locale}>
<Header />
<main className="flex-grow animate-fade-in">
<main className="flex-grow animate-fade-in overflow-visible">
{children}
</main>
<Footer />

View File

@@ -6,6 +6,11 @@ import ProductTabs from '@/components/ProductTabs';
import RequestQuoteForm from '@/components/RequestQuoteForm';
import RelatedProducts from '@/components/RelatedProducts';
import Link from 'next/link';
import Image from 'next/image';
import { getTranslations } from 'next-intl/server';
import { Section, Container, Heading, Badge, Button } from '@/components/ui';
import ProductsIllustration from '@/components/products/ProductsIllustration';
import Scribble from '@/components/Scribble';
interface ProductPageProps {
params: {
@@ -17,10 +22,28 @@ interface ProductPageProps {
const components = {
ProductTechnicalData,
ProductTabs,
p: (props: any) => <div {...props} className="mb-4" />,
p: (props: any) => <p {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed mb-8" />,
h2: (props: any) => (
<div className="relative mt-20 mb-10">
<h2 {...props} className="text-3xl md:text-4xl lg:text-5xl font-extrabold text-primary tracking-tight" />
<div className="absolute -bottom-4 left-0 w-24 h-1.5 bg-accent rounded-full" />
</div>
),
h3: (props: any) => <h3 {...props} className="text-2xl md:text-3xl lg:text-4xl font-bold text-primary mt-16 mb-6 tracking-tight" />,
ul: (props: any) => <ul {...props} className="list-disc pl-8 mb-10 space-y-4 text-text-secondary text-lg md:text-xl" />,
li: (props: any) => <li {...props} className="pl-2 marker:text-accent marker:font-bold" />,
table: (props: any) => (
<div className="overflow-x-auto my-8">
<table {...props} className="min-w-full divide-y divide-neutral-dark border border-neutral-dark" />
<div className="overflow-x-auto my-16 rounded-3xl border border-neutral-dark/10 shadow-xl bg-white p-1">
<table {...props} className="min-w-full divide-y divide-neutral-dark/10" />
</div>
),
th: (props: any) => <th {...props} className="px-8 py-6 bg-neutral-light/50 text-left text-xs font-black uppercase tracking-[0.25em] text-primary/60" />,
td: (props: any) => <td {...props} className="px-8 py-6 text-text-secondary border-t border-neutral-dark/5 text-lg md:text-xl" />,
hr: () => <hr className="my-20 border-t-2 border-neutral-dark/5" />,
blockquote: (props: any) => (
<div className="my-12 p-8 md:p-12 bg-primary-dark rounded-3xl relative overflow-hidden group">
<div className="absolute top-0 right-0 w-48 h-48 bg-accent/10 rounded-full -translate-y-1/2 translate-x-1/2 blur-3xl group-hover:bg-accent/20 transition-colors duration-700" />
<div className="relative z-10 italic text-2xl md:text-3xl text-white/90 leading-relaxed font-medium" {...props} />
</div>
),
};
@@ -28,49 +51,90 @@ const components = {
export default async function ProductPage({ params }: ProductPageProps) {
const { locale, slug } = params;
const productSlug = slug[slug.length - 1];
const t = await getTranslations('Products');
// Check if it's a category page
const categories = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
if (categories.includes(productSlug)) {
const allProducts = await getAllProducts(locale);
const categoryTitle = productSlug.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
const categoryKey = productSlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
const categoryTitle = t(`categories.${categoryKey}.title`);
// Filter products for this category
// Note: MDX categories are like "Low Voltage Cables"
const filteredProducts = allProducts.filter(p =>
p.frontmatter.categories.some(cat => cat.toLowerCase().replace(/\s+/g, '-') === productSlug)
);
return (
<div className="container mx-auto px-4 py-12">
<h1 className="text-4xl font-bold text-primary mb-8">{categoryTitle}</h1>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProducts.map((product) => (
<Link
key={product.slug}
href={`/${locale}/products/${productSlug}/${product.slug}`}
className="group block bg-white rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-all border border-neutral-dark"
>
<div className="aspect-[4/3] relative bg-neutral-light">
{product.frontmatter.images?.[0] && (
<img
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
className="w-full h-full object-contain p-4 group-hover:scale-105 transition-transform duration-300"
/>
)}
</div>
<div className="p-6">
<h2 className="text-xl font-bold text-text-primary group-hover:text-primary transition-colors">
{product.frontmatter.title}
</h2>
<p className="text-text-secondary mt-2 line-clamp-2 text-sm">
{product.frontmatter.description}
</p>
</div>
</Link>
))}
</div>
<div className="flex flex-col min-h-screen bg-white">
<section className="relative min-h-[50vh] flex items-center pt-32 pb-20 overflow-hidden bg-primary-dark">
<ProductsIllustration />
<Container className="relative z-10">
<div className="max-w-4xl animate-slide-up">
<nav className="flex items-center mb-8 text-white/40 text-sm font-bold uppercase tracking-widest">
<Link href={`/${locale}/products`} className="hover:text-accent transition-colors">{t('title')}</Link>
<span className="mx-3 opacity-30">/</span>
<span className="text-white/90">{categoryTitle}</span>
</nav>
<h1 className="text-5xl md:text-7xl lg:text-8xl font-extrabold text-white mb-8 tracking-tight leading-[1.05]">
{categoryTitle}
</h1>
<div className="h-1.5 w-24 bg-accent rounded-full" />
</div>
</Container>
</section>
<Section className="bg-neutral-light relative z-20 -mt-12 rounded-t-[48px] md:rounded-t-[64px]">
<Container>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredProducts.map((product) => (
<Link
key={product.slug}
href={`/${locale}/products/${productSlug}/${product.slug}`}
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
>
<div className="aspect-[4/3] relative bg-neutral-light/30 p-12 overflow-hidden">
{product.frontmatter.images?.[0] && (
<>
<Image
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
fill
className="object-contain p-8 transition-transform duration-700 group-hover:scale-110 z-10"
/>
{/* Subtle reflection/shadow effect */}
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 w-2/3 h-4 bg-black/5 blur-xl rounded-full" />
</>
)}
</div>
<div className="p-8 md:p-10">
<div className="flex flex-wrap gap-2 mb-4">
{product.frontmatter.categories.map((cat, i) => (
<span key={i} className="text-[10px] font-bold uppercase tracking-widest text-primary/40">
{cat}
</span>
))}
</div>
<h2 className="text-2xl md:text-3xl font-bold text-text-primary group-hover:text-primary transition-colors mb-4 leading-tight">
{product.frontmatter.title}
</h2>
<p className="text-text-secondary line-clamp-2 text-base leading-relaxed mb-8">
{product.frontmatter.description}
</p>
<div className="flex items-center text-primary font-bold group-hover:text-accent-dark transition-colors">
<span className="border-b-2 border-primary/10 group-hover:border-accent-dark transition-colors pb-1">
{t('details')}
</span>
<svg className="w-5 h-5 ml-3 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
</div>
</Link>
))}
</div>
</Container>
</Section>
</div>
);
}
@@ -81,63 +145,127 @@ export default async function ProductPage({ params }: ProductPageProps) {
notFound();
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-primary mb-4">{product.frontmatter.title}</h1>
<div className="flex flex-wrap gap-2 mb-4">
{product.frontmatter.categories.map((cat, idx) => (
<span key={idx} className="bg-neutral-dark text-text-secondary px-2 py-1 rounded text-sm">
{cat}
</span>
))}
</div>
</div>
const isFallback = (product.frontmatter as any).isFallback;
const categorySlug = slug[0];
const categoryKey = categorySlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
const categoryTitle = t(`categories.${categoryKey}.title`);
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
{/* Main Content Area */}
<div className="bg-white p-6 rounded-lg shadow-sm border border-neutral-dark">
<MDXRemote source={product.content} components={components} />
return (
<div className="flex flex-col min-h-screen bg-neutral-light">
{/* Product Hero */}
<section className="relative pt-40 pb-32 overflow-hidden bg-primary-dark">
<ProductsIllustration />
<Container className="relative z-10">
<div className="max-w-5xl animate-slide-up">
<nav className="flex items-center mb-8 text-white/40 text-sm font-bold uppercase tracking-widest">
<Link href={`/${locale}/products`} className="hover:text-accent transition-colors">{t('title')}</Link>
<span className="mx-3 opacity-30">/</span>
<Link href={`/${locale}/products/${categorySlug}`} className="hover:text-accent transition-colors">{categoryTitle}</Link>
<span className="mx-3 opacity-30">/</span>
<span className="text-white/90">{product.frontmatter.title}</span>
</nav>
<div className="flex flex-col lg:flex-row lg:items-end justify-between gap-12">
<div className="flex-1">
{isFallback && (
<div className="mb-6 inline-flex items-center px-3 py-1 rounded-md bg-accent/10 border border-accent/20 text-accent text-[10px] font-bold uppercase tracking-widest">
<span className="w-1.5 h-1.5 rounded-full bg-accent mr-2 animate-pulse" />
{t('englishVersion')}
</div>
)}
<div className="flex flex-wrap gap-3 mb-8">
{product.frontmatter.categories.map((cat, idx) => (
<Badge key={idx} variant="accent" className="bg-white/10 text-white/90 border-white/10 backdrop-blur-md px-4 py-1.5">
{cat}
</Badge>
))}
</div>
<h1 className="text-5xl md:text-7xl lg:text-8xl font-extrabold text-white mb-0 tracking-tight leading-[1.05]">
{product.frontmatter.title}
</h1>
</div>
</div>
</div>
{/* Related Products */}
<RelatedProducts
currentSlug={productSlug}
categories={product.frontmatter.categories}
locale={locale}
/>
</div>
<div className="lg:col-span-1">
<div className="sticky top-4 space-y-6">
{/* Image Gallery */}
{product.frontmatter.images && product.frontmatter.images.length > 0 && (
<div className="bg-white p-4 rounded-lg shadow-sm border border-neutral-dark">
<div className="relative aspect-square mb-4 bg-neutral-light rounded overflow-hidden">
<img
</Container>
</section>
<Section className="bg-neutral-light relative z-20 -mt-16 pt-0">
<Container>
{/* Large Product Image Section */}
{product.frontmatter.images && product.frontmatter.images.length > 0 && (
<div className="relative -mt-24 mb-24 animate-slide-up" style={{ animationDelay: '200ms' }}>
<div className="bg-white shadow-2xl border border-neutral-dark/5 overflow-hidden p-10 md:p-16 lg:p-20">
<div className="relative w-full aspect-[21/9]">
<Image
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
className="w-full h-full object-contain"
fill
className="object-contain transition-transform duration-1000 hover:scale-105"
priority
/>
{/* Subtle reflection/shadow effect */}
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-3/4 h-12 bg-black/5 blur-3xl rounded-[100%]" />
</div>
{product.frontmatter.images.length > 1 && (
<div className="grid grid-cols-4 gap-2">
{product.frontmatter.images.slice(1, 5).map((img, idx) => (
<div key={idx} className="relative aspect-square border border-neutral-dark rounded overflow-hidden bg-neutral-light">
<img src={img} alt="" className="w-full h-full object-cover" />
<div className="flex justify-center gap-6 mt-16">
{product.frontmatter.images.slice(0, 5).map((img, idx) => (
<div key={idx} className="relative w-24 h-24 md:w-32 md:h-32 border-2 border-neutral-dark/10 rounded-xl overflow-hidden bg-neutral-light/20 hover:border-accent transition-all duration-300 cursor-pointer group">
<Image src={img} alt="" fill className="object-cover transition-transform duration-500 group-hover:scale-110" />
</div>
))}
</div>
)}
</div>
)}
</div>
)}
<div className="flex flex-col lg:flex-row gap-16 lg:gap-24 relative items-start overflow-visible">
<div className="flex-1 w-full">
{/* Main Content Area */}
<div className="prose prose-lg md:prose-xl prose-slate max-w-none prose-headings:text-primary prose-headings:font-extrabold prose-a:text-primary prose-strong:text-primary prose-img:rounded-3xl prose-img:shadow-2xl">
<MDXRemote source={product.content} components={components} />
</div>
{/* Related Products */}
<RelatedProducts
currentSlug={productSlug}
categories={product.frontmatter.categories}
locale={locale}
/>
</div>
{/* Request Quote Form */}
<RequestQuoteForm productName={product.frontmatter.title} />
<div className="w-full lg:w-[450px] xl:w-[500px] lg:sticky lg:top-32">
<div className="lg:translate-x-12 xl:translate-x-20">
{/* Request Quote Form */}
<div className="bg-white rounded-3xl shadow-2xl border border-neutral-dark/5 overflow-hidden">
<div className="bg-primary-dark p-6 md:p-8 text-white relative overflow-hidden">
<div className="absolute top-0 right-0 w-40 h-40 bg-accent/10 rounded-full -translate-y-1/2 translate-x-1/2 blur-3xl" />
{/* Product Thumbnail for Sticky State */}
{product.frontmatter.images?.[0] && (
<div className="relative w-full aspect-[16/9] mb-6 rounded-xl overflow-hidden bg-white/10 backdrop-blur-md p-3 border border-white/10 z-10">
<Image
src={product.frontmatter.images[0]}
alt=""
fill
className="object-contain p-1"
/>
</div>
)}
<h3 className="text-xl md:text-2xl font-extrabold mb-2 relative z-10 tracking-tight">{t('requestQuote')}</h3>
<p className="text-white/60 text-sm relative z-10 leading-relaxed">{t('requestQuoteDesc')}</p>
</div>
<div className="p-6 md:p-8">
<RequestQuoteForm productName={product.frontmatter.title} />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</Container>
</Section>
</div>
);
}

View File

@@ -1,7 +1,9 @@
import Link from 'next/link';
import Image from 'next/image';
import { useTranslations } from 'next-intl';
import { Section, Container, Heading, Card, Badge } from '@/components/ui';
import { Section, Container, Heading, Card, Badge, Button } from '@/components/ui';
import ProductsIllustration from '@/components/products/ProductsIllustration';
import Scribble from '@/components/Scribble';
interface ProductsPageProps {
params: {
@@ -46,52 +48,74 @@ export default function ProductsPage({ params }: ProductsPageProps) {
return (
<div className="flex flex-col min-h-screen bg-neutral-light">
{/* Hero Section */}
<section className="bg-primary-dark text-white py-32 relative overflow-hidden">
<div className="absolute inset-0 opacity-30">
<div className="absolute top-0 right-0 w-1/2 h-full bg-accent/10 skew-x-12 translate-x-1/4" />
</div>
<section className="relative min-h-[70vh] flex items-center pt-40 pb-32 overflow-hidden bg-primary-dark">
<ProductsIllustration />
<Container className="relative z-10">
<div className="max-w-4xl animate-slide-up">
<Heading level={1} subtitle="Product Portfolio" className="text-white mb-6">
<span className="text-white">{t('title')}</span>
</Heading>
<p className="text-2xl text-white/70 leading-relaxed max-w-2xl">
<Badge variant="accent" className="mb-8 bg-white/10 text-white border border-white/20 backdrop-blur-md px-4 py-1.5">
{t('heroSubtitle')}
</Badge>
<h1 className="text-5xl md:text-7xl lg:text-8xl font-extrabold text-white mb-8 tracking-tight leading-[1.05]">
{t.rich('title', {
green: (chunks) => (
<span className="relative inline-block">
<span className="relative z-10 text-accent italic">{chunks}</span>
<Scribble variant="circle" className="w-[140%] h-[140%] -top-[20%] -left-[20%] text-accent/30" />
</span>
)
})}
</h1>
<p className="text-xl md:text-2xl text-white/70 leading-relaxed max-w-2xl mb-12">
{t('subtitle')}
</p>
<div className="flex flex-wrap gap-6">
<Button href="#categories" variant="accent" size="lg" className="group">
{t('viewProducts')}
<span className="ml-3 transition-transform group-hover:translate-y-1"></span>
</Button>
</div>
</div>
</Container>
</section>
<Section className="bg-neutral-light -mt-16 relative z-20">
<Section id="categories" className="bg-neutral-light relative z-20 -mt-16 rounded-t-[48px] md:rounded-t-[64px] pt-24">
<Container>
<div className="grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 lg:gap-12">
{categories.map((category, idx) => (
<Link key={idx} href={category.href} className="group block">
<Card className="h-full border-none shadow-xl hover:shadow-2xl transition-all duration-700 rounded-[40px] overflow-hidden bg-white">
<div className="relative h-[400px] overflow-hidden">
<Card className="h-full border-none shadow-sm hover:shadow-2xl transition-all duration-500 rounded-[32px] md:rounded-[48px] overflow-hidden bg-white">
<div className="relative h-[300px] md:h-[400px] overflow-hidden">
<Image
src={category.img}
alt={category.title}
fill
className="object-cover transition-transform duration-1000 group-hover:scale-110"
className="object-cover transition-transform duration-1000 group-hover:scale-105"
/>
<div className="absolute inset-0 bg-gradient-to-t from-primary-dark/90 via-primary-dark/20 to-transparent" />
<div className="absolute top-8 right-8 w-20 h-20 bg-white/10 backdrop-blur-xl rounded-3xl flex items-center justify-center border border-white/20 shadow-2xl">
<img src={category.icon} alt="" className="w-12 h-12 brightness-0 invert" />
<div className="absolute inset-0 bg-gradient-to-t from-primary-dark/60 via-transparent to-transparent opacity-80 group-hover:opacity-90 transition-opacity duration-500" />
<div className="absolute top-6 right-6 md:top-8 md:right-8 w-16 h-16 md:w-20 md:h-20 bg-white/10 backdrop-blur-md rounded-2xl md:rounded-[24px] flex items-center justify-center border border-white/20 shadow-2xl transition-all duration-500 group-hover:scale-110 group-hover:bg-white/20">
<img src={category.icon} alt="" className="w-10 h-10 md:w-12 md:h-12 brightness-0 invert opacity-80" />
</div>
<div className="absolute bottom-10 left-10 right-10">
<Badge variant="accent" className="mb-4 shadow-lg">Category</Badge>
<h2 className="text-4xl font-bold text-white mb-4 leading-tight">{category.title}</h2>
<div className="absolute bottom-8 left-8 md:bottom-10 md:left-10 right-8 md:right-10">
<Badge variant="accent" className="mb-4 shadow-lg bg-accent text-primary-dark border-none">
{t('categoryLabel')}
</Badge>
<h2 className="text-3xl md:text-4xl font-bold text-white leading-tight transition-transform duration-500 group-hover:translate-x-1">
{category.title}
</h2>
</div>
</div>
<div className="p-12">
<p className="text-text-secondary text-xl leading-relaxed mb-10">
<div className="p-8 md:p-10">
<p className="text-text-secondary text-lg leading-relaxed mb-8">
{category.desc}
</p>
<div className="flex items-center text-primary font-extrabold text-lg group-hover:text-accent-dark transition-colors">
{t('viewProducts')}
<div className="ml-4 w-12 h-12 rounded-full bg-primary-light flex items-center justify-center text-primary group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300">
<svg className="w-6 h-6 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div className="flex items-center text-primary font-bold text-lg group-hover:text-accent-dark transition-colors">
<span className="border-b-2 border-primary/10 group-hover:border-accent-dark transition-colors pb-1">
{t('viewProducts')}
</span>
<div className="ml-4 w-10 h-10 rounded-full bg-primary-light flex items-center justify-center text-primary group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300 shadow-sm">
<svg className="w-5 h-5 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
@@ -107,19 +131,19 @@ export default function ProductsPage({ params }: ProductsPageProps) {
{/* Technical Support CTA */}
<Section className="bg-white">
<Container>
<div className="bg-primary-dark rounded-[60px] p-16 md:p-24 relative overflow-hidden">
<div className="bg-primary-dark rounded-[48px] md:rounded-[64px] p-12 md:p-20 lg:p-24 relative overflow-hidden">
<div className="absolute top-0 right-0 w-1/2 h-full bg-accent/5 -skew-x-12 translate-x-1/4" />
<div className="relative z-10 flex flex-col lg:flex-row items-center justify-between gap-12">
<div className="max-w-2xl text-center lg:text-left">
<h2 className="text-4xl md:text-5xl font-bold text-white mb-6">Need Technical Assistance?</h2>
<h2 className="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-8 tracking-tight">{t('cta.title')}</h2>
<p className="text-xl text-white/70 leading-relaxed">
Our team of experts is ready to help you find the perfect cable solution for your specific technical requirements and environmental conditions.
{t('cta.description')}
</p>
</div>
<Link href={`/${params.locale}/contact`} className="inline-flex items-center justify-center h-20 px-12 rounded-full bg-accent text-primary-dark font-extrabold text-xl hover:bg-accent-dark transition-all shadow-2xl shadow-accent/20 group">
Contact Experts
<Button href={`/${params.locale}/contact`} variant="accent" size="xl" className="group whitespace-nowrap">
{t('cta.button')}
<span className="ml-4 transition-transform group-hover:translate-x-2">&rarr;</span>
</Link>
</Button>
</div>
</div>
</Container>

View File

@@ -99,18 +99,15 @@ export default function TeamPage() {
</p>
</div>
</div>
<div className="lg:col-span-6 grid grid-cols-2 gap-6">
{[
{ label: 'Founded', value: '1998' },
{ label: 'Global Reach', value: '45+' },
{ label: 'Team Members', value: '120+' },
{ label: 'Innovation Awards', value: '12' },
].map((stat, i) => (
<div key={i} className="p-8 bg-white/5 backdrop-blur-md border border-white/10 rounded-[32px] hover:bg-white/10 transition-colors">
<div className="text-4xl font-extrabold text-accent mb-2">{stat.value}</div>
<div className="text-sm font-bold uppercase tracking-widest text-white/50">{stat.label}</div>
</div>
))}
<div className="lg:col-span-6 grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="p-8 bg-white/5 backdrop-blur-md border border-white/10 rounded-[32px] hover:bg-white/10 transition-colors">
<div className="text-4xl font-extrabold text-accent mb-2">Expertise</div>
<div className="text-sm font-bold uppercase tracking-widest text-white/50">Decades of Industry Knowledge</div>
</div>
<div className="p-8 bg-white/5 backdrop-blur-md border border-white/10 rounded-[32px] hover:bg-white/10 transition-colors">
<div className="text-4xl font-extrabold text-accent mb-2">Network</div>
<div className="text-sm font-bold uppercase tracking-widest text-white/50">Strong Global Partnerships</div>
</div>
</div>
</div>
</Container>

View File

@@ -30,12 +30,12 @@ export default function Footer() {
Leading the way in cable infrastructure and sustainable energy solutions. Quality, innovation, and reliability since 1998.
</p>
<div className="flex gap-4">
{['linkedin', 'twitter', 'facebook'].map((social) => (
<a key={social} href="#" className="w-12 h-12 rounded-full bg-white/5 flex items-center justify-center hover:bg-accent hover:text-primary-dark transition-all duration-300 border border-white/10">
<span className="sr-only">{social}</span>
<div className="w-5 h-5 bg-current rounded-sm opacity-80" />
</a>
))}
<a href="https://www.linkedin.com/company/klz-vertriebs-gmbh/" target="_blank" rel="noopener noreferrer" className="w-12 h-12 rounded-full bg-white/5 flex items-center justify-center hover:bg-accent hover:text-primary-dark transition-all duration-300 border border-white/10">
<span className="sr-only">LinkedIn</span>
<svg className="w-5 h-5 fill-current" viewBox="0 0 24 24">
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
</svg>
</a>
</div>
</div>

View File

@@ -46,14 +46,12 @@ export default function Header() {
"fixed top-0 left-0 right-0 z-50 transition-all duration-500",
{
"bg-transparent py-8": isHomePage && !isScrolled,
"bg-white/90 backdrop-blur-xl shadow-2xl py-4 border-b border-primary/5": !isHomePage || isScrolled,
"bg-primary py-4 shadow-2xl": !isHomePage || isScrolled,
}
);
const textColorClass = (isHomePage && !isScrolled) ? "text-white" : "text-primary";
const logoSrc = (isHomePage && !isScrolled)
? "/logo-white.svg"
: "/logo-blue.svg";
const textColorClass = "text-white";
const logoSrc = "/logo-white.svg";
return (
<>
@@ -107,7 +105,7 @@ export default function Header() {
<Button
href={`/${currentLocale}/contact`}
variant={isHomePage && !isScrolled ? "white" : "primary"}
variant="white"
size="md"
className="px-8 shadow-xl"
>
@@ -124,7 +122,7 @@ export default function Header() {
</div>
</div>
</header>
{!isHomePage && <div className="h-32 md:h-40" />}
{/* Removed spacer div that caused white gap */}
</>
);
}

View File

@@ -1,5 +1,7 @@
import Link from 'next/link';
import Image from 'next/image';
import { getAllProducts } from '@/lib/mdx';
import { getTranslations } from 'next-intl/server';
interface RelatedProductsProps {
currentSlug: string;
@@ -9,6 +11,7 @@ interface RelatedProductsProps {
export default async function RelatedProducts({ currentSlug, categories, locale }: RelatedProductsProps) {
const allProducts = await getAllProducts(locale);
const t = await getTranslations('Products');
// Filter products: same category, not current product
const related = allProducts
@@ -16,47 +19,72 @@ export default async function RelatedProducts({ currentSlug, categories, locale
p.slug !== currentSlug &&
p.frontmatter.categories.some(cat => categories.includes(cat))
)
.slice(0, 4); // Limit to 4
.slice(0, 3); // Limit to 3 for better spacing
if (related.length === 0) return null;
return (
<div className="mt-16 pt-8 border-t border-neutral-dark">
<h2 className="text-2xl font-bold text-primary-dark mb-6">Related Products</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{related.map((product) => (
<Link
key={product.slug}
href={`/${locale}/products/${product.slug}`}
className="group block bg-white rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow border border-neutral-dark"
>
<div className="aspect-[4/3] relative bg-neutral-light">
{product.frontmatter.images?.[0] ? (
<img
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
className="w-full h-full object-contain p-4 group-hover:scale-105 transition-transform duration-300"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-text-secondary">
No Image
</div>
)}
</div>
<div className="p-4">
<h3 className="font-semibold text-lg text-text-primary group-hover:text-primary transition-colors">
{product.frontmatter.title}
</h3>
<div className="mt-2 flex flex-wrap gap-1">
{product.frontmatter.categories.slice(0, 1).map((cat, idx) => (
<span key={idx} className="text-xs bg-neutral text-text-secondary px-2 py-1 rounded">
{cat}
</span>
))}
<div className="mt-32 pt-32 border-t border-neutral-dark/10">
<div className="flex items-end justify-between mb-12">
<div>
<h2 className="text-3xl md:text-4xl font-extrabold text-primary tracking-tight mb-4">
{t('relatedProductsTitle') || 'Related Products'}
</h2>
<div className="h-1.5 w-20 bg-accent rounded-full" />
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{related.map((product) => {
// Find the category slug for the link
const catSlug = product.frontmatter.categories[0].toLowerCase().replace(/\s+/g, '-');
return (
<Link
key={product.slug}
href={`/${locale}/products/${catSlug}/${product.slug}`}
className="group block bg-white rounded-[32px] overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-500 border border-neutral-dark/5"
>
<div className="aspect-[16/10] relative bg-neutral-light/30 p-8 overflow-hidden">
{product.frontmatter.images?.[0] ? (
<>
<Image
src={product.frontmatter.images[0]}
alt={product.frontmatter.title}
fill
className="object-contain p-4 transition-transform duration-700 group-hover:scale-110 z-10"
/>
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 w-2/3 h-3 bg-black/5 blur-lg rounded-full" />
</>
) : (
<div className="w-full h-full flex items-center justify-center text-text-secondary/30 font-bold uppercase tracking-widest text-xs">
No Image
</div>
)}
</div>
</div>
</Link>
))}
<div className="p-8">
<div className="flex flex-wrap gap-2 mb-3">
{product.frontmatter.categories.slice(0, 1).map((cat, idx) => (
<span key={idx} className="text-[10px] font-bold uppercase tracking-widest text-primary/40">
{cat}
</span>
))}
</div>
<h3 className="text-xl font-bold text-text-primary group-hover:text-primary transition-colors leading-tight">
{product.frontmatter.title}
</h3>
<div className="mt-6 flex items-center text-primary text-sm font-bold group-hover:text-accent-dark transition-colors">
<span className="border-b-2 border-primary/10 group-hover:border-accent-dark transition-colors pb-0.5">
{t('details')}
</span>
<svg className="w-4 h-4 ml-2 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
</div>
</Link>
);
})}
</div>
</div>
);

View File

@@ -1,12 +1,14 @@
'use client';
import React, { useState } from 'react';
import { useTranslations } from 'next-intl';
interface RequestQuoteFormProps {
productName: string;
}
export default function RequestQuoteForm({ productName }: RequestQuoteFormProps) {
const t = useTranslations('Products.form');
const [email, setEmail] = useState('');
const [request, setRequest] = useState('');
const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle');
@@ -28,74 +30,84 @@ export default function RequestQuoteForm({ productName }: RequestQuoteFormProps)
if (status === 'success') {
return (
<div className="bg-green-50 border border-green-200 text-green-800 p-6 rounded-lg">
<h3 className="text-lg font-semibold mb-2">Request Sent!</h3>
<p>Thank you for your interest in {productName}. We will get back to you shortly.</p>
<div className="bg-accent/5 border border-accent/20 text-primary-dark p-6 rounded-[32px] text-center animate-fade-in">
<div className="w-12 h-12 bg-accent rounded-full flex items-center justify-center mx-auto mb-4 shadow-lg shadow-accent/20">
<svg className="w-6 h-6 text-primary-dark" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="text-xl font-extrabold mb-2 tracking-tight">{t('successTitle')}</h3>
<p className="text-text-secondary text-sm leading-relaxed mb-6">
{t('successDesc', { productName })}
</p>
<button
onClick={() => setStatus('idle')}
className="mt-4 text-sm font-medium underline hover:text-green-900"
className="inline-flex items-center text-[10px] font-bold uppercase tracking-[0.2em] text-primary hover:text-accent transition-colors group"
>
Send another request
<span className="border-b-2 border-primary/10 group-hover:border-accent transition-colors pb-1">
{t('sendAnother')}
</span>
</button>
</div>
);
}
return (
<div className="bg-neutral-light p-6 rounded-lg border border-neutral-dark">
<h3 className="text-xl font-semibold mb-4 text-primary-dark">Request Cable</h3>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="product" className="block text-sm font-medium text-text-secondary mb-1">
Product
</label>
<input
type="text"
id="product"
value={productName}
disabled
className="w-full px-3 py-2 bg-neutral border border-neutral-dark rounded text-text-secondary cursor-not-allowed"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-text-primary mb-1">
Email <span className="text-red-500">*</span>
</label>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-4">
<div className="space-y-1.5">
<input
type="email"
id="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3 py-2 bg-white border border-neutral-dark rounded focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
placeholder="your@email.com"
className="w-full px-4 py-3 bg-neutral-light/50 border border-neutral-dark/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent/20 focus:border-accent focus:bg-white transition-all duration-300 placeholder:text-neutral-dark/40 text-sm"
placeholder={t('email')}
/>
</div>
<div>
<label htmlFor="request" className="block text-sm font-medium text-text-primary mb-1">
Request <span className="text-red-500">*</span>
</label>
<div className="space-y-1.5">
<textarea
id="request"
required
rows={4}
value={request}
onChange={(e) => setRequest(e.target.value)}
className="w-full px-3 py-2 bg-white border border-neutral-dark rounded focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
placeholder="Describe your request, e.g. the cross section etc."
className="w-full px-4 py-3 bg-neutral-light/50 border border-neutral-dark/10 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent/20 focus:border-accent focus:bg-white transition-all duration-300 placeholder:text-neutral-dark/40 text-sm resize-none"
placeholder={t('message')}
/>
</div>
</div>
<div className="space-y-4">
<button
type="submit"
disabled={status === 'submitting'}
className="w-full bg-primary text-white font-medium py-2 px-4 rounded hover:bg-primary-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
className="w-full bg-primary text-white font-bold py-3.5 px-6 rounded-xl hover:bg-primary-dark hover:shadow-lg active:scale-[0.98] transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 group"
>
{status === 'submitting' ? 'Sending...' : 'Request Cable'}
{status === 'submitting' ? (
<>
<svg className="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span className="text-sm">{t('submitting')}</span>
</>
) : (
<>
<span className="text-sm">{t('submit')}</span>
<svg className="w-4 h-4 transition-transform group-hover:translate-x-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</>
)}
</button>
</form>
</div>
<p className="text-[8px] text-center text-text-secondary/40 uppercase tracking-[0.15em] font-medium px-2">
{t('privacyNote')}
</p>
</div>
</form>
);
}

View File

@@ -34,18 +34,15 @@ export default function Experience() {
</p>
</div>
<div className="mt-16 grid grid-cols-2 md:grid-cols-4 gap-8">
{[
{ label: 'Years Experience', value: '25+' },
{ label: 'Projects Completed', value: '1.2k' },
{ label: 'Global Partners', value: '85' },
{ label: 'Cable Types', value: '450+' },
].map((stat, i) => (
<div key={i} className="animate-fade-in" style={{ animationDelay: `${i * 100}ms` }}>
<div className="text-4xl font-extrabold text-accent mb-2">{stat.value}</div>
<div className="text-sm font-bold uppercase tracking-widest text-white/60">{stat.label}</div>
</div>
))}
<div className="mt-16 grid grid-cols-1 md:grid-cols-2 gap-12">
<div className="animate-fade-in">
<div className="text-4xl font-extrabold text-accent mb-4">Certified Quality</div>
<div className="text-lg font-bold uppercase tracking-widest text-white/60">VDE Approved & Trusted by Major Energy Suppliers</div>
</div>
<div className="animate-fade-in" style={{ animationDelay: '100ms' }}>
<div className="text-4xl font-extrabold text-accent mb-4">Full Spectrum</div>
<div className="text-lg font-bold uppercase tracking-widest text-white/60">From 1kV to 220kV Solutions</div>
</div>
</div>
</div>
</Container>

View File

@@ -2,26 +2,14 @@ import React from 'react';
import { useTranslations } from 'next-intl';
import { Container, Button } from '@/components/ui';
import Scribble from '@/components/Scribble';
import HeroIllustration from './HeroIllustration';
export default function Hero() {
const t = useTranslations('Home.hero');
return (
<section className="relative h-[90vh] flex items-center justify-center overflow-hidden bg-primary-dark">
<div className="absolute inset-0 z-0">
<video
className="w-full h-full object-cover scale-105 animate-slow-zoom"
autoPlay
muted
loop
playsInline
poster="/uploads/2025/02/Still-2025-02-10-104337_1.1.1.webp"
>
<source src="/uploads/2025/02/header.webm" type="video/webm" />
<source src="/uploads/2025/02/header.mp4" type="video/mp4" />
</video>
<div className="absolute inset-0 bg-gradient-to-b from-primary-dark/40 via-primary-dark/20 to-primary-dark/60" />
</div>
<HeroIllustration />
<Container className="relative z-10 text-left text-white w-full">
<div className="max-w-5xl animate-slide-up">
@@ -41,7 +29,7 @@ export default function Hero() {
<span className="ml-3 transition-transform group-hover:translate-x-1">&rarr;</span>
</Button>
<Button href="/products" variant="white" size="xl" className="group">
Explore Products
{t('exploreProducts')}
</Button>
</div>
</div>

View File

@@ -0,0 +1,747 @@
'use client';
import React from 'react';
// Isometric grid configuration - true 2:1 isometric projection
const CELL_WIDTH = 120;
const CELL_HEIGHT = 60; // Half of width for 2:1 isometric
// Convert grid coordinates to isometric screen coordinates
// Using standard isometric projection where x goes right-down, y goes right-up
function gridToScreen(col: number, row: number): { x: number; y: number } {
return {
x: (col - row) * (CELL_WIDTH / 2),
y: (col + row) * (CELL_HEIGHT / 2),
};
}
// Grid layout (10 columns x 8 rows)
// Energy flow: Solar/Wind (left) → Substations (center) → Transmission → City (right)
const GRID = {
cols: 10,
rows: 8,
};
// Infrastructure positions - precisely on grid intersections
const INFRASTRUCTURE = {
// Solar panels (two groups)
solar: [
// Group 1 - bottom-left
{ col: 0, row: 5 },
{ col: 1, row: 5 },
{ col: 0, row: 6 },
{ col: 1, row: 6 },
// Group 2 - middle-bottom
{ col: 2, row: 7 },
{ col: 3, row: 7 },
{ col: 2, row: 8 },
{ col: 3, row: 8 },
],
// Wind turbines (two groups)
wind: [
// Group 1 - top-left
{ col: 0, row: 1 },
{ col: 1, row: 2 },
{ col: 2, row: 1 },
// Group 2 - top-center
{ col: 3, row: 0 },
{ col: 4, row: 1 },
{ col: 5, row: 0 },
],
// Substations
substations: [
{ col: 3, row: 3, type: 'collection' }, // Main collection substation
{ col: 6, row: 4, type: 'distribution' }, // Distribution substation (right)
{ col: 5, row: 7, type: 'distribution' }, // Distribution substation (bottom-left)
],
// Transmission towers (along the routes)
towers: [
{ col: 4, row: 3 },
{ col: 5, row: 4 },
{ col: 4, row: 5 },
{ col: 5, row: 6 },
],
// City/Buildings (right side)
city: [
{ col: 8, row: 3, type: 'tall' },
{ col: 9, row: 4, type: 'medium' },
{ col: 8, row: 5, type: 'small' },
{ col: 9, row: 5, type: 'medium' },
],
// City 2 (bottom-left area)
city2: [
{ col: 6, row: 8, type: 'medium' },
{ col: 7, row: 7, type: 'tall' },
{ col: 7, row: 8, type: 'small' },
],
// Trees (decorative, scattered around)
trees: [
{ col: 0, row: 3 },
{ col: 2, row: 6 },
{ col: 3, row: 1 },
{ col: 6, row: 2 },
{ col: 6, row: 6 },
],
};
// Power line connections - grid-aligned paths only (no diagonals)
// Each group meets at a collection point, then flows to main substation
const POWER_LINES = [
// === WIND GROUP 1 (top-left) - meet at (1,1) then to substation ===
// Turbine at (0,1) → collection point (1,1)
{ from: { col: 0, row: 1 }, to: { col: 1, row: 1 } },
// Turbine at (1,2) → up to (1,1)
{ from: { col: 1, row: 2 }, to: { col: 1, row: 1 } },
// Turbine at (2,1) → left to (1,1)
{ from: { col: 2, row: 1 }, to: { col: 1, row: 1 } },
// Collection point (1,1) → down to (1,3) → right to substation (3,3)
{ from: { col: 1, row: 1 }, to: { col: 1, row: 3 } },
{ from: { col: 1, row: 3 }, to: { col: 3, row: 3 } },
// === WIND GROUP 2 (top-center) - meet at (4,1) then to substation ===
// Turbine at (3,0) → right to (4,0) → down to (4,1)
{ from: { col: 3, row: 0 }, to: { col: 4, row: 0 } },
{ from: { col: 4, row: 0 }, to: { col: 4, row: 1 } },
// Turbine at (4,1) is the collection point
// Turbine at (5,0) → down to (5,1) → left to (4,1)
{ from: { col: 5, row: 0 }, to: { col: 5, row: 1 } },
{ from: { col: 5, row: 1 }, to: { col: 4, row: 1 } },
// Collection point (4,1) → down to (4,3) → left to substation (3,3)
{ from: { col: 4, row: 1 }, to: { col: 4, row: 3 } },
{ from: { col: 4, row: 3 }, to: { col: 3, row: 3 } },
// === SOLAR GROUP 1 (bottom-left) - meet at (1,5) then to substation ===
// Panels at (0,5), (1,5), (0,6), (1,6) → collection at (1,5)
{ from: { col: 0, row: 5 }, to: { col: 1, row: 5 } },
{ from: { col: 0, row: 6 }, to: { col: 0, row: 5 } },
{ from: { col: 1, row: 6 }, to: { col: 1, row: 5 } },
// Collection point (1,5) → up to (1,3) → right to substation (3,3)
{ from: { col: 1, row: 5 }, to: { col: 1, row: 3 } },
// === SOLAR GROUP 2 (middle-bottom) - meet at (3,7) then to substation ===
// Panels at (2,7), (3,7), (2,8), (3,8) → collection at (3,7)
{ from: { col: 2, row: 7 }, to: { col: 3, row: 7 } },
{ from: { col: 2, row: 8 }, to: { col: 2, row: 7 } },
{ from: { col: 3, row: 8 }, to: { col: 3, row: 7 } },
// Collection point (3,7) → up to (3,3) substation
{ from: { col: 3, row: 7 }, to: { col: 3, row: 5 } },
{ from: { col: 3, row: 5 }, to: { col: 3, row: 3 } },
// === MAIN TRANSMISSION: Substation (3,3) → Towers → Distribution → City ===
// Substation to first tower
{ from: { col: 3, row: 3 }, to: { col: 4, row: 3 } },
// First tower to second tower (grid-aligned)
{ from: { col: 4, row: 3 }, to: { col: 5, row: 3 } },
{ from: { col: 5, row: 3 }, to: { col: 5, row: 4 } },
// Second tower to distribution substation (right)
{ from: { col: 5, row: 4 }, to: { col: 6, row: 4 } },
// Distribution to city 1 (grid-aligned)
{ from: { col: 6, row: 4 }, to: { col: 7, row: 4 } },
{ from: { col: 7, row: 4 }, to: { col: 8, row: 4 } },
// Branch to buildings (city 1)
{ from: { col: 8, row: 4 }, to: { col: 8, row: 3 } },
{ from: { col: 8, row: 4 }, to: { col: 8, row: 5 } },
{ from: { col: 8, row: 3 }, to: { col: 9, row: 3 } },
{ from: { col: 9, row: 3 }, to: { col: 9, row: 4 } },
{ from: { col: 8, row: 5 }, to: { col: 9, row: 5 } },
// === SECOND ROUTE: Substation (3,3) → Towers → Distribution (5,7) → City 2 ===
// Branch from main substation down
{ from: { col: 3, row: 3 }, to: { col: 3, row: 5 } },
{ from: { col: 3, row: 5 }, to: { col: 4, row: 5 } },
// Tower at (4,5) to tower at (5,6)
{ from: { col: 4, row: 5 }, to: { col: 5, row: 5 } },
{ from: { col: 5, row: 5 }, to: { col: 5, row: 6 } },
// Tower to distribution substation (bottom-left)
{ from: { col: 5, row: 6 }, to: { col: 5, row: 7 } },
// Distribution to city 2
{ from: { col: 5, row: 7 }, to: { col: 6, row: 7 } },
{ from: { col: 6, row: 7 }, to: { col: 6, row: 8 } },
{ from: { col: 6, row: 7 }, to: { col: 7, row: 7 } },
{ from: { col: 7, row: 7 }, to: { col: 7, row: 8 } },
];
export default function HeroIllustration() {
return (
<div className="absolute inset-0 z-0 overflow-hidden bg-primary">
<svg
viewBox="-400 -200 1800 1100"
className="w-full h-full"
preserveAspectRatio="xMidYMid slice"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
{/* Electric energy flow gradient */}
<linearGradient id="energy-pulse" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#82ed20" stopOpacity="0" />
<stop offset="30%" stopColor="#82ed20" stopOpacity="0.6" />
<stop offset="50%" stopColor="#9bf14d" stopOpacity="1" />
<stop offset="70%" stopColor="#82ed20" stopOpacity="0.6" />
<stop offset="100%" stopColor="#82ed20" stopOpacity="0" />
</linearGradient>
{/* Wind flow gradient */}
<linearGradient id="wind-flow" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="white" stopOpacity="0" />
<stop offset="30%" stopColor="white" stopOpacity="0.4" />
<stop offset="50%" stopColor="white" stopOpacity="0.6" />
<stop offset="70%" stopColor="white" stopOpacity="0.4" />
<stop offset="100%" stopColor="white" stopOpacity="0" />
</linearGradient>
{/* Sun ray gradient */}
<linearGradient id="sun-ray" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor="#FFD700" stopOpacity="0.6" />
<stop offset="50%" stopColor="#FFD700" stopOpacity="0.3" />
<stop offset="100%" stopColor="#82ed20" stopOpacity="0.1" />
</linearGradient>
{/* Glow filter */}
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
{/* Soft glow for nodes */}
<filter id="soft-glow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="2" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
{/* Sun glow filter */}
<filter id="sun-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* Main scene container - positioned to the right */}
<g transform="translate(900, 100)">
{/* === ISOMETRIC GRID === */}
<g opacity="0.15">
{/* Horizontal grid lines (going from top-left to bottom-right) */}
{[...Array(GRID.rows + 1)].map((_, row) => {
const start = gridToScreen(0, row);
const end = gridToScreen(GRID.cols, row);
return (
<line
key={`h-${row}`}
x1={start.x}
y1={start.y}
x2={end.x}
y2={end.y}
stroke="white"
strokeWidth="1"
/>
);
})}
{/* Vertical grid lines (going from top-right to bottom-left) */}
{[...Array(GRID.cols + 1)].map((_, col) => {
const start = gridToScreen(col, 0);
const end = gridToScreen(col, GRID.rows);
return (
<line
key={`v-${col}`}
x1={start.x}
y1={start.y}
x2={end.x}
y2={end.y}
stroke="white"
strokeWidth="1"
/>
);
})}
</g>
{/* Grid intersection nodes */}
<g opacity="0.2">
{[...Array(GRID.cols + 1)].map((_, col) =>
[...Array(GRID.rows + 1)].map((_, row) => {
const pos = gridToScreen(col, row);
return (
<circle
key={`node-${col}-${row}`}
cx={pos.x}
cy={pos.y}
r="2"
fill="white"
/>
);
})
)}
</g>
{/* === POWER LINES (Base cables) === */}
<g stroke="white" strokeWidth="2" strokeOpacity="0.25">
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
return (
<line
key={`cable-${i}`}
x1={from.x}
y1={from.y}
x2={to.x}
y2={to.y}
/>
);
})}
</g>
{/* === ANIMATED ENERGY FLOW === */}
<g filter="url(#glow)">
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
const length = Math.sqrt(
Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2)
);
return (
<line
key={`flow-${i}`}
x1={from.x}
y1={from.y}
x2={to.x}
y2={to.y}
stroke="url(#energy-pulse)"
strokeWidth="3"
strokeLinecap="round"
strokeDasharray={`${length * 0.2} ${length * 0.8}`}
>
<animate
attributeName="stroke-dashoffset"
from={length}
to={0}
dur={`${1.5 + (i % 3) * 0.5}s`}
repeatCount="indefinite"
/>
</line>
);
})}
</g>
{/* === SOLAR PANELS === */}
{INFRASTRUCTURE.solar.map((panel, i) => {
const pos = gridToScreen(panel.col, panel.row);
return (
<g key={`solar-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Panel base */}
<path
d="M -20 0 L 0 -10 L 20 0 L 0 10 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Panel surface (tilted) */}
<path
d="M -15 -5 L 0 -15 L 15 -5 L 0 5 Z"
fill="white"
fillOpacity="0.15"
stroke="white"
strokeWidth="1"
strokeOpacity="0.5"
/>
{/* Panel grid lines */}
<line x1="-7" y1="-10" x2="7" y2="0" stroke="white" strokeWidth="0.5" strokeOpacity="0.3" />
<line x1="0" y1="-15" x2="0" y2="5" stroke="white" strokeWidth="0.5" strokeOpacity="0.3" />
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.4" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.3;0.6;0.3" dur="2s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === WIND TURBINES === */}
{INFRASTRUCTURE.wind.map((turbine, i) => {
const pos = gridToScreen(turbine.col, turbine.row);
return (
<g key={`wind-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<ellipse cx="0" cy="0" rx="10" ry="5" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Tower */}
<line x1="0" y1="0" x2="0" y2="-60" stroke="white" strokeWidth="2" strokeOpacity="0.5" />
{/* Nacelle */}
<ellipse cx="0" cy="-60" rx="6" ry="3" fill="white" fillOpacity="0.3" stroke="white" strokeWidth="1" />
{/* Blades */}
<g transform="translate(0, -60)">
{[0, 120, 240].map((angle, j) => (
<line
key={`blade-${i}-${j}`}
x1="0"
y1="0"
x2="0"
y2="-30"
stroke="white"
strokeWidth="1.5"
strokeOpacity="0.6"
transform={`rotate(${angle})`}
>
<animateTransform
attributeName="transform"
type="rotate"
from={`${angle} 0 0`}
to={`${angle + 360} 0 0`}
dur={`${3 + i}s`}
repeatCount="indefinite"
/>
</line>
))}
<circle r="3" fill="white" fillOpacity="0.4" />
</g>
{/* Connection glow */}
<circle r="5" fill="#82ed20" fillOpacity="0.4" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.3;0.6;0.3" dur="2.5s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === SUBSTATIONS === */}
{INFRASTRUCTURE.substations.map((sub, i) => {
const pos = gridToScreen(sub.col, sub.row);
const isCollection = sub.type === 'collection';
return (
<g key={`substation-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base platform */}
<path
d="M -25 0 L 0 -12 L 25 0 L 0 12 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building */}
<path
d={isCollection
? "M -18 0 L -18 -20 L 0 -32 L 18 -20 L 18 0"
: "M -22 0 L -22 -25 L 0 -37 L 22 -25 L 22 0"
}
fill="white"
fillOpacity="0.08"
stroke="white"
strokeWidth="1"
strokeOpacity="0.5"
/>
{/* Equipment */}
<rect x="-10" y="-12" width="6" height="8" fill="white" fillOpacity="0.2" stroke="white" strokeWidth="0.5" />
<rect x="4" y="-12" width="6" height="8" fill="white" fillOpacity="0.2" stroke="white" strokeWidth="0.5" />
{/* Insulators */}
<line x1="-7" y1="-12" x2="-7" y2="-22" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<line x1="7" y1="-12" x2="7" y2="-22" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<circle cx="-7" cy="-22" r="2" fill="white" fillOpacity="0.4" />
<circle cx="7" cy="-22" r="2" fill="white" fillOpacity="0.4" />
{/* Connection glow */}
<circle r="8" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="r" values="6;10;6" dur="3s" repeatCount="indefinite" />
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur="3s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === TRANSMISSION TOWERS === */}
{INFRASTRUCTURE.towers.map((tower, i) => {
const pos = gridToScreen(tower.col, tower.row);
return (
<g key={`tower-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<ellipse cx="0" cy="0" rx="8" ry="4" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Tower legs */}
<path d="M -6 0 L -3 -45 M 6 0 L 3 -45" stroke="white" strokeWidth="1.5" strokeOpacity="0.5" />
{/* Cross braces */}
<path d="M -5 -10 L 5 -10 M -4 -20 L 4 -20 M -3 -30 L 3 -30 M -3 -45 L 3 -45" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Cross arms */}
<line x1="-12" y1="-40" x2="12" y2="-40" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<line x1="-10" y1="-32" x2="10" y2="-32" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
{/* Insulators */}
<circle cx="-10" cy="-40" r="1.5" fill="white" fillOpacity="0.4" />
<circle cx="10" cy="-40" r="1.5" fill="white" fillOpacity="0.4" />
{/* Connection glow */}
<circle r="5" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur="2s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === CITY BUILDINGS === */}
{INFRASTRUCTURE.city.map((building, i) => {
const pos = gridToScreen(building.col, building.row);
const heights = { tall: 70, medium: 45, small: 30 };
const height = heights[building.type as keyof typeof heights];
return (
<g key={`building-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<path
d="M -12 0 L 0 -6 L 12 0 L 0 6 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Building front */}
<path
d={`M -12 0 L -12 -${height} L 0 -${height + 6} L 0 -6 Z`}
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building side */}
<path
d={`M 0 -6 L 0 -${height + 6} L 12 -${height} L 12 0 Z`}
fill="white"
fillOpacity="0.05"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Windows */}
{[...Array(Math.floor(height / 15))].map((_, w) => (
<g key={`window-${i}-${w}`}>
<rect x="-9" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="-4" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="3" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
<rect x="7" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
</g>
))}
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur={`${2 + i * 0.3}s`} repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === CITY 2 BUILDINGS (bottom-left) === */}
{INFRASTRUCTURE.city2.map((building, i) => {
const pos = gridToScreen(building.col, building.row);
const heights = { tall: 70, medium: 45, small: 30 };
const height = heights[building.type as keyof typeof heights];
return (
<g key={`building2-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<path
d="M -12 0 L 0 -6 L 12 0 L 0 6 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Building front */}
<path
d={`M -12 0 L -12 -${height} L 0 -${height + 6} L 0 -6 Z`}
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building side */}
<path
d={`M 0 -6 L 0 -${height + 6} L 12 -${height} L 12 0 Z`}
fill="white"
fillOpacity="0.05"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Windows */}
{[...Array(Math.floor(height / 15))].map((_, w) => (
<g key={`window2-${i}-${w}`}>
<rect x="-9" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="-4" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="3" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
<rect x="7" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
</g>
))}
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur={`${2.5 + i * 0.3}s`} repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === TREES === */}
{INFRASTRUCTURE.trees.map((tree, i) => {
const pos = gridToScreen(tree.col, tree.row);
return (
<g key={`tree-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Trunk */}
<line x1="0" y1="0" x2="0" y2="-15" stroke="white" strokeWidth="2" strokeOpacity="0.3" />
{/* Foliage - layered circles for tree crown */}
<ellipse cx="0" cy="-22" rx="10" ry="8" fill="white" fillOpacity="0.12" stroke="white" strokeWidth="0.5" strokeOpacity="0.2" />
<ellipse cx="-5" cy="-26" rx="7" ry="6" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="0.5" strokeOpacity="0.15" />
<ellipse cx="5" cy="-26" rx="7" ry="6" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="0.5" strokeOpacity="0.15" />
<ellipse cx="0" cy="-30" rx="6" ry="5" fill="white" fillOpacity="0.08" stroke="white" strokeWidth="0.5" strokeOpacity="0.1" />
</g>
);
})}
{/* === ABSTRACT WIND EFFECTS === */}
{INFRASTRUCTURE.wind.map((turbine, i) => {
const pos = gridToScreen(turbine.col, turbine.row);
return (
<g key={`wind-effect-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Wind swoosh lines - curved paths flowing toward turbine */}
{[0, 1, 2].map((j) => (
<path
key={`wind-line-${i}-${j}`}
d={`M ${-80 - j * 15} ${-70 - j * 8} Q ${-50 - j * 10} ${-65 - j * 5} ${-20} ${-60}`}
stroke="url(#wind-flow)"
strokeWidth="2"
fill="none"
strokeLinecap="round"
opacity="0"
>
<animate
attributeName="opacity"
values="0;0.6;0"
dur={`${2 + j * 0.5}s`}
begin={`${j * 0.7 + i * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="stroke-dashoffset"
from="100"
to="0"
dur={`${2 + j * 0.5}s`}
begin={`${j * 0.7 + i * 0.3}s`}
repeatCount="indefinite"
/>
</path>
))}
{/* Additional wind particles */}
{[0, 1, 2, 3].map((j) => (
<circle
key={`wind-particle-${i}-${j}`}
r="1.5"
fill="white"
opacity="0"
>
<animate
attributeName="cx"
values={`${-70 - j * 10};${-10}`}
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values={`${-75 - j * 5};${-60}`}
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0;0.5;0"
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
</circle>
))}
</g>
);
})}
{/* === SCHEMATIC SUN RAYS === */}
{/* Simple downward rays above each solar panel */}
{INFRASTRUCTURE.solar.map((panel, i) => {
const pos = gridToScreen(panel.col, panel.row);
return (
<g key={`sun-ray-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Three short schematic rays coming down to panel */}
{[-8, 0, 8].map((offset, j) => (
<line
key={`ray-${i}-${j}`}
x1={offset}
y1={-45}
x2={offset * 0.3}
y2={-18}
stroke="#FFD700"
strokeWidth="1.5"
strokeOpacity="0.4"
strokeLinecap="round"
strokeDasharray="4 6"
>
<animate
attributeName="strokeOpacity"
values="0.2;0.5;0.2"
dur={`${2 + j * 0.3}s`}
begin={`${i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="stroke-dashoffset"
from="10"
to="0"
dur="1.5s"
repeatCount="indefinite"
/>
</line>
))}
</g>
);
})}
{/* === ENERGY PARTICLES === */}
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
return (
<circle
key={`particle-${i}`}
r="3"
fill="#82ed20"
filter="url(#soft-glow)"
>
<animate
attributeName="cx"
values={`${from.x};${to.x}`}
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values={`${from.y};${to.y}`}
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0;0.8;0"
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
</circle>
);
})}
</g>
</svg>
<div className="absolute inset-0 bg-gradient-to-b from-primary/10 via-transparent to-primary/90" />
</div>
);
}

View File

@@ -40,17 +40,18 @@ export default function MeetTheTeam() {
<span className="ml-3 transition-transform group-hover:translate-x-2">&rarr;</span>
</Button>
<div className="flex -space-x-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="w-14 h-14 rounded-full border-4 border-primary-dark bg-neutral-medium overflow-hidden">
<div className="w-full h-full bg-primary-light flex items-center justify-center text-primary font-bold text-xs">
KLZ
</div>
<div className="flex items-center gap-4">
<div className="flex -space-x-4">
<div className="w-14 h-14 rounded-full border-4 border-primary-dark overflow-hidden relative">
<Image src="/uploads/2024/12/DSC07768-Large.webp" alt="Michael Bodemer" fill className="object-cover" />
</div>
<div className="w-14 h-14 rounded-full border-4 border-primary-dark overflow-hidden relative">
<Image src="/uploads/2024/12/DSC07963-Large.webp" alt="Klaus Mintel" fill className="object-cover" />
</div>
))}
<div className="w-14 h-14 rounded-full border-4 border-primary-dark bg-accent flex items-center justify-center text-primary-dark font-bold text-sm">
+12
</div>
<span className="text-white/60 font-bold text-sm uppercase tracking-widest">
{t('andNetwork')}
</span>
</div>
</div>
</div>

View File

@@ -40,7 +40,7 @@ export default function ProductCategories() {
];
return (
<Section className="bg-neutral-light py-0">
<Section className="bg-neutral-light py-0 -mt-px">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4">
{categories.map((category, idx) => (
<Link key={idx} href={category.href} className="group block relative h-[500px] lg:h-[650px] overflow-hidden border-r border-white/10 last:border-r-0">
@@ -63,7 +63,7 @@ export default function ProductCategories() {
</p>
</div>
<div className="flex items-center text-accent font-bold tracking-wider uppercase text-sm opacity-0 group-hover:opacity-100 transition-all duration-500 delay-100">
Explore Category <span className="ml-2 transition-transform group-hover:translate-x-2">&rarr;</span>
{t('exploreCategory')} <span className="ml-2 transition-transform group-hover:translate-x-2">&rarr;</span>
</div>
</div>
</Link>

View File

@@ -19,7 +19,7 @@ export default function WhatWeDo() {
</p>
<div className="mt-12 p-8 bg-primary-light rounded-2xl border border-primary/10">
<p className="text-primary font-bold text-lg italic">
"We don't just deliver cables; we deliver the infrastructure for a sustainable future."
"{t('quote')}"
</p>
</div>
</div>

View File

@@ -0,0 +1,100 @@
'use client';
import React from 'react';
export default function ProductsIllustration() {
return (
<div className="absolute inset-0 z-0 overflow-hidden pointer-events-none bg-primary">
<svg
viewBox="0 0 1000 500"
className="w-full h-full"
preserveAspectRatio="xMidYMid slice"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id="scan-beam" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#82ed20" stopOpacity="0" />
<stop offset="50%" stopColor="#82ed20" stopOpacity="0.5" />
<stop offset="100%" stopColor="#82ed20" stopOpacity="0" />
</linearGradient>
</defs>
{/* 3D Cable Wireframe Construction */}
<g transform="translate(100, 250) rotate(-10)">
{/* Inner Core Strands (Twisted) */}
{[...Array(5)].map((_, i) => (
<path
key={`strand-${i}`}
d={`M 0 ${i*10 - 20} Q 400 ${i*10 - 60} 800 ${i*10 - 20}`}
stroke="white"
strokeWidth="2"
strokeOpacity="0.8"
fill="none"
>
<animate attributeName="d"
values={`M 0 ${i*10 - 20} Q 400 ${i*10 - 60} 800 ${i*10 - 20};
M 0 ${i*10 - 20} Q 400 ${i*10 + 20} 800 ${i*10 - 20};
M 0 ${i*10 - 20} Q 400 ${i*10 - 60} 800 ${i*10 - 20}`}
dur="4s"
repeatCount="indefinite"
/>
</path>
))}
{/* Insulation Layers (Ellipses along the path) */}
{[...Array(8)].map((_, i) => (
<ellipse
key={`ring-${i}`}
cx={100 + i * 100}
cy="0"
rx="40"
ry="80"
stroke="white"
strokeWidth="1"
strokeOpacity="0.2"
fill="none"
/>
))}
{/* Outer Sheath (Top and Bottom Lines) */}
<path d="M 0 -80 L 800 -80" stroke="white" strokeWidth="1" strokeOpacity="0.3" strokeDasharray="10 5" />
<path d="M 0 80 L 800 80" stroke="white" strokeWidth="1" strokeOpacity="0.3" strokeDasharray="10 5" />
{/* Scanning Ring (Animated) */}
<g>
<ellipse cx="0" cy="0" rx="50" ry="90" stroke="#82ed20" strokeWidth="2" strokeOpacity="0.8" fill="none">
<animate attributeName="cx" from="0" to="800" dur="3s" repeatCount="indefinite" />
<animate attributeName="rx" values="50;45;50" dur="0.5s" repeatCount="indefinite" />
</ellipse>
{/* Scan Beam */}
<rect x="-50" y="-100" width="100" height="200" fill="url(#scan-beam)" opacity="0.3">
<animate attributeName="x" from="-50" to="750" dur="3s" repeatCount="indefinite" />
</rect>
</g>
</g>
{/* Floating Data/Tech Elements */}
<g transform="translate(0, 0)">
{[...Array(20)].map((_, i) => (
<rect
key={`bit-${i}`}
x={Math.random() * 1000}
y={Math.random() * 500}
width={Math.random() * 20 + 5}
height="2"
fill="white"
fillOpacity="0.3"
>
<animate attributeName="opacity" values="0;0.5;0" dur={`${Math.random() * 2 + 1}s`} repeatCount="indefinite" />
<animate attributeName="width" values="5;30;5" dur={`${Math.random() * 2 + 1}s`} repeatCount="indefinite" />
</rect>
))}
</g>
</svg>
<div className="absolute inset-0 bg-gradient-to-r from-primary/80 via-transparent to-primary/80" />
</div>
);
}

View File

@@ -29,6 +29,27 @@ export async function getProductBySlug(slug: string, locale: string): Promise<Pr
}
if (!fs.existsSync(filePath)) {
// Fallback to English if locale is not 'en'
if (locale !== 'en') {
const enProductsDir = path.join(process.cwd(), 'data', 'products', 'en');
let enFilePath = path.join(enProductsDir, `${slug}.mdx`);
if (!fs.existsSync(enFilePath)) {
enFilePath = path.join(enProductsDir, `${slug}-2.mdx`);
}
if (fs.existsSync(enFilePath)) {
const fileContent = fs.readFileSync(enFilePath, 'utf8');
const { data, content } = matter(fileContent);
return {
slug,
frontmatter: {
...data,
isFallback: true
} as ProductFrontmatter & { isFallback?: boolean },
content,
};
}
}
return null;
}

View File

@@ -106,7 +106,34 @@
"Products": {
"title": "Unsere Produkte",
"subtitle": "Entdecken Sie unser umfassendes Sortiment an hochwertigen Kabeln für jede Anwendung.",
"heroSubtitle": "Produktportfolio",
"categoryLabel": "Kategorie",
"viewProducts": "Produkte ansehen",
"exploreCategory": "Kategorie entdecken",
"cta": {
"title": "Benötigen Sie technische Unterstützung?",
"description": "Unser Expertenteam hilft Ihnen gerne dabei, die perfekte Kabellösung für Ihre spezifischen technischen Anforderungen und Umgebungsbedingungen zu finden.",
"button": "Experten kontaktieren"
},
"details": "Details",
"relatedProductsTitle": "Ähnliche Produkte",
"requestQuote": "Angebot anfordern",
"requestQuoteDesc": "Erhalten Sie technische Spezifikationen und Preise für Ihr Projekt.",
"form": {
"contactInfo": "Kontaktinformationen",
"projectDetails": "Projektdetails",
"email": "E-Mail-Adresse",
"emailPlaceholder": "ihre@email.de",
"message": "Ihre Nachricht",
"messagePlaceholder": "Beschreiben Sie Ihre Projektanforderungen...",
"submit": "Anfrage senden",
"submitting": "Wird gesendet...",
"successTitle": "Anfrage gesendet!",
"successDesc": "Vielen Dank für Ihr Interesse an {productName}. Unser technisches Team wird sich in Kürze bei Ihnen melden.",
"sendAnother": "Weitere Anfrage senden",
"privacyNote": "Mit dem Absenden erklären Sie sich mit unserer Datenschutzerklärung einverstanden"
},
"englishVersion": "Englische Version",
"categories": {
"lowVoltage": {
"title": "Niederspannungskabel",
@@ -129,11 +156,13 @@
"Home": {
"hero": {
"title": "Wir helfen beim Ausbau der Energiekabelnetze für eine <green>grüne</green> Zukunft",
"cta": "Lassen Sie uns reden"
"cta": "Lassen Sie uns reden",
"exploreProducts": "Produkte entdecken"
},
"whatWeDo": {
"title": "Was wir tun",
"subtitle": "Wir sorgen dafür, dass der Strom fließt mit qualitätsgeprüften Kabeln. Von der Niederspannung bis zur Hochspannung.",
"quote": "Wir liefern nicht nur Kabel; wir liefern die Infrastruktur für eine nachhaltige Zukunft.",
"items": [
{
"title": "Belieferung von Energieversorgern, Wind- und Solarparks, Industrie und Handel",
@@ -183,7 +212,8 @@
"meetTheTeam": {
"title": "Lernen Sie das Team hinter KLZ kennen",
"description": "Bei KLZ ist unser Team die Kraft hinter den Kabeln. Von erfahrenen Experten wie Michael und Klaus bis hin zu einer engagierten Gruppe von Planern, Logistikspezialisten und Kundendienstmitarbeitern spielt jedes Mitglied eine entscheidende Rolle. Gemeinsam verbinden wir jahrzehntelange Erfahrung, innovatives Denken und das gemeinsame Engagement für die Bereitstellung zuverlässiger Energielösungen.",
"cta": "Unser Team ansehen"
"cta": "Unser Team ansehen",
"andNetwork": "& Unser Experten-Netzwerk"
},
"gallery": {
"title": "Starke Verbindungen für eine nachhaltige Welt.",

View File

@@ -106,7 +106,34 @@
"Products": {
"title": "Our Products",
"subtitle": "Explore our comprehensive range of high-quality cables designed for every application.",
"heroSubtitle": "Product Portfolio",
"categoryLabel": "Category",
"viewProducts": "View Products",
"exploreCategory": "Explore Category",
"cta": {
"title": "Need Technical Assistance?",
"description": "Our team of experts is ready to help you find the perfect cable solution for your specific technical requirements and environmental conditions.",
"button": "Contact Experts"
},
"details": "Details",
"relatedProductsTitle": "Related Products",
"requestQuote": "Request a Quote",
"requestQuoteDesc": "Get technical specifications and pricing for your project.",
"form": {
"contactInfo": "Contact Information",
"projectDetails": "Project Details",
"email": "Email Address",
"emailPlaceholder": "your@email.com",
"message": "Your Message",
"messagePlaceholder": "Describe your project requirements...",
"submit": "Send Request",
"submitting": "Sending...",
"successTitle": "Request Sent!",
"successDesc": "Thank you for your interest in {productName}. Our technical team will get back to you shortly.",
"sendAnother": "Send another request",
"privacyNote": "By submitting you agree to our privacy policy"
},
"englishVersion": "English Version",
"categories": {
"lowVoltage": {
"title": "Low Voltage Cables",
@@ -129,11 +156,13 @@
"Home": {
"hero": {
"title": "We are helping to expand the energy cable networks for a <green>green</green> future",
"cta": "Let's talk"
"cta": "Let's talk",
"exploreProducts": "Explore Products"
},
"whatWeDo": {
"title": "What we do",
"subtitle": "We ensure that the electricity flows with quality-tested cables. From low voltage up to high voltage.",
"quote": "We don't just deliver cables; we deliver the infrastructure for a sustainable future.",
"items": [
{
"title": "Supply to energy suppliers, wind and solar parks, industry and trade",
@@ -183,7 +212,8 @@
"meetTheTeam": {
"title": "Meet the team behind KLZ",
"description": "At KLZ, our team is the power behind the cables. From seasoned experts like Michael and Klaus to a dedicated group of planners, logistics specialists, and customer support professionals, every member plays a vital role. Together, we combine decades of experience, innovative thinking, and a shared commitment to delivering reliable energy solutions.",
"cta": "Checkout our team"
"cta": "Checkout our team",
"andNetwork": "& Our Expert Network"
},
"gallery": {
"title": "Strong Connections for a Sustainable World.",

File diff suppressed because one or more lines are too long