Der klassische Agentur-Weg ist oft langsam und teuer. Mein Ansatz ist radikal anders: Ich baue zuerst, dann reden wir über Details.
diff --git a/src/components/CTA.tsx b/src/components/CTA.tsx
deleted file mode 100644
index 6736a91..0000000
--- a/src/components/CTA.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import * as React from 'react';
-import Link from 'next/link';
-import { ArrowRight } from 'lucide-react';
-import { Reveal } from './Reveal';
-
-export const CTA: React.FC = () => {
- return (
-
-
-
-
-
?
-
-
-
- Interesse?
- Fragen kostet nichts.
-
-
-
-
- Schreiben Sie mir einfach, was Sie brauchen. Ich sage Ihnen ehrlich, ob ich es mache.
-
-
-
-
- Projekt anfragen
-
-
-
-
-
-
-
-
- );
-};
diff --git a/src/components/Landing/FlowingPath.tsx b/src/components/Landing/FlowingPath.tsx
deleted file mode 100644
index 4a39ca1..0000000
--- a/src/components/Landing/FlowingPath.tsx
+++ /dev/null
@@ -1,535 +0,0 @@
-'use client';
-
-import * as React from 'react';
-import { useEffect, useRef, useState, useCallback } from 'react';
-import { motion, useScroll, useTransform, useSpring, useAnimationFrame } from 'framer-motion';
-
-interface FlowingPathProps {
- className?: string;
-}
-
-interface PathNode {
- x: number;
- y: number;
- type: 'curve' | 'line';
-}
-
-export const FlowingPath: React.FC = ({ className = '' }) => {
- const containerRef = useRef(null);
- const [pathData, setPathData] = useState('');
- const [pathLength, setPathLength] = useState(0);
- const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
- const pathRef = useRef(null);
-
- const { scrollYProgress } = useScroll();
- const [vh, setVh] = useState(0);
-
- useEffect(() => {
- setVh(window.innerHeight);
- const handleResize = () => setVh(window.innerHeight);
- window.addEventListener('resize', handleResize);
- return () => window.removeEventListener('resize', handleResize);
- }, []);
-
- // Calculate the progress needed to keep the dot at the center of the viewport
- const centeredProgress = useTransform(
- scrollYProgress,
- (latest) => {
- if (dimensions.height === 0 || vh === 0) return latest;
-
- // Total scrollable distance
- const scrollRange = dimensions.height - vh;
- const currentScrollY = latest * scrollRange;
-
- // We want the dot to be at y = currentScrollY + vh/2
- // But at the very top (latest=0), we want it at y=0 (or above)
- // At the very bottom (latest=1), we want it at y=dimensions.height
-
- let targetY;
- if (latest < 0.05) {
- // Transition from top of page to center of viewport
- targetY = (latest / 0.05) * (vh / 2);
- } else if (latest > 0.95) {
- // Transition from center of viewport to bottom of page
- const p = (latest - 0.95) / 0.05;
- targetY = (currentScrollY + vh / 2) * (1 - p) + dimensions.height * p;
- } else {
- targetY = currentScrollY + vh / 2;
- }
-
- return Math.max(0, Math.min(1, targetY / dimensions.height));
- }
- );
-
- // Smooth spring animation for the path drawing
- const smoothProgress = useSpring(centeredProgress, {
- stiffness: 100, // Much higher stiffness for immediate response
- damping: 40, // Higher damping to prevent any overshoot/backward motion
- restDelta: 0.0001,
- mass: 0.5, // Lower mass for faster acceleration
- });
-
- // Transform scroll progress to path drawing
- const pathDrawn = useTransform(smoothProgress, [0, 1], [0, 1]);
-
- // Dot transforms
- const dot1Distance = useTransform(smoothProgress, [0, 1], ['0%', '100%']);
- const dot2Distance = useTransform(smoothProgress, [0, 1], ['0%', '98%']);
-
- // Generate a flowing path that wraps around content sections
- const generatePath = useCallback((width: number, height: number): string => {
- const margin = width < 768 ? 20 : 80;
- const contentWidth = Math.min(width * 0.9, 1200);
- const leftEdge = (width - contentWidth) / 2 - margin;
- const rightEdge = (width + contentWidth) / 2 + margin;
- const centerX = width / 2;
-
- const sectionHeight = height / 8;
-
- const nodes: PathNode[] = [
- { x: centerX - 100, y: 0, type: 'line' },
- { x: centerX - 100, y: sectionHeight * 0.2, type: 'curve' },
-
- // Hero section - wide sweep
- { x: rightEdge, y: sectionHeight * 0.8, type: 'curve' },
- { x: rightEdge - 50, y: sectionHeight * 1.5, type: 'curve' },
-
- // Section 2 - tight curve to left
- { x: leftEdge + 50, y: sectionHeight * 2.2, type: 'curve' },
- { x: leftEdge, y: sectionHeight * 3.0, type: 'curve' },
-
- // Section 3 - middle flow
- { x: centerX + 150, y: sectionHeight * 3.8, type: 'curve' },
- { x: centerX - 150, y: sectionHeight * 4.6, type: 'curve' },
-
- // Section 4 - right side
- { x: rightEdge - 20, y: sectionHeight * 5.4, type: 'curve' },
- { x: rightEdge - 100, y: sectionHeight * 6.2, type: 'curve' },
-
- // Section 5 - back to left
- { x: leftEdge + 100, y: sectionHeight * 7.0, type: 'curve' },
-
- // Final - center
- { x: centerX, y: sectionHeight * 7.6, type: 'curve' },
- { x: centerX, y: height, type: 'line' },
- ];
-
- let path = `M ${nodes[0].x} ${nodes[0].y}`;
-
- for (let i = 1; i < nodes.length; i++) {
- const prev = nodes[i - 1];
- const curr = nodes[i];
-
- if (curr.type === 'curve') {
- const dy = curr.y - prev.y;
- const cp1y = prev.y + dy * 0.4;
- const cp2y = prev.y + dy * 0.6;
- path += ` C ${prev.x} ${cp1y}, ${curr.x} ${cp2y}, ${curr.x} ${curr.y}`;
- } else {
- path += ` L ${curr.x} ${curr.y}`;
- }
- }
-
- return path;
- }, []);
-
- // Generate secondary decorative paths that follow the main path with some divergence
- const generateSecondaryPath = useCallback((width: number, height: number, seed: number): string => {
- const margin = width < 768 ? 20 : 80;
- const contentWidth = Math.min(width * 0.9, 1200);
- const leftEdge = (width - contentWidth) / 2 - margin;
- const rightEdge = (width + contentWidth) / 2 + margin;
- const centerX = width / 2;
-
- const sectionHeight = height / 8;
-
- const random = (i: number) => {
- const x = Math.sin(seed + i) * 10000;
- return x - Math.floor(x);
- };
-
- // Base nodes similar to generatePath but with randomized offsets
- const nodes = [
- { x: centerX - 100 + (random(0) - 0.5) * 40, y: 0 },
- { x: centerX - 100 + (random(1) - 0.5) * 40, y: sectionHeight * 0.2 },
- { x: rightEdge + (random(2) - 0.5) * 60, y: sectionHeight * 0.8 },
- { x: rightEdge - 50 + (random(3) - 0.5) * 60, y: sectionHeight * 1.5 },
- { x: leftEdge + 50 + (random(4) - 0.5) * 60, y: sectionHeight * 2.2 },
- { x: leftEdge + (random(5) - 0.5) * 60, y: sectionHeight * 3.0 },
- { x: centerX + 150 + (random(6) - 0.5) * 80, y: sectionHeight * 3.8 },
- { x: centerX - 150 + (random(7) - 0.5) * 80, y: sectionHeight * 4.6 },
- { x: rightEdge - 20 + (random(8) - 0.5) * 60, y: sectionHeight * 5.4 },
- { x: rightEdge - 100 + (random(9) - 0.5) * 60, y: sectionHeight * 6.2 },
- { x: leftEdge + 100 + (random(10) - 0.5) * 60, y: sectionHeight * 7.0 },
- { x: centerX + (random(11) - 0.5) * 40, y: sectionHeight * 7.6 },
- { x: centerX + (random(12) - 0.5) * 40, y: height }
- ];
-
- let path = `M ${nodes[0].x} ${nodes[0].y}`;
- for (let i = 1; i < nodes.length; i++) {
- const prev = nodes[i - 1];
- const curr = nodes[i];
- const dy = curr.y - prev.y;
- const cp1y = prev.y + dy * 0.4;
- const cp2y = prev.y + dy * 0.6;
- path += ` C ${prev.x} ${cp1y}, ${curr.x} ${cp2y}, ${curr.x} ${curr.y}`;
- }
-
- return path;
- }, []);
-
- // Update dimensions and path on resize
- useEffect(() => {
- const updateDimensions = () => {
- if (typeof window !== 'undefined') {
- const width = window.innerWidth;
- const height = document.documentElement.scrollHeight;
- setDimensions({ width, height });
- setPathData(generatePath(width, height));
- }
- };
-
- updateDimensions();
-
- // Debounced resize handler
- let timeoutId: NodeJS.Timeout;
- const handleResize = () => {
- clearTimeout(timeoutId);
- timeoutId = setTimeout(updateDimensions, 100);
- };
-
- window.addEventListener('resize', handleResize);
-
- // Also update when content might have changed
- const observer = new ResizeObserver(handleResize);
- if (document.body) {
- observer.observe(document.body);
- }
-
- return () => {
- window.removeEventListener('resize', handleResize);
- observer.disconnect();
- clearTimeout(timeoutId);
- };
- }, [generatePath]);
-
- // Get path length after path is rendered
- useEffect(() => {
- if (pathRef.current) {
- setPathLength(pathRef.current.getTotalLength());
- }
- }, [pathData]);
-
- if (dimensions.width === 0 || dimensions.height === 0) {
- return null;
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Background decorative paths - static, very subtle */}
-
-
-
- {/* Main flowing path - draws as you scroll */}
-
-
- {/* Accent path - fully filled, static */}
-
-
- {/* Secondary accent path - fully filled, static */}
-
-
- {/* Extra static dashed lines with unique random paths */}
-
-
-
-
- {/* Pulse System */}
-
-
- {/* Animated dot that travels along the path */}
-
-
- {/* Secondary traveling dot */}
-
-
- {/* Node markers at key positions */}
- {[0.12, 0.28, 0.45, 0.62, 0.82].map((pos, i) => (
-
- ))}
-
-
- );
-};
-
-interface NodeMarkerProps {
- pos: number;
- pathData: string;
- progress: any;
-}
-
-const NodeMarker: React.FC = ({ pos, pathData, progress }) => {
- const isActive = useTransform(
- progress,
- [pos - 0.05, pos, pos + 0.05],
- [0, 1, 1]
- );
-
- const scale = useTransform(
- progress,
- [pos - 0.05, pos, pos + 0.05],
- [0.8, 1.4, 1]
- );
-
- const glowOpacity = useTransform(
- progress,
- [pos - 0.02, pos, pos + 0.02],
- [0, 0.6, 0]
- );
-
- return (
-
-
-
-
-
- );
-};
-
-interface Pulse {
- id: number;
- x: number;
- y: number;
- life: number;
-}
-
-const PulseSystem: React.FC<{
- pathRef: React.RefObject;
- progress: any;
- pathLength: number;
- checkpoints: number[];
-}> = ({ pathRef, progress, pathLength, checkpoints }) => {
- const [pulses, setPulses] = useState([]);
- const lastProgress = useRef(0);
- const pulseId = useRef(0);
-
- useAnimationFrame((time, delta) => {
- if (!pathRef.current || pathLength === 0) return;
-
- const currentProgress = progress.get();
- const point = pathRef.current.getPointAtLength(currentProgress * pathLength);
-
- // Check for checkpoint pulses
- checkpoints.forEach(cp => {
- const crossed = (lastProgress.current < cp && currentProgress >= cp) ||
- (lastProgress.current > cp && currentProgress <= cp);
-
- if (crossed) {
- const checkpointPoint = pathRef.current!.getPointAtLength(cp * pathLength);
- setPulses(prev => [...prev, {
- id: pulseId.current++,
- x: checkpointPoint.x,
- y: checkpointPoint.y,
- life: 1.0,
- }].slice(-8));
- }
- });
- lastProgress.current = currentProgress;
-
- // Update pulses
- setPulses(prev => prev.map(p => ({ ...p, life: p.life - 0.015 })).filter(p => p.life > 0));
- });
-
- return (
-
- {pulses.map(p => (
-
- ))}
-
- );
-};
-
-export default FlowingPath;
diff --git a/src/components/Landing/index.ts b/src/components/Landing/index.ts
index 0161484..af62dbb 100644
--- a/src/components/Landing/index.ts
+++ b/src/components/Landing/index.ts
@@ -1,7 +1,7 @@
-export * from './HeroItem';
-export * from './FeatureCard';
-export * from './ComparisonRow';
-export * from './ServiceCard';
export * from './AbstractLines';
+export * from './ComparisonRow';
+export * from './FeatureCard';
+export * from './HeroItem';
export * from './ParticleNetwork';
-export * from './FlowingPath';
+export * from './ServiceCard';
+