diff --git a/src/components/Features.tsx b/src/components/Features.tsx index c29d33b..367e4d7 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -1,4 +1,7 @@ import Pathfinding from '@/components/Pathfinding' +import MessageBus from '@/components/MessageBus' +import ProxyDetection from '@/components/ProxyDetection' +import ProxyForwarding from '@/components/ProxyForwarding' export function Features() { return ( @@ -38,11 +41,7 @@ export function Features() {
- +

Communication

@@ -59,11 +58,7 @@ export function Features() {

- +

Discovery

@@ -101,11 +96,7 @@ export function Features() {

- +

Delivery

diff --git a/src/components/MessageBus.tsx b/src/components/MessageBus.tsx new file mode 100644 index 0000000..b63c4ba --- /dev/null +++ b/src/components/MessageBus.tsx @@ -0,0 +1,150 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-72" + bg?: string; // default white +}; + +/** Palette (gray/black + accent only) */ +const ACCENT = '#00b8db'; +const STROKE = '#111827'; // black-ish +const GRAY = '#9CA3AF'; +const GRAY_LT = '#E5E7EB'; + +function Envelope({ + x, y, w = 88, h = 56, fill = GRAY_LT, accent = false, delay = 0, duration = 1.6, + path = 'none', // 'left1' | 'left2' | 'rightTop' | 'rightBottom' | 'none' + reverse = false, +}: { + x: number; y: number; w?: number; h?: number; fill?: string; accent?: boolean; + delay?: number; duration?: number; path?: 'left1'|'left2'|'rightTop'|'rightBottom'|'none'; reverse?: boolean; +}) { + const prefersReduced = useReducedMotion(); + + // simple keyframe paths (straight line segments) + const paths: Record = { + left1: { x: [x, 380], y: [y, 220] }, + left2: { x: [x, 380], y: [y, 220] }, + rightTop: { x: [380, 720], y: [220, 150] }, + rightBottom: { x: [380, 720], y: [220, 290] }, + none: { x: [x], y: [y] }, + }; + + const k = paths[path]; + + return ( + + {/* envelope body */} + + {/* flap */} + + + ); +} + +export default function MessageBus({ className, bg = '#ffffff' }: Props) { + const W = 900; + const H = 460; + + return ( +

+ ); +} diff --git a/src/components/ProxyDetection.tsx b/src/components/ProxyDetection.tsx new file mode 100644 index 0000000..6ddaeb7 --- /dev/null +++ b/src/components/ProxyDetection.tsx @@ -0,0 +1,194 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-64" + bg?: string; // defaults to white +}; + +// Palette (only these) +const ACCENT = '#00b8db'; +const STROKE = '#111827'; +const GRAY = '#9CA3AF'; +const GRAY_LT = '#E5E7EB'; + +function Magnifier({ + x = 0, + y = 0, + flip = false, + delay = 0, + duration = 3, +}: { + x?: number; + y?: number; + flip?: boolean; // rotate handle direction + delay?: number; + duration?: number; +}) { + const prefersReduced = useReducedMotion(); + + return ( + + {/* glass */} + + {/* subtle scanning pulse inside the glass */} + + {/* handle */} + + + + + + ); +} + +function ServerBox({ + x, + y, + w = 88, + h = 50, + delay = 0, + accentPulse = false, +}: { + x: number; + y: number; + w?: number; + h?: number; + delay?: number; + accentPulse?: boolean; +}) { + const prefersReduced = useReducedMotion(); + + return ( + + {/* outer box */} + + {/* top bar */} + + {/* activity line */} + + {/* “detected” indicator */} + + + ); +} + +export default function ProxyDetection({ className, bg = '#ffffff' }: Props) { + // Canvas + const W = 900; + const H = 180; + + // Layout: a row of proxy servers + const rowY = H / 2; + const xs = [180, 320, 460, 600, 740]; + + // Sequence timings so boxes light up as magnifier passes + const delays = [0.8, 0.6, 0.4, 0.2, 0.0]; + + return ( + + ); +} diff --git a/src/components/ProxyForwarding.tsx b/src/components/ProxyForwarding.tsx new file mode 100644 index 0000000..a76517e --- /dev/null +++ b/src/components/ProxyForwarding.tsx @@ -0,0 +1,175 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-72" + bg?: string; // default white +}; + +/** Palette */ +const ACCENT = '#00b8db'; +const STROKE = '#111827'; // black-ish +const GRAY = '#9CA3AF'; +const GRAY_LT = '#E5E7EB'; + +function Laptop({ x, y }: { x: number; y: number }) { + return ( + + {/* screen */} + + + {/* base */} + + + ); +} + +function ServerStack({ x, y }: { x: number; y: number }) { + return ( + + {[0, 1, 2].map((i) => ( + + + + + + + + ))} + {/* tiny rack base */} + + + + ); +} + +function Cloud({ x, y }: { x: number; y: number }) { + return ( + + + + + + + + ); +} + +function Arrow({ d, delay = 0 }: { d: string; delay?: number }) { + return ( + + ); +} + +/** Small packet traveling along keyframe x/y arrays */ +function Packet({ + xs, + ys, + delay = 0, + color = ACCENT, + duration = 2.2, +}: { + xs: number[]; + ys: number[]; + delay?: number; + color?: string; + duration?: number; +}) { + const prefersReduced = useReducedMotion(); + return ( + + ); +} + +export default function ProxyForwarding({ className, bg = '#ffffff' }: Props) { + const W = 1000; + const H = 420; + + // Key points + const C1 = { x: 140, y: 90 }; + const C2 = { x: 140, y: 210 }; + const C3 = { x: 140, y: 330 }; + + const PROXY = { x: 420, y: 210 }; + const CLOUD = { x: 640, y: 210 }; + const DEST = { x: 860, y: 210 }; + + return ( + + ); +}