This commit is contained in:
2026-01-19 02:19:11 +01:00
parent 4f6264f2e2
commit 79016fbe97
17 changed files with 134 additions and 42 deletions

View File

@@ -47,7 +47,7 @@ export default async function BlogIndex({ params: { locale } }: BlogIndexProps)
<Container className="relative z-10"> <Container className="relative z-10">
<div className="max-w-4xl animate-slide-up"> <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 && ( {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"> <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 +119,10 @@ export default async function BlogIndex({ params: { locale } }: BlogIndexProps)
{post.frontmatter.excerpt} {post.frontmatter.excerpt}
</p> </p>
<div className="mt-auto pt-4 md:pt-8 border-t border-neutral-medium flex items-center justify-between"> <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')} {t('readMore')}
</span> </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"> <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" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg> </svg>

View File

@@ -34,7 +34,7 @@ export default function ContactPage() {
</Heading> </Heading>
<div className="space-y-4 md:space-y-8"> <div className="space-y-4 md:space-y-8">
<div className="flex items-start gap-4 md:gap-6 group"> <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"> <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="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" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
@@ -49,7 +49,7 @@ export default function ContactPage() {
</div> </div>
<div className="flex items-start gap-4 md:gap-6 group"> <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"> <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" /> <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> </svg>
@@ -61,7 +61,7 @@ export default function ContactPage() {
</div> </div>
<div className="flex items-start gap-4 md:gap-6 group"> <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"> <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" /> <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> </svg>
@@ -133,7 +133,7 @@ export default function ContactPage() {
/> />
</div> </div>
<div className="md:col-span-2 pt-2 md:pt-4"> <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')} {t('form.submit')}
</Button> </Button>
</div> </div>

View File

@@ -1,6 +1,7 @@
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';
import { MDXRemote } from 'next-mdx-remote/rsc'; import { MDXRemote } from 'next-mdx-remote/rsc';
import { getProductBySlug, getAllProducts } from '@/lib/mdx'; import { getProductBySlug, getAllProducts } from '@/lib/mdx';
import { getDatasheetPath } from '@/lib/datasheets';
import ProductTechnicalData from '@/components/ProductTechnicalData'; import ProductTechnicalData from '@/components/ProductTechnicalData';
import ProductTabs from '@/components/ProductTabs'; import ProductTabs from '@/components/ProductTabs';
import RequestQuoteForm from '@/components/RequestQuoteForm'; import RequestQuoteForm from '@/components/RequestQuoteForm';
@@ -110,7 +111,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
return ( return (
<div className="flex flex-col min-h-screen bg-white"> <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"> <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"> <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"> <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> <Link href={`/${locale}/products`} className="hover:text-accent transition-colors">{t('title')}</Link>
@@ -186,6 +187,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
notFound(); notFound();
} }
const datasheetPath = getDatasheetPath(productSlug, locale);
const isFallback = (product.frontmatter as any).isFallback; const isFallback = (product.frontmatter as any).isFallback;
const categorySlug = slug[0]; const categorySlug = slug[0];
const categoryKey = categorySlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase()); const categoryKey = categorySlug.replace(/-cables$/, '').replace(/-([a-z])/g, (g) => g[1].toUpperCase());
@@ -195,7 +197,7 @@ export default async function ProductPage({ params }: ProductPageProps) {
<div className="flex flex-col min-h-screen bg-neutral-light"> <div className="flex flex-col min-h-screen bg-neutral-light">
{/* Product Hero */} {/* Product Hero */}
<section className="relative pt-40 pb-32 overflow-hidden bg-primary-dark"> <section className="relative pt-40 pb-32 overflow-hidden bg-primary-dark">
<Container className="relative z-10"> <Container className="relative z-10">
<div className="max-w-5xl animate-slide-up"> <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"> <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> <Link href={`/${locale}/products`} className="hover:text-accent transition-colors">{t('title')}</Link>
@@ -326,6 +328,34 @@ export default async function ProductPage({ params }: ProductPageProps) {
<RequestQuoteForm productName={product.frontmatter.title} /> <RequestQuoteForm productName={product.frontmatter.title} />
</div> </div>
</div> </div>
{/* Datasheet Download */}
{datasheetPath && (
<div className="mt-8 bg-white rounded-3xl shadow-xl border border-neutral-dark/5 overflow-hidden group hover:shadow-2xl transition-all duration-500">
<div className="p-6 md:p-8 flex items-center gap-6">
<div className="w-16 h-16 rounded-2xl bg-accent/10 flex items-center justify-center flex-shrink-0 group-hover:bg-accent group-hover:text-white transition-colors duration-500 text-accent">
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} 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">
<h3 className="text-lg font-bold text-primary mb-1">{t('downloadDatasheet')}</h3>
<p className="text-text-secondary text-sm mb-4">{t('downloadDatasheetDesc')}</p>
<a
href={datasheetPath}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center text-accent font-bold hover:text-accent-dark transition-colors"
>
<span>{t('downloadDatasheet')}</span>
<svg className="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
</svg>
</a>
</div>
</div>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -51,7 +51,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"> <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"> <Container className="relative z-10">
<div className="max-w-4xl animate-slide-up"> <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')} {t('heroSubtitle')}
</Badge> </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]"> <h1 className="text-4xl md:text-7xl lg:text-8xl font-extrabold text-white mb-4 md:mb-8 tracking-tight leading-[1.05]">
@@ -112,11 +112,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"> <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} {category.desc}
</p> </p>
<div className="flex items-center text-primary font-bold text-base md:text-lg group-hover:text-accent-dark transition-colors"> <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-primary/10 group-hover:border-accent-dark transition-colors pb-1"> <span className="border-b-2 border-saturated/10 group-hover:border-accent-dark transition-colors pb-1">
{t('viewProducts')} {t('viewProducts')}
</span> </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"> <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" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg> </svg>

View File

@@ -22,7 +22,7 @@ export default function TeamPage() {
</div> </div>
<Container className="relative z-10 text-center text-white max-w-5xl animate-slide-up"> <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"> <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')} {t('hero.subtitle')}
</h1> </h1>
@@ -45,7 +45,7 @@ export default function TeamPage() {
<div className="relative mb-6 md:mb-12"> <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" /> <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"> <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> </p>
</div> </div>
<p className="text-base md:text-xl leading-relaxed text-white/70 mb-6 md:mb-12 max-w-xl"> <p className="text-base md:text-xl leading-relaxed text-white/70 mb-6 md:mb-12 max-w-xl">
@@ -129,17 +129,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 className="absolute inset-0 bg-gradient-to-t from-white/60 lg:bg-gradient-to-l lg:from-primary-dark/20 to-transparent" />
</div> </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"> <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-primary/5 skew-x-12 -translate-x-1/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"> <div className="relative z-10">
<Badge variant="primary" className="mb-4 md:mb-8">{t('klaus.role')}</Badge> <Badge variant="saturated" 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"> <Heading level={2} className="text-saturated mb-6 md:mb-10 text-3xl md:text-6xl">
{t('klaus.name')} {t('klaus.name')}
</Heading> </Heading>
<div className="relative mb-6 md:mb-12"> <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"> <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> </p>
</div> </div>
<p className="text-base md:text-xl leading-relaxed text-text-secondary mb-6 md:mb-12 max-w-xl"> <p className="text-base md:text-xl leading-relaxed text-text-secondary mb-6 md:mb-12 max-w-xl">
@@ -147,7 +147,7 @@ export default function TeamPage() {
</p> </p>
<Button <Button
href="https://www.linkedin.com/in/klaus-mintel-b80a8b193/" href="https://www.linkedin.com/in/klaus-mintel-b80a8b193/"
variant="primary" variant="saturated"
size="lg" size="lg"
className="group w-full md:w-auto md:h-16 md:px-10 md:text-xl active:scale-95 transition-transform" className="group w-full md:w-auto md:h-16 md:px-10 md:text-xl active:scale-95 transition-transform"
> >

View File

@@ -31,7 +31,7 @@ export default function Hero() {
{t('cta')} {t('cta')}
<span className="ml-3 transition-transform group-hover:translate-x-1">&rarr;</span> <span className="ml-3 transition-transform group-hover:translate-x-1">&rarr;</span>
</Button> </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')} {t('exploreProducts')}
</Button> </Button>
</div> </div>

View File

@@ -17,8 +17,8 @@ export default function WhatWeDo() {
<p className="text-lg md:text-xl text-text-secondary leading-relaxed"> <p className="text-lg md:text-xl text-text-secondary leading-relaxed">
{t('subtitle')} {t('subtitle')}
</p> </p>
<div className="mt-8 md:mt-12 p-6 md:p-8 bg-primary-light rounded-2xl border border-primary/10"> <div className="mt-8 md:mt-12 p-6 md:p-8 bg-saturated/10 rounded-2xl border border-saturated/10">
<p className="text-primary font-bold text-base md:text-lg italic"> <p className="text-saturated font-bold text-base md:text-lg italic">
"{t('quote')}" "{t('quote')}"
</p> </p>
</div> </div>
@@ -28,12 +28,12 @@ export default function WhatWeDo() {
{[0, 1, 2, 3].map((idx) => ( {[0, 1, 2, 3].map((idx) => (
<div key={idx} className="group"> <div key={idx} className="group">
<div className="flex items-center gap-4 mb-4 md:mb-6"> <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} {idx + 1}
</span> </span>
<div className="h-px flex-grow bg-neutral-medium" /> <div className="h-px flex-grow bg-neutral-medium" />
</div> </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> <p className="text-text-secondary text-base md:text-lg leading-relaxed">{t(`items.${idx}.description`)}</p>
</div> </div>
))} ))}

View File

@@ -26,7 +26,7 @@ export default function WhyChooseUs() {
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg> </svg>
</div> </div>
<span className="font-bold text-primary">{t(`features.${i}`)}</span> <span className="font-bold text-saturated">{t(`features.${i}`)}</span>
</div> </div>
))} ))}
</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"> <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) => ( {[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 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"> <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-primary font-bold text-xl group-hover:text-primary-dark">0{idx + 1}</span> <span className="text-saturated font-bold text-xl group-hover:text-primary-dark">0{idx + 1}</span>
</div> </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> <p className="text-text-secondary text-lg leading-relaxed">{t(`items.${idx}.description`)}</p>
</div> </div>
))} ))}

View File

@@ -1,11 +1,12 @@
import React from 'react'; import React from 'react';
import { cn } from './utils'; import { cn } from './utils';
export function Badge({ children, className, variant = 'primary' }: { children: React.ReactNode, className?: string, variant?: 'primary' | 'accent' | 'neutral' }) { export function Badge({ children, className, variant = 'primary' }: { children: React.ReactNode, className?: string, variant?: 'primary' | 'accent' | 'neutral' | 'saturated' }) {
const variants = { const variants = {
primary: 'bg-primary-light text-primary', primary: 'bg-primary-light text-primary',
accent: 'bg-accent-light text-accent-dark', accent: 'bg-accent-light text-accent-dark',
neutral: 'bg-neutral-medium text-text-secondary', neutral: 'bg-neutral-medium text-text-secondary',
saturated: 'bg-saturated text-white',
}; };
return ( return (

View File

@@ -3,7 +3,7 @@ import Link from 'next/link';
import { cn } from './utils'; import { cn } from './utils';
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> { export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'accent' | 'outline' | 'ghost' | 'white'; variant?: 'primary' | 'secondary' | 'accent' | 'saturated' | 'outline' | 'ghost' | 'white';
size?: 'sm' | 'md' | 'lg' | 'xl'; size?: 'sm' | 'md' | 'lg' | 'xl';
href?: string; href?: string;
className?: string; className?: string;
@@ -17,6 +17,7 @@ export function Button({ className, variant = 'primary', size = 'md', href, ...p
primary: 'bg-primary text-white hover:bg-primary-dark shadow-md hover:shadow-lg', 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', 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', 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', outline: 'border-2 border-primary bg-transparent hover:bg-primary hover:text-white text-primary',
ghost: 'hover:bg-primary-light text-primary', ghost: 'hover:bg-primary-light text-primary',
white: 'bg-white text-primary hover:bg-neutral-light shadow-md hover:shadow-lg', white: 'bg-white text-primary hover:bg-neutral-light shadow-md hover:shadow-lg',

View File

@@ -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+&amp;]: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="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&amp;]: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="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]">
<div class="markdown prose w-full break-words dark:prose-invert dark"> <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> </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+&amp;]: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="min-h-8 text-message flex w-full flex-col items-end gap-2 whitespace-normal break-words text-start [.text-message+&amp;]: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="flex w-full flex-col gap-1 empty:hidden first:pt-[3px]">
<div class="markdown prose w-full break-words dark:prose-invert dark"> <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> </div>
</div> </div>

View File

@@ -60,13 +60,13 @@ locale: en
<h1>Michael Bodemer</h1> <h1>Michael Bodemer</h1>
<h2>&#8220;Challenges exist to be solved, not to debate how complicated they are.&#8221;</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 lets face it, thats often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. Hes not just detail-oriented; hes a driving force—whether its in planning, customer interactions, or securing the best cables for every project. Michael Bodemer is the go-to guy when things get complicated—and lets face it, thats often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. Hes not just detail-oriented; hes a driving force—whether its in planning, customer interactions, or securing the best cables for every project.
<h3><strong>A Legacy of Excellence in Every Connection</strong></h3> <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, weve 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>At KLZ, our expertise is built on generations of dedication to the energy industry. With decades of hands-on experience, weve 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 industrys early days, our story is a reminder of how far cables have come and how much care has always gone into connecting the world. <p>Paired with historic illustrations from the industrys 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> <h1>Klaus Mintel</h1>
<h2>&#8220;Sometimes all it takes is a clear head and a good cable to make the world a little better.&#8221;</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 isnt just a problem solver; hes a strategic thinker who knows how to get to the point with a touch of humor. 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 isnt just a problem solver; hes a strategic thinker who knows how to get to the point with a touch of humor.
<h2>Our manifesto</h2> <h2>Our manifesto</h2>

54
lib/datasheets.ts Normal file
View 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;
}

View File

@@ -46,7 +46,7 @@
}, },
"michael": { "michael": {
"name": "Michael Bodemer", "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.", "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", "linkedin": "Michael's LinkedIn",
"role": "Geschäftsführer" "role": "Geschäftsführer"
@@ -63,7 +63,7 @@
}, },
"klaus": { "klaus": {
"name": "Klaus Mintel", "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.", "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", "linkedin": "Klaus' LinkedIn",
"role": "Gründer & Visionär" "role": "Gründer & Visionär"
@@ -155,6 +155,8 @@
"relatedProductsTitle": "Ähnliche Produkte", "relatedProductsTitle": "Ähnliche Produkte",
"requestQuote": "Angebot anfordern", "requestQuote": "Angebot anfordern",
"requestQuoteDesc": "Erhalten Sie technische Spezifikationen und Preise für Ihr Projekt.", "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": { "form": {
"contactInfo": "Kontaktinformationen", "contactInfo": "Kontaktinformationen",
"projectDetails": "Projektdetails", "projectDetails": "Projektdetails",

View File

@@ -46,7 +46,7 @@
}, },
"michael": { "michael": {
"name": "Michael Bodemer", "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 lets face it, thats often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. Hes not just detail-oriented; hes a driving force—whether its in planning, customer interactions, or securing the best cables for every project.", "description": "Michael Bodemer is the go-to guy when things get complicated—and lets face it, thats often the case with cable networks. With sharp insight and a knack for practical solutions, Michael is one of our key players. Hes not just detail-oriented; hes a driving force—whether its in planning, customer interactions, or securing the best cables for every project.",
"linkedin": "Check Michael's LinkedIn", "linkedin": "Check Michael's LinkedIn",
"role": "Managing Director" "role": "Managing Director"
@@ -63,7 +63,7 @@
}, },
"klaus": { "klaus": {
"name": "Klaus Mintel", "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 isnt just a problem solver; hes a strategic thinker who knows how to get to the point with a touch of humor.", "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 isnt just a problem solver; hes a strategic thinker who knows how to get to the point with a touch of humor.",
"linkedin": "Check Klaus' LinkedIn", "linkedin": "Check Klaus' LinkedIn",
"role": "Founder & Visionary" "role": "Founder & Visionary"
@@ -155,6 +155,8 @@
"relatedProductsTitle": "Related Products", "relatedProductsTitle": "Related Products",
"requestQuote": "Request a Quote", "requestQuote": "Request a Quote",
"requestQuoteDesc": "Get technical specifications and pricing for your project.", "requestQuoteDesc": "Get technical specifications and pricing for your project.",
"downloadDatasheet": "Download Datasheet",
"downloadDatasheetDesc": "Get the full technical specifications in PDF format.",
"form": { "form": {
"contactInfo": "Contact Information", "contactInfo": "Contact Information",
"projectDetails": "Project Details", "projectDetails": "Project Details",

View File

@@ -9,6 +9,8 @@
--color-primary-dark: #000d26; --color-primary-dark: #000d26;
--color-primary-light: #e6ebf5; --color-primary-light: #e6ebf5;
--color-saturated: #011dff; /* Saturated Blue Accent */
--color-secondary: #003d82; --color-secondary: #003d82;
--color-secondary-light: #0056b3; --color-secondary-light: #0056b3;

File diff suppressed because one or more lines are too long