Compare commits
13 Commits
ac2add1984
...
6adf97a096
| Author | SHA1 | Date | |
|---|---|---|---|
| 6adf97a096 | |||
| 4abcc3fdf5 | |||
| 6797303628 | |||
| 2feb73b982 | |||
| b99258f886 | |||
| 5c9b2e3f5a | |||
| e8b6b13a3b | |||
| 1f624f3d7f | |||
| 40c553d6f6 | |||
| a32c12692c | |||
| 79016fbe97 | |||
| 4f6264f2e2 | |||
| 46266a7bbc |
@@ -2,6 +2,7 @@ import { notFound } from 'next/navigation';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { Section, Container, Heading, Badge } from '@/components/ui';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
interface PageProps {
|
||||
params: {
|
||||
@@ -10,6 +11,36 @@ interface PageProps {
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale, slug } }: PageProps): Promise<Metadata> {
|
||||
const { getPageBySlug } = await import('@/lib/pages');
|
||||
const pageData = await getPageBySlug(slug, locale);
|
||||
|
||||
if (!pageData) return {};
|
||||
|
||||
return {
|
||||
title: pageData.frontmatter.title,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
alternates: {
|
||||
canonical: `/${locale}/${slug}`,
|
||||
languages: {
|
||||
'de': `/de/${slug}`,
|
||||
'en': `/en/${slug}`,
|
||||
'x-default': `/en/${slug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
url: `https://klz-cables.com/${locale}/${slug}`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${pageData.frontmatter.title} | KLZ Cables`,
|
||||
description: pageData.frontmatter.excerpt || '',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function StandardPage({ params: { locale, slug } }: PageProps) {
|
||||
const { getPageBySlug } = await import('@/lib/pages');
|
||||
const pageData = await getPageBySlug(slug, locale);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { getPostBySlug, getAdjacentPosts } from '@/lib/blog';
|
||||
import { getPostBySlug, getAdjacentPosts, getReadingTime, getHeadings } from '@/lib/blog';
|
||||
import { Metadata } from 'next';
|
||||
|
||||
interface BlogPostProps {
|
||||
@@ -16,12 +16,19 @@ export async function generateMetadata({ params: { locale, slug } }: BlogPostPro
|
||||
if (!post) return {};
|
||||
|
||||
const description = post.frontmatter.excerpt || '';
|
||||
|
||||
return {
|
||||
title: post.frontmatter.title,
|
||||
description: description,
|
||||
alternates: {
|
||||
canonical: `/${locale}/blog/${slug}`,
|
||||
languages: {
|
||||
'de': `/de/blog/${slug}`,
|
||||
'en': `/en/blog/${slug}`,
|
||||
'x-default': `/en/blog/${slug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: post.frontmatter.title,
|
||||
title: `${post.frontmatter.title} | KLZ Cables`,
|
||||
description: description,
|
||||
type: 'article',
|
||||
publishedTime: post.frontmatter.date,
|
||||
@@ -30,7 +37,7 @@ export async function generateMetadata({ params: { locale, slug } }: BlogPostPro
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.frontmatter.title,
|
||||
title: `${post.frontmatter.title} | KLZ Cables`,
|
||||
description: description,
|
||||
},
|
||||
};
|
||||
@@ -38,7 +45,7 @@ export async function generateMetadata({ params: { locale, slug } }: BlogPostPro
|
||||
|
||||
import Link from 'next/link';
|
||||
import VisualLinkPreview from '@/components/blog/VisualLinkPreview';
|
||||
import Callout from '@/components/blog/Callout';
|
||||
import { Callout } from '@/components/ui';
|
||||
import HighlightBox from '@/components/blog/HighlightBox';
|
||||
import Stats from '@/components/blog/Stats';
|
||||
import AnimatedImage from '@/components/blog/AnimatedImage';
|
||||
@@ -46,7 +53,10 @@ import ChatBubble from '@/components/blog/ChatBubble';
|
||||
import SplitHeading from '@/components/blog/SplitHeading';
|
||||
import PostNavigation from '@/components/blog/PostNavigation';
|
||||
import PowerCTA from '@/components/blog/PowerCTA';
|
||||
import ShareButton from '@/components/blog/ShareButton';
|
||||
import TableOfContents from '@/components/blog/TableOfContents';
|
||||
import StickyNarrative from '@/components/blog/StickyNarrative';
|
||||
import TechnicalGrid from '@/components/blog/TechnicalGrid';
|
||||
import ComparisonGrid from '@/components/blog/ComparisonGrid';
|
||||
|
||||
const components = {
|
||||
VisualLinkPreview,
|
||||
@@ -57,6 +67,10 @@ const components = {
|
||||
ChatBubble,
|
||||
PowerCTA,
|
||||
SplitHeading,
|
||||
StickyNarrative,
|
||||
TechnicalGrid,
|
||||
ComparisonGrid,
|
||||
h1: () => null,
|
||||
a: ({ href, children, ...props }: any) => {
|
||||
if (href?.startsWith('/')) {
|
||||
return (
|
||||
@@ -182,77 +196,63 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro
|
||||
notFound();
|
||||
}
|
||||
|
||||
const headings = getHeadings(post.content);
|
||||
|
||||
return (
|
||||
<article className="bg-white min-h-screen font-sans">
|
||||
<article className="bg-white min-h-screen font-sans selection:bg-primary/10 selection:text-primary">
|
||||
|
||||
{/* Featured Image Header */}
|
||||
{post.frontmatter.featuredImage && (
|
||||
<div className="relative w-full h-[60vh] min-h-[400px] overflow-hidden group">
|
||||
{post.frontmatter.featuredImage ? (
|
||||
<div className="relative w-full h-[70vh] min-h-[500px] overflow-hidden group">
|
||||
<div
|
||||
className="absolute inset-0 bg-cover bg-center transition-transform duration-[2s] ease-out scale-105 group-hover:scale-100"
|
||||
className="absolute inset-0 bg-cover bg-center transition-transform duration-[3s] ease-out scale-110 group-hover:scale-100"
|
||||
style={{ backgroundImage: `url(${post.frontmatter.featuredImage})` }}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/40 to-transparent" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-neutral-dark via-neutral-dark/40 to-transparent" />
|
||||
|
||||
{/* Title overlay on image */}
|
||||
<div className="absolute inset-0 flex flex-col justify-end p-8 md:p-16 lg:p-24">
|
||||
<div className="container mx-auto max-w-3xl">
|
||||
{post.frontmatter.category && (
|
||||
<span className="inline-block px-4 py-1.5 bg-primary/90 backdrop-blur-sm text-white text-sm font-bold uppercase tracking-wider rounded-full mb-6 shadow-lg transform transition-transform hover:scale-105">
|
||||
{post.frontmatter.category}
|
||||
</span>
|
||||
)}
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold text-white mb-6 leading-tight drop-shadow-xl">
|
||||
{post.frontmatter.title}
|
||||
</h1>
|
||||
<div className="flex items-center gap-4 text-white/90 text-sm md:text-base font-medium">
|
||||
<time dateTime={post.frontmatter.date}>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1.5 h-1.5 bg-primary rounded-full" />
|
||||
<span>KLZ Cables</span>
|
||||
<div className="ml-auto hidden md:block">
|
||||
<ShareButton
|
||||
title={post.frontmatter.title}
|
||||
text={post.frontmatter.excerpt || ''}
|
||||
url={`https://klz-cables.com/${locale}/blog/${slug}`}
|
||||
locale={locale}
|
||||
/>
|
||||
<div className="absolute inset-0 flex flex-col justify-end pb-16 md:pb-24">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-4xl">
|
||||
{post.frontmatter.category && (
|
||||
<div className="overflow-hidden mb-6">
|
||||
<span className="inline-block px-4 py-1.5 bg-accent text-neutral-dark text-xs font-bold uppercase tracking-[0.2em] rounded-sm animate-slight-fade-in-from-bottom">
|
||||
{post.frontmatter.category}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-8 leading-[1.1] drop-shadow-2xl animate-slight-fade-in-from-bottom [animation-delay:200ms]">
|
||||
{post.frontmatter.title}
|
||||
</h1>
|
||||
<div className="flex flex-wrap items-center gap-6 text-white/80 text-sm md:text-base font-medium animate-slight-fade-in-from-bottom [animation-delay:400ms]">
|
||||
<time dateTime={post.frontmatter.date}>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1 h-1 bg-white/30 rounded-full" />
|
||||
<span>{getReadingTime(post.content)} min read</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content */}
|
||||
<div className="container mx-auto px-4 py-16 md:py-24 max-w-3xl">
|
||||
{/* Mobile Share Button */}
|
||||
<div className="md:hidden mb-8 flex justify-end">
|
||||
<ShareButton
|
||||
title={post.frontmatter.title}
|
||||
text={post.frontmatter.excerpt || ''}
|
||||
url={`https://klz-cables.com/${locale}/blog/${slug}`}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
{/* If no featured image, show header here */}
|
||||
{!post.frontmatter.featuredImage && (
|
||||
<header className="mb-16 text-center">
|
||||
) : (
|
||||
<header className="pt-32 pb-16 bg-neutral-50 border-b border-neutral-100">
|
||||
<div className="container mx-auto px-4 max-w-4xl">
|
||||
{post.frontmatter.category && (
|
||||
<div className="mb-6">
|
||||
<span className="inline-block px-4 py-1.5 bg-primary/10 text-primary text-sm font-bold uppercase tracking-wider rounded-full">
|
||||
<span className="inline-block px-4 py-1.5 bg-primary/10 text-primary text-xs font-bold uppercase tracking-[0.2em] rounded-sm">
|
||||
{post.frontmatter.category}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-4xl md:text-5xl lg:text-6xl font-extrabold text-text-primary mb-8 leading-tight">
|
||||
<h1 className="text-4xl md:text-6xl font-bold text-text-primary mb-8 leading-tight">
|
||||
{post.frontmatter.title}
|
||||
</h1>
|
||||
<div className="flex items-center justify-center gap-4 text-text-secondary font-medium">
|
||||
<div className="flex items-center gap-6 text-text-secondary font-medium">
|
||||
<time dateTime={post.frontmatter.date}>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(locale, {
|
||||
year: 'numeric',
|
||||
@@ -260,75 +260,92 @@ export default async function BlogPost({ params: { locale, slug } }: BlogPostPro
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
<span className="w-1.5 h-1.5 bg-primary rounded-full" />
|
||||
<span>KLZ Cables</span>
|
||||
<span className="w-1 h-1 bg-neutral-300 rounded-full" />
|
||||
<span>{getReadingTime(post.content)} min read</span>
|
||||
</div>
|
||||
</header>
|
||||
)}
|
||||
|
||||
{/* Excerpt/Lead paragraph if available */}
|
||||
{post.frontmatter.excerpt && (
|
||||
<div className="mb-16">
|
||||
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-6 py-2">
|
||||
{post.frontmatter.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
)}
|
||||
|
||||
{/* Main content with enhanced styling */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-xl prose-img:shadow-lg">
|
||||
<MDXRemote source={post.content} components={components} />
|
||||
</div>
|
||||
{/* Main Content Area with Sticky Narrative Layout */}
|
||||
<div className="container mx-auto px-4 py-16 md:py-24">
|
||||
<div className="sticky-narrative-container">
|
||||
{/* Left Column: Content */}
|
||||
<div className="sticky-narrative-content">
|
||||
{/* Excerpt/Lead paragraph if available */}
|
||||
{post.frontmatter.excerpt && (
|
||||
<div className="mb-16 animate-slight-fade-in-from-bottom [animation-delay:600ms]">
|
||||
<p className="text-xl md:text-2xl text-text-primary leading-relaxed font-medium border-l-4 border-primary pl-8 py-2 italic">
|
||||
{post.frontmatter.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Structured Data */}
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: post.frontmatter.title,
|
||||
datePublished: post.frontmatter.date,
|
||||
image: post.frontmatter.featuredImage ? `https://klz-cables.com${post.frontmatter.featuredImage}` : undefined,
|
||||
author: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
url: 'https://klz-cables.com',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://klz-cables.com/logo.png', // Assuming logo exists
|
||||
},
|
||||
},
|
||||
description: post.frontmatter.excerpt,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
{/* Main content with enhanced styling */}
|
||||
<div className="prose prose-lg md:prose-xl max-w-none prose-headings:font-bold prose-headings:text-text-primary prose-p:text-text-secondary prose-p:leading-relaxed prose-a:text-primary prose-a:no-underline hover:prose-a:underline prose-img:rounded-2xl prose-img:shadow-2xl prose-blockquote:border-primary prose-blockquote:bg-primary/5 prose-blockquote:rounded-r-2xl prose-strong:text-primary animate-slight-fade-in-from-bottom [animation-delay:800ms]">
|
||||
<MDXRemote source={post.content} components={components} />
|
||||
</div>
|
||||
|
||||
{/* Power CTA */}
|
||||
<div className="mt-20">
|
||||
<PowerCTA locale={locale} />
|
||||
</div>
|
||||
{/* Power CTA */}
|
||||
<div className="mt-24 animate-slight-fade-in-from-bottom">
|
||||
<PowerCTA locale={locale} />
|
||||
</div>
|
||||
|
||||
{/* Post Navigation */}
|
||||
<PostNavigation prev={prev} next={next} locale={locale} />
|
||||
{/* Post Navigation */}
|
||||
<div className="mt-16">
|
||||
<PostNavigation prev={prev} next={next} locale={locale} />
|
||||
</div>
|
||||
|
||||
{/* Back to blog link */}
|
||||
<div className="mt-16 pt-10 border-t border-neutral-200 text-center">
|
||||
<Link
|
||||
href={`/${locale}/blog`}
|
||||
className="inline-flex items-center gap-2 text-text-secondary hover:text-primary font-medium text-lg transition-colors group"
|
||||
>
|
||||
<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="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
{locale === 'de' ? 'Zurück zur Übersicht' : 'Back to Overview'}
|
||||
</Link>
|
||||
{/* Back to blog link */}
|
||||
<div className="mt-16 pt-10 border-t border-neutral-100">
|
||||
<Link
|
||||
href={`/${locale}/blog`}
|
||||
className="inline-flex items-center gap-3 text-text-secondary hover:text-primary font-bold text-sm uppercase tracking-widest transition-all group"
|
||||
>
|
||||
<svg className="w-5 h-5 transition-transform group-hover:-translate-x-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
{locale === 'de' ? 'Zurück zur Übersicht' : 'Back to Overview'}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column: Sticky Sidebar */}
|
||||
<aside className="sticky-narrative-sidebar hidden lg:block">
|
||||
<div className="space-y-12">
|
||||
<TableOfContents headings={headings} locale={locale} />
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Structured Data */}
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: post.frontmatter.title,
|
||||
datePublished: post.frontmatter.date,
|
||||
image: post.frontmatter.featuredImage ? `https://klz-cables.com${post.frontmatter.featuredImage}` : undefined,
|
||||
author: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
url: 'https://klz-cables.com',
|
||||
},
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'KLZ Cables',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: 'https://klz-cables.com/logo.png',
|
||||
},
|
||||
},
|
||||
description: post.frontmatter.excerpt,
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,24 @@ export async function generateMetadata({ params: { locale } }: BlogIndexProps) {
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('description'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/blog`,
|
||||
languages: {
|
||||
'de': '/de/blog',
|
||||
'en': '/en/blog',
|
||||
'x-default': '/en/blog',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('description'),
|
||||
url: `https://klz-cables.com/${locale}/blog`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('description'),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +65,7 @@ export default async function BlogIndex({ params: { locale } }: BlogIndexProps)
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div className="max-w-4xl animate-slide-up">
|
||||
<Badge variant="accent" className="mb-4 md:mb-6">{t('featuredPost')}</Badge>
|
||||
<Badge variant="saturated" className="mb-4 md:mb-6">{t('featuredPost')}</Badge>
|
||||
{featuredPost && (
|
||||
<>
|
||||
<h1 className="text-3xl md:text-7xl font-extrabold text-white mb-4 md:mb-8 leading-[1.1] line-clamp-3 md:line-clamp-none">
|
||||
@@ -119,10 +137,10 @@ export default async function BlogIndex({ params: { locale } }: BlogIndexProps)
|
||||
{post.frontmatter.excerpt}
|
||||
</p>
|
||||
<div className="mt-auto pt-4 md:pt-8 border-t border-neutral-medium flex items-center justify-between">
|
||||
<span className="text-primary text-sm md:text-base font-extrabold group-hover:text-accent-dark transition-colors">
|
||||
<span className="text-saturated text-sm md:text-base font-extrabold group-hover:text-accent-dark transition-colors">
|
||||
{t('readMore')}
|
||||
</span>
|
||||
<div className="w-8 h-8 md:w-10 md: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">
|
||||
<div className="w-8 h-8 md:w-10 md:h-10 rounded-full bg-primary-light flex items-center justify-center text-saturated group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300">
|
||||
<svg className="w-4 h-4 md:w-5 md: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>
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Section, Container, Button, Heading, Card } from '@/components/ui';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { Section, Container, Button, Heading, Card, Input, Textarea, Label } from '@/components/ui';
|
||||
|
||||
interface ContactPageProps {
|
||||
params: {
|
||||
locale: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: ContactPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Contact' });
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('subtitle'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/contact`,
|
||||
languages: {
|
||||
'de': '/de/contact',
|
||||
'en': '/en/contact',
|
||||
'x-default': '/en/contact',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
url: `https://klz-cables.com/${locale}/contact`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function ContactPage() {
|
||||
const t = useTranslations('Contact');
|
||||
@@ -34,7 +68,7 @@ export default function ContactPage() {
|
||||
</Heading>
|
||||
<div className="space-y-4 md:space-y-8">
|
||||
<div className="flex items-start gap-4 md:gap-6 group">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl 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 flex-shrink-0">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-saturated/10 flex items-center justify-center text-saturated group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300 shadow-sm flex-shrink-0">
|
||||
<svg className="w-5 h-5 md:w-7 md:h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
@@ -49,7 +83,7 @@ export default function ContactPage() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 md:gap-6 group">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl 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 flex-shrink-0">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-saturated/10 flex items-center justify-center text-saturated group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300 shadow-sm flex-shrink-0">
|
||||
<svg className="w-5 h-5 md:w-7 md:h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
|
||||
</svg>
|
||||
@@ -61,7 +95,7 @@ export default function ContactPage() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 md:gap-6 group">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl 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 flex-shrink-0">
|
||||
<div className="w-10 h-10 md:w-14 md:h-14 rounded-xl md:rounded-2xl bg-saturated/10 flex items-center justify-center text-saturated group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300 shadow-sm flex-shrink-0">
|
||||
<svg className="w-5 h-5 md:w-7 md:h-7" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
@@ -97,46 +131,43 @@ export default function ContactPage() {
|
||||
</Heading>
|
||||
<form className="grid grid-cols-1 md:grid-cols-2 gap-4 md:gap-8">
|
||||
<div className="space-y-1 md:space-y-2">
|
||||
<label htmlFor="name" className="text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest">{t('form.name')}</label>
|
||||
<input
|
||||
<Label htmlFor="name">{t('form.name')}</Label>
|
||||
<Input
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
autoComplete="name"
|
||||
enterKeyHint="next"
|
||||
className="w-full px-4 md:px-6 py-2.5 md:py-4 bg-neutral rounded-xl md:rounded-2xl border-2 border-transparent focus:border-primary focus:bg-white transition-all outline-none text-sm md:text-lg"
|
||||
placeholder={t('form.namePlaceholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1 md:space-y-2">
|
||||
<label htmlFor="email" className="text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest">{t('form.email')}</label>
|
||||
<input
|
||||
<Label htmlFor="email">{t('form.email')}</Label>
|
||||
<Input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
inputMode="email"
|
||||
enterKeyHint="next"
|
||||
className="w-full px-4 md:px-6 py-2.5 md:py-4 bg-neutral rounded-xl md:rounded-2xl border-2 border-transparent focus:border-primary focus:bg-white transition-all outline-none text-sm md:text-lg"
|
||||
placeholder={t('form.emailPlaceholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 space-y-1 md:space-y-2">
|
||||
<label htmlFor="message" className="text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest">{t('form.message')}</label>
|
||||
<textarea
|
||||
<Label htmlFor="message">{t('form.message')}</Label>
|
||||
<Textarea
|
||||
id="message"
|
||||
name="message"
|
||||
rows={4}
|
||||
enterKeyHint="send"
|
||||
className="w-full px-4 md:px-6 py-2.5 md:py-4 bg-neutral rounded-xl md:rounded-2xl border-2 border-transparent focus:border-primary focus:bg-white transition-all outline-none text-sm md:text-lg resize-none"
|
||||
placeholder={t('form.messagePlaceholder')}
|
||||
required
|
||||
></textarea>
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 pt-2 md:pt-4">
|
||||
<Button type="submit" size="lg" className="w-full shadow-xl shadow-primary/20 md:h-16 md:px-10 md:text-xl active:scale-[0.98] transition-transform">
|
||||
<Button type="submit" variant="saturated" size="lg" className="w-full shadow-xl shadow-saturated/20 md:h-16 md:px-10 md:text-xl active:scale-[0.98] transition-transform">
|
||||
{t('form.submit')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -13,15 +13,20 @@ export async function generateMetadata({params: {locale}}: {params: {locale: str
|
||||
return {
|
||||
title: {
|
||||
default: t('title'),
|
||||
template: `%s | ${t('title')}`
|
||||
template: `%s | KLZ Cables`
|
||||
},
|
||||
description: t('description'),
|
||||
metadataBase: new URL('https://klz-cables.com'),
|
||||
icons: {
|
||||
icon: '/favicon.ico',
|
||||
apple: '/apple-touch-icon.png',
|
||||
},
|
||||
alternates: {
|
||||
canonical: `/${locale}`,
|
||||
languages: {
|
||||
'de-DE': '/de',
|
||||
'en-US': '/en',
|
||||
'de': '/de',
|
||||
'en': '/en',
|
||||
'x-default': '/en',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Hero from '@/components/home/Hero';
|
||||
import ProductCategories from '@/components/home/ProductCategories';
|
||||
import WhatWeDo from '@/components/home/WhatWeDo';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import { getProductBySlug, getAllProducts } from '@/lib/mdx';
|
||||
import ProductTechnicalData from '@/components/ProductTechnicalData';
|
||||
import ProductSidebar from '@/components/ProductSidebar';
|
||||
import ProductTabs from '@/components/ProductTabs';
|
||||
import RequestQuoteForm from '@/components/RequestQuoteForm';
|
||||
import ProductTechnicalData from '@/components/ProductTechnicalData';
|
||||
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 Scribble from '@/components/Scribble';
|
||||
import { Badge, Container, Section } from '@/components/ui';
|
||||
import { getDatasheetPath } from '@/lib/datasheets';
|
||||
import { getAllProducts, getProductBySlug } from '@/lib/mdx';
|
||||
import { Metadata } from 'next';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
interface ProductPageProps {
|
||||
params: {
|
||||
@@ -34,11 +34,24 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
return {
|
||||
title: categoryTitle,
|
||||
description: categoryDesc,
|
||||
alternates: {
|
||||
canonical: `/${locale}/products/${productSlug}`,
|
||||
languages: {
|
||||
'de': `/de/products/${productSlug}`,
|
||||
'en': `/en/products/${productSlug}`,
|
||||
'x-default': `/en/products/${productSlug}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: categoryTitle,
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
url: `https://klz-cables.com/${locale}/products/${productSlug}`,
|
||||
}
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${categoryTitle} | KLZ Cables`,
|
||||
description: categoryDesc,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,15 +61,23 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
return {
|
||||
title: product.frontmatter.title,
|
||||
description: product.frontmatter.description,
|
||||
alternates: {
|
||||
canonical: `/${locale}/products/${slug.join('/')}`,
|
||||
languages: {
|
||||
'de': `/de/products/${slug.join('/')}`,
|
||||
'en': `/en/products/${slug.join('/')}`,
|
||||
'x-default': `/en/products/${slug.join('/')}`,
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: product.frontmatter.title,
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
description: product.frontmatter.description,
|
||||
type: 'website',
|
||||
url: `https://klz-cables.com/${locale}/products/${slug.join('/')}`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: product.frontmatter.title,
|
||||
title: `${product.frontmatter.title} | KLZ Cables`,
|
||||
description: product.frontmatter.description,
|
||||
},
|
||||
};
|
||||
@@ -65,28 +86,35 @@ export async function generateMetadata({ params }: ProductPageProps): Promise<Me
|
||||
const components = {
|
||||
ProductTechnicalData,
|
||||
ProductTabs,
|
||||
p: (props: any) => <p {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed mb-8" />,
|
||||
p: (props: any) => <p {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed mb-8 font-medium" />,
|
||||
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 className="relative mb-16">
|
||||
<h2 {...props} className="text-4xl md:text-5xl font-black text-primary tracking-tighter uppercase mb-6" />
|
||||
<div className="w-20 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" />,
|
||||
h3: (props: any) => <h3 {...props} className="text-2xl md:text-3xl font-black text-primary mb-10 tracking-tight uppercase" />,
|
||||
ul: (props: any) => <ul {...props} className="list-none pl-0 mb-10" />,
|
||||
section: (props: any) => <div {...props} className="block" />,
|
||||
li: (props: any) => (
|
||||
<li className="flex items-start gap-4 group mb-4 last:mb-0">
|
||||
<div className="mt-2.5 w-2 h-2 rounded-full bg-accent flex-shrink-0 group-hover:scale-125 transition-transform" />
|
||||
<span {...props} className="text-lg md:text-xl text-text-secondary leading-relaxed font-medium" />
|
||||
</li>
|
||||
),
|
||||
strong: (props: any) => <strong {...props} className="font-black text-primary" />,
|
||||
table: (props: any) => (
|
||||
<div className="overflow-x-auto my-16 rounded-3xl border border-neutral-dark/10 shadow-xl bg-white p-1">
|
||||
<div className="overflow-x-auto my-20 rounded-[32px] 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" />,
|
||||
th: (props: any) => <th {...props} className="px-8 py-6 bg-neutral-light/50 text-left text-[10px] 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 font-medium" />,
|
||||
hr: () => <hr className="my-24 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 className="my-20 p-10 md:p-16 bg-primary-dark rounded-[40px] relative overflow-hidden group">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 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-4xl text-white/90 leading-relaxed font-black tracking-tight" {...props} />
|
||||
</div>
|
||||
),
|
||||
};
|
||||
@@ -105,13 +133,16 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
|
||||
// Filter products for this category
|
||||
const filteredProducts = allProducts.filter(p =>
|
||||
p.frontmatter.categories.some(cat => cat.toLowerCase().replace(/\s+/g, '-') === productSlug)
|
||||
p.frontmatter.categories.some(cat =>
|
||||
cat.toLowerCase().replace(/\s+/g, '-') === productSlug ||
|
||||
cat === categoryTitle
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<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">
|
||||
<Container className="relative z-10">
|
||||
<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>
|
||||
@@ -126,7 +157,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<Section className="bg-neutral-light relative z-20 -mt-12 rounded-t-[48px] md:rounded-t-[64px]">
|
||||
<Section className="bg-neutral-light relative">
|
||||
<Container>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{filteredProducts.map((product) => (
|
||||
@@ -187,55 +218,87 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const datasheetPath = getDatasheetPath(productSlug, locale);
|
||||
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`);
|
||||
|
||||
const sidebar = (
|
||||
<ProductSidebar
|
||||
productName={product.frontmatter.title}
|
||||
productImage={product.frontmatter.images?.[0]}
|
||||
datasheetPath={datasheetPath}
|
||||
/>
|
||||
);
|
||||
|
||||
const productComponents = {
|
||||
...components,
|
||||
ProductTabs: (props: any) => <ProductTabs {...props} sidebar={sidebar} />,
|
||||
};
|
||||
|
||||
// Pre-process content to convert raw HTML tags to Markdown so they use our custom components
|
||||
const processedContent = product.content
|
||||
.replace(/<h2[^>]*>(.*?)<\/h2>/g, '\n## $1\n')
|
||||
.replace(/<h3[^>]*>(.*?)<\/h3>/g, '\n### $1\n')
|
||||
.replace(/<p[^>]*>(.*?)<\/p>/g, '\n$1\n')
|
||||
.replace(/<ul[^>]*>(.*?)<\/ul>/gs, '\n$1\n')
|
||||
.replace(/<li[^>]*>(.*?)<\/li>/g, '\n- $1\n')
|
||||
.replace(/<strong[^>]*>(.*?)<\/strong>/g, '**$1**')
|
||||
.replace(/<section[^>]*>/g, '')
|
||||
.replace(/<\/section>/g, '');
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-neutral-light">
|
||||
<div className="flex flex-col min-h-screen bg-white relative">
|
||||
{/* Product Hero */}
|
||||
<section className="relative pt-40 pb-32 overflow-hidden bg-primary-dark">
|
||||
<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">
|
||||
<section className="relative pt-40 pb-24 overflow-hidden bg-primary-dark">
|
||||
{/* Background Decorative Elements */}
|
||||
<div className="absolute top-0 right-0 w-1/2 h-full bg-gradient-to-l from-accent/5 to-transparent pointer-events-none" />
|
||||
<div className="absolute -top-24 -right-24 w-96 h-96 bg-accent/10 rounded-full blur-3xl pointer-events-none" />
|
||||
|
||||
<Container className="relative z-10">
|
||||
<div className="max-w-4xl animate-slide-up">
|
||||
<nav className="flex items-center mb-12 text-white/40 text-[10px] font-black uppercase tracking-[0.2em]">
|
||||
<Link href={`/${locale}/products`} className="hover:text-accent transition-colors">{t('title')}</Link>
|
||||
<span className="mx-3 opacity-30">/</span>
|
||||
<span className="mx-4 opacity-20">/</span>
|
||||
<Link href={`/${locale}/products/${categorySlug}`} className="hover:text-accent transition-colors">{categoryTitle}</Link>
|
||||
<span className="mx-3 opacity-30">/</span>
|
||||
<span className="mx-4 opacity-20">/</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 flex-col lg:flex-row lg:items-center 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" />
|
||||
<div className="mb-8 inline-flex items-center px-4 py-2 rounded-full bg-accent/10 border border-accent/20 text-accent text-[10px] font-black uppercase tracking-[0.2em] backdrop-blur-md">
|
||||
<span className="w-2 h-2 rounded-full bg-accent mr-3 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">
|
||||
<Badge key={idx} variant="accent" className="bg-white/5 text-white/80 border-white/10 backdrop-blur-md px-5 py-2 text-[10px] font-black tracking-[0.15em]">
|
||||
{cat}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<h1 className="text-5xl md:text-7xl lg:text-8xl font-extrabold text-white mb-0 tracking-tight leading-[1.05]">
|
||||
<h1 className="text-6xl md:text-8xl lg:text-9xl font-black text-white mb-8 tracking-tighter leading-[0.9] uppercase">
|
||||
{product.frontmatter.title}
|
||||
</h1>
|
||||
<p className="text-xl md:text-2xl text-white/60 max-w-2xl leading-relaxed font-medium">
|
||||
{product.frontmatter.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<Section className="bg-neutral-light relative z-20 -mt-16 pt-0">
|
||||
<Container>
|
||||
<Section className="bg-white relative">
|
||||
<Container className="relative">
|
||||
{/* 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 -mt-32 mb-32 animate-slide-up" style={{ animationDelay: '200ms' }}>
|
||||
<div className="bg-white shadow-[0_32px_64px_-12px_rgba(0,0,0,0.1)] rounded-[48px] border border-neutral-dark/5 overflow-hidden p-12 md:p-20 lg:p-24">
|
||||
<div className="relative w-full aspect-[21/9]">
|
||||
<Image
|
||||
src={product.frontmatter.images[0]}
|
||||
@@ -249,10 +312,10 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
</div>
|
||||
|
||||
{product.frontmatter.images.length > 1 && (
|
||||
<div className="flex justify-center gap-6 mt-16">
|
||||
<div className="flex justify-center gap-8 mt-20">
|
||||
{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 key={idx} className="relative w-24 h-24 md:w-32 md:h-32 border-2 border-neutral-dark/5 rounded-3xl overflow-hidden bg-neutral-light/30 hover:border-accent transition-all duration-500 cursor-pointer group p-4">
|
||||
<Image src={img} alt="" fill className="object-contain p-4 transition-transform duration-700 group-hover:scale-110" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -261,11 +324,11 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
</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">
|
||||
<div className="relative">
|
||||
<div className="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 className="max-w-none">
|
||||
<MDXRemote source={processedContent} components={productComponents} />
|
||||
</div>
|
||||
|
||||
{/* Structured Data */}
|
||||
@@ -292,43 +355,16 @@ export default async function ProductPage({ params }: ProductPageProps) {
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Related Products */}
|
||||
<RelatedProducts
|
||||
currentSlug={productSlug}
|
||||
categories={product.frontmatter.categories}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
)}
|
||||
</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>
|
||||
{/* Related Products Section */}
|
||||
<div className="mt-16 pt-16 border-t border-neutral-dark/5">
|
||||
<RelatedProducts
|
||||
currentSlug={productSlug}
|
||||
categories={product.frontmatter.categories}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
</Section>
|
||||
@@ -1,135 +0,0 @@
|
||||
import { ImageResponse } from 'next/og';
|
||||
import { getProductBySlug } from '@/lib/mdx';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export default async function Image({ params: { locale, slug } }: { params: { locale: string, slug: string } }) {
|
||||
const productSlug = slug;
|
||||
const product = await getProductBySlug(productSlug, locale);
|
||||
|
||||
if (!product) {
|
||||
return new ImageResponse(
|
||||
<div style={{ display: 'flex', width: '100%', height: '100%', backgroundColor: '#001a4d' }} />
|
||||
);
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: 'white',
|
||||
padding: '60px',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{/* Left Side: Content */}
|
||||
<div style={{ display: 'flex', flexDirection: 'column', width: '55%' }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'bold',
|
||||
color: '#001a4d',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.2em',
|
||||
marginBottom: '20px',
|
||||
opacity: 0.6,
|
||||
}}
|
||||
>
|
||||
KLZ Cables | {product.frontmatter.categories[0]}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '72px',
|
||||
fontWeight: '900',
|
||||
color: '#001a4d',
|
||||
lineHeight: '1.1',
|
||||
marginBottom: '30px',
|
||||
}}
|
||||
>
|
||||
{product.frontmatter.title}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '28px',
|
||||
color: '#4a5568',
|
||||
lineHeight: '1.4',
|
||||
}}
|
||||
>
|
||||
{product.frontmatter.description}
|
||||
</div>
|
||||
|
||||
{/* Accent Line */}
|
||||
<div
|
||||
style={{
|
||||
marginTop: '40px',
|
||||
width: '100px',
|
||||
height: '8px',
|
||||
backgroundColor: '#00ff99',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right Side: Product Image */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: '40%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{product.frontmatter.images?.[0] && (
|
||||
<img
|
||||
src={product.frontmatter.images[0].startsWith('http') ? product.frontmatter.images[0] : `https://klz-cables.com${product.frontmatter.images[0]}`}
|
||||
alt=""
|
||||
style={{
|
||||
maxWidth: '100%',
|
||||
maxHeight: '80%',
|
||||
objectFit: 'contain',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{/* Subtle shadow under product */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '10%',
|
||||
width: '60%',
|
||||
height: '20px',
|
||||
backgroundColor: 'rgba(0,0,0,0.05)',
|
||||
borderRadius: '50%',
|
||||
filter: 'blur(10px)',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Branding Corner */}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '40px',
|
||||
right: '60px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: '24px', fontWeight: 'bold', color: '#001a4d' }}>KLZ</div>
|
||||
<div style={{ fontSize: '24px', fontWeight: 'normal', color: '#00ff99', marginLeft: '4px' }}>Cables</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Section, Container, Card, Badge, Button } from '@/components/ui';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import Reveal from '@/components/Reveal';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import { Badge, Button, Card, Container, Section } from '@/components/ui';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface ProductsPageProps {
|
||||
params: {
|
||||
@@ -11,6 +13,32 @@ interface ProductsPageProps {
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: ProductsPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Products' });
|
||||
return {
|
||||
title: t('title'),
|
||||
description: t('subtitle'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/products`,
|
||||
languages: {
|
||||
'de': '/de/products',
|
||||
'en': '/en/products',
|
||||
'x-default': '/en/products',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
url: `https://klz-cables.com/${locale}/products`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('title')} | KLZ Cables`,
|
||||
description: t('subtitle'),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function ProductsPage({ params }: ProductsPageProps) {
|
||||
const t = useTranslations('Products');
|
||||
|
||||
@@ -51,7 +79,7 @@ export default function ProductsPage({ params }: ProductsPageProps) {
|
||||
<section className="relative min-h-[50vh] md:min-h-[70vh] flex items-center pt-32 pb-20 md:pt-40 md:pb-32 overflow-hidden bg-primary-dark">
|
||||
<Container className="relative z-10">
|
||||
<div className="max-w-4xl animate-slide-up">
|
||||
<Badge variant="accent" className="mb-4 md:mb-8 bg-white/10 text-white border border-white/20 backdrop-blur-md px-3 py-1 md:px-4 md:py-1.5">
|
||||
<Badge variant="saturated" className="mb-4 md:mb-8 shadow-lg px-3 py-1 md:px-4 md:py-1.5">
|
||||
{t('heroSubtitle')}
|
||||
</Badge>
|
||||
<h1 className="text-4xl md:text-7xl lg:text-8xl font-extrabold text-white mb-4 md:mb-8 tracking-tight leading-[1.05]">
|
||||
@@ -77,7 +105,7 @@ export default function ProductsPage({ params }: ProductsPageProps) {
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
<Section id="categories" className="bg-neutral-light relative z-20 -mt-8 md:-mt-16 rounded-t-[32px] md:rounded-t-[64px] pt-12 md:pt-24">
|
||||
<Section id="categories" className="bg-neutral-light relative">
|
||||
<Container>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8 lg:gap-12">
|
||||
{categories.map((category, idx) => (
|
||||
@@ -112,11 +140,11 @@ export default function ProductsPage({ params }: ProductsPageProps) {
|
||||
<p className="text-text-secondary text-sm md:text-lg leading-relaxed mb-4 md:mb-8 line-clamp-2 md:line-clamp-none">
|
||||
{category.desc}
|
||||
</p>
|
||||
<div className="flex items-center text-primary font-bold text-base md: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">
|
||||
<div className="flex items-center text-saturated font-bold text-base md:text-lg group-hover:text-accent-dark transition-colors">
|
||||
<span className="border-b-2 border-saturated/10 group-hover:border-accent-dark transition-colors pb-1">
|
||||
{t('viewProducts')}
|
||||
</span>
|
||||
<div className="ml-3 md:ml-4 w-8 h-8 md:w-10 md: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">
|
||||
<div className="ml-3 md:ml-4 w-8 h-8 md:w-10 md:h-10 rounded-full bg-primary-light flex items-center justify-center text-saturated group-hover:bg-accent group-hover:text-primary-dark transition-all duration-300 shadow-sm">
|
||||
<svg className="w-4 h-4 md:w-5 md: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>
|
||||
|
||||
@@ -1,8 +1,42 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { Metadata } from 'next';
|
||||
import { Section, Container, Heading, Badge, Button } from '@/components/ui';
|
||||
import Image from 'next/image';
|
||||
import Reveal from '@/components/Reveal';
|
||||
|
||||
interface TeamPageProps {
|
||||
params: {
|
||||
locale: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params: { locale } }: TeamPageProps): Promise<Metadata> {
|
||||
const t = await getTranslations({ locale, namespace: 'Team' });
|
||||
return {
|
||||
title: t('hero.subtitle'),
|
||||
description: t('hero.title'),
|
||||
alternates: {
|
||||
canonical: `/${locale}/team`,
|
||||
languages: {
|
||||
'de': '/de/team',
|
||||
'en': '/en/team',
|
||||
'x-default': '/en/team',
|
||||
},
|
||||
},
|
||||
openGraph: {
|
||||
title: `${t('hero.subtitle')} | KLZ Cables`,
|
||||
description: t('hero.title'),
|
||||
url: `https://klz-cables.com/${locale}/team`,
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: `${t('hero.subtitle')} | KLZ Cables`,
|
||||
description: t('hero.title'),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default function TeamPage() {
|
||||
const t = useTranslations('Team');
|
||||
|
||||
@@ -22,7 +56,7 @@ export default function TeamPage() {
|
||||
</div>
|
||||
|
||||
<Container className="relative z-10 text-center text-white max-w-5xl animate-slide-up">
|
||||
<Badge variant="accent" className="mb-4 md:mb-8 shadow-lg">{t('hero.badge')}</Badge>
|
||||
<Badge variant="saturated" className="mb-4 md:mb-8 shadow-lg">{t('hero.badge')}</Badge>
|
||||
<h1 className="text-3xl md:text-7xl lg:text-8xl font-extrabold tracking-tight leading-[1.1] mb-4 md:mb-8">
|
||||
{t('hero.subtitle')}
|
||||
</h1>
|
||||
@@ -45,7 +79,7 @@ export default function TeamPage() {
|
||||
<div className="relative mb-6 md:mb-12">
|
||||
<div className="absolute -left-4 md:-left-8 top-0 bottom-0 w-1 md:w-1.5 bg-accent rounded-full" />
|
||||
<p className="text-lg md:text-3xl font-bold italic leading-relaxed pl-5 md:pl-8 text-white/90">
|
||||
"{t('michael.quote')}"
|
||||
{t('michael.quote')}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-base md:text-xl leading-relaxed text-white/70 mb-6 md:mb-12 max-w-xl">
|
||||
@@ -129,17 +163,17 @@ export default function TeamPage() {
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-white/60 lg:bg-gradient-to-l lg:from-primary-dark/20 to-transparent" />
|
||||
</div>
|
||||
<Reveal className="w-full lg:w-1/2 p-6 md:p-24 lg:p-32 flex flex-col justify-center bg-neutral-light text-primary relative order-2">
|
||||
<div className="absolute top-0 left-0 w-32 h-full bg-primary/5 skew-x-12 -translate-x-1/2" />
|
||||
<Reveal className="w-full lg:w-1/2 p-6 md:p-24 lg:p-32 flex flex-col justify-center bg-neutral-light text-saturated relative order-2">
|
||||
<div className="absolute top-0 left-0 w-32 h-full bg-saturated/5 skew-x-12 -translate-x-1/2" />
|
||||
<div className="relative z-10">
|
||||
<Badge variant="primary" className="mb-4 md:mb-8">{t('klaus.role')}</Badge>
|
||||
<Heading level={2} className="text-primary mb-6 md:mb-10 text-3xl md:text-6xl">
|
||||
<Badge variant="saturated" className="mb-4 md:mb-8">{t('klaus.role')}</Badge>
|
||||
<Heading level={2} className="text-saturated mb-6 md:mb-10 text-3xl md:text-6xl">
|
||||
{t('klaus.name')}
|
||||
</Heading>
|
||||
<div className="relative mb-6 md:mb-12">
|
||||
<div className="absolute -left-4 md:-left-8 top-0 bottom-0 w-1 md:w-1.5 bg-primary rounded-full" />
|
||||
<div className="absolute -left-4 md:-left-8 top-0 bottom-0 w-1 md:w-1.5 bg-saturated rounded-full" />
|
||||
<p className="text-lg md:text-3xl font-bold italic leading-relaxed pl-5 md:pl-8 text-text-secondary">
|
||||
"{t('klaus.quote')}"
|
||||
{t('klaus.quote')}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-base md:text-xl leading-relaxed text-text-secondary mb-6 md:mb-12 max-w-xl">
|
||||
@@ -147,7 +181,7 @@ export default function TeamPage() {
|
||||
</p>
|
||||
<Button
|
||||
href="https://www.linkedin.com/in/klaus-mintel-b80a8b193/"
|
||||
variant="primary"
|
||||
variant="saturated"
|
||||
size="lg"
|
||||
className="group w-full md:w-auto md:h-16 md:px-10 md:text-xl active:scale-95 transition-transform"
|
||||
>
|
||||
|
||||
25
app/manifest.ts
Normal file
25
app/manifest.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: 'KLZ Cables',
|
||||
short_name: 'KLZ',
|
||||
description: 'Premium Cable Solutions',
|
||||
start_url: '/',
|
||||
display: 'standalone',
|
||||
background_color: '#001a4d',
|
||||
theme_color: '#001a4d',
|
||||
icons: [
|
||||
{
|
||||
src: '/favicon.ico',
|
||||
sizes: 'any',
|
||||
type: 'image/x-icon',
|
||||
},
|
||||
{
|
||||
src: '/apple-touch-icon.png',
|
||||
sizes: '180x180',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
12
app/robots.ts
Normal file
12
app/robots.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: '*',
|
||||
allow: '/',
|
||||
disallow: ['/api/', '/health/'],
|
||||
},
|
||||
sitemap: 'https://klz-cables.com/sitemap.xml',
|
||||
};
|
||||
}
|
||||
74
app/sitemap.ts
Normal file
74
app/sitemap.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
import { getAllProducts } from '@/lib/mdx';
|
||||
import { getAllPosts } from '@/lib/blog';
|
||||
import { getAllPages } from '@/lib/pages';
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const baseUrl = 'https://klz-cables.com';
|
||||
const locales = ['de', 'en'];
|
||||
|
||||
const routes = [
|
||||
'',
|
||||
'/blog',
|
||||
'/contact',
|
||||
'/team',
|
||||
'/products',
|
||||
'/products/low-voltage-cables',
|
||||
'/products/medium-voltage-cables',
|
||||
'/products/high-voltage-cables',
|
||||
'/products/solar-cables',
|
||||
];
|
||||
|
||||
const sitemapEntries: MetadataRoute.Sitemap = [];
|
||||
|
||||
for (const locale of locales) {
|
||||
// Static routes
|
||||
for (const route of routes) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}${route}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: route === '' ? 'daily' : 'weekly',
|
||||
priority: route === '' ? 1 : 0.8,
|
||||
});
|
||||
}
|
||||
|
||||
// Products
|
||||
const products = await getAllProducts(locale);
|
||||
for (const product of products) {
|
||||
// We need to find the category for the product to build the URL
|
||||
// In this project, products are under /products/[category]/[slug]
|
||||
// The category is in product.frontmatter.categories
|
||||
const category = product.frontmatter.categories[0]?.toLowerCase().replace(/\s+/g, '-') || 'other';
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/products/${category}/${product.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.7,
|
||||
});
|
||||
}
|
||||
|
||||
// Blog posts
|
||||
const posts = await getAllPosts(locale);
|
||||
for (const post of posts) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/blog/${post.slug}`,
|
||||
lastModified: new Date(post.frontmatter.date),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.6,
|
||||
});
|
||||
}
|
||||
|
||||
// Static pages
|
||||
const pages = await getAllPages(locale);
|
||||
for (const page of pages) {
|
||||
sitemapEntries.push({
|
||||
url: `${baseUrl}/${locale}/${page.slug}`,
|
||||
lastModified: new Date(),
|
||||
changeFrequency: 'monthly',
|
||||
priority: 0.5,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sitemapEntries;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export default function Footer() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-primary-dark text-white py-24 relative overflow-hidden">
|
||||
<footer className="bg-primary text-white py-24 relative overflow-hidden">
|
||||
<div className="absolute top-0 left-0 w-full h-px bg-gradient-to-r from-transparent via-white/20 to-transparent" />
|
||||
|
||||
<Container>
|
||||
|
||||
@@ -147,7 +147,7 @@ export default function Header() {
|
||||
|
||||
{/* Mobile Menu Overlay */}
|
||||
<div className={cn(
|
||||
"fixed inset-0 bg-primary-dark z-40 lg:hidden transition-all duration-500 ease-in-out flex flex-col",
|
||||
"fixed inset-0 bg-primary z-40 lg:hidden transition-all duration-500 ease-in-out flex flex-col",
|
||||
isMobileMenuOpen ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-full pointer-events-none"
|
||||
)}>
|
||||
<div className="flex-grow flex flex-col justify-center items-center p-8 space-y-8">
|
||||
|
||||
97
components/ProductSidebar.tsx
Normal file
97
components/ProductSidebar.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import RequestQuoteForm from '@/components/RequestQuoteForm';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import { cn } from '@/components/ui/utils';
|
||||
|
||||
interface ProductSidebarProps {
|
||||
productName: string;
|
||||
productImage?: string;
|
||||
datasheetPath?: string | null;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function ProductSidebar({ productName, productImage, datasheetPath, className }: ProductSidebarProps) {
|
||||
const t = useTranslations('Products');
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-4 animate-slight-fade-in-from-bottom", className)}>
|
||||
{/* Request Quote Form Card */}
|
||||
<div className="bg-white rounded-3xl border border-neutral-medium shadow-sm transition-all duration-500 hover:shadow-2xl hover:-translate-y-1 overflow-hidden group/card">
|
||||
<div className="bg-primary p-6 text-white relative overflow-hidden">
|
||||
{/* Background Accent - Saturated Blue Glow */}
|
||||
<div className="absolute top-0 right-0 w-40 h-40 bg-saturated/30 rounded-full -translate-y-1/2 translate-x-1/2 blur-[80px] pointer-events-none" />
|
||||
|
||||
{/* Product Thumbnail with Reflection */}
|
||||
{productImage && (
|
||||
<div className="relative w-full aspect-[16/10] mb-6 rounded-2xl overflow-hidden bg-white/5 backdrop-blur-md p-4 border border-white/10 z-10 group">
|
||||
<div className="relative w-full h-full transition-transform duration-1000 ease-out group-hover:scale-105">
|
||||
<Image
|
||||
src={productImage}
|
||||
alt={productName}
|
||||
fill
|
||||
className="object-contain p-2 drop-shadow-[0_20px_30px_rgba(0,0,0,0.4)]"
|
||||
/>
|
||||
{/* Subtle Reflection Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-white/20 via-transparent to-transparent opacity-30 pointer-events-none" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="inline-block relative mb-2">
|
||||
<h3 className="text-xl font-heading font-black m-0 tracking-tighter uppercase leading-none">
|
||||
{t('requestQuote')}
|
||||
</h3>
|
||||
<Scribble
|
||||
variant="underline"
|
||||
className="w-full h-3 -bottom-3 left-0 text-accent/80"
|
||||
color="var(--color-accent)"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-white/60 text-xs m-0 mt-2 leading-relaxed font-medium max-w-[90%]">
|
||||
{t('requestQuoteDesc')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6 bg-neutral-light/50">
|
||||
<RequestQuoteForm productName={productName} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Datasheet Download */}
|
||||
{datasheetPath && (
|
||||
<a
|
||||
href={datasheetPath}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block bg-white rounded-2xl border border-neutral-medium overflow-hidden group transition-all duration-500 hover:shadow-xl hover:border-saturated/30 hover:-translate-y-0.5"
|
||||
>
|
||||
<div className="p-4 flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-neutral-medium/20 flex items-center justify-center flex-shrink-0 group-hover:bg-saturated group-hover:text-white transition-all duration-500 text-saturated border border-transparent group-hover:border-white/20">
|
||||
<svg className="w-6 h-6 transition-transform duration-500 group-hover:scale-110" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="text-sm font-heading font-black text-neutral-dark m-0 uppercase tracking-tighter leading-tight group-hover:text-saturated transition-colors duration-300">
|
||||
{t('downloadDatasheet')}
|
||||
</h3>
|
||||
<p className="text-text-secondary text-[10px] m-0 mt-0.5 font-semibold leading-tight truncate uppercase tracking-widest opacity-60">
|
||||
{t('downloadDatasheetDesc')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="text-neutral-dark/20 group-hover:text-saturated transition-all duration-500 transform group-hover:translate-x-1">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2.5} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,18 +3,37 @@ import React from 'react';
|
||||
interface ProductTabsProps {
|
||||
children: React.ReactNode;
|
||||
technicalData?: React.ReactNode;
|
||||
sidebar?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function ProductTabs({ children, technicalData }: ProductTabsProps) {
|
||||
export default function ProductTabs({ children, technicalData, sidebar }: ProductTabsProps) {
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
<div className="prose max-w-none">
|
||||
{children}
|
||||
<div className="space-y-24">
|
||||
<div className="flex flex-col lg:flex-row gap-12 items-start">
|
||||
<div className="flex-1 min-w-0">
|
||||
{sidebar && (
|
||||
<div className="lg:hidden mb-12">
|
||||
{sidebar}
|
||||
</div>
|
||||
)}
|
||||
<div className="max-w-none">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{sidebar && (
|
||||
<div className="hidden lg:block sticky top-32 w-[350px] xl:w-[400px] flex-shrink-0">
|
||||
{sidebar}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{technicalData && (
|
||||
<div className="pt-8 border-t border-neutral-dark">
|
||||
<h2 className="text-2xl font-bold text-primary-dark mb-6">Technical Specifications</h2>
|
||||
<div className="pt-24 border-t-2 border-neutral-dark/5">
|
||||
<div className="mb-16">
|
||||
<h2 className="text-4xl md:text-5xl font-black text-primary tracking-tighter uppercase mb-4">Technical Specifications</h2>
|
||||
<div className="h-1.5 w-24 bg-accent rounded-full" />
|
||||
</div>
|
||||
<div className="not-prose">
|
||||
{technicalData}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
|
||||
interface KeyValueItem {
|
||||
label: string;
|
||||
@@ -21,20 +24,33 @@ interface ProductTechnicalDataProps {
|
||||
}
|
||||
|
||||
export default function ProductTechnicalData({ data }: ProductTechnicalDataProps) {
|
||||
const t = useTranslations('Products');
|
||||
const [expandedTables, setExpandedTables] = useState<Record<number, boolean>>({});
|
||||
|
||||
if (!data) return null;
|
||||
const { technicalItems = [], voltageTables = [] } = data;
|
||||
|
||||
const toggleTable = (idx: number) => {
|
||||
setExpandedTables(prev => ({
|
||||
...prev,
|
||||
[idx]: !prev[idx]
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-16">
|
||||
{technicalItems.length > 0 && (
|
||||
<div className="bg-neutral-light p-6 rounded-lg shadow-sm">
|
||||
<h3 className="text-xl font-semibold mb-4">General Data</h3>
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-4">
|
||||
<div className="bg-white p-8 md:p-12 rounded-[32px] shadow-sm border border-neutral-dark/5">
|
||||
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
|
||||
<div className="w-2 h-8 bg-accent rounded-full" />
|
||||
General Data
|
||||
</h3>
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-12 gap-y-8">
|
||||
{technicalItems.map((item, idx) => (
|
||||
<div key={idx} className="flex flex-col border-b border-neutral-dark pb-2 last:border-0">
|
||||
<dt className="text-sm text-text-secondary">{item.label}</dt>
|
||||
<dd className="text-base font-medium text-text-primary">
|
||||
{item.value} {item.unit && <span className="text-sm text-text-secondary ml-1">{item.unit}</span>}
|
||||
<div key={idx} className="flex flex-col group">
|
||||
<dt className="text-sm font-bold uppercase tracking-widest text-primary/40 mb-2 group-hover:text-accent transition-colors">{item.label}</dt>
|
||||
<dd className="text-lg font-semibold text-text-primary">
|
||||
{item.value} {item.unit && <span className="text-sm font-normal text-text-secondary ml-1">{item.unit}</span>}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
@@ -42,57 +58,84 @@ export default function ProductTechnicalData({ data }: ProductTechnicalDataProps
|
||||
</div>
|
||||
)}
|
||||
|
||||
{voltageTables.map((table, idx) => (
|
||||
<div key={idx} className="bg-neutral-light p-6 rounded-lg shadow-sm overflow-hidden">
|
||||
<h3 className="text-xl font-semibold mb-4">
|
||||
{table.voltageLabel !== 'Voltage unknown' && table.voltageLabel !== 'Spannung unbekannt'
|
||||
? table.voltageLabel
|
||||
: 'Technical Specifications'}
|
||||
</h3>
|
||||
|
||||
{table.metaItems.length > 0 && (
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-6 bg-neutral p-4 rounded">
|
||||
{table.metaItems.map((item, mIdx) => (
|
||||
<div key={mIdx}>
|
||||
<dt className="text-xs text-text-secondary uppercase tracking-wider">{item.label}</dt>
|
||||
<dd className="font-medium">{item.value} {item.unit}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-full divide-y divide-neutral-dark">
|
||||
<thead className="bg-neutral">
|
||||
<tr>
|
||||
<th scope="col" className="px-3 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider sticky left-0 bg-neutral z-10">
|
||||
Configuration
|
||||
</th>
|
||||
{table.columns.map((col, cIdx) => (
|
||||
<th key={cIdx} scope="col" className="px-3 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider whitespace-nowrap">
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-neutral-dark">
|
||||
{table.rows.map((row, rIdx) => (
|
||||
<tr key={rIdx} className="hover:bg-neutral-light transition-colors">
|
||||
<td className="px-3 py-3 text-sm font-medium text-text-primary sticky left-0 bg-white z-10 whitespace-nowrap">
|
||||
{row.configuration}
|
||||
</td>
|
||||
{row.cells.map((cell, cellIdx) => (
|
||||
<td key={cellIdx} className="px-3 py-3 text-sm text-text-secondary whitespace-nowrap">
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
{voltageTables.map((table, idx) => {
|
||||
const isExpanded = expandedTables[idx];
|
||||
const hasManyRows = table.rows.length > 10;
|
||||
|
||||
return (
|
||||
<div key={idx} className="bg-white p-8 md:p-12 rounded-[32px] shadow-sm border border-neutral-dark/5 overflow-hidden">
|
||||
<h3 className="text-2xl font-bold text-primary mb-8 flex items-center gap-3">
|
||||
<div className="w-2 h-8 bg-accent rounded-full" />
|
||||
{table.voltageLabel !== 'Voltage unknown' && table.voltageLabel !== 'Spannung unbekannt'
|
||||
? table.voltageLabel
|
||||
: 'Technical Specifications'}
|
||||
</h3>
|
||||
|
||||
{table.metaItems.length > 0 && (
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8 mb-12 bg-neutral-light/50 p-8 rounded-2xl border border-neutral-dark/5">
|
||||
{table.metaItems.map((item, mIdx) => (
|
||||
<div key={mIdx}>
|
||||
<dt className="text-[10px] font-black uppercase tracking-[0.2em] text-primary/40 mb-1">{item.label}</dt>
|
||||
<dd className="font-bold text-primary">{item.value} {item.unit}</dd>
|
||||
</div>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</dl>
|
||||
)}
|
||||
|
||||
<div className="relative">
|
||||
<div
|
||||
className={`overflow-x-auto -mx-8 md:-mx-12 px-8 md:px-12 transition-all duration-500 ease-in-out ${
|
||||
!isExpanded && hasManyRows ? 'max-h-[400px] overflow-y-hidden' : 'max-h-[none]'
|
||||
}`}
|
||||
>
|
||||
<table className="min-w-full border-separate border-spacing-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] sticky left-0 bg-white z-10 border-b border-neutral-dark/10">
|
||||
Config.
|
||||
</th>
|
||||
{table.columns.map((col, cIdx) => (
|
||||
<th key={cIdx} scope="col" className="px-3 py-3 text-left text-[10px] font-black text-primary/40 uppercase tracking-[0.2em] whitespace-nowrap border-b border-neutral-dark/10">
|
||||
{col.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-neutral-dark/5">
|
||||
{table.rows.map((row, rIdx) => (
|
||||
<tr key={rIdx} className="hover:bg-neutral-light/50 transition-colors group">
|
||||
<td className="px-3 py-2 text-xs font-bold text-primary sticky left-0 bg-white group-hover:bg-neutral-light/50 z-10 whitespace-nowrap">
|
||||
{row.configuration}
|
||||
</td>
|
||||
{row.cells.map((cell, cellIdx) => (
|
||||
<td key={cellIdx} className="px-3 py-2 text-xs text-text-secondary whitespace-nowrap">
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{!isExpanded && hasManyRows && (
|
||||
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-white via-white/80 to-transparent z-20 pointer-events-none" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasManyRows && (
|
||||
<div className="mt-8 flex justify-center">
|
||||
<button
|
||||
onClick={() => toggleTable(idx)}
|
||||
className="px-8 py-3 rounded-full bg-primary text-white text-sm font-bold uppercase tracking-widest hover:bg-accent hover:text-primary transition-all duration-300 shadow-lg hover:shadow-accent/20"
|
||||
>
|
||||
{isExpanded ? t('showLess') : t('showMore')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { getAllProducts } from '@/lib/mdx';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface RelatedProductsProps {
|
||||
currentSlug: string;
|
||||
@@ -24,7 +24,7 @@ export default async function RelatedProducts({ currentSlug, categories, locale
|
||||
if (related.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="mt-32 pt-32 border-t border-neutral-dark/10">
|
||||
<div className="">
|
||||
<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">
|
||||
@@ -37,7 +37,14 @@ export default async function RelatedProducts({ currentSlug, categories, locale
|
||||
<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, '-');
|
||||
const categorySlugs = ['low-voltage-cables', 'medium-voltage-cables', 'high-voltage-cables', 'solar-cables'];
|
||||
const catSlug = categorySlugs.find(slug => {
|
||||
const key = slug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
||||
const title = t(`categories.${key}.title`);
|
||||
return product.frontmatter.categories.some(cat =>
|
||||
cat.toLowerCase().replace(/\s+/g, '-') === slug || cat === title
|
||||
);
|
||||
}) || 'low-voltage-cables';
|
||||
|
||||
return (
|
||||
<Link
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Input, Textarea, Button } from '@/components/ui';
|
||||
|
||||
interface RequestQuoteFormProps {
|
||||
productName: string;
|
||||
@@ -30,19 +31,19 @@ export default function RequestQuoteForm({ productName }: RequestQuoteFormProps)
|
||||
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<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">
|
||||
<div className="bg-accent/5 border border-accent/20 text-primary-dark p-4 rounded-xl text-center animate-fade-in !mt-0">
|
||||
<div className="w-10 h-10 bg-accent rounded-full flex items-center justify-center mx-auto mb-3 shadow-lg shadow-accent/20">
|
||||
<svg className="w-5 h-5 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">
|
||||
<h3 className="text-base font-extrabold mb-1 tracking-tight !mt-0">{t('successTitle')}</h3>
|
||||
<p className="text-text-secondary text-xs leading-tight mb-4 !mt-0">
|
||||
{t('successDesc', { productName })}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setStatus('idle')}
|
||||
className="inline-flex items-center text-[10px] font-bold uppercase tracking-[0.2em] text-primary hover:text-accent transition-colors group"
|
||||
className="inline-flex items-center text-[9px] font-bold uppercase tracking-[0.2em] text-primary hover:text-accent transition-colors group"
|
||||
>
|
||||
<span className="border-b-2 border-primary/10 group-hover:border-accent transition-colors pb-1">
|
||||
{t('sendAnother')}
|
||||
@@ -53,58 +54,58 @@ export default function RequestQuoteForm({ productName }: RequestQuoteFormProps)
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<input
|
||||
<form onSubmit={handleSubmit} className="space-y-3 !mt-0">
|
||||
<div className="space-y-2 !mt-0">
|
||||
<div className="space-y-1 !mt-0">
|
||||
<Input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
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')}
|
||||
className="h-9 text-xs !mt-0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<textarea
|
||||
<div className="space-y-1 !mt-0">
|
||||
<Textarea
|
||||
id="request"
|
||||
required
|
||||
rows={4}
|
||||
rows={3}
|
||||
value={request}
|
||||
onChange={(e) => setRequest(e.target.value)}
|
||||
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')}
|
||||
className="text-xs !mt-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<button
|
||||
<div className="space-y-2 !mt-0">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={status === 'submitting'}
|
||||
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"
|
||||
className="w-full py-2 rounded-lg flex items-center justify-center gap-2 group !mt-0"
|
||||
>
|
||||
{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">
|
||||
<svg className="animate-spin h-3 w-3 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-xs">{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">
|
||||
<span className="text-xs">{t('submit')}</span>
|
||||
<svg className="w-3 h-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>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<p className="text-[8px] text-center text-text-secondary/40 uppercase tracking-[0.15em] font-medium px-2">
|
||||
<p className="text-[7px] text-center text-text-secondary/40 uppercase tracking-[0.15em] font-medium px-2 !mt-1 !mb-0">
|
||||
{t('privacyNote')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { motion, Variants } from 'framer-motion';
|
||||
import { cn } from '@/components/ui';
|
||||
|
||||
interface ScribbleProps {
|
||||
@@ -8,6 +11,18 @@ interface ScribbleProps {
|
||||
}
|
||||
|
||||
export default function Scribble({ variant, className, color = '#82ed20' }: ScribbleProps) {
|
||||
const pathVariants: Variants = {
|
||||
hidden: { pathLength: 0, opacity: 0 },
|
||||
visible: {
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 1.8,
|
||||
ease: "easeInOut",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (variant === 'circle') {
|
||||
return (
|
||||
<svg
|
||||
@@ -16,12 +31,14 @@ export default function Scribble({ variant, className, color = '#82ed20' }: Scri
|
||||
viewBox="0 0 800 350"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<path
|
||||
style={{ animationDuration: '1.8s' }}
|
||||
<motion.path
|
||||
variants={pathVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
transform="matrix(0.9791300296783447,0,0,0.9791300296783447,400,179)"
|
||||
strokeLinejoin="miter"
|
||||
fillOpacity="0"
|
||||
pathLength="1"
|
||||
strokeMiterlimit="4"
|
||||
stroke={color}
|
||||
strokeOpacity="1"
|
||||
@@ -40,11 +57,13 @@ export default function Scribble({ variant, className, color = '#82ed20' }: Scri
|
||||
viewBox="-400 -55 730 60"
|
||||
preserveAspectRatio="none"
|
||||
>
|
||||
<path
|
||||
style={{ animationDuration: '1.8s' }}
|
||||
<motion.path
|
||||
variants={pathVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
d="m -383.25 -6 c 55.25 -22 130.75 -33.5 293.25 -38 c 54.5 -0.5 195 -2.5 401 15"
|
||||
stroke={color}
|
||||
pathLength="1"
|
||||
strokeWidth="20"
|
||||
fill="none"
|
||||
/>
|
||||
|
||||
@@ -47,24 +47,36 @@ export default function AnimatedImage({
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`relative overflow-hidden rounded-xl shadow-lg my-12 ${className}`}
|
||||
className={`relative overflow-hidden rounded-2xl shadow-2xl my-16 group ${className}`}
|
||||
>
|
||||
<div className={`
|
||||
absolute inset-0 bg-primary/10 z-10 pointer-events-none transition-opacity duration-1000
|
||||
${isLoaded && isInView ? 'opacity-0' : 'opacity-100'}
|
||||
`} />
|
||||
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
className={`
|
||||
duration-1000 ease-out w-full h-auto
|
||||
${isLoaded && isInView ? 'scale-100 blur-0 opacity-100' : 'scale-110 blur-xl opacity-0'}
|
||||
duration-[1.5s] ease-out w-full h-auto transition-all
|
||||
${isLoaded && isInView ? 'scale-100 blur-0 opacity-100' : 'scale-110 blur-2xl opacity-0'}
|
||||
group-hover:scale-105
|
||||
`}
|
||||
onLoad={() => setIsLoaded(true)}
|
||||
priority={priority}
|
||||
/>
|
||||
|
||||
{/* Subtle reflection overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-tr from-white/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700 pointer-events-none" />
|
||||
|
||||
{alt && (
|
||||
<p className="text-sm text-text-secondary text-center mt-3 italic px-4 pb-4">
|
||||
{alt}
|
||||
</p>
|
||||
<div className="absolute bottom-0 left-0 right-0 p-6 bg-gradient-to-t from-black/60 to-transparent translate-y-full group-hover:translate-y-0 transition-transform duration-500">
|
||||
<p className="text-sm text-white font-medium italic">
|
||||
{alt}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -46,15 +46,18 @@ export default function ChatBubble({
|
||||
</div>
|
||||
|
||||
<div className={`
|
||||
relative px-6 py-4 rounded-2xl shadow-sm border
|
||||
relative px-8 py-6 rounded-3xl shadow-sm border transition-all duration-300 hover:shadow-md
|
||||
${isRight
|
||||
? 'bg-primary text-white rounded-tr-none border-primary'
|
||||
? 'bg-neutral-dark text-white rounded-tr-none border-neutral-dark'
|
||||
: 'bg-white text-text-primary rounded-tl-none border-neutral-200'
|
||||
}
|
||||
`}>
|
||||
<div className={`prose prose-sm max-w-none ${isRight ? 'prose-invert' : ''}`}>
|
||||
<div className={`prose prose-lg max-w-none ${isRight ? 'prose-invert' : ''}`}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{/* Industrial accent dot */}
|
||||
<div className={`absolute top-4 ${isRight ? 'left-4' : 'right-4'} w-1.5 h-1.5 rounded-full ${isRight ? 'bg-primary' : 'bg-primary/30'}`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
50
components/blog/ComparisonGrid.tsx
Normal file
50
components/blog/ComparisonGrid.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ComparisonGridItem {
|
||||
label: string;
|
||||
leftValue: string;
|
||||
rightValue: string;
|
||||
}
|
||||
|
||||
interface ComparisonGridProps {
|
||||
title?: string;
|
||||
leftLabel: string;
|
||||
rightLabel: string;
|
||||
items: ComparisonGridItem[];
|
||||
}
|
||||
|
||||
export default function ComparisonGrid({ title, leftLabel, rightLabel, items }: ComparisonGridProps) {
|
||||
return (
|
||||
<div className="my-16 not-prose">
|
||||
{title && (
|
||||
<h3 className="text-2xl font-bold text-text-primary mb-8">
|
||||
{title}
|
||||
</h3>
|
||||
)}
|
||||
<div className="border border-neutral-200 rounded-3xl overflow-hidden shadow-sm bg-white">
|
||||
<div className="grid grid-cols-3 bg-neutral-50 border-b border-neutral-200">
|
||||
<div className="p-4 md:p-6"></div>
|
||||
<div className="p-4 md:p-6 text-center font-bold text-primary uppercase tracking-widest border-l border-neutral-200 text-xs md:text-sm">
|
||||
{leftLabel}
|
||||
</div>
|
||||
<div className="p-4 md:p-6 text-center font-bold text-primary uppercase tracking-widest border-l border-neutral-200 text-xs md:text-sm">
|
||||
{rightLabel}
|
||||
</div>
|
||||
</div>
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="grid grid-cols-3 border-b border-neutral-200 last:border-0 hover:bg-neutral-50 transition-colors group">
|
||||
<div className="p-4 md:p-6 font-bold text-text-primary flex items-center text-sm md:text-base">
|
||||
{item.label}
|
||||
</div>
|
||||
<div className="p-4 md:p-6 text-center text-text-secondary border-l border-neutral-200 flex items-center justify-center group-hover:text-text-primary transition-colors text-sm md:text-base">
|
||||
{item.leftValue}
|
||||
</div>
|
||||
<div className="p-4 md:p-6 text-center text-text-secondary border-l border-neutral-200 flex items-center justify-center group-hover:text-text-primary transition-colors text-sm md:text-base">
|
||||
{item.rightValue}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import Scribble from '@/components/Scribble';
|
||||
|
||||
interface HighlightBoxProps {
|
||||
title?: string;
|
||||
@@ -7,21 +8,31 @@ interface HighlightBoxProps {
|
||||
}
|
||||
|
||||
const colorStyles = {
|
||||
primary: 'bg-gradient-to-br from-primary/10 to-primary/5 border-primary/30',
|
||||
secondary: 'bg-gradient-to-br from-blue-50 to-blue-100/50 border-blue-200',
|
||||
accent: 'bg-gradient-to-br from-green-50 to-green-100/50 border-green-200',
|
||||
primary: 'bg-gradient-to-br from-primary/5 to-transparent border-primary/20',
|
||||
secondary: 'bg-gradient-to-br from-blue-50/50 to-transparent border-blue-200/50',
|
||||
accent: 'bg-gradient-to-br from-accent/5 to-transparent border-accent/20',
|
||||
};
|
||||
|
||||
export default function HighlightBox({ title, children, color = 'primary' }: HighlightBoxProps) {
|
||||
return (
|
||||
<div className={`my-10 p-8 rounded-2xl border-2 ${colorStyles[color]} shadow-sm`}>
|
||||
<div className={`my-12 p-8 md:p-10 rounded-3xl border ${colorStyles[color]} shadow-sm relative overflow-hidden group`}>
|
||||
{/* Industrial accent corner */}
|
||||
<div className="absolute top-0 right-0 w-16 h-16 bg-primary/5 -mr-8 -mt-8 rotate-45 transition-transform group-hover:scale-110" />
|
||||
|
||||
{title && (
|
||||
<h3 className="text-2xl font-bold text-text-primary mb-4 flex items-center gap-3">
|
||||
<span className="w-1.5 h-8 bg-primary rounded-full"></span>
|
||||
{title}
|
||||
<h3 className="text-2xl font-bold text-text-primary mb-6 flex items-center gap-4 relative">
|
||||
<span className="relative">
|
||||
{title}
|
||||
{color === 'accent' && (
|
||||
<Scribble
|
||||
variant="underline"
|
||||
className="absolute -bottom-2 left-0 w-full h-3 text-accent/40"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</h3>
|
||||
)}
|
||||
<div className="prose prose-lg max-w-none">
|
||||
<div className="prose prose-lg max-w-none relative z-10">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,47 +9,63 @@ export default function PowerCTA({ locale }: PowerCTAProps) {
|
||||
const isDe = locale === 'de';
|
||||
|
||||
return (
|
||||
<div className="my-12 p-8 md:p-10 bg-white rounded-xl shadow-lg border border-neutral-100 relative overflow-hidden">
|
||||
{/* Decorative background element */}
|
||||
<div className="absolute top-0 right-0 w-32 h-32 bg-primary/5 rounded-bl-full -mr-10 -mt-10" />
|
||||
<div className="my-16 p-10 md:p-16 bg-neutral-dark rounded-[2rem] shadow-2xl relative overflow-hidden group">
|
||||
{/* Industrial background pattern */}
|
||||
<div className="absolute inset-0 opacity-10 pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(#3b82f6_1px,transparent_1px)] [background-size:40px_40px]" />
|
||||
</div>
|
||||
|
||||
{/* Decorative accent */}
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-primary/20 rounded-full blur-3xl -mr-32 -mt-32 transition-transform group-hover:scale-110 duration-1000" />
|
||||
|
||||
<div className="relative z-10">
|
||||
<h3 className="text-2xl font-bold text-text-primary mb-4 flex items-center gap-3">
|
||||
<span className="text-3xl">💡</span>
|
||||
{isDe ? 'Benötigen Sie Strom?' : 'Need power?'}
|
||||
<span className="text-primary">{isDe ? 'Wir sind für Sie da!' : "We've got you covered!"}</span>
|
||||
<div className="inline-block px-4 py-1 bg-accent/20 text-accent text-xs font-bold uppercase tracking-[0.2em] rounded-full mb-8">
|
||||
{isDe ? 'Lösungen' : 'Solutions'}
|
||||
</div>
|
||||
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-white mb-8 leading-tight">
|
||||
{isDe ? 'Bereit für die' : 'Ready for the'}
|
||||
<span className="text-accent block">{isDe ? 'Energiewende?' : 'Energy Transition?'}</span>
|
||||
</h3>
|
||||
|
||||
<p className="text-lg text-text-secondary mb-6 leading-relaxed">
|
||||
<p className="text-xl text-white/70 mb-10 leading-relaxed max-w-2xl">
|
||||
{isDe
|
||||
? 'Von der Planung von Wind- und Solarparks bis zur Lieferung hochwertiger Energiekabel wie NA2XS(F)2Y, NAYY und NA2XY erwecken wir Energienetze zum Leben.'
|
||||
: 'From wind and solar park planning to delivering high-quality energy cables like NA2XS(F)2Y, NAYY, and NA2XY, we bring energy networks to life.'
|
||||
? 'Von der Planung von Wind- und Solarparks bis zur Lieferung hochwertiger Energiekabel erwecken wir Ihre Projekte zum Leben.'
|
||||
: 'From wind and solar park planning to delivering high-quality energy cables, we bring your projects to life.'
|
||||
}
|
||||
</p>
|
||||
|
||||
<ul className="space-y-2 mb-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
|
||||
{[
|
||||
isDe ? 'Schnelle Lieferung dank unseres strategischen Hubs.' : 'Fast delivery thanks to our strategic hub.',
|
||||
isDe ? 'Nachhaltige Lösungen für eine grünere Zukunft.' : 'Sustainable solutions for a greener tomorrow.',
|
||||
isDe ? 'Vertraut von Branchenführern in Deutschland, Österreich und den Niederlanden.' : 'Trusted by industry leaders in Germany, Austria and the Netherlands.'
|
||||
isDe ? 'Strategischer Hub für schnelle Lieferung' : 'Strategic hub for fast delivery',
|
||||
isDe ? 'Nachhaltige Kabelinfrastruktur' : 'Sustainable cable infrastructure',
|
||||
isDe ? 'Expertenberatung für Großprojekte' : 'Expert consulting for large-scale projects',
|
||||
isDe ? 'Zertifizierte Qualität nach EU-Standards' : 'Certified quality according to EU standards'
|
||||
].map((item, i) => (
|
||||
<li key={i} className="flex items-start gap-2 text-text-secondary">
|
||||
<span className="text-green-500 mt-1">✅</span>
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
<div key={i} className="flex items-center gap-4 text-white/80">
|
||||
<div className="w-6 h-6 rounded-full bg-accent/20 flex items-center justify-center flex-shrink-0">
|
||||
<svg className="w-3 h-3 text-accent" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-sm font-medium">{item}</span>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center">
|
||||
<p className="font-medium text-text-primary">
|
||||
{isDe ? 'Lassen Sie uns gemeinsam eine grünere Zukunft gestalten.' : "Let's power a greener future together."}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-6 items-start sm:items-center pt-8 border-t border-white/10">
|
||||
<Link
|
||||
href={`/${locale}/contact`}
|
||||
className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white font-bold rounded-lg hover:bg-primary/90 transition-all shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
|
||||
className="inline-flex items-center gap-3 px-8 py-4 bg-primary text-white font-bold rounded-full hover:bg-primary/90 transition-all shadow-xl hover:shadow-primary/20 transform hover:-translate-y-1 group/btn"
|
||||
>
|
||||
{isDe ? '👉 Kontakt aufnehmen' : '👉 Get in touch'}
|
||||
{isDe ? 'Projekt anfragen' : 'Inquire Project'}
|
||||
<svg className="w-5 h-5 transition-transform group-hover/btn: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>
|
||||
</Link>
|
||||
<p className="text-white/50 text-sm font-medium">
|
||||
{isDe ? 'Kostenlose Erstberatung für Ihr Vorhaben.' : 'Free initial consultation for your project.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
34
components/blog/ReadingProgressBar.tsx
Normal file
34
components/blog/ReadingProgressBar.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
export default function ReadingProgressBar() {
|
||||
const [completion, setCompletion] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const updateScrollCompletion = () => {
|
||||
const currentProgress = window.scrollY;
|
||||
const scrollHeight = document.body.scrollHeight - window.innerHeight;
|
||||
if (scrollHeight) {
|
||||
setCompletion(
|
||||
Number((currentProgress / scrollHeight).toFixed(2)) * 100
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', updateScrollCompletion);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', updateScrollCompletion);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="fixed top-0 left-0 w-full h-1 z-50 bg-neutral-100">
|
||||
<div
|
||||
className="h-full bg-primary transition-all duration-150 ease-out"
|
||||
style={{ width: `${completion}%` }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +1,22 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
interface SplitHeadingProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export default function SplitHeading({ children, className = '' }: SplitHeadingProps) {
|
||||
const elementRef = useRef<HTMLDivElement>(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setIsVisible(true);
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
|
||||
if (elementRef.current) {
|
||||
observer.observe(elementRef.current);
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
export default function SplitHeading({ children, className = '', id }: SplitHeadingProps) {
|
||||
return (
|
||||
<div
|
||||
ref={elementRef}
|
||||
className={`transform transition-all duration-1000 ease-out ${
|
||||
isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-10'
|
||||
} ${className}`}
|
||||
id={id}
|
||||
className={className}
|
||||
>
|
||||
<h3 className="text-2xl md:text-3xl font-bold leading-tight text-text-primary">
|
||||
<h2 className="text-2xl md:text-3xl font-bold leading-tight text-text-primary">
|
||||
{children}
|
||||
</h3>
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,28 @@ interface StatsProps {
|
||||
|
||||
export default function Stats({ stats }: StatsProps) {
|
||||
return (
|
||||
<div className="my-12 grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="my-16 grid grid-cols-1 md:grid-cols-3 border border-neutral-200 rounded-2xl overflow-hidden shadow-sm">
|
||||
{stats.map((stat, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-gradient-to-br from-primary/5 to-primary/10 p-6 rounded-xl border border-primary/20 text-center hover:shadow-md transition-shadow"
|
||||
className={`p-8 flex flex-col items-center text-center transition-all duration-500 hover:bg-neutral-50 group ${
|
||||
index !== stats.length - 1 ? 'border-b md:border-b-0 md:border-r border-neutral-200' : ''
|
||||
}`}
|
||||
>
|
||||
{stat.icon && (
|
||||
<div className="text-primary mb-3 flex justify-center">
|
||||
<div className="text-primary mb-4 transform transition-transform group-hover:scale-110 duration-500">
|
||||
{stat.icon}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-4xl font-bold text-primary mb-2">
|
||||
<div className="text-5xl font-bold text-text-primary mb-3 tracking-tight">
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-text-secondary font-medium">
|
||||
<div className="text-xs font-bold text-text-secondary uppercase tracking-[0.2em]">
|
||||
{stat.label}
|
||||
</div>
|
||||
|
||||
{/* Industrial accent line */}
|
||||
<div className="w-8 h-[2px] bg-primary/20 mt-6 transition-all group-hover:w-16 group-hover:bg-primary duration-500" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
36
components/blog/StickyNarrative.tsx
Normal file
36
components/blog/StickyNarrative.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
|
||||
interface StickyNarrativeItem {
|
||||
title: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface StickyNarrativeProps {
|
||||
title: string;
|
||||
items: StickyNarrativeItem[];
|
||||
}
|
||||
|
||||
export default function StickyNarrative({ title, items }: StickyNarrativeProps) {
|
||||
return (
|
||||
<div className="my-24 grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-20 items-start not-prose">
|
||||
<div className="lg:col-span-4 lg:sticky lg:top-32">
|
||||
<h3 className="text-3xl md:text-4xl font-bold text-primary leading-tight">
|
||||
{title}
|
||||
</h3>
|
||||
<div className="w-16 h-1.5 bg-accent mt-8 rounded-full" />
|
||||
</div>
|
||||
<div className="lg:col-span-8 space-y-12 md:space-y-16">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="group border-b border-neutral-200 pb-12 last:border-0 last:pb-0">
|
||||
<h4 className="text-xl md:text-2xl font-bold text-text-primary mb-4 group-hover:text-primary transition-colors">
|
||||
{item.title}
|
||||
</h4>
|
||||
<div className="text-lg text-text-secondary leading-relaxed">
|
||||
{item.content}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
86
components/blog/TableOfContents.tsx
Normal file
86
components/blog/TableOfContents.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { cn } from '@/components/ui/utils';
|
||||
|
||||
interface TocItem {
|
||||
id: string;
|
||||
text: string;
|
||||
level: number;
|
||||
}
|
||||
|
||||
interface TableOfContentsProps {
|
||||
headings: TocItem[];
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export default function TableOfContents({ headings, locale }: TableOfContentsProps) {
|
||||
const [activeId, setActiveId] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
const observerOptions = {
|
||||
rootMargin: '-100px 0% -80% 0%',
|
||||
threshold: 0
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
setActiveId(entry.target.id);
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
const elements = headings.map((h) => document.getElementById(h.id));
|
||||
elements.forEach((el) => {
|
||||
if (el) observer.observe(el);
|
||||
});
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [headings]);
|
||||
|
||||
if (headings.length === 0) return null;
|
||||
|
||||
return (
|
||||
<nav className="hidden lg:block w-full ml-12">
|
||||
<div className="relative pl-6 border-l border-neutral-200">
|
||||
<h4 className="text-xs font-bold uppercase tracking-[0.2em] text-text-primary/50 mb-6">
|
||||
{locale === 'de' ? 'Inhalt' : 'Table of Contents'}
|
||||
</h4>
|
||||
<ul className="space-y-4">
|
||||
{headings.map((heading) => (
|
||||
<li
|
||||
key={heading.id}
|
||||
style={{ paddingLeft: `${(heading.level - 2) * 1}rem` }}
|
||||
className="relative"
|
||||
>
|
||||
{activeId === heading.id && (
|
||||
<div className="absolute -left-[25px] top-0 w-[2px] h-full bg-primary transition-all duration-300" />
|
||||
)}
|
||||
<a
|
||||
href={`#${heading.id}`}
|
||||
className={cn(
|
||||
"text-sm transition-all duration-300 hover:text-primary block leading-snug",
|
||||
activeId === heading.id
|
||||
? "text-primary font-bold translate-x-1"
|
||||
: "text-text-secondary font-medium hover:translate-x-1"
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const element = document.getElementById(heading.id);
|
||||
if (element) {
|
||||
const yOffset = -100;
|
||||
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset;
|
||||
window.scrollTo({ top: y, behavior: 'smooth' });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
43
components/blog/TechnicalGrid.tsx
Normal file
43
components/blog/TechnicalGrid.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import Scribble from '@/components/Scribble';
|
||||
|
||||
interface TechnicalGridItem {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface TechnicalGridProps {
|
||||
title?: string;
|
||||
items: TechnicalGridItem[];
|
||||
}
|
||||
|
||||
export default function TechnicalGrid({ title, items }: TechnicalGridProps) {
|
||||
return (
|
||||
<div className="my-16 not-prose">
|
||||
{title && (
|
||||
<h3 className="text-2xl font-bold text-text-primary mb-8 flex items-center gap-4 relative">
|
||||
<span className="relative inline-block">
|
||||
{title}
|
||||
<Scribble
|
||||
variant="underline"
|
||||
className="absolute -bottom-2 left-0 w-full h-3 text-accent/40"
|
||||
/>
|
||||
</span>
|
||||
</h3>
|
||||
)}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="bg-white p-8 rounded-2xl border border-neutral-200 shadow-sm hover:shadow-md transition-all duration-300 group relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-16 h-16 bg-primary/5 -mr-8 -mt-8 rotate-45 transition-transform group-hover:scale-110" />
|
||||
<span className="block text-xs font-bold text-primary uppercase tracking-[0.2em] mb-3 opacity-70">
|
||||
{item.label}
|
||||
</span>
|
||||
<span className="text-lg text-text-secondary leading-relaxed group-hover:text-text-primary transition-colors">
|
||||
{item.value}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -10,36 +10,64 @@ interface VisualLinkPreviewProps {
|
||||
}
|
||||
|
||||
export default function VisualLinkPreview({ url, title, summary, image }: VisualLinkPreviewProps) {
|
||||
const hostname = (() => {
|
||||
try {
|
||||
return new URL(url).hostname;
|
||||
} catch {
|
||||
return url;
|
||||
}
|
||||
})();
|
||||
|
||||
return (
|
||||
<Link href={url} target="_blank" rel="noopener noreferrer" className="block my-8 no-underline group">
|
||||
<div className="flex flex-col md:flex-row border border-neutral-dark rounded-lg overflow-hidden bg-white hover:shadow-md transition-shadow">
|
||||
<div className="relative w-full md:w-48 h-48 md:h-auto flex-shrink-0 bg-neutral-light flex items-center justify-center">
|
||||
<Link href={url} target="_blank" rel="noopener noreferrer" className="block my-12 no-underline group">
|
||||
<div className="flex flex-col md:flex-row border border-neutral-200 rounded-2xl overflow-hidden bg-white transition-all duration-500 hover:shadow-2xl hover:border-primary/20 hover:-translate-y-1 group">
|
||||
<div className="relative w-full md:w-64 h-48 md:h-auto flex-shrink-0 bg-neutral-50 overflow-hidden">
|
||||
{image ? (
|
||||
<img
|
||||
<Image
|
||||
src={image}
|
||||
alt={title}
|
||||
className="w-full h-full object-cover"
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover transition-transform duration-700 group-hover:scale-110"
|
||||
/>
|
||||
) : (
|
||||
<div className="text-neutral-dark">No Image</div>
|
||||
<div className="w-full h-full flex items-center justify-center bg-primary/5">
|
||||
<svg className="w-12 h-12 text-primary/20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
{/* Industrial overlay */}
|
||||
<div className="absolute inset-0 bg-primary/10 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none" />
|
||||
</div>
|
||||
<div className="p-4 flex flex-col justify-center">
|
||||
<h3 className="text-lg font-bold text-primary mb-2 group-hover:underline line-clamp-2">
|
||||
|
||||
<div className="p-8 flex flex-col justify-center relative">
|
||||
{/* Industrial accent corner */}
|
||||
<div className="absolute top-0 right-0 w-12 h-12 bg-primary/5 -mr-6 -mt-6 rotate-45 transition-transform group-hover:scale-110" />
|
||||
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-primary/60 bg-primary/5 px-2 py-0.5 rounded">
|
||||
External Link
|
||||
</span>
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-text-secondary/40">
|
||||
{hostname}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-text-primary mb-3 group-hover:text-primary transition-colors line-clamp-2 leading-tight">
|
||||
{title}
|
||||
</h3>
|
||||
<p className="text-text-secondary text-sm line-clamp-3">
|
||||
|
||||
<p className="text-text-secondary text-base line-clamp-2 leading-relaxed mb-4">
|
||||
{summary}
|
||||
</p>
|
||||
<span className="text-xs text-text-secondary mt-2 opacity-70">
|
||||
{(() => {
|
||||
try {
|
||||
return new URL(url).hostname;
|
||||
} catch (e) {
|
||||
return url;
|
||||
}
|
||||
})()}
|
||||
</span>
|
||||
|
||||
<div className="flex items-center gap-2 text-primary font-bold text-xs uppercase tracking-widest">
|
||||
<span>Read more</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function CTA() {
|
||||
const locale = useLocale();
|
||||
|
||||
return (
|
||||
<Section className="bg-primary-dark text-white py-32 relative overflow-hidden">
|
||||
<Section className="bg-primary text-white py-32 relative overflow-hidden">
|
||||
<div className="absolute top-0 right-0 w-1/3 h-full bg-accent/5 -skew-x-12 translate-x-1/2" />
|
||||
<div className="absolute bottom-0 left-0 w-1/4 h-1/2 bg-primary/10 rounded-full blur-3xl -translate-x-1/2 translate-y-1/2" />
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ export default function Experience() {
|
||||
className="object-cover object-center scale-105 animate-slow-zoom"
|
||||
unoptimized
|
||||
/>
|
||||
<div className="absolute inset-0 bg-primary-dark/80 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary-dark via-primary-dark/40 to-transparent" />
|
||||
<div className="absolute inset-0 bg-primary/80 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary via-primary/40 to-transparent" />
|
||||
</div>
|
||||
|
||||
<Container className="relative z-10">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Container, Button } from '@/components/ui';
|
||||
import { Container, Button, Section, Heading } from '@/components/ui';
|
||||
import Scribble from '@/components/Scribble';
|
||||
import HeroIllustration from './HeroIllustration';
|
||||
|
||||
@@ -8,12 +8,12 @@ export default function Hero() {
|
||||
const t = useTranslations('Home.hero');
|
||||
|
||||
return (
|
||||
<section className="relative h-[70vh] md:h-[90vh] flex items-center justify-center overflow-hidden bg-primary-dark">
|
||||
<Section className="relative h-[70vh] md:h-[90vh] flex items-center justify-center overflow-hidden bg-primary py-0 md:py-0 lg:py-0">
|
||||
<HeroIllustration />
|
||||
|
||||
<Container className="relative z-10 text-left text-white w-full">
|
||||
<div className="max-w-5xl animate-slide-up">
|
||||
<h1 className="text-4xl md:text-7xl lg:text-8xl font-extrabold mb-4 md:mb-8 tracking-tight leading-[1.05] max-w-[15ch] md:max-w-none">
|
||||
<Heading level={1} className="mb-4 md:mb-8 tracking-tight leading-[1.05] max-w-[15ch] md:max-w-none text-white">
|
||||
{t.rich('title', {
|
||||
green: (chunks) => (
|
||||
<span className="relative inline-block">
|
||||
@@ -22,8 +22,8 @@ export default function Hero() {
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</h1>
|
||||
<p className="text-lg md:text-2xl text-white/70 leading-relaxed max-w-2xl mb-8 md:mb-12 line-clamp-2 md:line-clamp-none">
|
||||
</Heading>
|
||||
<p className="text-lg md:text-2xl text-white leading-relaxed max-w-2xl mb-8 md:mb-12 line-clamp-2 md:line-clamp-none">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div className="flex flex-col md:flex-row gap-3 md:gap-6">
|
||||
@@ -31,7 +31,7 @@ export default function Hero() {
|
||||
{t('cta')}
|
||||
<span className="ml-3 transition-transform group-hover:translate-x-1">→</span>
|
||||
</Button>
|
||||
<Button href="/products" variant="ghost" size="lg" className="group w-full md:w-auto text-white hover:bg-white/10 md:bg-white md:text-primary md:hover:bg-neutral-light md:h-16 md:px-10 md:text-xl">
|
||||
<Button href="/products" variant="ghost" size="lg" className="group w-full md:w-auto text-white hover:bg-white/10 md:bg-white md:text-saturated md:hover:bg-neutral-light md:h-16 md:px-10 md:text-xl">
|
||||
{t('exploreProducts')}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -43,6 +43,6 @@ export default function Hero() {
|
||||
<div className="w-1 h-2 bg-white rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ export default function MeetTheTeam() {
|
||||
className="object-cover scale-105 animate-slow-zoom"
|
||||
unoptimized
|
||||
/>
|
||||
<div className="absolute inset-0 bg-primary-dark/70 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-primary-dark via-primary-dark/20 to-transparent" />
|
||||
<div className="absolute inset-0 bg-primary/70 mix-blend-multiply" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-primary via-primary/20 to-transparent" />
|
||||
</div>
|
||||
|
||||
<Container className="relative z-10">
|
||||
@@ -43,10 +43,10 @@ export default function MeetTheTeam() {
|
||||
|
||||
<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">
|
||||
<div className="w-14 h-14 rounded-full border-4 border-primary overflow-hidden relative">
|
||||
<Image src="/uploads/2024/12/DSC07768-Large.webp" alt={teamT('michael.name')} fill className="object-cover" />
|
||||
</div>
|
||||
<div className="w-14 h-14 rounded-full border-4 border-primary-dark overflow-hidden relative">
|
||||
<div className="w-14 h-14 rounded-full border-4 border-primary overflow-hidden relative">
|
||||
<Image src="/uploads/2024/12/DSC07963-Large.webp" alt={teamT('klaus.name')} fill className="object-cover" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function ProductCategories() {
|
||||
];
|
||||
|
||||
return (
|
||||
<Section className="bg-neutral-light py-0 -mt-px">
|
||||
<Section className="bg-neutral-light py-0 md:py-0 lg: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-[400px] md:h-[500px] lg:h-[650px] overflow-hidden border-b md:border-b-0 md:border-r border-white/10 last:border-0">
|
||||
|
||||
@@ -5,7 +5,7 @@ export default function VideoSection() {
|
||||
const t = useTranslations('Home.video');
|
||||
|
||||
return (
|
||||
<section className="relative h-[70vh] overflow-hidden bg-primary-dark">
|
||||
<section className="relative h-[70vh] overflow-hidden bg-primary">
|
||||
<video
|
||||
className="w-full h-full object-cover opacity-60"
|
||||
autoPlay
|
||||
@@ -15,7 +15,7 @@ export default function VideoSection() {
|
||||
>
|
||||
<source src="/uploads/2024/12/making-of-metal-cable-on-factory-2023-11-27-04-55-16-utc-2.webm" type="video/mp4" />
|
||||
</video>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-primary-dark/60 via-transparent to-primary-dark/60 flex items-center justify-center">
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-primary/60 via-transparent to-primary/60 flex items-center justify-center">
|
||||
<div className="max-w-5xl px-6 text-center animate-slide-up">
|
||||
<h2 className="text-4xl md:text-6xl lg:text-7xl font-extrabold text-white leading-[1.1]">
|
||||
{t.rich('title', {
|
||||
|
||||
@@ -17,8 +17,8 @@ export default function WhatWeDo() {
|
||||
<p className="text-lg md:text-xl text-text-secondary leading-relaxed">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
<div className="mt-8 md:mt-12 p-6 md:p-8 bg-primary-light rounded-2xl border border-primary/10">
|
||||
<p className="text-primary font-bold text-base md:text-lg italic">
|
||||
<div className="mt-8 md:mt-12 p-6 md:p-8 bg-saturated/10 rounded-2xl border border-saturated/10">
|
||||
<p className="text-saturated font-bold text-base md:text-lg italic">
|
||||
"{t('quote')}"
|
||||
</p>
|
||||
</div>
|
||||
@@ -28,12 +28,12 @@ export default function WhatWeDo() {
|
||||
{[0, 1, 2, 3].map((idx) => (
|
||||
<div key={idx} className="group">
|
||||
<div className="flex items-center gap-4 mb-4 md:mb-6">
|
||||
<span className="flex items-center justify-center w-10 h-10 md:w-12 md:h-12 rounded-full bg-primary text-white font-bold text-base md:text-lg shadow-lg shadow-primary/20 group-hover:scale-110 transition-transform">
|
||||
<span className="flex items-center justify-center w-10 h-10 md:w-12 md:h-12 rounded-full bg-saturated text-white font-bold text-base md:text-lg shadow-lg shadow-saturated/20 group-hover:scale-110 transition-transform">
|
||||
{idx + 1}
|
||||
</span>
|
||||
<div className="h-px flex-grow bg-neutral-medium" />
|
||||
</div>
|
||||
<h3 className="text-xl md:text-2xl font-bold mb-3 md:mb-4 text-primary group-hover:text-accent-dark transition-colors">{t(`items.${idx}.title`)}</h3>
|
||||
<h3 className="text-xl md:text-2xl font-bold mb-3 md:mb-4 text-saturated group-hover:text-accent-dark transition-colors">{t(`items.${idx}.title`)}</h3>
|
||||
<p className="text-text-secondary text-base md:text-lg leading-relaxed">{t(`items.${idx}.description`)}</p>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function WhyChooseUs() {
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-bold text-primary">{t(`features.${i}`)}</span>
|
||||
<span className="font-bold text-saturated">{t(`features.${i}`)}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -35,10 +35,10 @@ export default function WhyChooseUs() {
|
||||
<div className="lg:col-span-8 grid grid-cols-1 md:grid-cols-2 gap-8 order-2 lg:order-1">
|
||||
{[0, 1, 2, 3].map((idx) => (
|
||||
<div key={idx} className="p-10 bg-white rounded-3xl border border-neutral-medium hover:border-accent transition-all duration-500 hover:shadow-xl group">
|
||||
<div className="w-14 h-14 bg-primary-light rounded-2xl flex items-center justify-center mb-8 group-hover:bg-accent transition-colors duration-500">
|
||||
<span className="text-primary font-bold text-xl group-hover:text-primary-dark">0{idx + 1}</span>
|
||||
<div className="w-14 h-14 bg-saturated/10 rounded-2xl flex items-center justify-center mb-8 group-hover:bg-accent transition-colors duration-500">
|
||||
<span className="text-saturated font-bold text-xl group-hover:text-primary-dark">0{idx + 1}</span>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold mb-4 text-primary">{t(`items.${idx}.title`)}</h3>
|
||||
<h3 className="text-2xl font-bold mb-4 text-saturated">{t(`items.${idx}.title`)}</h3>
|
||||
<p className="text-text-secondary text-lg leading-relaxed">{t(`items.${idx}.description`)}</p>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -1,130 +1 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'secondary' | 'accent' | 'outline' | 'ghost' | 'white';
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||
href?: string;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Button({ className, variant = 'primary', size = 'md', href, ...props }: ButtonProps) {
|
||||
const baseStyles = 'inline-flex items-center justify-center rounded-full font-semibold transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none active:scale-95';
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-primary text-white hover:bg-primary-dark shadow-md hover:shadow-lg',
|
||||
secondary: 'bg-secondary text-white hover:bg-secondary-light shadow-md hover:shadow-lg',
|
||||
accent: 'bg-accent text-primary-dark hover:bg-accent-dark shadow-md hover:shadow-lg',
|
||||
outline: 'border-2 border-primary bg-transparent hover:bg-primary hover:text-white text-primary',
|
||||
ghost: 'hover:bg-primary-light text-primary',
|
||||
white: 'bg-white text-primary hover:bg-neutral-light shadow-md hover:shadow-lg',
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
sm: 'h-9 px-4 text-sm',
|
||||
md: 'h-11 px-6 text-base',
|
||||
lg: 'h-14 px-8 text-lg',
|
||||
xl: 'h-16 px-10 text-xl',
|
||||
};
|
||||
|
||||
const styles = cn(baseStyles, variants[variant], sizes[size], className);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link href={href} className={styles}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={styles} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export function Section({ className, children, ...props }: React.HTMLAttributes<HTMLElement>) {
|
||||
return (
|
||||
<section className={cn('py-16 md:py-28 lg:py-36 overflow-hidden content-visibility-auto', className)} {...props}>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function Container({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div className={cn('container mx-auto px-4 md:px-12 lg:px-16 max-w-7xl', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Heading({
|
||||
level = 2,
|
||||
children,
|
||||
className,
|
||||
subtitle,
|
||||
align = 'left'
|
||||
}: {
|
||||
level?: 1 | 2 | 3 | 4;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
subtitle?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}) {
|
||||
const Tag = `h${level}` as any;
|
||||
|
||||
const sizes = {
|
||||
1: 'text-4xl md:text-6xl lg:text-7xl xl:text-8xl font-extrabold leading-[1.1]',
|
||||
2: 'text-3xl md:text-5xl lg:text-6xl font-bold leading-tight',
|
||||
3: 'text-2xl md:text-3xl lg:text-4xl font-bold',
|
||||
4: 'text-xl md:text-2xl font-bold',
|
||||
};
|
||||
|
||||
const alignments = {
|
||||
left: 'text-left',
|
||||
center: 'text-center mx-auto',
|
||||
right: 'text-right',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('mb-8 md:mb-16', alignments[align], className)}>
|
||||
{subtitle && (
|
||||
<span className="inline-block text-accent font-bold tracking-widest uppercase text-xs md:text-sm mb-3 md:mb-4 animate-fade-in">
|
||||
{subtitle}
|
||||
</span>
|
||||
)}
|
||||
<Tag className={cn(sizes[level as keyof typeof sizes], 'text-primary')}>
|
||||
{children}
|
||||
</Tag>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Card({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div className={cn('premium-card overflow-hidden', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Badge({ children, className, variant = 'primary' }: { children: React.ReactNode, className?: string, variant?: 'primary' | 'accent' | 'neutral' }) {
|
||||
const variants = {
|
||||
primary: 'bg-primary-light text-primary',
|
||||
accent: 'bg-accent-light text-accent-dark',
|
||||
neutral: 'bg-neutral-medium text-text-secondary',
|
||||
};
|
||||
|
||||
return (
|
||||
<span className={cn('inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider', variants[variant], className)}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
export * from './ui/index';
|
||||
|
||||
17
components/ui/Badge.tsx
Normal file
17
components/ui/Badge.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Badge({ children, className, variant = 'primary' }: { children: React.ReactNode, className?: string, variant?: 'primary' | 'accent' | 'neutral' | 'saturated' }) {
|
||||
const variants = {
|
||||
primary: 'bg-primary-light text-primary',
|
||||
accent: 'bg-accent-light text-accent-dark',
|
||||
neutral: 'bg-neutral-medium text-text-secondary',
|
||||
saturated: 'bg-saturated text-white',
|
||||
};
|
||||
|
||||
return (
|
||||
<span className={cn('inline-flex items-center px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider', variants[variant], className)}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
46
components/ui/Button.tsx
Normal file
46
components/ui/Button.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { cn } from './utils';
|
||||
|
||||
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'secondary' | 'accent' | 'saturated' | 'outline' | 'ghost' | 'white';
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||
href?: string;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Button({ className, variant = 'primary', size = 'md', href, ...props }: ButtonProps) {
|
||||
const baseStyles = 'inline-flex items-center justify-center rounded-full font-semibold transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none active:scale-95';
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-primary text-white hover:bg-primary-dark shadow-md hover:shadow-lg',
|
||||
secondary: 'bg-secondary text-white hover:bg-secondary-light shadow-md hover:shadow-lg',
|
||||
accent: 'bg-accent text-primary-dark hover:bg-accent-dark shadow-md hover:shadow-lg',
|
||||
saturated: 'bg-saturated text-white hover:bg-primary shadow-md hover:shadow-lg',
|
||||
outline: 'border-2 border-primary bg-transparent hover:bg-primary hover:text-white text-primary',
|
||||
ghost: 'hover:bg-primary-light text-primary',
|
||||
white: 'bg-white text-primary hover:bg-neutral-light shadow-md hover:shadow-lg',
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
sm: 'h-9 px-4 text-sm',
|
||||
md: 'h-11 px-6 text-base',
|
||||
lg: 'h-14 px-8 text-lg',
|
||||
xl: 'h-16 px-10 text-xl',
|
||||
};
|
||||
|
||||
const styles = cn(baseStyles, variants[variant], sizes[size], className);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link href={href} className={styles}>
|
||||
{props.children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={styles} {...props} />
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
interface CalloutProps {
|
||||
type?: 'info' | 'warning' | 'success' | 'tip';
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const icons = {
|
||||
@@ -43,11 +45,11 @@ const iconColors = {
|
||||
tip: 'text-purple-500',
|
||||
};
|
||||
|
||||
export default function Callout({ type = 'info', title, children }: CalloutProps) {
|
||||
export function Callout({ type = 'info', title, children, className }: CalloutProps) {
|
||||
return (
|
||||
<div className={`my-8 p-6 rounded-xl border-2 ${styles[type]}`}>
|
||||
<div className={cn('my-8 p-6 rounded-xl border-2', styles[type], className)}>
|
||||
<div className="flex gap-4">
|
||||
<div className={`flex-shrink-0 ${iconColors[type]}`}>
|
||||
<div className={cn('flex-shrink-0', iconColors[type])}>
|
||||
{icons[type]}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
10
components/ui/Card.tsx
Normal file
10
components/ui/Card.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Card({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div className={cn('premium-card overflow-hidden', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
components/ui/Container.tsx
Normal file
10
components/ui/Container.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Container({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div className={cn('container mx-auto px-4 md:px-12 lg:px-16 max-w-7xl', className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
44
components/ui/Heading.tsx
Normal file
44
components/ui/Heading.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Heading({
|
||||
level = 2,
|
||||
children,
|
||||
className,
|
||||
subtitle,
|
||||
align = 'left'
|
||||
}: {
|
||||
level?: 1 | 2 | 3 | 4;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
subtitle?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}) {
|
||||
const Tag = `h${level}` as any;
|
||||
|
||||
const sizes = {
|
||||
1: 'text-4xl md:text-6xl lg:text-7xl xl:text-8xl font-extrabold leading-[1.1]',
|
||||
2: 'text-3xl md:text-5xl lg:text-6xl font-bold leading-tight',
|
||||
3: 'text-2xl md:text-3xl lg:text-4xl font-bold',
|
||||
4: 'text-xl md:text-2xl font-bold',
|
||||
};
|
||||
|
||||
const alignments = {
|
||||
left: 'text-left',
|
||||
center: 'text-center mx-auto',
|
||||
right: 'text-right',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('mb-8 md:mb-16 text-primary', alignments[align], className)}>
|
||||
{subtitle && (
|
||||
<span className="inline-block text-accent font-bold tracking-widest uppercase text-xs md:text-sm mb-3 md:mb-4 animate-fade-in">
|
||||
{subtitle}
|
||||
</span>
|
||||
)}
|
||||
<Tag className={cn(sizes[level as keyof typeof sizes])}>
|
||||
{children}
|
||||
</Tag>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
components/ui/Input.tsx
Normal file
18
components/ui/Input.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Input({ className, ...props }: InputProps) {
|
||||
return (
|
||||
<input
|
||||
className={cn(
|
||||
'w-full px-4 md:px-6 py-2.5 md:py-4 bg-neutral-light/50 border border-neutral-dark/10 rounded-xl md:rounded-2xl 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 md:text-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
18
components/ui/Label.tsx
Normal file
18
components/ui/Label.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Label({ className, ...props }: LabelProps) {
|
||||
return (
|
||||
<label
|
||||
className={cn(
|
||||
'text-[10px] md:text-xs font-extrabold text-primary uppercase tracking-widest',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
10
components/ui/Section.tsx
Normal file
10
components/ui/Section.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Section({ className, children, ...props }: React.HTMLAttributes<HTMLElement>) {
|
||||
return (
|
||||
<section className={cn('py-16 md:py-28 lg:py-36 overflow-hidden content-visibility-auto', className)} {...props}>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
18
components/ui/Textarea.tsx
Normal file
18
components/ui/Textarea.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function Textarea({ className, ...props }: TextareaProps) {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
'w-full px-4 md:px-6 py-2.5 md:py-4 bg-neutral-light/50 border border-neutral-dark/10 rounded-xl md:rounded-2xl 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 md:text-lg resize-none',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
11
components/ui/index.ts
Normal file
11
components/ui/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export * from './utils';
|
||||
export * from './Button';
|
||||
export * from './Section';
|
||||
export * from './Container';
|
||||
export * from './Heading';
|
||||
export * from './Card';
|
||||
export * from './Badge';
|
||||
export * from './Input';
|
||||
export * from './Textarea';
|
||||
export * from './Label';
|
||||
export * from './Callout';
|
||||
6
components/ui/utils.ts
Normal file
6
components/ui/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
@@ -11,33 +11,16 @@ Die Vision ist klar: Ein Europa, das seinen Strom zu 100 % aus erneuerbaren Ener
|
||||
Am Ende geht es nicht nur um mehr Strom, sondern um kluge Netze, die ihn zuverlässig und verlustarm transportieren können.
|
||||
## **Das Problem: Alte Netze für eine neue Energiezukunft**
|
||||
Die heutige Strominfrastruktur wurde für zentrale Großkraftwerke gebaut. Doch erneuerbare Energien funktionieren anders: Sie sind dezentral, wetterabhängig und benötigen flexible Netze. Das führt zu einem massiven **Umbau-Bedarf**.
|
||||
### **Warum unser Netz aktuell überfordert ist:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Problem</th>
|
||||
<th>Ursache</th>
|
||||
<th>Lösung?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Netzengpässe</td>
|
||||
<td>Alte Leitungen für zentrale Kraftwerke, nicht für dezentrale Energie</td>
|
||||
<td>Neue Hoch- & Mittelspannungskabel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Abregelung von Solar- & Windstrom</td>
|
||||
<td>Netz kann nicht genug Strom aufnehmen</td>
|
||||
<td>Smart Grids & Speicherlösungen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lange Transportwege</td>
|
||||
<td>Erzeugung oft weit entfernt vom Verbrauch</td>
|
||||
<td>Hochleistungskabel & lokale Netze</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Herausforderungen im Stromnetz"
|
||||
items={[
|
||||
{ title: "Netzengpässe", content: "**Ursache:** Alte Leitungen für zentrale Kraftwerke, nicht für dezentrale Energie. **Lösung:** Neue Hoch- & Mittelspannungskabel." },
|
||||
{ title: "Abregelung von Solar- & Windstrom", content: "**Ursache:** Netz kann nicht genug Strom aufnehmen. **Lösung:** Smart Grids & Speicherlösungen." },
|
||||
{ title: "Lange Transportwege", content: "**Ursache:** Erzeugung oft weit entfernt vom Verbrauch. **Lösung:** Hochleistungskabel & lokale Netze." }
|
||||
]}
|
||||
/>
|
||||
|
||||
⚠️ Ein Netz aus der Vergangenheit kann keine Zukunftsenergie transportieren!
|
||||
Wer heute nur in erneuerbare Energieanlagen investiert, aber die Kabelinfrastruktur ignoriert, wird morgen teuren ungenutzten Strom haben.
|
||||
## **Welche Kabel brauchen wir für die Energiewende?**
|
||||
@@ -51,39 +34,19 @@ Nicht jedes Kabel ist gleich – und nicht jedes Kabel ist für die Herausforder
|
||||
💡 Je besser das Kabel, desto weniger Strom geht unterwegs verloren – und desto grüner wird die Energie!
|
||||
## Solar- und Windparks allein reichen nicht
|
||||
Ohne passende Kabel bleibt der Strom dort, wo er erzeugt wird. Doch welcher Netzausbau macht wirklich Sinn?
|
||||
### Erdkabel vs. Freileitungen – Was ist die bessere Wahl?
|
||||
Ein zentrales Thema beim Netzausbau ist die Frage, ob neue Stromtrassen als Freileitungen oder als Erdkabel verlegt werden sollen. Beide Varianten haben ihre Vor- und Nachteile, doch langfristig bietet die Erdverkabelung deutliche Vorteile in Sachen Zuverlässigkeit, Umweltschutz und Netzstabilität.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kriterium</th>
|
||||
<th>Erdkabel</th>
|
||||
<th>Freileitung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Netzstabilität</td>
|
||||
<td>Sehr hoch</td>
|
||||
<td>Mittel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Umweltverträglichkeit</td>
|
||||
<td>Unauffällig, keine Eingriffe ins Landschaftsbild</td>
|
||||
<td>Sichtbar, problematisch für Vögel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wartung & Lebensdauer</td>
|
||||
<td>Kaum Wartung, langlebig</td>
|
||||
<td>Witterungsanfällig, kürzere Lebensdauer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Kosten</td>
|
||||
<td>Höher in der Installation, aber effizienter im Betrieb</td>
|
||||
<td>Günstiger zu errichten, aber höhere Folgekosten</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Erdkabel vs. Freileitungen – Was ist die bessere Wahl?"
|
||||
leftLabel="Erdkabel"
|
||||
rightLabel="Freileitung"
|
||||
items={[
|
||||
{ label: "Netzstabilität", leftValue: "Sehr hoch", rightValue: "Mittel" },
|
||||
{ label: "Umweltverträglichkeit", leftValue: "Unauffällig, keine Eingriffe ins Landschaftsbild", rightValue: "Sichtbar, problematisch für Vögel" },
|
||||
{ label: "Wartung & Lebensdauer", leftValue: "Kaum Wartung, langlebig", rightValue: "Witterungsanfällig, kürzere Lebensdauer" },
|
||||
{ label: "Kosten", leftValue: "Höher in der Installation, aber effizienter im Betrieb", rightValue: "Günstiger zu errichten, aber höhere Folgekosten" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Während Freileitungen in der Vergangenheit aufgrund der geringeren Baukosten bevorzugt wurden, sprechen moderne Anforderungen an Netzstabilität, Umweltschutz und Ästhetik zunehmend für Erdkabel. In vielen Ländern setzt sich daher die Erdverkabelung als Standard für neue Hoch- und Mittelspannungstrassen durch.
|
||||
Wer sich detaillierter mit diesem Thema befassen möchte, findet hier eine umfassende Analyse der Unterschiede zwischen Erdkabeln und Freileitungen. [https://www.hochspannungsblog.at/wissenswertes/netzaufbau/vergleich-freileitung-erdkabel](https://www.hochspannungsblog.at/wissenswertes/netzaufbau/vergleich-freileitung-erdkabel)
|
||||
<VisualLinkPreview
|
||||
|
||||
@@ -17,33 +17,17 @@ Moderne Onshore-Windparks bestehen nicht nur aus Turbinen, sondern aus einem kom
|
||||
Was viele unterschätzen: Die Kabelstrecken in einem Windpark machen oft einen erheblichen Teil der Gesamtinvestition aus. Sie sind nicht nur Verbindungsglied – sie sind die **kritische Infrastruktur**, auf der alles aufbaut.
|
||||
## Ganzheitliche Planung: Grundlage für nachhaltige Infrastruktur
|
||||
Die Integration von Windparks in das Stromnetz erfordert eine systemische Herangehensweise. Eine fundierte Planung berücksichtigt dabei nicht nur die Leistungsanforderungen, sondern auch Umgebungsbedingungen, Ausbauszenarien und Genehmigungsprozesse.
|
||||
**Zu den zentralen Planungsaspekten zählen:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bereich</th>
|
||||
<th>Planungsschwerpunkt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Trassenführung</td>
|
||||
<td>Geologie, Eigentumsverhältnisse, Schutzgebiete</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Netzanschluss</td>
|
||||
<td>Spannungsebene, Einspeisepunkte, Redundanz</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lastprofil</td>
|
||||
<td>Bemessung für Dauer- und Spitzenlasten</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Skalierbarkeit</td>
|
||||
<td>Erweiterungspotenziale für zukünftige Anlagen</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Zentrale Planungsaspekte"
|
||||
items={[
|
||||
{ title: "Trassenführung", content: "Geologie, Eigentumsverhältnisse, Schutzgebiete" },
|
||||
{ title: "Netzanschluss", content: "Spannungsebene, Einspeisepunkte, Redundanz" },
|
||||
{ title: "Lastprofil", content: "Bemessung für Dauer- und Spitzenlasten" },
|
||||
{ title: "Skalierbarkeit", content: "Erweiterungspotenziale für zukünftige Anlagen" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Professionelle Planung schafft nicht nur Versorgungssicherheit, sondern senkt langfristig die Betriebskosten und ermöglicht eine flexible Reaktion auf Netzanforderungen.
|
||||
Hier finden Sie weitere Informationen, wie Windenergie grundlegend funktioniert:
|
||||
|
||||
|
||||
@@ -9,62 +9,32 @@ category: Kabel Technologie
|
||||
### **Wachstum braucht Struktur**
|
||||
**Wachstum klingt gut –** mehr Projekte, mehr Kunden, mehr Umsatz.<br />Aber echtes, nachhaltiges Wachstum braucht mehr als nur Tempo: Es braucht **Transparenz, Planung und Kontrolle**.
|
||||
Damit aus ehrgeizigen Zielen kein Blindflug wird, haben wir entschieden, uns gezielt zu verstärken. Denn je größer die Projekte werden, desto wichtiger wird die Fähigkeit, Entwicklungen frühzeitig zu erkennen und gezielt zu steuern.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Warum wir unser Controlling ausbauen**</th>
|
||||
<th>**Was wir damit erreichen wollen**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Mehr Projekte im In- und Ausland</td>
|
||||
<td>Klare Zahlen und belastbare Prognosen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Steigende Anforderungen im Vertrieb</td>
|
||||
<td>Bessere Übersicht über Trends und Margen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Komplexere Prozesse</td>
|
||||
<td>Schnellere, fundierte Entscheidungen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nachhaltiges Wachstum</td>
|
||||
<td>Stabilität statt Zufall</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Warum wir unser Controlling ausbauen"
|
||||
items={[
|
||||
{ label: "Mehr Projekte im In- und Ausland", value: "Klare Zahlen und belastbare Prognosen" },
|
||||
{ label: "Steigende Anforderungen im Vertrieb", value: "Bessere Übersicht über Trends und Margen" },
|
||||
{ label: "Komplexere Prozesse", value: "Schnellere, fundierte Entscheidungen" },
|
||||
{ label: "Nachhaltiges Wachstum", value: "Stabilität statt Zufall" }
|
||||
]}
|
||||
/>
|
||||
|
||||
**Kurz gesagt:** Wir wollen nicht einfach nur wachsen – wir wollen verstehen, <em>wie</em> wir wachsen.<br />Deshalb setzen wir künftig noch stärker auf **qualitatives Controlling** und freuen uns über Unterstützung, die genau das möglich macht.
|
||||
### **Neue Stärke im Team**
|
||||
Mit [**Julia Havasi**](https://www.linkedin.com/in/julia-havasi-18556b233/) haben wir genau die Verstärkung gefunden, die wir gesucht haben: analytisch stark, strukturiert im Denken und mit einem guten Gespür für die Dynamik zwischen Zahlen und Menschen.
|
||||
Als **Senior Financial & Sales Controller** übernimmt Julia künftig die Verantwortung für unser Finanz- und Vertriebscontrolling. Ihr Ziel: **mehr Klarheit, mehr Weitblick, mehr Substanz** in jeder Entscheidung.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Aufgabenbereich**</th>
|
||||
<th>**Ziel**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Finanzcontrolling**</td>
|
||||
<td>Saubere Zahlen, klare Strukturen und nachvollziehbare Reports</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Sales Controlling**</td>
|
||||
<td>Vertriebszahlen analysieren, Potenziale erkennen, Trends ableiten</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Prognosen & Analysen**</td>
|
||||
<td>Frühzeitige Einschätzung von Marktbewegungen und Investitionschancen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Reporting & Kommunikation**</td>
|
||||
<td>Komplexe Daten so aufbereiten, dass sie jeder versteht – schnell und präzise</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Aufgabenbereiche & Ziele"
|
||||
items={[
|
||||
{ label: "Finanzcontrolling", value: "Saubere Zahlen, klare Strukturen und nachvollziehbare Reports" },
|
||||
{ label: "Sales Controlling", value: "Vertriebszahlen analysieren, Potenziale erkennen, Trends ableiten" },
|
||||
{ label: "Prognosen & Analysen", value: "Frühzeitige Einschätzung von Marktbewegungen und Investitionschancen" },
|
||||
{ label: "Reporting & Kommunikation", value: "Komplexe Daten so aufbereiten, dass sie jeder versteht – schnell und präzise" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Julia wird damit eine zentrale Rolle in der weiteren Entwicklung von KLZ spielen – als Schnittstelle zwischen **Management, Vertrieb und Strategie**.<br />Oder, um es etwas lockerer zu sagen: Sie sorgt dafür, dass wir nicht nur wissen, **wo wir stehen**, sondern auch **wohin wir gehen**.
|
||||
### **Erfahrung, die verbindet**
|
||||
**Zahlenverständnis trifft Praxiserfahrung.**<br />Mit über 13 Jahren Erfahrung im Controlling und Vertrieb bringt [**Julia Havasi**](https://www.linkedin.com/in/julia-havasi-18556b233/) das ideale Zusammenspiel aus analytischer Präzision und unternehmerischem Denken mit.
|
||||
|
||||
@@ -9,48 +9,21 @@ category: Kabel Technologie
|
||||
Gerade bei Kabeln wie **NA2XS(F)2Y** oder **NAYY **für** Windkraftanlagen** entscheidet die Materialwahl über Kosten, Leistung und Lebensdauer. Kupfer überzeugt durch eine hohe elektrische Leitfähigkeit, während Aluminium mit niedrigen Kosten und geringem Gewicht punktet. Doch welches Material ist **technisch **und** wirtschaftlich** langfristig die bessere Wahl? Dieser Artikel liefert eine detaillierte Analyse der Vor- und Nachteile beider Optionen.
|
||||
## Elektrische und mechanische Eigenschaften im Vergleich
|
||||
Kupfer ist seit Jahrzehnten das bevorzugte Material für elektrische Leitungen. Es besitzt eine hohe Leitfähigkeit und eine ausgezeichnete mechanische Stabilität. Aluminium hingegen ist deutlich leichter, hat aber eine geringere elektrische Leitfähigkeit. Das bedeutet, dass Aluminiumkabel für die gleiche Stromübertragung einen größeren Querschnitt benötigen.
|
||||
### Vergleich der Eigenschaften
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Eigenschaft</th>
|
||||
<th>Kupfer</th>
|
||||
<th>Aluminium</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Elektrische Leitfähigkeit**</td>
|
||||
<td>58 MS/m</td>
|
||||
<td>35 MS/m</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Dichte (g/cm³)**</td>
|
||||
<td>8,96</td>
|
||||
<td>2,70</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Korrosionsbeständigkeit**</td>
|
||||
<td>Sehr hoch</td>
|
||||
<td>Mittel (Oxidbildung)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Mechanische Festigkeit**</td>
|
||||
<td>Hoch</td>
|
||||
<td>Mittel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Gewicht**</td>
|
||||
<td>Hoch</td>
|
||||
<td>Gering</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Preis pro Tonne**</td>
|
||||
<td>8.000 – 9.000 €</td>
|
||||
<td>2.300 – 2.500 €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Vergleich der Eigenschaften"
|
||||
leftLabel="Kupfer"
|
||||
rightLabel="Aluminium"
|
||||
items={[
|
||||
{ label: "Elektrische Leitfähigkeit", leftValue: "58 MS/m", rightValue: "35 MS/m" },
|
||||
{ label: "Dichte (g/cm³)", leftValue: "8,96", rightValue: "2,70" },
|
||||
{ label: "Korrosionsbeständigkeit", leftValue: "Sehr hoch", rightValue: "Mittel (Oxidbildung)" },
|
||||
{ label: "Mechanische Festigkeit", leftValue: "Hoch", rightValue: "Mittel" },
|
||||
{ label: "Gewicht", leftValue: "Hoch", rightValue: "Gering" },
|
||||
{ label: "Preis pro Tonne", leftValue: "8.000 – 9.000 €", rightValue: "2.300 – 2.500 €" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Aluminium kann zwar durch seine Gewichtsersparnis bei Transport und Installation Vorteile bieten, benötigt aber größere Querschnitte, um die gleiche Leistung zu übertragen. Dies kann sich auf die Platzanforderungen in Kabeltrassen und die mechanische Stabilität auswirken. Zudem neigt Aluminium stärker zur Oxidation, was zu Kontaktproblemen führen kann, während Kupfer seine Leitfähigkeit über lange Zeiträume ohne große Qualitätseinbußen behält. Besonders in feuchten oder salzhaltigen Umgebungen, wie Offshore-Windparks, kann dies ein entscheidender Faktor sein.
|
||||
## Kosten: Anschaffung, Installation und Betrieb
|
||||
### Materialkosten
|
||||
@@ -106,53 +79,22 @@ Sowohl Kupfer als auch Aluminium sind vollständig recycelbar, allerdings gibt e
|
||||
### Langfristige Nachhaltigkeit in Windparks
|
||||
Aluminium bietet klare Vorteile in der Herstellung und im Recycling, während Kupfer aufgrund seiner Langlebigkeit und geringen Wartungsanforderungen langfristig nachhaltiger sein kann. Für Windparks bedeutet dies, dass die Wahl des richtigen Materials auch eine ökologische Entscheidung ist.
|
||||
Fazit: Aluminium punktet durch seinen geringeren CO₂-Fußabdruck in der Herstellung und seine hervorragende Recyclingfähigkeit, während Kupfer durch seine Langlebigkeit weniger häufig ersetzt werden muss und dadurch ebenfalls zur Nachhaltigkeit beiträgt.
|
||||
## Welche Lösung ist die beste für Windparks?
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Faktor</th>
|
||||
<th>Kupfer</th>
|
||||
<th>Aluminium</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Effizienz**</td>
|
||||
<td>Besser</td>
|
||||
<td>Höhere Verluste</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Kosten (Material & Anschaffung)**</td>
|
||||
<td>Teurer</td>
|
||||
<td>Günstiger</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Installationsaufwand**</td>
|
||||
<td>Schwerer, aufwendiger</td>
|
||||
<td>Leichter, einfacher</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Betriebskosten (Verluste & Wartung)**</td>
|
||||
<td>Geringer</td>
|
||||
<td>Höher</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Korrosionsbeständigkeit**</td>
|
||||
<td>Sehr gut</td>
|
||||
<td>Mittel</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Lebensdauer**</td>
|
||||
<td>Länger</td>
|
||||
<td>Kürzer</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Umweltfreundlichkeit**</td>
|
||||
<td>Hoher Energieaufwand</td>
|
||||
<td>Besser mit Recycling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Welche Lösung ist die beste für Windparks?"
|
||||
leftLabel="Kupfer"
|
||||
rightLabel="Aluminium"
|
||||
items={[
|
||||
{ label: "Effizienz", leftValue: "Besser", rightValue: "Höhere Verluste" },
|
||||
{ label: "Kosten (Material & Anschaffung)", leftValue: "Teurer", rightValue: "Günstiger" },
|
||||
{ label: "Installationsaufwand", leftValue: "Schwerer, aufwendiger", rightValue: "Leichter, einfacher" },
|
||||
{ label: "Betriebskosten (Verluste & Wartung)", leftValue: "Geringer", rightValue: "Höher" },
|
||||
{ label: "Korrosionsbeständigkeit", leftValue: "Sehr gut", rightValue: "Mittel" },
|
||||
{ label: "Lebensdauer", leftValue: "Länger", rightValue: "Kürzer" },
|
||||
{ label: "Umweltfreundlichkeit", leftValue: "Hoher Energieaufwand", rightValue: "Besser mit Recycling" }
|
||||
]}
|
||||
/>
|
||||
|
||||
### Empfohlene Anwendung je nach Einsatzzweck
|
||||
- Aluminium eignet sich für lange **Mittelspannungs-Trassen**, wo Gewicht und Kosten entscheidend sind.
|
||||
- Kupfer ist ideal für **Netzübergänge, Umspannwerke **und** kritische Bereiche**, wo Effizienz und Langlebigkeit im Fokus stehen.
|
||||
|
||||
@@ -20,37 +20,18 @@ Projekte im Bereich von 10 bis 30 kV bringen wiederkehrende Anforderungen mit
|
||||
Die Realität auf der Baustelle zeigt: Ein Kabel, das nicht flexibel genug ist, zu hohe Biegeradien hat oder thermisch schnell an seine Grenzen kommt, verzögert nicht nur die Umsetzung – es gefährdet auch die Betriebssicherheit.
|
||||
<h4>Was das NA2XS(F)2Y für moderne Energieinfrastruktur interessant macht</h4>
|
||||
Das [**NA2XS(F)2Y**](/de/products/medium-voltage-cables/na2xsf2y/) begegnet diesen Anforderungen mit einer durchdachten, praxisbewährten Konstruktion. Es ist ausgelegt für den **langfristigen Einsatz unter Last** und zeigt seine Stärken besonders in industriellen und energietechnischen Netzen.
|
||||
**Wichtige Merkmale im Überblick:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Merkmal</th>
|
||||
<th>Vorteil für Ihr Projekt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Aluminiumleiter**</td>
|
||||
<td>Hohe Leitfähigkeit, geringe Übertragungsverluste</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**XLPE-Isolierung**</td>
|
||||
<td>Hohe thermische Belastbarkeit, langlebig und stabil</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**EMV-optimiertes Design**</td>
|
||||
<td>Störungsfreier Betrieb in sensiblen Netzumgebungen</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Normgerechter Aufbau (IEC)**</td>
|
||||
<td>Sicherheit bei Ausschreibung, Prüfung und Betrieb</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Robuste Außenkonstruktion**</td>
|
||||
<td>Geeignet für alle gängigen Verlegeverfahren</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Wichtige Merkmale im Überblick"
|
||||
items={[
|
||||
{ label: "Aluminiumleiter", value: "Hohe Leitfähigkeit, geringe Übertragungsverluste" },
|
||||
{ label: "XLPE-Isolierung", value: "Hohe thermische Belastbarkeit, langlebig und stabil" },
|
||||
{ label: "EMV-optimiertes Design", value: "Störungsfreier Betrieb in sensiblen Netzumgebungen" },
|
||||
{ label: "Normgerechter Aufbau (IEC)", value: "Sicherheit bei Ausschreibung, Prüfung und Betrieb" },
|
||||
{ label: "Robuste Außenkonstruktion", value: "Geeignet für alle gängigen Verlegeverfahren" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Das bedeutet: Mit dem [NA2XS(F)2Y](/de/products/medium-voltage-cables/na2xsf2y/) lassen sich Netze realisieren, die nicht nur auf dem Papier funktionieren, sondern auch im praktischen Betrieb – dauerhaft, wartungsarm und sicher.
|
||||
Warum der Netzausbau wichtig ist, erfahren Sie hier:
|
||||
<VisualLinkPreview
|
||||
|
||||
@@ -13,33 +13,16 @@ Ohne ein durchdachtes Recyclingkonzept würden enorme Mengen an Holz, Stahl und
|
||||
##
|
||||
### Materialien und ihre Wiederverwertung
|
||||
Kabeltrommeln bestehen aus unterschiedlichen Materialien, die jeweils verschiedene Recyclingmöglichkeiten bieten. Eine gezielte Rückführung hängt davon ab, ob das Material wiederverwertet oder weiterverarbeitet werden kann.
|
||||
<h4>Hauptmaterialien von Kabeltrommeln und ihre Recyclingoptionen</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Material**</th>
|
||||
<th>**Eigenschaften**</th>
|
||||
<th>**Recyclingmöglichkeiten**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Holz**</td>
|
||||
<td>Biologisch abbaubar, leicht zu reparieren</td>
|
||||
<td>Upcycling, Biomasse, Palettenbau</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Stahl**</td>
|
||||
<td>Stabil, wiederverwendbar, korrosionsbeständig</td>
|
||||
<td>Einschmelzen, Wiederverwertung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Kunststoff**</td>
|
||||
<td>Witterungsbeständig, leicht, langlebig</td>
|
||||
<td>Granulat-Herstellung, Upcycling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Materialien und ihre Wiederverwertung"
|
||||
items={[
|
||||
{ title: "Holz", content: "**Eigenschaften:** Biologisch abbaubar, leicht zu reparieren. **Recycling:** Upcycling, Biomasse, Palettenbau." },
|
||||
{ title: "Stahl", content: "**Eigenschaften:** Stabil, wiederverwendbar, korrosionsbeständig. **Recycling:** Einschmelzen, Wiederverwertung." },
|
||||
{ title: "Kunststoff", content: "**Eigenschaften:** Witterungsbeständig, leicht, langlebig. **Recycling:** Granulat-Herstellung, Upcycling." }
|
||||
]}
|
||||
/>
|
||||
|
||||
Je nach Zustand der Trommeln können sie direkt wiederverwendet, repariert oder in ihre Einzelbestandteile zerlegt werden. Besonders Holz kann vielseitig genutzt werden, während Stahl und Kunststoff wertvolle Rohstoffe für neue Produkte liefern.
|
||||
Um Rohstoffverluste zu vermeiden, ist es entscheidend, beschädigte Kabeltrommeln nicht als Abfall zu betrachten, sondern als wertvolle Ressource.
|
||||
### Der Recyclingprozess: Von der Rücknahme zur Wiederverwertung
|
||||
|
||||
@@ -10,33 +10,16 @@ Eine sichere und nachhaltige Energiezukunft ist nur mit neuen Technologien, smar
|
||||
Doch wie sieht die Energieversorgung der Zukunft aus? Welche Rolle spielen Solarenergie, Windkraft und Kabelinfrastruktur? In diesem Artikel werfen wir einen Blick auf die wichtigsten Entwicklungen – von intelligenter Netzsteuerung bis hin zu nachhaltigen Kabelsystemen.
|
||||
## Solarenergie: Die Revolution auf unseren Dächern und Feldern
|
||||
Solarenergie hat sich längst von einer Nischenlösung zur tragenden Säule der Energiewende entwickelt. Neue Technologien machen Photovoltaik effizienter, flexibler und wirtschaftlicher – und das nicht nur auf Hausdächern, sondern auch auf Ackerflächen, in Fassaden und schwimmend auf Seen.
|
||||
### Die wichtigsten Innovationen in der Photovoltaik
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Technologie</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Vorteil</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Tandem-Solarzellen**</td>
|
||||
<td>Kombination aus Silizium und Perowskit für höhere Effizienz</td>
|
||||
<td>Bis zu 30 % mehr Leistung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Agri-PV**</td>
|
||||
<td>Solarmodule über landwirtschaftlichen Flächen</td>
|
||||
<td>Doppelte Flächennutzung für Energie und Ernte</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Bifaziale Module**</td>
|
||||
<td>Nutzen Licht von beiden Seiten</td>
|
||||
<td>10–20 % mehr Ertrag durch Reflexion</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Innovationen in der Photovoltaik"
|
||||
items={[
|
||||
{ title: "Tandem-Solarzellen", content: "**Beschreibung:** Kombination aus Silizium und Perowskit für höhere Effizienz. **Vorteil:** Bis zu 30 % mehr Leistung." },
|
||||
{ title: "Agri-PV", content: "**Beschreibung:** Solarmodule über landwirtschaftlichen Flächen. **Vorteil:** Doppelte Flächennutzung für Energie und Ernte." },
|
||||
{ title: "Bifaziale Module", content: "**Beschreibung:** Nutzen Licht von beiden Seiten. **Vorteil:** 10–20 % mehr Ertrag durch Reflexion." }
|
||||
]}
|
||||
/>
|
||||
|
||||
Doch die größte Herausforderung bleibt die Netzintegration: Solarenergie wird vor allem tagsüber produziert – doch unser Strombedarf ist morgens und abends am höchsten. Die Lösung? Smarte Speichertechnologien und intelligente Netzsteuerung, die Sonnenstrom genau dann verfügbar macht, wenn er gebraucht wird.
|
||||
## Windkraft: Höher, stärker, effizienter
|
||||
Windkraft ist neben der Solarenergie der wichtigste Pfeiler der erneuerbaren Energien. Während Offshore-Windparks auf dem Meer gigantische Mengen Strom liefern, sind Onshore-Windkraftanlagen nach wie vor das Rückgrat der nachhaltigen Energieversorgung.
|
||||
|
||||
@@ -17,29 +17,15 @@ Netzanschlusskabel für Windparks sind nicht einfach nur dickere Versionen von S
|
||||
- **Teilentladungen**, die über Jahre hinweg die Isolierung schädigen können
|
||||
- Elektromagnetische Einflüsse, die **Schirmung und Erdung** der Kabel erforderlich machen
|
||||
|
||||
### Thermische Belastungen
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Belastungsfaktor</th>
|
||||
<th>Auswirkung auf das Kabel</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Temperaturwechsel**</td>
|
||||
<td>Materialausdehnung, Risse in der Isolation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Dauerbelastung durch hohe Ströme**</td>
|
||||
<td>Erwärmung der Kabeladern</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Wärmeableitung**</td>
|
||||
<td>Entscheidend für die zulässige Strombelastbarkeit</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<TechnicalGrid
|
||||
title="Thermische Belastungen"
|
||||
items={[
|
||||
{ label: "Temperaturwechsel", value: "Materialausdehnung, Risse in der Isolation" },
|
||||
{ label: "Dauerbelastung", value: "Erwärmung der Kabeladern durch hohe Ströme" },
|
||||
{ label: "Wärmeableitung", value: "Entscheidend für die zulässige Strombelastbarkeit" }
|
||||
]}
|
||||
/>
|
||||
|
||||
### Umwelteinflüsse
|
||||
🌧 **Feuchtigkeit & Wasser** – Eindringen von Wasser kann die Isolation zerstören<br />🔥 **UV-Strahlung & extreme Temperaturen** – Gerade bei oberirdischer Verlegung relevant<br />🌍 **Chemische Einwirkungen & Bodenbewegungen** – Besonders bei Erdkabeln ein kritischer Faktor
|
||||
## Material und Konstruktion – Was macht ein gutes Netzanschlusskabel aus?
|
||||
|
||||
@@ -37,37 +37,18 @@ Niederspannung ist der Einstiegspunkt jeder elektrischen Infrastruktur. Kabel di
|
||||
In der Windkraftinfrastruktur wird das **NAYY** häufig für Beleuchtung, Kontrollsysteme oder die interne Stromverteilung in Betriebsgebäuden genutzt. Es ist robust, wartungsarm und bewährt sich seit Jahrzehnten in der Praxis.
|
||||
## Mittelspannungskabel – Die Arbeitstiere im Windpark
|
||||
Mittelspannungskabel sind das Rückgrat eines jeden Windparks. Sie decken den Spannungsbereich von **1 kV bis 45 kV** ab und sind essenziell für die Energieverteilung zwischen Windenergieanlagen und den Sammelpunkten. Diese Kabel sind enorm belastbar, müssen hohen Temperaturen, Spannungsfeldern und mechanischen Einflüssen standhalten.
|
||||
### Aufbau (am Beispiel NA2XS(F)2Y):
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Komponente</th>
|
||||
<th>Funktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Leiter</td>
|
||||
<td>Stromübertragung (Kupfer oder Aluminium)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Innenleiterschicht</td>
|
||||
<td>Feldsteuerung, Spannungsoptimierung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Isolierung (XLPE)</td>
|
||||
<td>Hohe elektrische Festigkeit, temperaturbeständig</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Schirmung</td>
|
||||
<td>Schutz vor Störungen, Erdung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Außenmantel</td>
|
||||
<td>Mechanischer Schutz, UV- und wasserresistent</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Aufbau (am Beispiel NA2XS(F)2Y)"
|
||||
items={[
|
||||
{ label: "Leiter", value: "Stromübertragung (Kupfer oder Aluminium)" },
|
||||
{ label: "Innenleiterschicht", value: "Feldsteuerung, Spannungsoptimierung" },
|
||||
{ label: "Isolierung (XLPE)", value: "Hohe elektrische Festigkeit, temperaturbeständig" },
|
||||
{ label: "Schirmung", value: "Schutz vor Störungen, Erdung" },
|
||||
{ label: "Außenmantel", value: "Mechanischer Schutz, UV- und wasserresistent" }
|
||||
]}
|
||||
/>
|
||||
|
||||
**Typische Kabeltypen:**
|
||||
- NA2XS(F)2Y (Aluminiumleiter, mit Feldsteuerung)
|
||||
- N2XSY (Kupferleiter, besonders leitfähig)
|
||||
|
||||
@@ -26,33 +26,17 @@ Typische Ursachen für Verzögerungen:
|
||||
- fehlende Abstimmung zwischen Lieferanten, Tiefbau und Montage
|
||||
|
||||
Gerade bei **Windparkprojekten** mit mehreren Kilometern des [**NA2XS(F)2Y**](/de/products/medium-voltage-cables/na2xsf2y/) ist eine exakte **Lieferkoordination** entscheidend. Teil- und Komplettlieferungen müssen so geplant sein, dass sie sich an den tatsächlichen **Baufortschritt** anpassen.
|
||||
**Effiziente Logistiklösungen können hier den Unterschied machen:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Herausforderung</th>
|
||||
<th>Lösung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Unterschiedliche Baufortschritte pro Turbine</td>
|
||||
<td>Teil- und Abschnittslieferungen passend zum Bauplan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eng getaktete Montagefenster</td>
|
||||
<td>Just-in-Time-Kabellieferung auf Baustelle</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fehlende Lagerflächen vor Ort</td>
|
||||
<td>Temporäre, projektbezogene Zwischenlagerung</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Witterungsabhängige Arbeiten</td>
|
||||
<td>Flexible Umlenkung von Lieferterminen und Materialzuteilung</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Effiziente Logistiklösungen"
|
||||
items={[
|
||||
{ label: "Unterschiedliche Baufortschritte pro Turbine", value: "Teil- und Abschnittslieferungen passend zum Bauplan" },
|
||||
{ label: "Eng getaktete Montagefenster", value: "Just-in-Time-Kabellieferung auf Baustelle" },
|
||||
{ label: "Fehlende Lagerflächen vor Ort", value: "Temporäre, projektbezogene Zwischenlagerung" },
|
||||
{ label: "Witterungsabhängige Arbeiten", value: "Flexible Umlenkung von Lieferterminen und Materialzuteilung" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Mit einer präzisen Planung der [**Kabelkapazitäten**](https://www.a-eberle.de/infobrief/infobrief-20/) und einer reaktionsfähigen Logistik lässt sich auch unter hohem Zeitdruck effizient arbeiten. So bleibt der **Netzanschluss des Windparks** termingerecht – und der Energiefluss gesichert.
|
||||
Sie möchten wissen, welche Kabelarten in einem Windpark verlegt werden? Dann schauen Sie sich diesen Artikel an:
|
||||
|
||||
@@ -62,32 +46,17 @@ Sie möchten wissen, welche Kabelarten in einem Windpark verlegt werden? Dann sc
|
||||
summary="Die Verkabelung ist ein zentrales Element jeder Windkraftanlage und beeinflusst maßgeblich die Effizienz, Sicherheit und Wirtschaftlichkeit eines Windparks.…"
|
||||
image="https://wind-turbine.com/i/53689/68738caa5e58ffdf06031cf2/2/1200/630/68738c85497af_KabelfreinenWindparkpng.png"
|
||||
/>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Herausforderung</th>
|
||||
<th>Praxisgerechte Lösung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hohe Liefermengen auf engem Baustellenareal</td>
|
||||
<td>Projektbezogene Lagerung in regionalen Hubs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Unterschiedliche Trommelgrößen für Mittelspannung und Niederspannung</td>
|
||||
<td>Abstimmung der Trommelabmessungen auf Zugkraft und Trommelgewicht</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Empfindliche Kabelmäntel bei Lagerung im Freien</td>
|
||||
<td>Witterungsbeständige Spezialverpackung und UV-Schutz</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fehlende Übersicht bei vielen Kabellieferungen</td>
|
||||
<td>Digitale Lieferübersichten und klare Trommelkennzeichnung</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Herausforderungen in der Praxis"
|
||||
items={[
|
||||
{ label: "Hohe Liefermengen auf engem Baustellenareal", value: "Projektbezogene Lagerung in regionalen Hubs" },
|
||||
{ label: "Unterschiedliche Trommelgrößen", value: "Abstimmung der Trommelabmessungen auf Zugkraft und Trommelgewicht" },
|
||||
{ label: "Empfindliche Kabelmäntel", value: "Witterungsbeständige Spezialverpackung und UV-Schutz" },
|
||||
{ label: "Fehlende Übersicht", value: "Digitale Lieferübersichten und klare Trommelkennzeichnung" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Eine klare [**Kabellogistikstrategie**](https://logistik-heute.de/galerien/mammutprojekt-kabellogistik-wie-kommen-tausende-tonnen-hgue-erdkabel-fuer-die-energiewende-zum-einsatzort-40875.html) ist der Schlüssel, um Materialengpässe und teure Stillstände zu vermeiden. So bleibt der Überblick gewahrt – selbst bei Projekten mit mehreren dutzend Kilometern **Windparkverkabelung**.
|
||||
Wer die **Verpackung, Lagerung und Kennzeichnung** frühzeitig in die Planung integriert, stellt sicher, dass die **Windpark Kabel** genau dort ankommen, wo sie gebraucht werden – ohne Zeitverlust und ohne Risiko für die Bauabfolge.
|
||||
## Herausforderung 3: Kurzfristige Projektänderungen
|
||||
|
||||
@@ -35,48 +35,21 @@ Während Standardleitungen bei extremen Temperaturen, mechanischen Belastungen o
|
||||
Kurz gesagt: Das H1Z2Z2-K 6mm² ist keine Lösung von der Stange – es ist ein spezialisiertes Energiekabel für eine Branche, die keine Kompromisse kennt.
|
||||
## Technische Daten und Aufbau im Detail
|
||||
Eine der Stärken dieses Kabels liegt in seinem Materialaufbau und der daraus resultierenden thermischen und mechanischen Belastbarkeit.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Eigenschaft**</th>
|
||||
<th>**Wert / Beschreibung**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Leiter</td>
|
||||
<td>Feindrähtiger, verzinnter Kupferleiter (Klasse 5)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Nennspannung</td>
|
||||
<td>1500 V DC (EN 50618 konform)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Prüfspannung</td>
|
||||
<td>6.5 kV</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Temperaturbereich Betrieb</td>
|
||||
<td>-40 °C bis +90 °C (Leiter max. +120 °C)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Isolierung und Mantel</td>
|
||||
<td>Vernetztes Polyolefin, halogenfrei</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Außendurchmesser (6mm²)</td>
|
||||
<td>ca. 6,4 mm</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Biegeradius</td>
|
||||
<td>min. 4 × Kabeldurchmesser</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max. Strombelastbarkeit (frei verlegt)</td>
|
||||
<td>bis 70 A (je nach Umgebungstemperatur)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Technische Daten & Aufbau"
|
||||
items={[
|
||||
{ label: "Leiter", value: "Feindrähtiger, verzinnter Kupferleiter (Klasse 5)" },
|
||||
{ label: "Nennspannung", value: "1500 V DC (EN 50618 konform)" },
|
||||
{ label: "Prüfspannung", value: "6.5 kV" },
|
||||
{ label: "Temperaturbereich Betrieb", value: "-40 °C bis +90 °C (Leiter max. +120 °C)" },
|
||||
{ label: "Isolierung und Mantel", value: "Vernetztes Polyolefin, halogenfrei" },
|
||||
{ label: "Außendurchmesser (6mm²)", value: "ca. 6,4 mm" },
|
||||
{ label: "Biegeradius", value: "min. 4 × Kabeldurchmesser" },
|
||||
{ label: "Max. Strombelastbarkeit", value: "bis 70 A (je nach Umgebungstemperatur)" }
|
||||
]}
|
||||
/>
|
||||
|
||||
## Normen und Zertifizierungen: EN 50618 & Co.
|
||||
Das H1Z2Z2-K 6mm² erfüllt alle wesentlichen Standards für den Einsatz in Photovoltaikanlagen. Diese Normen garantieren Sicherheit, Langlebigkeit und Konformität mit gesetzlichen Vorgaben.
|
||||
### EN 50618 – europäischer Standard für Solarkabel
|
||||
|
||||
@@ -11,33 +11,16 @@ The vision is clear: A Europe powered 100% by renewable energy. But while solar
|
||||
In the end, it’s not just about generating more power, but about smart grids that can transport it reliably and with minimal losses.
|
||||
## The problem: Old grids for a new energy future
|
||||
Today’s power infrastructure was built for centralized large-scale power plants. But renewable energy works differently: It is decentralized, weather-dependent, and requires flexible grids. This creates a massive need for restructuring.
|
||||
### Why our grid is currently overwhelmed:
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Problem</th>
|
||||
<th>Cause</th>
|
||||
<th>Solution?</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Grid bottlenecks**</td>
|
||||
<td>Old power lines designed for central plants, not decentralized energy</td>
|
||||
<td>New high- & medium-voltage cables</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Curtailment of solar & wind power**</td>
|
||||
<td>Grid cannot absorb enough electricity</td>
|
||||
<td>Smart grids & storage solutions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Long transmission distances**</td>
|
||||
<td>Generation is often far from consumption</td>
|
||||
<td>High-performance cables & local grids</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Why our grid is currently overwhelmed"
|
||||
items={[
|
||||
{ title: "Grid bottlenecks", content: "**Cause:** Old power lines designed for central plants, not decentralized energy. **Solution:** New high- & medium-voltage cables." },
|
||||
{ title: "Curtailment of solar & wind power", content: "**Cause:** Grid cannot absorb enough electricity. **Solution:** Smart grids & storage solutions." },
|
||||
{ title: "Long transmission distances", content: "**Cause:** Generation is often far from consumption. **Solution:** High-performance cables & local grids." }
|
||||
]}
|
||||
/>
|
||||
|
||||
⚠️ A grid from the past cannot transport the energy of the future!
|
||||
Anyone investing only in renewable energy systems today while ignoring cable infrastructure will be left with expensive, unused electricity tomorrow.
|
||||
## Which cables do we need for the energy transition?
|
||||
@@ -48,39 +31,19 @@ Not all cables are created equal – and not every cable is suited for the chall
|
||||
💡 The better the cable, the less electricity is lost along the way – and the greener the energy becomes!
|
||||
## Solar and wind farms aren’t enough
|
||||
Without the right cables, electricity stays where it’s generated. But what kind of grid expansion really makes sense?
|
||||
### Underground cables vs. overhead lines – which is the better choice?
|
||||
A key question in grid expansion is whether new power lines should be built as overhead lines or underground cables. Both options have pros and cons, but in the long run, underground cabling offers significant advantages in terms of reliability, environmental protection, and grid stability.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Criteria</th>
|
||||
<th>Underground Cable</th>
|
||||
<th>Overhead Line</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Grid stability**</td>
|
||||
<td>Very high</td>
|
||||
<td>Moderate</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Environmental impact**</td>
|
||||
<td>Unobtrusive, no disruption to landscapes</td>
|
||||
<td>Visible, problematic for birds</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Maintenance & lifespan**</td>
|
||||
<td>Minimal maintenance, long lifespan</td>
|
||||
<td>Weather-sensitive, shorter lifespan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Costs**</td>
|
||||
<td>Higher installation costs, but more efficient operation</td>
|
||||
<td>Cheaper to build, but higher long-term costs</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Underground cables vs. overhead lines – which is the better choice?"
|
||||
leftLabel="Underground Cable"
|
||||
rightLabel="Overhead Line"
|
||||
items={[
|
||||
{ label: "Grid stability", leftValue: "Very high", rightValue: "Moderate" },
|
||||
{ label: "Environmental impact", leftValue: "Unobtrusive, no disruption to landscapes", rightValue: "Visible, problematic for birds" },
|
||||
{ label: "Maintenance & lifespan", leftValue: "Minimal maintenance, long lifespan", rightValue: "Weather-sensitive, shorter lifespan" },
|
||||
{ label: "Costs", leftValue: "Higher installation costs, but more efficient operation", rightValue: "Cheaper to build, but higher long-term costs" }
|
||||
]}
|
||||
/>
|
||||
|
||||
In the past, overhead lines were favored due to lower construction costs. However, modern demands for grid stability, environmental protection, and aesthetics increasingly support underground cables. As a result, many countries are now adopting underground cabling as the standard for new high- and medium-voltage power lines.
|
||||
For those who want to dive deeper into the topic, here’s a **detailed analysis** comparing overhead lines and underground cables:
|
||||
<VisualLinkPreview
|
||||
|
||||
@@ -9,48 +9,21 @@ category: Kabel Technologie
|
||||
Particularly with cables such as **NA2XS(F)2Y** or **NAYY** for **wind turbines**, the choice of material determines costs, performance and service life. Copper impresses with its high electrical conductivity, while aluminum scores with low costs and low weight. But which material is technically and economically the better choice in the long term? This article provides a detailed analysis of the advantages and disadvantages of both options.
|
||||
### Electrical and Mechanical Properties Compared
|
||||
Copper has been the preferred material for electrical wiring for decades. It offers high conductivity and excellent mechanical stability. Aluminum, on the other hand, is significantly lighter but has lower electrical conductivity. This means aluminum cables require a larger cross-section to transmit the same current.
|
||||
<h4>Comparison of Properties</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Copper</th>
|
||||
<th>Aluminum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Electrical Conductivity</td>
|
||||
<td>58 MS/m</td>
|
||||
<td>35 MS/m</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Density (g/cm³)</td>
|
||||
<td>8.96</td>
|
||||
<td>2.70</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Corrosion Resistance</td>
|
||||
<td>Very high</td>
|
||||
<td>Medium (oxidation)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mechanical Strength</td>
|
||||
<td>High</td>
|
||||
<td>Medium</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weight</td>
|
||||
<td>High</td>
|
||||
<td>Low</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Price per ton</td>
|
||||
<td>€8,000 – 9,000</td>
|
||||
<td>€2,300 – 2,500</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Comparison of Properties"
|
||||
leftLabel="Copper"
|
||||
rightLabel="Aluminum"
|
||||
items={[
|
||||
{ label: "Electrical Conductivity", leftValue: "58 MS/m", rightValue: "35 MS/m" },
|
||||
{ label: "Density (g/cm³)", leftValue: "8.96", rightValue: "2.70" },
|
||||
{ label: "Corrosion Resistance", leftValue: "Very high", rightValue: "Medium (oxidation)" },
|
||||
{ label: "Mechanical Strength", leftValue: "High", rightValue: "Medium" },
|
||||
{ label: "Weight", leftValue: "High", rightValue: "Low" },
|
||||
{ label: "Price per ton", leftValue: "€8,000 – 9,000", rightValue: "€2,300 – 2,500" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Although aluminum offers weight savings in transport and installation, it requires larger cross-sections to achieve the same performance. This can impact space requirements in cable trays and mechanical stability. Additionally, aluminum is more prone to oxidation, which can lead to contact issues, whereas copper maintains its conductivity over long periods without significant quality loss. In humid or salty environments, such as offshore wind farms, this can be a crucial factor.
|
||||
### Costs: Acquisition, Installation, and Operation
|
||||
<h4>Material Costs</h4>
|
||||
@@ -110,54 +83,22 @@ Aluminum excels in **production efficiency and recycling**, while **copper’s d
|
||||
- **Copper** lasts longer, requires **fewer replacements**, and thus also contributes to sustainability.
|
||||
|
||||
Ultimately, the best choice depends on **whether short-term efficiency or long-term durability** is the priority.
|
||||
### Which Solution is Best for Wind Farms?
|
||||
<h4>Comparison of Key Factors</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Factor</th>
|
||||
<th>Copper</th>
|
||||
<th>Aluminum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Efficiency**</td>
|
||||
<td>Better</td>
|
||||
<td>Higher losses</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Cost (Material & Purchase)**</td>
|
||||
<td>More expensive</td>
|
||||
<td>Cheaper</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Installation Effort**</td>
|
||||
<td>Heavier, more complex</td>
|
||||
<td>Lighter, easier</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Operating Costs (Losses & Maintenance)**</td>
|
||||
<td>Lower</td>
|
||||
<td>Higher</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Corrosion Resistance**</td>
|
||||
<td>Very good</td>
|
||||
<td>Medium</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Lifespan**</td>
|
||||
<td>Longer</td>
|
||||
<td>Shorter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Environmental Impact**</td>
|
||||
<td>High energy consumption</td>
|
||||
<td>Better with recycling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ComparisonGrid
|
||||
title="Which Solution is Best for Wind Farms?"
|
||||
leftLabel="Copper"
|
||||
rightLabel="Aluminum"
|
||||
items={[
|
||||
{ label: "Efficiency", leftValue: "Better", rightValue: "Higher losses" },
|
||||
{ label: "Cost (Material & Purchase)", leftValue: "More expensive", rightValue: "Cheaper" },
|
||||
{ label: "Installation Effort", leftValue: "Heavier, more complex", rightValue: "Lighter, easier" },
|
||||
{ label: "Operating Costs (Losses & Maintenance)", leftValue: "Lower", rightValue: "Higher" },
|
||||
{ label: "Corrosion Resistance", leftValue: "Very good", rightValue: "Medium" },
|
||||
{ label: "Lifespan", leftValue: "Longer", rightValue: "Shorter" },
|
||||
{ label: "Environmental Impact", leftValue: "High energy consumption", rightValue: "Better with recycling" }
|
||||
]}
|
||||
/>
|
||||
|
||||
### Recommended Applications
|
||||
- **Aluminum** is ideal for **long medium-voltage routes**, where weight and cost are crucial factors.
|
||||
- **Copper** is the better choice for **grid connections, substations, and critical areas**, where **efficiency and longevity** matter most.
|
||||
|
||||
@@ -56,34 +56,15 @@ Typical causes of delays:
|
||||
|
||||
Especially for **wind farm projects** involving several kilometers of [**NA2XS(F)2Y**](/en/products/medium-voltage-cables/na2xsf2y/), precise **delivery coordination** is essential. Partial and complete deliveries must be scheduled to match the actual **construction progress**.
|
||||
|
||||
**Efficient logistics solutions can make the difference:**
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Challenge</th>
|
||||
<th>Solution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Different construction progress per turbine</td>
|
||||
<td>Partial and phased deliveries matched to the build schedule</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tight installation windows</td>
|
||||
<td>Just-in-time cable delivery to site</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Limited storage space on site</td>
|
||||
<td>Temporary, project-specific intermediate storage</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Weather-dependent operations</td>
|
||||
<td>Flexible adjustment of delivery schedules and material allocation</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<TechnicalGrid
|
||||
title="Efficient Logistics Solutions"
|
||||
items={[
|
||||
{ label: "Different construction progress per turbine", value: "Partial and phased deliveries matched to the build schedule" },
|
||||
{ label: "Tight installation windows", value: "Just-in-time cable delivery to site" },
|
||||
{ label: "Limited storage space on site", value: "Temporary, project-specific intermediate storage" },
|
||||
{ label: "Weather-dependent operations", value: "Flexible adjustment of delivery schedules and material allocation" }
|
||||
]}
|
||||
/>
|
||||
|
||||
With precise [**cable capacity**](https://www.a-eberle.de/infobrief/infobrief-20/) planning and responsive logistics, even high-pressure timelines can be handled efficiently. This ensures the **wind farm’s grid connection** stays on schedule – and energy flows reliably.
|
||||
|
||||
@@ -114,34 +95,15 @@ The bigger the project, the more complex the **material coordination** becomes:
|
||||
|
||||
**Our experience shows:** Planning for storage and packaging units in advance not only saves time but also reduces the risk of material loss and reorders.
|
||||
|
||||
**Typical requirements and solutions:**
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Challenge</th>
|
||||
<th>Practical solution</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>High delivery volumes on tight site space</td>
|
||||
<td>Project-specific storage in regional hubs</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Different drum sizes for medium- and low-voltage</td>
|
||||
<td>Adjust drum dimensions to pulling force and weight</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sensitive cable sheaths when stored outdoors</td>
|
||||
<td>Weatherproof packaging and UV protection</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lack of overview with many cable deliveries</td>
|
||||
<td>Digital delivery summaries and clear drum labeling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<TechnicalGrid
|
||||
title="Typical Requirements and Solutions"
|
||||
items={[
|
||||
{ label: "High delivery volumes on tight site space", value: "Project-specific storage in regional hubs" },
|
||||
{ label: "Different drum sizes", value: "Adjust drum dimensions to pulling force and weight" },
|
||||
{ label: "Sensitive cable sheaths", value: "Weatherproof packaging and UV protection" },
|
||||
{ label: "Lack of overview", value: "Digital delivery summaries and clear drum labeling" }
|
||||
]}
|
||||
/>
|
||||
|
||||
A clear [**cable logistics strategy**](https://logistik-heute.de/galerien/mammutprojekt-kabellogistik-wie-kommen-tausende-tonnen-hgue-erdkabel-fuer-die-energiewende-zum-einsatzort-40875.html) is the key to avoiding material shortages and costly downtime. This helps maintain control – even for projects involving dozens of kilometers of **wind farm cabling**.
|
||||
|
||||
|
||||
@@ -10,33 +10,16 @@ A secure and sustainable energy future is only possible with new technologies, s
|
||||
But what will the energy supply of the future look like? What role will solar energy, wind power and cable infrastructure play? In this article, we take a look at the most important developments – from intelligent grid control to sustainable cable systems.
|
||||
## Solar Energy: the revolution on our roofs and fields
|
||||
Solar energy has long evolved from a niche solution into a cornerstone of the energy transition. New technologies are making photovoltaics more efficient, flexible, and economical—not just on rooftops but also on farmland, building facades, and even floating on lakes.
|
||||
### The most important innovations in photovoltaics
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Technology</th>
|
||||
<th>Description</th>
|
||||
<th>Advantage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Tandem solar cells</td>
|
||||
<td>Combination of silicon and perovskite for higher efficiency</td>
|
||||
<td>Up to 30% more power output</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Agri-PV</td>
|
||||
<td>Solar panels above agricultural land</td>
|
||||
<td>Dual land use for energy and crops</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bifacial modules</td>
|
||||
<td>Capture light from both sides</td>
|
||||
<td>10–20% higher yield through reflection</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Innovations in Photovoltaics"
|
||||
items={[
|
||||
{ title: "Tandem solar cells", content: "**Description:** Combination of silicon and perovskite for higher efficiency. **Advantage:** Up to 30% more power output." },
|
||||
{ title: "Agri-PV", content: "**Description:** Solar panels above agricultural land. **Advantage:** Dual land use for energy and crops." },
|
||||
{ title: "Bifacial modules", content: "**Description:** Capture light from both sides. **Advantage:** 10–20% higher yield through reflection." }
|
||||
]}
|
||||
/>
|
||||
|
||||
However, the biggest challenge remains grid integration: Solar energy is primarily generated during the day, but our electricity demand peaks in the morning and evening. The solution? Smart storage technologies and intelligent grid management that make solar power available exactly when it’s needed.
|
||||
## Wind Power: higher, stronger, more efficient
|
||||
Wind power is, alongside solar energy, the most important pillar of renewable energy. While offshore wind farms at sea generate massive amounts of electricity, onshore wind turbines remain the backbone of sustainable energy supply.
|
||||
|
||||
@@ -17,33 +17,17 @@ Modern onshore wind farms consist not only of turbines, but of a complex network
|
||||
What many underestimate: The cable routes in a wind farm often make up a significant part of the total investment. They are not just a link – they are the **critical infrastructure** on which everything is built.
|
||||
## Holistic Planning: Foundation for Sustainable Infrastructure
|
||||
Integrating wind farms into the power grid requires a systemic approach. Sound planning takes into account not only performance requirements, but also environmental conditions, expansion scenarios and approval processes.
|
||||
**Key planning aspects include:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Area</th>
|
||||
<th>Planning Focus</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Route Guidance</td>
|
||||
<td>Geology, ownership, protected areas</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Grid Connection</td>
|
||||
<td>Voltage level, feed-in points, redundancy</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Load Profile</td>
|
||||
<td>Design for base and peak loads</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Scalability</td>
|
||||
<td>Expansion potential for future systems</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Key Planning Aspects"
|
||||
items={[
|
||||
{ title: "Route Guidance", content: "Geology, ownership, protected areas" },
|
||||
{ title: "Grid Connection", content: "Voltage level, feed-in points, redundancy" },
|
||||
{ title: "Load Profile", content: "Design for base and peak loads" },
|
||||
{ title: "Scalability", content: "Expansion potential for future systems" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Professional planning not only ensures security of supply, but also reduces operating costs in the long term and enables flexible responses to grid requirements.
|
||||
You can find more information here on how wind energy basically works:
|
||||
|
||||
|
||||
@@ -9,62 +9,32 @@ category: Cable Technology
|
||||
### **Growth needs structure**
|
||||
**Growth sounds good –** more projects, more customers, more revenue.<br />But real, sustainable growth needs more than just speed: it needs **transparency, planning, and control**.
|
||||
To ensure that ambitious goals don't turn into a blind flight, we have decided to specifically strengthen our team. Because the larger the projects become, the more important the ability to recognize developments early and steer them specifically becomes.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Why we are expanding our controlling**</th>
|
||||
<th>**What we want to achieve with it**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>More projects at home and abroad</td>
|
||||
<td>Clear figures and reliable forecasts</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Increasing requirements in sales</td>
|
||||
<td>Better overview of trends and margins</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>More complex processes</td>
|
||||
<td>Faster, well-founded decisions</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sustainable growth</td>
|
||||
<td>Stability instead of coincidence</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Why we are expanding our controlling"
|
||||
items={[
|
||||
{ label: "More projects at home and abroad", value: "Clear figures and reliable forecasts" },
|
||||
{ label: "Increasing requirements in sales", value: "Better overview of trends and margins" },
|
||||
{ label: "More complex processes", value: "Faster, well-founded decisions" },
|
||||
{ label: "Sustainable growth", value: "Stability instead of coincidence" }
|
||||
]}
|
||||
/>
|
||||
|
||||
**In short:** We don't just want to grow – we want to understand <em>how</em> we grow.<br />That's why we will rely even more on **qualitative controlling** in the future and are happy about support that makes exactly that possible.
|
||||
### **New strength in the team**
|
||||
With [**Julia Havasi**](https://www.linkedin.com/in/julia-havasi-18556b233/) we have found exactly the reinforcement we were looking for: analytically strong, structured in thinking, and with a good sense for the dynamics between numbers and people.
|
||||
As **Senior Financial & Sales Controller**, Julia will be responsible for our financial and sales controlling in the future. Her goal: **more clarity, more foresight, more substance** in every decision.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Area of responsibility**</th>
|
||||
<th>**Goal**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Financial Controlling**</td>
|
||||
<td>Clean figures, clear structures, and comprehensible reports</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Sales Controlling**</td>
|
||||
<td>Analyze sales figures, identify potential, derive trends</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Forecasts & Analyses**</td>
|
||||
<td>Early assessment of market movements and investment opportunities</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Reporting & Communication**</td>
|
||||
<td>Prepare complex data so that everyone understands it – quickly and precisely</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Areas of Responsibility & Goals"
|
||||
items={[
|
||||
{ label: "Financial Controlling", value: "Clean figures, clear structures, and comprehensible reports" },
|
||||
{ label: "Sales Controlling", value: "Analyze sales figures, identify potential, derive trends" },
|
||||
{ label: "Forecasts & Analyses", value: "Early assessment of market movements and investment opportunities" },
|
||||
{ label: "Reporting & Communication", value: "Prepare complex data so that everyone understands it – quickly and precisely" }
|
||||
]}
|
||||
/>
|
||||
|
||||
Julia will thus play a central role in the further development of KLZ – as an interface between **management, sales, and strategy**.<br />Or, to put it more casually: she ensures that we not only know **where we stand**, but also **where we are going**.
|
||||
### **Experience that connects**
|
||||
**Understanding of numbers meets practical experience.**<br />With over 13 years of experience in controlling and sales, [**Julia Havasi**](https://www.linkedin.com/in/julia-havasi-18556b233/) brings the ideal combination of analytical precision and entrepreneurial thinking.
|
||||
|
||||
@@ -11,33 +11,16 @@ Cable drums play a crucial role in the wind power industry—they ensure the saf
|
||||
Without a well-thought-out recycling concept, vast amounts of wood, steel, and plastic would go to waste. However, efficient solutions already exist to return cable drums to the raw material cycle and minimize environmental impact.
|
||||
### Materials and their reuse
|
||||
Cable drums are made from different materials, each offering unique recycling possibilities. The way they are reintegrated into the circular economy depends on whether the material can be directly reused or needs further processing.
|
||||
<h4>Main materials of cable drums and their recycling options</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Material**</th>
|
||||
<th>**Properties**</th>
|
||||
<th>**Recycling options**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Wood**</td>
|
||||
<td>Biodegradable, easy to repair</td>
|
||||
<td>Upcycling, biomass, pallet production</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Steel**</td>
|
||||
<td>Durable, reusable, corrosion-resistant</td>
|
||||
<td>Melting down, reprocessing</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Plastic**</td>
|
||||
<td>Weather-resistant, lightweight, long-lasting</td>
|
||||
<td>Granulate production, upcycling</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<StickyNarrative
|
||||
title="Materials and their Reuse"
|
||||
items={[
|
||||
{ title: "Wood", content: "**Properties:** Biodegradable, easy to repair. **Recycling:** Upcycling, biomass, pallet production." },
|
||||
{ title: "Steel", content: "**Properties:** Durable, reusable, corrosion-resistant. **Recycling:** Melting down, reprocessing." },
|
||||
{ title: "Plastic", content: "**Properties:** Weather-resistant, lightweight, long-lasting. **Recycling:** Granulate production, upcycling." }
|
||||
]}
|
||||
/>
|
||||
|
||||
Depending on their condition, cable drums can be directly reused, repaired, or dismantled into their individual components. Wood, in particular, offers versatile reuse possibilities, while steel and plastic serve as valuable raw materials for new products.
|
||||
To prevent raw material waste, it is essential to view damaged cable drums not as disposable waste but as a valuable resource.
|
||||
### The recycling process: from return to reuse
|
||||
|
||||
@@ -42,48 +42,21 @@ While standard cables quickly reach their limits under extreme temperatures, mec
|
||||
In short: the H1Z2Z2-K 6mm² is no off-the-shelf solution – it’s a specialized energy cable for an industry that doesn’t compromise.
|
||||
## Technical specifications and construction in detail
|
||||
One of the strengths of this cable lies in its material structure and the resulting thermal and mechanical durability.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>**Property**</th>
|
||||
<th>**Value / Description**</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Conductor</td>
|
||||
<td>Fine-stranded, tinned copper conductor (Class 5)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Rated voltage</td>
|
||||
<td>1500 V DC (compliant with EN 50618)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Test voltage</td>
|
||||
<td>6.5 kV</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Operating temperature range</td>
|
||||
<td>-40 °C to +90 °C (conductor max. +120 °C)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Insulation and sheath</td>
|
||||
<td>Cross-linked polyolefin, halogen-free</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Outer diameter (6mm²)</td>
|
||||
<td>approx. 6.4 mm</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bending radius</td>
|
||||
<td>min. 4 × cable diameter</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max. current capacity (free laid)</td>
|
||||
<td>up to 70 A (depending on ambient temperature)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Technical Specifications & Construction"
|
||||
items={[
|
||||
{ label: "Conductor", value: "Fine-stranded, tinned copper conductor (Class 5)" },
|
||||
{ label: "Rated voltage", value: "1500 V DC (compliant with EN 50618)" },
|
||||
{ label: "Test voltage", value: "6.5 kV" },
|
||||
{ label: "Operating temperature range", value: "-40 °C to +90 °C (conductor max. +120 °C)" },
|
||||
{ label: "Insulation and sheath", value: "Cross-linked polyolefin, halogen-free" },
|
||||
{ label: "Outer diameter (6mm²)", value: "approx. 6.4 mm" },
|
||||
{ label: "Bending radius", value: "min. 4 × cable diameter" },
|
||||
{ label: "Max. current capacity", value: "up to 70 A (depending on ambient temperature)" }
|
||||
]}
|
||||
/>
|
||||
|
||||
## Standards and certifications: EN 50618 & more
|
||||
The H1Z2Z2-K 6mm² meets all key standards for use in photovoltaic systems. These standards ensure safety, durability, and compliance with legal requirements.
|
||||
### EN 50618 – European standard for solar cables
|
||||
|
||||
@@ -20,37 +20,18 @@ Projects in the 10 to 30 kV range bring recurring demands – regardless of whet
|
||||
The reality on construction sites shows: a cable that lacks flexibility, has large bending radii, or reaches its thermal limits too quickly not only delays implementation – it also endangers operational safety.
|
||||
<h4>Why the NA2XS(F)2Y is ideal for modern energy infrastructure</h4>
|
||||
The [**NA2XS(F)2Y**](/en/products/medium-voltage-cables/na2xsf2y/) meets these requirements with a well-thought-out, field-proven design. It is built for **long-term operation under load** and shows its strengths particularly in industrial and energy networks.
|
||||
**Key features at a glance:**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature</th>
|
||||
<th>Benefit for your project</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>**Aluminum conductor**</td>
|
||||
<td>High conductivity, low transmission losses</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**XLPE insulation**</td>
|
||||
<td>High thermal resistance, durable and stable</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**EMC-optimized design**</td>
|
||||
<td>Interference-free operation in sensitive network environments</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Standard-compliant design (IEC)**</td>
|
||||
<td>Safety in tenders, testing, and operation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**Robust outer construction**</td>
|
||||
<td>Suitable for all common installation methods</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Key Features at a Glance"
|
||||
items={[
|
||||
{ label: "Aluminum conductor", value: "High conductivity, low transmission losses" },
|
||||
{ label: "XLPE insulation", value: "High thermal resistance, durable and stable" },
|
||||
{ label: "EMC-optimized design", value: "Interference-free operation in sensitive network environments" },
|
||||
{ label: "Standard-compliant design (IEC)", value: "Safety in tenders, testing, and operation" },
|
||||
{ label: "Robust outer construction", value: "Suitable for all common installation methods" }
|
||||
]}
|
||||
/>
|
||||
|
||||
In other words: with the [NA2XS(F)2Y](/en/products/medium-voltage-cables/na2xsf2y/), you can build networks that not only work on paper but also perform reliably in practice – long-term, low-maintenance, and safe.
|
||||
Learn more about why grid expansion is so important here:
|
||||
|
||||
|
||||
@@ -14,29 +14,16 @@ Grid connection cables for wind farms are not just thicker versions of standard
|
||||
✔ High tensile forces when pulling and laying the cables<br />✔ Bending radii that must be maintained to prevent insulation damage<br />✔ Vibrations from wind turbines that transfer through the foundations to the cables
|
||||
### **Electrical stress**
|
||||
High voltage spikes due to sudden feed-in fluctuations<br />Partial discharges that can damage insulation over the years<br />Electromagnetic influences requiring shielding and grounding of the cables
|
||||
### **Thermal loads**
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Load factor</th>
|
||||
<th>Impact on the cable</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Temperature fluctuations</td>
|
||||
<td>Material expansion, cracks in the insulation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Continuous high current load</td>
|
||||
<td>Heating of the cable conductors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Heat dissipation</td>
|
||||
<td>Crucial for permissible current capacity</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<TechnicalGrid
|
||||
title="Thermal Loads"
|
||||
items={[
|
||||
{ label: "Temperature fluctuations", value: "Material expansion, cracks in the insulation" },
|
||||
{ label: "Continuous high current load", value: "Heating of the cable conductors" },
|
||||
{ label: "Heat dissipation", value: "Crucial for permissible current capacity" }
|
||||
]}
|
||||
/>
|
||||
|
||||
### **Environmental influences**
|
||||
🌧 Moisture & water – Water ingress can destroy insulation<br />🔥 UV radiation & extreme temperatures – Particularly relevant for above-ground installation<br />🌍 Chemical exposure & ground movements – A critical factor, especially for underground cables
|
||||
## **Material and construction – What makes a good grid connection cable?**
|
||||
|
||||
@@ -80,7 +80,7 @@ locale: de
|
||||
<div class="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" dir="auto" data-message-author-role="assistant" data-message-id="66eb3f45-dd35-419f-8c8e-be50fee94d71" data-message-model-slug="gpt-4o">
|
||||
<div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]">
|
||||
<div class="markdown prose w-full break-words dark:prose-invert dark">
|
||||
<h2>„Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.“</h2>
|
||||
<h2>Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@ locale: de
|
||||
<div class="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&]:mt-5" dir="auto" data-message-author-role="assistant" data-message-id="d3bd1bc9-d279-4699-991f-cd5809bda6d7" data-message-model-slug="gpt-4o">
|
||||
<div class="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]">
|
||||
<div class="markdown prose w-full break-words dark:prose-invert dark">
|
||||
<h2>„Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.“</h2>
|
||||
<h2>Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -60,13 +60,13 @@ locale: en
|
||||
|
||||
<h1>Michael Bodemer</h1>
|
||||
|
||||
<h2>“Challenges exist to be solved, not to debate how complicated they are.”</h2>
|
||||
<h2>Challenges exist to be solved, not to debate how complicated they are.</h2>
|
||||
Michael Bodemer is the go-to guy when things get complicated—and let’s face it, that’s often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. He’s not just detail-oriented; he’s a driving force—whether it’s in planning, customer interactions, or securing the best cables for every project.
|
||||
<h3><strong>A Legacy of Excellence in Every Connection</strong></h3>
|
||||
<p>At KLZ, our expertise is built on generations of dedication to the energy industry. With decades of hands-on experience, we’ve grown alongside the evolution of cable technology, combining traditional craftsmanship with modern innovation. Each project we take on reflects a deep understanding of what it takes to create lasting, reliable energy solutions.</p>
|
||||
<p>Paired with historic illustrations from the industry’s early days, our story is a reminder of how far cables have come – and how much care has always gone into connecting the world.
|
||||
<h1>Klaus Mintel</h1>
|
||||
|
||||
<h2>“Sometimes all it takes is a clear head and a good cable to make the world a little better.”</h2>
|
||||
<h2>Sometimes all it takes is a clear head and a good cable to make the world a little better.</h2>
|
||||
Klaus is the man with the experience, bringing perspective and calm to the table—even when cable chaos threatens to take over. With impressive industry knowledge and a network as solid as our cables, he ensures everything runs smoothly. Klaus isn’t just a problem solver; he’s a strategic thinker who knows how to get to the point with a touch of humor.
|
||||
<h2>Our manifesto</h2>
|
||||
|
||||
@@ -19,6 +19,7 @@ The design should feel **Industrial yet Modern**, **Reliable**, and **Sustainabl
|
||||
|
||||
### 2.2 Color Strategy
|
||||
- **Trust (Primary Blue)**: Deep, vibrant blue used for core branding and primary actions. Represents stability and the flow of electricity.
|
||||
- **Saturated Blue (#011dff)**: A high-intensity blue used for specific high-impact accents and digital-first elements.
|
||||
- **Growth (Accent Green)**: A bright, energetic green used exclusively for highlights related to renewable energy and the future.
|
||||
- **Foundation (Neutrals)**: A range of blacks, dark grays, and clean whites to provide a professional, industrial backdrop.
|
||||
|
||||
|
||||
22
lib/blog.ts
22
lib/blog.ts
@@ -73,3 +73,25 @@ export async function getAdjacentPosts(slug: string, locale: string): Promise<{
|
||||
|
||||
return { prev, next };
|
||||
}
|
||||
|
||||
export function getReadingTime(content: string): number {
|
||||
const wordsPerMinute = 200;
|
||||
const noOfWords = content.split(/\s/g).length;
|
||||
const minutes = noOfWords / wordsPerMinute;
|
||||
return Math.ceil(minutes);
|
||||
}
|
||||
|
||||
export function getHeadings(content: string): { id: string; text: string; level: number }[] {
|
||||
const headingLines = content.split('\n').filter((line) => line.match(/^#{2,3}\s/));
|
||||
|
||||
return headingLines.map((line) => {
|
||||
const level = line.match(/^#+/)?.[0].length || 0;
|
||||
const text = line.replace(/^#+\s/, '').trim();
|
||||
const id = text
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/\s+/g, '-');
|
||||
|
||||
return { id, text, level };
|
||||
});
|
||||
}
|
||||
|
||||
54
lib/datasheets.ts
Normal file
54
lib/datasheets.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
/**
|
||||
* Finds the datasheet PDF path for a given product slug and locale.
|
||||
* Checks public/datasheets for matching files.
|
||||
*/
|
||||
export function getDatasheetPath(slug: string, locale: string): string | null {
|
||||
const datasheetsDir = path.join(process.cwd(), 'public', 'datasheets');
|
||||
|
||||
if (!fs.existsSync(datasheetsDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Normalize slug: remove common suffixes that might not be in the PDF filename
|
||||
const normalizedSlug = slug.replace(/-hv$|-mv$/, '');
|
||||
|
||||
// List of patterns to try for the current locale
|
||||
const patterns = [
|
||||
`${slug}-${locale}.pdf`,
|
||||
`${slug}-2-${locale}.pdf`,
|
||||
`${slug}-3-${locale}.pdf`,
|
||||
`${normalizedSlug}-${locale}.pdf`,
|
||||
`${normalizedSlug}-2-${locale}.pdf`,
|
||||
`${normalizedSlug}-3-${locale}.pdf`,
|
||||
];
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const filePath = path.join(datasheetsDir, pattern);
|
||||
if (fs.existsSync(filePath)) {
|
||||
return `/datasheets/${pattern}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to English if locale is not 'en'
|
||||
if (locale !== 'en') {
|
||||
const enPatterns = [
|
||||
`${slug}-en.pdf`,
|
||||
`${slug}-2-en.pdf`,
|
||||
`${slug}-3-en.pdf`,
|
||||
`${normalizedSlug}-en.pdf`,
|
||||
`${normalizedSlug}-2-en.pdf`,
|
||||
`${normalizedSlug}-3-en.pdf`,
|
||||
];
|
||||
for (const pattern of enPatterns) {
|
||||
const filePath = path.join(datasheetsDir, pattern);
|
||||
if (fs.existsSync(filePath)) {
|
||||
return `/datasheets/${pattern}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -73,6 +73,13 @@ export async function getAllProductSlugs(locale: string): Promise<string[]> {
|
||||
|
||||
export async function getAllProducts(locale: string): Promise<ProductMdx[]> {
|
||||
const slugs = await getAllProductSlugs(locale);
|
||||
const products = await Promise.all(slugs.map(slug => getProductBySlug(slug, locale)));
|
||||
let allSlugs = slugs;
|
||||
|
||||
if (locale !== 'en') {
|
||||
const enSlugs = await getAllProductSlugs('en');
|
||||
allSlugs = Array.from(new Set([...slugs, ...enSlugs]));
|
||||
}
|
||||
|
||||
const products = await Promise.all(allSlugs.map(slug => getProductBySlug(slug, locale)));
|
||||
return products.filter((p): p is ProductMdx => p !== null);
|
||||
}
|
||||
|
||||
14
lib/pages.ts
14
lib/pages.ts
@@ -32,3 +32,17 @@ export async function getPageBySlug(slug: string, locale: string): Promise<PageM
|
||||
content,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllPages(locale: string): Promise<PageMdx[]> {
|
||||
const pagesDir = path.join(process.cwd(), 'data', 'pages', locale);
|
||||
if (!fs.existsSync(pagesDir)) return [];
|
||||
|
||||
const files = fs.readdirSync(pagesDir);
|
||||
const pages = await Promise.all(
|
||||
files
|
||||
.filter(file => file.endsWith('.mdx'))
|
||||
.map(file => getPageBySlug(file.replace(/\.mdx$/, ''), locale))
|
||||
);
|
||||
|
||||
return pages.filter((p): p is PageMdx => p !== null);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "KLZ Cables - Hochwertige Kabel",
|
||||
"description": "Ihr Partner für hochwertige Kabel.",
|
||||
"meta": {
|
||||
"title": "KLZ Cables",
|
||||
"description": "Premium-Kabellösungen"
|
||||
"title": "KLZ Cables | Hochwertige Strom- & Solarkabel",
|
||||
"description": "Ihr Experte für hochwertige Stromkabel, Mittelspannungslösungen und Solarkabel. Zuverlässige Infrastruktur für eine grüne Energiezukunft."
|
||||
}
|
||||
},
|
||||
"Navigation": {
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"michael": {
|
||||
"name": "Michael Bodemer",
|
||||
"quote": "„Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.“",
|
||||
"quote": "Herausforderungen sind da, um gelöst zu werden – nicht, um über ihre Komplexität zu diskutieren.",
|
||||
"description": "Michael Bodemer ist unser Mann, wenn es kompliziert wird – und das ist bei Kabelnetzen oft der Fall. Mit seinem scharfen Blick und einem Händchen für praktikable Lösungen ist er eine unserer zentralen Säulen. Michael denkt nicht nur an Details, er treibt Projekte voran – sei es in der Planung, im Kundengespräch oder bei der Auswahl der besten Kabel für jedes Vorhaben.",
|
||||
"linkedin": "Michael's LinkedIn",
|
||||
"role": "Geschäftsführer"
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
"klaus": {
|
||||
"name": "Klaus Mintel",
|
||||
"quote": "„Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.“",
|
||||
"quote": "Manchmal braucht es nur einen klaren Kopf und das richtige Kabel, um die Welt ein Stück besser zu machen.",
|
||||
"description": "Klaus ist der Fels in der Brandung – selbst wenn das Kabelchaos überhandnimmt. Mit jahrzehntelanger Erfahrung und einem stabilen Netzwerk sorgt er dafür, dass alles glatt läuft. Er denkt nicht nur in Lösungen, sondern bringt auch Humor und den nötigen Weitblick mit, um selbst komplexe Themen locker auf den Punkt zu bringen.",
|
||||
"linkedin": "Klaus' LinkedIn",
|
||||
"role": "Gründer & Visionär"
|
||||
@@ -155,6 +155,8 @@
|
||||
"relatedProductsTitle": "Ähnliche Produkte",
|
||||
"requestQuote": "Angebot anfordern",
|
||||
"requestQuoteDesc": "Erhalten Sie technische Spezifikationen und Preise für Ihr Projekt.",
|
||||
"downloadDatasheet": "Datenblatt herunterladen",
|
||||
"downloadDatasheetDesc": "Erhalten Sie die vollständigen technischen Spezifikationen als PDF.",
|
||||
"form": {
|
||||
"contactInfo": "Kontaktinformationen",
|
||||
"projectDetails": "Projektdetails",
|
||||
@@ -170,6 +172,8 @@
|
||||
"privacyNote": "Mit dem Absenden erklären Sie sich mit unserer Datenschutzerklärung einverstanden"
|
||||
},
|
||||
"englishVersion": "Englische Version",
|
||||
"showMore": "Mehr anzeigen",
|
||||
"showLess": "Weniger anzeigen",
|
||||
"categories": {
|
||||
"lowVoltage": {
|
||||
"title": "Niederspannungskabel",
|
||||
@@ -283,8 +287,8 @@
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "Neuigkeiten zu Kabeln und Energielösungen",
|
||||
"description": "Bleiben Sie auf dem Laufenden! Lesen Sie aktuelle Themen und Insights zu Kabeltechnologie, Energielösungen und branchenspezifischen Innovationen."
|
||||
"title": "Kabel-News & Energie-Insights | KLZ Cables Blog",
|
||||
"description": "Bleiben Sie informiert über die neuesten Trends in der Kabeltechnologie, Energieinfrastruktur und nachhaltigen Stromlösungen. Expertenwissen von KLZ Cables."
|
||||
},
|
||||
"featuredPost": "Hervorgehobener Beitrag",
|
||||
"readFullArticle": "Vollständigen Artikel lesen",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "KLZ Cables - High Quality Cables",
|
||||
"description": "Your partner for high quality cables.",
|
||||
"meta": {
|
||||
"title": "KLZ Cables",
|
||||
"description": "Premium Cable Solutions"
|
||||
"title": "KLZ Cables | High-Quality Power & Solar Cables",
|
||||
"description": "Your expert partner for high-quality power cables, medium voltage solutions, and solar cables. Reliable infrastructure for a green energy future."
|
||||
}
|
||||
},
|
||||
"Navigation": {
|
||||
@@ -46,7 +46,7 @@
|
||||
},
|
||||
"michael": {
|
||||
"name": "Michael Bodemer",
|
||||
"quote": "\"Challenges exist to be solved, not to debate how complicated they are.\"",
|
||||
"quote": "Challenges exist to be solved, not to debate how complicated they are.",
|
||||
"description": "Michael Bodemer is the go-to guy when things get complicated—and let’s face it, that’s often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. He’s not just detail-oriented; he’s a driving force—whether it’s in planning, customer interactions, or securing the best cables for every project.",
|
||||
"linkedin": "Check Michael's LinkedIn",
|
||||
"role": "Managing Director"
|
||||
@@ -63,7 +63,7 @@
|
||||
},
|
||||
"klaus": {
|
||||
"name": "Klaus Mintel",
|
||||
"quote": "\"Sometimes all it takes is a clear head and a good cable to make the world a little better.\"",
|
||||
"quote": "Sometimes all it takes is a clear head and a good cable to make the world a little better.",
|
||||
"description": "Klaus is the man with the experience, bringing perspective and calm to the table—even when cable chaos threatens to take over. With impressive industry knowledge and a network as solid as our cables, he ensures everything runs smoothly. Klaus isn’t just a problem solver; he’s a strategic thinker who knows how to get to the point with a touch of humor.",
|
||||
"linkedin": "Check Klaus' LinkedIn",
|
||||
"role": "Founder & Visionary"
|
||||
@@ -155,6 +155,8 @@
|
||||
"relatedProductsTitle": "Related Products",
|
||||
"requestQuote": "Request a Quote",
|
||||
"requestQuoteDesc": "Get technical specifications and pricing for your project.",
|
||||
"downloadDatasheet": "Download Datasheet",
|
||||
"downloadDatasheetDesc": "Get the full technical specifications in PDF format.",
|
||||
"form": {
|
||||
"contactInfo": "Contact Information",
|
||||
"projectDetails": "Project Details",
|
||||
@@ -170,6 +172,8 @@
|
||||
"privacyNote": "By submitting you agree to our privacy policy"
|
||||
},
|
||||
"englishVersion": "English Version",
|
||||
"showMore": "Show More",
|
||||
"showLess": "Show Less",
|
||||
"categories": {
|
||||
"lowVoltage": {
|
||||
"title": "Low Voltage Cables",
|
||||
@@ -283,8 +287,8 @@
|
||||
},
|
||||
"Blog": {
|
||||
"meta": {
|
||||
"title": "News on Cables and Energy Solutions",
|
||||
"description": "Stay up to date! Read current topics and insights on cable technology, energy solutions and industry-specific innovations."
|
||||
"title": "Cable Industry News & Energy Insights | KLZ Cables Blog",
|
||||
"description": "Stay informed with the latest trends in cable technology, energy infrastructure, and sustainable power solutions. Expert insights from KLZ Cables."
|
||||
},
|
||||
"featuredPost": "Featured Post",
|
||||
"readFullArticle": "Read Full Article",
|
||||
|
||||
43
package-lock.json
generated
43
package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"cheerio": "^1.1.2",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"framer-motion": "^12.27.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"i18next": "^25.7.3",
|
||||
"jsdom": "^27.4.0",
|
||||
@@ -8547,6 +8548,33 @@
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.27.1",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.27.1.tgz",
|
||||
"integrity": "sha512-cEAqO69kcZt3gL0TGua8WTgRQfv4J57nqt1zxHtLKwYhAwA0x9kDS/JbMa1hJbwkGY74AGJKvZ9pX/IqWZtZWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.27.1",
|
||||
"motion-utils": "^12.24.10",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@@ -11436,6 +11464,21 @@
|
||||
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.27.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.27.1.tgz",
|
||||
"integrity": "sha512-V/53DA2nBqKl9O2PMJleSUb/G0dsMMeZplZwgIQf5+X0bxIu7Q1cTv6DrjvTTGYRm3+7Y5wMlRZ1wT61boU/bQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.24.10"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.24.10",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz",
|
||||
"integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mri": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"cheerio": "^1.1.2",
|
||||
"clsx": "^2.1.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"framer-motion": "^12.27.1",
|
||||
"gray-matter": "^4.0.3",
|
||||
"i18next": "^25.7.3",
|
||||
"jsdom": "^27.4.0",
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
--color-primary-dark: #000d26;
|
||||
--color-primary-light: #e6ebf5;
|
||||
|
||||
--color-saturated: #011dff; /* Saturated Blue Accent */
|
||||
|
||||
--color-secondary: #003d82;
|
||||
--color-secondary-light: #0056b3;
|
||||
|
||||
@@ -34,6 +36,7 @@
|
||||
--animate-slide-up: slide-up 0.6s ease-out;
|
||||
--animate-slow-zoom: slow-zoom 20s linear infinite;
|
||||
--animate-reveal: reveal 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
--animate-slight-fade-in-from-bottom: slight-fade-in-from-bottom 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
|
||||
@keyframes fade-in {
|
||||
from { opacity: 0; }
|
||||
@@ -51,6 +54,10 @@
|
||||
from { opacity: 0; transform: translateY(30px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes slight-fade-in-from-bottom {
|
||||
from { opacity: 0; transform: translateY(15px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
|
||||
@@ -19,7 +19,7 @@ module.exports = {
|
||||
colors: {
|
||||
// Brand Colors
|
||||
primary: {
|
||||
DEFAULT: '#0117bf',
|
||||
DEFAULT: '#011dff',
|
||||
dark: '#000e7a',
|
||||
light: '#3344cc',
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user