hero
All checks were successful
Build & Deploy KLZ Cables / build-and-deploy (push) Successful in 3m39s

This commit is contained in:
2026-01-27 08:30:18 +01:00
parent 8eea94ceda
commit e284bb94af
2 changed files with 582 additions and 4 deletions

View File

@@ -19,7 +19,7 @@ export default function Hero() {
variants={containerVariants}
>
<motion.div variants={headingVariants}>
<Heading level={1} className="text-center md:text-left mb-6 md:mb-8 md:max-w-none text-white text-4xl sm:text-5xl md:text-7xl">
<Heading level={1} className="text-center md:text-left mb-6 md:mb-8 md:max-w-none text-white text-4xl sm:text-5xl md:text-7xl [text-shadow:_-2px_-2px_0_#002b49,_2px_-2px_0_#002b49,_-2px_2px_0_#002b49,_2px_2px_0_#002b49,_-2px_0_0_#002b49,_2px_0_0_#002b49,_0_-2px_0_#002b49,_0_2px_0_#002b49]">
{t.rich('title', {
green: (chunks) => (
<span className="relative inline-block">
@@ -31,7 +31,7 @@ export default function Hero() {
</motion.span>
<motion.div
variants={scribbleVariants}
className="w-[140%] h-[140%] -top-[20%] -left-[20%] text-accent/30 hidden md:block absolute"
className="w-[140%] h-[140%] -top-[20%] -left-[20%] text-accent/30 hidden md:block absolute -z-10"
>
<Scribble variant="circle" />
</motion.div>
@@ -65,7 +65,7 @@ export default function Hero() {
</Container>
<motion.div
className="relative md:absolute inset-0 z-0 w-full h-[40vh] md:h-full order-1 md:order-none md:mb-0 mt-32 scale-[1.2] md:scale-0 -mb-24 md:mt-0 overflow-visible"
className="relative md:absolute inset-0 z-0 w-full h-[40vh] md:h-full order-1 md:order-none md:mb-0 mt-[40px] md:mt-0 overflow-visible pointer-events-none"
initial={{ opacity: 0, scale: 0.95, filter: 'blur(20px)' }}
animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
transition={{ duration: 2.2, ease: 'easeOut', delay: 0.05 }}

View File

@@ -166,7 +166,7 @@ export default function HeroIllustration() {
<div className="absolute md:inset-0 z-0 overflow-visible bg-primary w-full h-full">
<svg
viewBox="400 0 1000 1100"
className="w-full h-full opacity-60 md:opacity-100 scale-[1.44] md:scale-100 translate-x-0 overflow-visible"
className="w-full h-full opacity-60 md:opacity-100 scale-[1.44] md:scale-100 translate-x-0 overflow-visible md:hidden"
preserveAspectRatio="xMidYMid meet"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -411,6 +411,584 @@ export default function HeroIllustration() {
);
})}
{/* === SUBSTATIONS === */}
{INFRASTRUCTURE.substations.map((sub, i) => {
const pos = gridToScreen(sub.col, sub.row);
const isCollection = sub.type === 'collection';
return (
<g key={`substation-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base platform */}
<path
d="M -25 0 L 0 -12 L 25 0 L 0 12 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building */}
<path
d={isCollection
? "M -18 0 L -18 -20 L 0 -32 L 18 -20 L 18 0"
: "M -22 0 L -22 -25 L 0 -37 L 22 -25 L 22 0"
}
fill="white"
fillOpacity="0.08"
stroke="white"
strokeWidth="1"
strokeOpacity="0.5"
/>
{/* Equipment */}
<rect x="-10" y="-12" width="6" height="8" fill="white" fillOpacity="0.2" stroke="white" strokeWidth="0.5" />
<rect x="4" y="-12" width="6" height="8" fill="white" fillOpacity="0.2" stroke="white" strokeWidth="0.5" />
{/* Insulators */}
<line x1="-7" y1="-12" x2="-7" y2="-22" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<line x1="7" y1="-12" x2="7" y2="-22" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<circle cx="-7" cy="-22" r="2" fill="white" fillOpacity="0.4" />
<circle cx="7" cy="-22" r="2" fill="white" fillOpacity="0.4" />
{/* Connection glow */}
<circle r="8" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="r" values="6;10;6" dur="3s" repeatCount="indefinite" />
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur="3s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === TRANSMISSION TOWERS === */}
{INFRASTRUCTURE.towers.map((tower, i) => {
const pos = gridToScreen(tower.col, tower.row);
return (
<g key={`tower-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<ellipse cx="0" cy="0" rx="8" ry="4" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Tower legs */}
<path d="M -6 0 L -3 -45 M 6 0 L 3 -45" stroke="white" strokeWidth="1.5" strokeOpacity="0.5" />
{/* Cross braces */}
<path d="M -5 -10 L 5 -10 M -4 -20 L 4 -20 M -3 -30 L 3 -30 M -3 -45 L 3 -45" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Cross arms */}
<line x1="-12" y1="-40" x2="12" y2="-40" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
<line x1="-10" y1="-32" x2="10" y2="-32" stroke="white" strokeWidth="1" strokeOpacity="0.4" />
{/* Insulators */}
<circle cx="-10" cy="-40" r="1.5" fill="white" fillOpacity="0.4" />
<circle cx="10" cy="-40" r="1.5" fill="white" fillOpacity="0.4" />
{/* Connection glow */}
<circle r="5" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur="2s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === CITY BUILDINGS === */}
{INFRASTRUCTURE.city.map((building, i) => {
const pos = gridToScreen(building.col, building.row);
const heights = { tall: 70, medium: 45, small: 30 };
const height = heights[building.type as keyof typeof heights];
return (
<g key={`building-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<path
d="M -12 0 L 0 -6 L 12 0 L 0 6 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Building front */}
<path
d={`M -12 0 L -12 -${height} L 0 -${height + 6} L 0 -6 Z`}
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building side */}
<path
d={`M 0 -6 L 0 -${height + 6} L 12 -${height} L 12 0 Z`}
fill="white"
fillOpacity="0.05"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Windows */}
{[...Array(Math.floor(height / 15))].map((_, w) => (
<g key={`window-${i}-${w}`}>
<rect x="-9" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="-4" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="3" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
<rect x="7" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
</g>
))}
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur={`${2 + i * 0.3}s`} repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === CITY 2 BUILDINGS (bottom-left) === */}
{INFRASTRUCTURE.city2.map((building, i) => {
const pos = gridToScreen(building.col, building.row);
const heights = { tall: 70, medium: 45, small: 30 };
const height = heights[building.type as keyof typeof heights];
return (
<g key={`building2-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<path
d="M -12 0 L 0 -6 L 12 0 L 0 6 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Building front */}
<path
d={`M -12 0 L -12 -${height} L 0 -${height + 6} L 0 -6 Z`}
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Building side */}
<path
d={`M 0 -6 L 0 -${height + 6} L 12 -${height} L 12 0 Z`}
fill="white"
fillOpacity="0.05"
stroke="white"
strokeWidth="1"
strokeOpacity="0.3"
/>
{/* Windows */}
{[...Array(Math.floor(height / 15))].map((_, w) => (
<g key={`window2-${i}-${w}`}>
<rect x="-9" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="-4" y={-12 - w * 15} width="3" height="4" fill="white" fillOpacity="0.2" />
<rect x="3" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
<rect x="7" y={-15 - w * 15} width="3" height="4" fill="white" fillOpacity="0.15" />
</g>
))}
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.3" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.2;0.5;0.2" dur={`${2.5 + i * 0.3}s`} repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === TREES === */}
{INFRASTRUCTURE.trees.map((tree, i) => {
const pos = gridToScreen(tree.col, tree.row);
return (
<g key={`tree-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Trunk */}
<line x1="0" y1="0" x2="0" y2="-15" stroke="white" strokeWidth="2" strokeOpacity="0.3" />
{/* Foliage - layered circles for tree crown */}
<ellipse cx="0" cy="-22" rx="10" ry="8" fill="white" fillOpacity="0.12" stroke="white" strokeWidth="0.5" strokeOpacity="0.2" />
<ellipse cx="-5" cy="-26" rx="7" ry="6" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="0.5" strokeOpacity="0.15" />
<ellipse cx="5" cy="-26" rx="7" ry="6" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="0.5" strokeOpacity="0.15" />
<ellipse cx="0" cy="-30" rx="6" ry="5" fill="white" fillOpacity="0.08" stroke="white" strokeWidth="0.5" strokeOpacity="0.1" />
</g>
);
})}
{/* === ABSTRACT WIND EFFECTS === */}
{INFRASTRUCTURE.wind.map((turbine, i) => {
const pos = gridToScreen(turbine.col, turbine.row);
return (
<g key={`wind-effect-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Wind swoosh lines - curved paths flowing toward turbine */}
{[0, 1, 2].map((j) => (
<path
key={`wind-line-${i}-${j}`}
d={`M ${-80 - j * 15} ${-70 - j * 8} Q ${-50 - j * 10} ${-65 - j * 5} ${-20} ${-60}`}
stroke="url(#wind-flow)"
strokeWidth="2"
fill="none"
strokeLinecap="round"
opacity="0"
>
<animate
attributeName="opacity"
values="0;0.6;0"
dur={`${2 + j * 0.5}s`}
begin={`${j * 0.7 + i * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="stroke-dashoffset"
from="100"
to="0"
dur={`${2 + j * 0.5}s`}
begin={`${j * 0.7 + i * 0.3}s`}
repeatCount="indefinite"
/>
</path>
))}
{/* Additional wind particles */}
{[0, 1, 2, 3].map((j) => (
<circle
key={`wind-particle-${i}-${j}`}
r="1.5"
fill="white"
opacity="0"
>
<animate
attributeName="cx"
values={`${-70 - j * 10};${-10}`}
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values={`${-75 - j * 5};${-60}`}
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0;0.5;0"
dur={`${1.5 + j * 0.3}s`}
begin={`${j * 0.4 + i * 0.2}s`}
repeatCount="indefinite"
/>
</circle>
))}
</g>
);
})}
{/* === SCHEMATIC SUN RAYS === */}
{/* Simple downward rays above each solar panel */}
{INFRASTRUCTURE.solar.map((panel, i) => {
const pos = gridToScreen(panel.col, panel.row);
return (
<g key={`sun-ray-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Three short schematic rays coming down to panel */}
{[-8, 0, 8].map((offset, j) => (
<line
key={`ray-${i}-${j}`}
x1={offset}
y1={-45}
x2={offset * 0.3}
y2={-18}
stroke="#FFD700"
strokeWidth="1.5"
strokeOpacity="0.4"
strokeLinecap="round"
strokeDasharray="4 6"
>
<animate
attributeName="strokeOpacity"
values="0.2;0.5;0.2"
dur={`${2 + j * 0.3}s`}
begin={`${i * 0.2}s`}
repeatCount="indefinite"
/>
<animate
attributeName="stroke-dashoffset"
from="10"
to="0"
dur="1.5s"
repeatCount="indefinite"
/>
</line>
))}
</g>
);
})}
{/* === ENERGY PARTICLES === */}
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
return (
<circle
key={`particle-${i}`}
r="3"
fill="#82ed20"
filter="url(#soft-glow)"
>
<animate
attributeName="cx"
values={`${from.x};${to.x}`}
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="cy"
values={`${from.y};${to.y}`}
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
values="0;0.8;0"
dur={`${1 + (i % 4) * 0.3}s`}
repeatCount="indefinite"
/>
</circle>
);
})}
</g>
</svg>
{/* Desktop SVG - Original */}
<svg
viewBox="-400 -200 1800 1100"
className="w-full h-full opacity-100 scale-100 translate-x-0 hidden md:block"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
{/* Electric energy flow gradient */}
<linearGradient id="energy-pulse" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#82ed20" stopOpacity="0" />
<stop offset="30%" stopColor="#82ed20" stopOpacity="0.6" />
<stop offset="50%" stopColor="#9bf14d" stopOpacity="1" />
<stop offset="70%" stopColor="#82ed20" stopOpacity="0.6" />
<stop offset="100%" stopColor="#82ed20" stopOpacity="0" />
</linearGradient>
{/* Wind flow gradient */}
<linearGradient id="wind-flow" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="white" stopOpacity="0" />
<stop offset="30%" stopColor="white" stopOpacity="0.4" />
<stop offset="50%" stopColor="white" stopOpacity="0.6" />
<stop offset="70%" stopColor="white" stopOpacity="0.4" />
<stop offset="100%" stopColor="white" stopOpacity="0" />
</linearGradient>
{/* Sun ray gradient */}
<linearGradient id="sun-ray" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor="#FFD700" stopOpacity="0.6" />
<stop offset="50%" stopColor="#FFD700" stopOpacity="0.3" />
<stop offset="100%" stopColor="#82ed20" stopOpacity="0.1" />
</linearGradient>
{/* Glow filter */}
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
{/* Soft glow for nodes */}
<filter id="soft-glow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="2" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
{/* Sun glow filter */}
<filter id="sun-glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* Main scene container - positioned to the right */}
<g transform="translate(900, 100)">
{/* === ISOMETRIC GRID === */}
<g opacity="0.15">
{/* Horizontal grid lines (going from top-left to bottom-right) */}
{[...Array(GRID.rows + 1)].map((_, row) => {
const start = gridToScreen(0, row);
const end = gridToScreen(GRID.cols, row);
return (
<line
key={`h-${row}`}
x1={start.x}
y1={start.y}
x2={end.x}
y2={end.y}
stroke="white"
strokeWidth="1"
/>
);
})}
{/* Vertical grid lines (going from top-right to bottom-left) */}
{[...Array(GRID.cols + 1)].map((_, col) => {
const start = gridToScreen(col, 0);
const end = gridToScreen(col, GRID.rows);
return (
<line
key={`v-${col}`}
x1={start.x}
y1={start.y}
x2={end.x}
y2={end.y}
stroke="white"
strokeWidth="1"
/>
);
})}
</g>
{/* Grid intersection nodes */}
<g opacity="0.2">
{[...Array(GRID.cols + 1)].map((_, col) =>
[...Array(GRID.rows + 1)].map((_, row) => {
const pos = gridToScreen(col, row);
return (
<circle
key={`node-${col}-${row}`}
cx={pos.x}
cy={pos.y}
r="2"
fill="white"
/>
);
})
)}
</g>
{/* === POWER LINES (Base cables) === */}
<g stroke="white" strokeWidth="2" strokeOpacity="0.25">
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
return (
<line
key={`cable-${i}`}
x1={from.x}
y1={from.y}
x2={to.x}
y2={to.y}
/>
);
})}
</g>
{/* === ANIMATED ENERGY FLOW === */}
<g filter="url(#glow)">
{POWER_LINES.map((line, i) => {
const from = gridToScreen(line.from.col, line.from.row);
const to = gridToScreen(line.to.col, line.to.row);
const length = Math.sqrt(
Math.pow(to.x - from.x, 2) + Math.pow(to.y - from.y, 2)
);
return (
<line
key={`flow-${i}`}
x1={from.x}
y1={from.y}
x2={to.x}
y2={to.y}
stroke="url(#energy-pulse)"
strokeWidth="3"
strokeLinecap="round"
strokeDasharray={`${length * 0.2} ${length * 0.8}`}
>
<animate
attributeName="stroke-dashoffset"
from={length}
to={0}
dur={`${1.5 + (i % 3) * 0.5}s`}
repeatCount="indefinite"
/>
</line>
);
})}
</g>
{/* === SOLAR PANELS === */}
{INFRASTRUCTURE.solar.map((panel, i) => {
const pos = gridToScreen(panel.col, panel.row);
return (
<g key={`solar-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Panel base */}
<path
d="M -20 0 L 0 -10 L 20 0 L 0 10 Z"
fill="white"
fillOpacity="0.1"
stroke="white"
strokeWidth="1"
strokeOpacity="0.4"
/>
{/* Panel surface (tilted) */}
<path
d="M -15 -5 L 0 -15 L 15 -5 L 0 5 Z"
fill="white"
fillOpacity="0.15"
stroke="white"
strokeWidth="1"
strokeOpacity="0.5"
/>
{/* Panel grid lines */}
<line x1="-7" y1="-10" x2="7" y2="0" stroke="white" strokeWidth="0.5" strokeOpacity="0.3" />
<line x1="0" y1="-15" x2="0" y2="5" stroke="white" strokeWidth="0.5" strokeOpacity="0.3" />
{/* Connection glow */}
<circle r="4" fill="#82ed20" fillOpacity="0.4" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.3;0.6;0.3" dur="2s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === WIND TURBINES === */}
{INFRASTRUCTURE.wind.map((turbine, i) => {
const pos = gridToScreen(turbine.col, turbine.row);
return (
<g key={`wind-${i}`} transform={`translate(${pos.x}, ${pos.y})`}>
{/* Base */}
<ellipse cx="0" cy="0" rx="10" ry="5" fill="white" fillOpacity="0.1" stroke="white" strokeWidth="1" strokeOpacity="0.3" />
{/* Tower */}
<line x1="0" y1="0" x2="0" y2="-60" stroke="white" strokeWidth="2" strokeOpacity="0.5" />
{/* Nacelle */}
<ellipse cx="0" cy="-60" rx="6" ry="3" fill="white" fillOpacity="0.3" stroke="white" strokeWidth="1" />
{/* Blades */}
<g transform="translate(0, -60)">
{[0, 120, 240].map((angle, j) => (
<line
key={`blade-${i}-${j}`}
x1="0"
y1="0"
x2="0"
y2="-30"
stroke="white"
strokeWidth="1.5"
strokeOpacity="0.6"
transform={`rotate(${angle})`}
>
<animateTransform
attributeName="transform"
type="rotate"
from={`${angle} 0 0`}
to={`${angle + 360} 0 0`}
dur={`${3 + i}s`}
repeatCount="indefinite"
/>
</line>
))}
<circle r="3" fill="white" fillOpacity="0.4" />
</g>
{/* Connection glow */}
<circle r="5" fill="#82ed20" fillOpacity="0.4" filter="url(#soft-glow)">
<animate attributeName="fillOpacity" values="0.3;0.6;0.3" dur="2.5s" repeatCount="indefinite" />
</circle>
</g>
);
})}
{/* === SUBSTATIONS === */}
{INFRASTRUCTURE.substations.map((sub, i) => {
const pos = gridToScreen(sub.col, sub.row);