websites
This commit is contained in:
@@ -5,14 +5,14 @@ import { PageHeader } from '../../src/components/PageHeader';
|
||||
import { Reveal } from '../../src/components/Reveal';
|
||||
import { Section } from '../../src/components/Section';
|
||||
import {
|
||||
ConceptSystem,
|
||||
ConceptCode,
|
||||
ConceptWebsite,
|
||||
ConceptAutomation,
|
||||
ConceptCommunication,
|
||||
ConceptMessy,
|
||||
ConceptTarget
|
||||
} from '../../src/components/Landing/Illustrations';
|
||||
SystemArchitecture,
|
||||
SpeedPerformance,
|
||||
SolidFoundation,
|
||||
LayerSeparation,
|
||||
DirectService,
|
||||
AgencyChaos,
|
||||
TaskDone
|
||||
} from '../../src/components/Landing/Illustrations/WebsitesDescriptive';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export default function WebsitesPage() {
|
||||
@@ -24,7 +24,7 @@ export default function WebsitesPage() {
|
||||
backgroundSize: '80px 80px'
|
||||
}} />
|
||||
|
||||
<PageHeader
|
||||
<PageHeader
|
||||
title={<>Websites, die <br /><span className="text-slate-300">einfach funktionieren.</span></>}
|
||||
description="Die meisten Websites funktionieren. Bis jemand sie anfasst. Ich baue Websites so, dass das alles egal ist."
|
||||
backLink={{ href: '/', label: 'Zurück' }}
|
||||
@@ -36,13 +36,13 @@ export default function WebsitesPage() {
|
||||
number="01"
|
||||
title="Der Ansatz"
|
||||
borderTop
|
||||
illustration={<ConceptSystem className="w-16 h-16" />}
|
||||
illustration={<SystemArchitecture className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
Ich baue Websites wie Systeme – <br />
|
||||
<span className="text-slate-300 font-serif italic">nicht wie Broschüren.</span>
|
||||
<span className="text-slate-300">nicht wie Broschüren.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -74,13 +74,13 @@ export default function WebsitesPage() {
|
||||
number="02"
|
||||
title="Performance"
|
||||
borderTop
|
||||
illustration={<ConceptTarget className="w-16 h-16" />}
|
||||
illustration={<SpeedPerformance className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
Geschwindigkeit ist <br />
|
||||
<span className="text-slate-300 font-serif italic">kein Extra. Sie ist Standard.</span>
|
||||
<span className="text-slate-300">kein Extra. Sie ist Standard.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -119,13 +119,13 @@ export default function WebsitesPage() {
|
||||
number="03"
|
||||
title="Wartungsfrei"
|
||||
borderTop
|
||||
illustration={<ConceptCode className="w-16 h-16" />}
|
||||
illustration={<SolidFoundation className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
Keine Plugins. <br />
|
||||
<span className="text-slate-300 font-serif italic">Keine Wartungshölle.</span>
|
||||
<span className="text-slate-300">Keine Wartungshölle.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -154,13 +154,13 @@ export default function WebsitesPage() {
|
||||
number="04"
|
||||
title="Inhalte"
|
||||
borderTop
|
||||
illustration={<ConceptAutomation className="w-16 h-16" />}
|
||||
illustration={<LayerSeparation className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
Inhalte & Technik <br />
|
||||
<span className="text-slate-300 font-serif italic">sind getrennt. Absichtlich.</span>
|
||||
<span className="text-slate-300">sind getrennt. Absichtlich.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -196,13 +196,13 @@ export default function WebsitesPage() {
|
||||
number="05"
|
||||
title="Service"
|
||||
borderTop
|
||||
illustration={<ConceptCommunication className="w-16 h-16" />}
|
||||
illustration={<DirectService className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
Änderungen sind <br />
|
||||
<span className="text-slate-300 font-serif italic">einfach. Wirklich.</span>
|
||||
<span className="text-slate-300">einfach. Wirklich.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -233,13 +233,13 @@ export default function WebsitesPage() {
|
||||
number="06"
|
||||
title="Warum"
|
||||
borderTop
|
||||
illustration={<ConceptMessy className="w-16 h-16" />}
|
||||
illustration={<AgencyChaos className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-12">
|
||||
<Reveal>
|
||||
<h3 className="text-3xl md:text-5xl font-bold text-slate-900 leading-tight tracking-tight max-w-3xl">
|
||||
15 Jahre Agenturen <br />
|
||||
<span className="text-slate-300 font-serif italic">waren genug.</span>
|
||||
<span className="text-slate-300">waren genug.</span>
|
||||
</h3>
|
||||
</Reveal>
|
||||
<Reveal delay={0.2}>
|
||||
@@ -252,17 +252,18 @@ export default function WebsitesPage() {
|
||||
</Section>
|
||||
|
||||
{/* Result */}
|
||||
<Section
|
||||
number="07"
|
||||
<Section
|
||||
number="07"
|
||||
title="Ergebnis"
|
||||
borderTop
|
||||
variant="gray"
|
||||
illustration={<TaskDone className="w-32 h-32" />}
|
||||
>
|
||||
<div className="space-y-16">
|
||||
<Reveal>
|
||||
<h2 className="text-5xl md:text-7xl font-bold text-slate-900 tracking-tighter leading-[0.9]">
|
||||
<h2 className="text-5xl md:text-8xl font-bold text-slate-900 tracking-tighter leading-[0.9]">
|
||||
Eine Website, die sich wie eine <br />
|
||||
<span className="text-slate-300 font-serif italic">erledigte Aufgabe</span> anfühlt.
|
||||
<span className="text-slate-300">erledigte Aufgabe</span> anfühlt.
|
||||
</h2>
|
||||
</Reveal>
|
||||
|
||||
@@ -282,7 +283,7 @@ export default function WebsitesPage() {
|
||||
</div>
|
||||
|
||||
<Reveal delay={0.4}>
|
||||
<div className="pt-16 border-t border-slate-100 flex flex-col md:flex-row justify-between items-start md:items-center gap-8">
|
||||
<div className="pt-16 border-t border-slate-200 flex flex-col md:flex-row justify-between items-start md:items-center gap-8">
|
||||
<div className="space-y-2">
|
||||
<div className="text-[10px] font-bold uppercase tracking-[0.3em] text-slate-400">Technik</div>
|
||||
<p className="text-slate-500 font-serif italic text-lg">
|
||||
|
||||
333
src/components/Landing/Illustrations/WebsitesDescriptive.tsx
Normal file
333
src/components/Landing/Illustrations/WebsitesDescriptive.tsx
Normal file
@@ -0,0 +1,333 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { IllustrationProps } from './types';
|
||||
|
||||
export const SystemArchitecture: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Central Core */}
|
||||
<motion.rect
|
||||
x="70" y="70" width="60" height="60" rx="12"
|
||||
className="fill-slate-900"
|
||||
initial={{ scale: 0, rotate: -45 }}
|
||||
whileInView={{ scale: 1, rotate: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ type: "spring", stiffness: 100, delay }}
|
||||
/>
|
||||
|
||||
{/* Orbiting Elements */}
|
||||
{[0, 90, 180, 270].map((angle, i) => {
|
||||
const rad = (angle * Math.PI) / 180;
|
||||
const x = 100 + Math.cos(rad) * 60 - 15;
|
||||
const y = 100 + Math.sin(rad) * 60 - 15;
|
||||
return (
|
||||
<motion.rect
|
||||
key={i}
|
||||
x={x} y={y} width="30" height="30" rx="8"
|
||||
className="fill-white stroke-slate-200"
|
||||
strokeWidth="1"
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
animate={{
|
||||
y: [y, y - 5, y],
|
||||
}}
|
||||
transition={{
|
||||
delay: delay + 0.2 + i * 0.1,
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
repeatDelay: i * 0.5,
|
||||
ease: "easeInOut"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Connection Lines */}
|
||||
<motion.path
|
||||
d="M 100 40 V 70 M 100 130 V 160 M 40 100 H 70 M 130 100 H 160"
|
||||
stroke="currentColor" strokeWidth="1"
|
||||
className="text-slate-200"
|
||||
strokeDasharray="4 4"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1, delay: delay + 0.6 }}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const SpeedPerformance: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* The "Built" Foundation */}
|
||||
<motion.path
|
||||
d="M 40 160 H 160"
|
||||
stroke="currentColor" strokeWidth="4" strokeLinecap="round"
|
||||
className="text-slate-900"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8, delay }}
|
||||
/>
|
||||
|
||||
{/* Bricks forming a structure */}
|
||||
{[
|
||||
{ x: 50, y: 130, w: 30 },
|
||||
{ x: 85, y: 130, w: 30 },
|
||||
{ x: 120, y: 130, w: 30 },
|
||||
{ x: 65, y: 105, w: 30 },
|
||||
{ x: 105, y: 105, w: 30 },
|
||||
].map((brick, i) => (
|
||||
<motion.rect
|
||||
key={i}
|
||||
x={brick.x} y={brick.y} width={brick.w} height="20" rx="2"
|
||||
className="fill-slate-100 stroke-slate-200"
|
||||
strokeWidth="1"
|
||||
initial={{ opacity: 0, y: brick.y + 10 }}
|
||||
whileInView={{ opacity: 1, y: brick.y }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.2 + i * 0.1 }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Lightning Bolt emerging from the "Built" structure */}
|
||||
<motion.path
|
||||
d="M 110 30 L 80 80 H 100 L 70 130"
|
||||
stroke="currentColor" strokeWidth="6" strokeLinecap="round" strokeLinejoin="round"
|
||||
className="text-slate-900"
|
||||
initial={{ pathLength: 0, filter: "drop-shadow(0 0 0px rgba(0,0,0,0))" }}
|
||||
whileInView={{ pathLength: 1, filter: "drop-shadow(0 0 8px rgba(0,0,0,0.1))" }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: delay + 1, ease: "easeOut" }}
|
||||
/>
|
||||
|
||||
{/* Speed Lines */}
|
||||
{[0, 1, 2].map((i) => (
|
||||
<motion.path
|
||||
key={i}
|
||||
d={`M ${130 + i * 10} ${50 + i * 20} H ${150 + i * 10}`}
|
||||
stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
||||
className="text-slate-200"
|
||||
initial={{ x: -10, opacity: 0 }}
|
||||
animate={{ x: 10, opacity: [0, 1, 0] }}
|
||||
transition={{ duration: 0.8, repeat: Infinity, delay: delay + 1.2 + i * 0.2 }}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const SolidFoundation: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Stacked Blocks */}
|
||||
<motion.rect
|
||||
x="40" y="140" width="120" height="30" rx="4"
|
||||
className="fill-slate-900"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
/>
|
||||
<motion.rect
|
||||
x="60" y="100" width="80" height="30" rx="4"
|
||||
className="fill-slate-400"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.2 }}
|
||||
/>
|
||||
<motion.rect
|
||||
x="80" y="60" width="40" height="30" rx="4"
|
||||
className="fill-slate-200"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.4 }}
|
||||
/>
|
||||
|
||||
{/* Shield Icon Overlay */}
|
||||
<motion.path
|
||||
d="M 100 30 L 120 40 V 60 C 120 80 100 90 100 90 C 100 90 80 80 80 60 V 40 L 100 30 Z"
|
||||
className="fill-white stroke-slate-900"
|
||||
strokeWidth="2"
|
||||
initial={{ scale: 0 }}
|
||||
whileInView={{ scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ type: "spring", delay: delay + 0.8 }}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const LayerSeparation: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Top Layer (Content) */}
|
||||
<motion.g
|
||||
initial={{ y: -20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
>
|
||||
<rect x="40" y="40" width="120" height="40" rx="8" className="fill-white stroke-slate-200" strokeWidth="1" />
|
||||
<rect x="55" y="55" width="30" height="10" rx="2" className="fill-slate-100" />
|
||||
<rect x="95" y="55" width="50" height="10" rx="2" className="fill-slate-100" />
|
||||
</motion.g>
|
||||
|
||||
{/* Gap with Arrows */}
|
||||
<motion.path
|
||||
d="M 100 90 V 110"
|
||||
stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
||||
className="text-slate-200"
|
||||
strokeDasharray="4 4"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.4 }}
|
||||
/>
|
||||
|
||||
{/* Bottom Layer (Code/Logic) */}
|
||||
<motion.g
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.2 }}
|
||||
>
|
||||
<rect x="40" y="120" width="120" height="40" rx="8" className="fill-slate-900" />
|
||||
<path d="M 60 140 L 70 135 L 60 130 M 140 140 L 130 135 L 140 130" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</motion.g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const DirectService: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Two Nodes */}
|
||||
<motion.circle
|
||||
cx="50" cy="100" r="15"
|
||||
className="fill-slate-200"
|
||||
initial={{ scale: 0 }}
|
||||
whileInView={{ scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
/>
|
||||
<motion.circle
|
||||
cx="150" cy="100" r="15"
|
||||
className="fill-slate-900"
|
||||
initial={{ scale: 0 }}
|
||||
whileInView={{ scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 0.2 }}
|
||||
/>
|
||||
|
||||
{/* Direct Connection */}
|
||||
<motion.path
|
||||
d="M 65 100 H 135"
|
||||
stroke="currentColor" strokeWidth="3" strokeLinecap="round"
|
||||
className="text-slate-900"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.8, delay: delay + 0.4 }}
|
||||
/>
|
||||
|
||||
{/* Pulse moving between */}
|
||||
<motion.circle r="4" className="fill-white">
|
||||
<animateMotion
|
||||
dur="2s"
|
||||
repeatCount="indefinite"
|
||||
path="M 65 100 H 135"
|
||||
/>
|
||||
</motion.circle>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const AgencyChaos: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Messy Path */}
|
||||
<motion.path
|
||||
d="M 30 100 C 50 40, 70 160, 90 100 C 110 40, 130 160, 170 100"
|
||||
stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
||||
className="text-slate-200"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 2, delay }}
|
||||
/>
|
||||
|
||||
{/* Intersecting Circles (Meetings) */}
|
||||
{[0, 1, 2].map((i) => (
|
||||
<motion.circle
|
||||
key={i}
|
||||
cx={60 + i * 40} cy={100 + (i % 2 === 0 ? -30 : 30)} r="20"
|
||||
className="stroke-slate-100 fill-white"
|
||||
strokeWidth="1"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
animate={{ scale: [1, 1.1, 1] }}
|
||||
transition={{
|
||||
delay: delay + 0.5 + i * 0.3,
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
repeatDelay: i * 0.5
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Slate "X" or Stop signs - NO COLOR */}
|
||||
<motion.path
|
||||
d="M 160 90 L 180 110 M 180 90 L 160 110"
|
||||
stroke="currentColor" strokeWidth="3" strokeLinecap="round"
|
||||
className="text-slate-300"
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
whileInView={{ opacity: 0.5, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 1.5 }}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const TaskDone: React.FC<IllustrationProps> = ({ className = "", delay = 0 }) => (
|
||||
<svg className={className} viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
{/* Checkmark Circle */}
|
||||
<motion.circle
|
||||
cx="100" cy="100" r="60"
|
||||
className="stroke-slate-900"
|
||||
strokeWidth="2"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1, delay }}
|
||||
/>
|
||||
|
||||
{/* Checkmark */}
|
||||
<motion.path
|
||||
d="M 70 100 L 90 120 L 135 75"
|
||||
stroke="currentColor" strokeWidth="8" strokeLinecap="round" strokeLinejoin="round"
|
||||
className="text-slate-900"
|
||||
initial={{ pathLength: 0 }}
|
||||
whileInView={{ pathLength: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: delay + 0.8 }}
|
||||
/>
|
||||
|
||||
{/* Confetti/Sparkles */}
|
||||
{[0, 45, 90, 135, 180, 225, 270, 315].map((angle, i) => {
|
||||
const rad = (angle * Math.PI) / 180;
|
||||
const x1 = 100 + Math.cos(rad) * 70;
|
||||
const y1 = 100 + Math.sin(rad) * 70;
|
||||
const x2 = 100 + Math.cos(rad) * 85;
|
||||
const y2 = 100 + Math.sin(rad) * 85;
|
||||
return (
|
||||
<motion.line
|
||||
key={i}
|
||||
x1={x1} y1={y1} x2={x2} y2={y2}
|
||||
stroke="currentColor" strokeWidth="2" strokeLinecap="round"
|
||||
className="text-slate-200"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: delay + 1.2 + i * 0.05 }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
);
|
||||
Reference in New Issue
Block a user