feat: Add static assets and content for the KLZ Cables showcase.

This commit is contained in:
2026-02-01 15:09:30 +01:00
parent 60da9b9e1f
commit f597fc2d78
374 changed files with 43204 additions and 10 deletions

View File

@@ -37,29 +37,33 @@ export const Header: React.FC = () => {
</div>
</div>
</Link>
<nav className="flex items-center gap-8">
<Link
href="/about"
className={`text-xs font-bold uppercase tracking-widest transition-colors ${
isActive('/about') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
className={`text-xs font-bold uppercase tracking-widest transition-colors ${isActive('/about') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
>
Über mich
</Link>
<Link
href="/websites"
className={`text-xs font-bold uppercase tracking-widest transition-colors ${
isActive('/websites') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
className={`text-xs font-bold uppercase tracking-widest transition-colors ${isActive('/websites') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
>
Websites
</Link>
<Link
href="/case-studies"
className={`text-xs font-bold uppercase tracking-widest transition-colors ${isActive('/case-studies') || pathname?.startsWith('/case-studies/') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
>
Case Studies
</Link>
<Link
href="/blog"
className={`text-xs font-bold uppercase tracking-widest transition-colors ${
isActive('/blog') || pathname?.startsWith('/blog/') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
className={`text-xs font-bold uppercase tracking-widest transition-colors ${isActive('/blog') || pathname?.startsWith('/blog/') ? 'text-slate-900' : 'text-slate-400 hover:text-slate-900'
}`}
>
Blog
</Link>

View File

@@ -0,0 +1,166 @@
'use client';
import React from 'react';
import { cn } from '../utils/cn';
interface IframeSectionProps {
src: string;
title?: string;
description?: string;
height?: string;
className?: string;
zoom?: number;
offsetY?: number;
clipHeight?: number;
browserFrame?: boolean;
allowScroll?: boolean;
desktopWidth?: number;
minimal?: boolean;
perspective?: boolean;
rotate?: number;
delay?: number;
noScale?: boolean;
}
export const IframeSection: React.FC<IframeSectionProps> = ({
src,
title,
description,
height = "500px",
className,
zoom,
offsetY = 0,
clipHeight,
browserFrame = false,
allowScroll = false,
desktopWidth = 1200,
minimal = false,
perspective = false,
rotate = 0,
delay = 0,
noScale = false
}) => {
const containerRef = React.useRef<HTMLDivElement>(null);
const [scale, setScale] = React.useState(1);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
if (!containerRef.current || noScale) {
setScale(1);
return;
}
const updateScale = () => {
if (containerRef.current) {
const currentWidth = containerRef.current.offsetWidth;
if (currentWidth > 0) {
const newScale = zoom || (currentWidth / desktopWidth);
setScale(newScale);
}
}
};
updateScale();
const observer = new ResizeObserver(updateScale);
observer.observe(containerRef.current);
return () => observer.disconnect();
}, [desktopWidth, zoom, noScale]);
const containerStyle = clipHeight
? { height: `${clipHeight * (noScale ? 1 : scale)}px` }
: { height };
return (
<div
className={cn("w-full group", !minimal && "space-y-6", className)}
style={{
opacity: 0,
animation: `fadeIn 0.8s ease-out ${delay}s forwards`
}}
>
{!minimal && (title || description) && (
<div className="space-y-2 px-1">
{title && <h4 className="text-2xl font-bold text-slate-900 tracking-tight leading-none">{title}</h4>}
{description && <p className="text-slate-400 text-sm font-medium">{description}</p>}
</div>
)}
<div
ref={containerRef}
className={cn(
"w-full relative overflow-hidden transition-all duration-700 ease-[cubic-bezier(0.23,1,0.32,1)]",
minimal ? "bg-transparent" : "bg-slate-100",
!minimal && (browserFrame ? "rounded-t-3xl border-t border-x border-slate-200 shadow-[0_32px_128px_-16px_rgba(0,0,0,0.1)]" : "rounded-3xl border border-slate-200/60 shadow-xl"),
perspective && "hover:scale-[1.01] hover:-translate-y-1",
noScale && "overflow-x-auto"
)}
style={{
...containerStyle,
perspective: perspective ? '1000px' : 'none',
transform: rotate ? `rotateY(${rotate}deg)` : 'none',
}}
>
{/* Loader Overlay */}
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-slate-50 z-30 transition-opacity duration-300">
<div className="w-8 h-8 border-2 border-slate-200 border-t-slate-900 rounded-full animate-spin" />
</div>
)}
{browserFrame && !minimal && (
<div className="absolute top-0 left-0 right-0 h-12 bg-slate-50/80 backdrop-blur-md border-b border-slate-200 flex items-center px-6 gap-3 z-10">
<div className="flex gap-2">
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-red-200 transition-colors" />
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-amber-200 transition-colors" />
<div className="w-3.5 h-3.5 rounded-full bg-slate-200 group-hover:bg-green-200 transition-colors" />
</div>
<div className="flex-1 max-w-md mx-auto bg-white/50 rounded-xl flex items-center justify-center px-4 h-7 border border-slate-200/50">
<span className="text-[11px] text-slate-400 font-bold tracking-tight truncate whitespace-nowrap">
https://klz-cables.com
</span>
</div>
<div className="w-16" />
</div>
)}
<div
className={cn(
"transition-transform duration-700",
(browserFrame && !minimal) ? "mt-12" : "mt-0",
noScale ? "static" : "origin-top-left absolute left-0 right-0 h-full"
)}
style={{
width: noScale ? '100%' : `${desktopWidth}px`,
transform: noScale ? 'none' : `scale(${scale})`,
height: noScale ? (clipHeight ? `${clipHeight}px` : '100%') : (clipHeight ? `${clipHeight + Math.abs(offsetY)}px` : `${100 / scale}%`),
}}
>
<iframe
src={src}
scrolling={allowScroll ? "yes" : "no"}
className={cn(
"w-full h-full border-none transition-opacity duration-500",
isLoading ? "opacity-0" : "opacity-100"
)}
onLoad={() => setIsLoading(false)}
style={{
transform: `translateY(-${offsetY}px)`,
pointerEvents: allowScroll ? 'auto' : 'none',
}}
title={title || "Interactive Project Preview"}
/>
</div>
{!allowScroll && !minimal && <div className="absolute inset-0 pointer-events-auto cursor-default z-20" />}
</div>
<style jsx global>{`
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
`}</style>
</div>
);
};