Files
www_projectmycelium_com/src/pages/home/animations/NoControl.tsx
sasha-astiadi eba1bb7047 feat: add architecture section with animated illustrations
- Created HomeArchitecture component showcasing decentralized network principles with SVG animations
- Added animated illustrations for mesh networking, no extraction, no control, and no central servers
- Enhanced CallToAction with cyan radial glow effect for visual emphasis
- Fixed audience gallery image paths to use correct /images/audiences/ directory
2025-11-12 15:47:58 +01:00

176 lines
4.4 KiB
TypeScript

"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string;
bg?: string;
};
const W = 760;
const H = 420;
/** Node component */
const Node = ({
x,
y,
r = 14,
accent = "#00b8db",
pulse = false,
faded = false,
}: {
x: number;
y: number;
r?: number;
accent?: string;
pulse?: boolean;
faded?: boolean;
}) => {
const prefers = useReducedMotion();
return (
<>
<motion.circle
cx={x}
cy={y}
r={r + 10}
stroke="#1F2937"
strokeWidth={2}
fill="none"
initial={{ opacity: 0 }}
animate={{ opacity: faded ? 0.3 : 0.9 }}
transition={{ duration: 0.6 }}
/>
<motion.circle
cx={x}
cy={y}
r={r}
fill={accent}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: faded ? 0.3 : 1,
scale: pulse && !prefers ? [1, 1.12, 1] : 1,
}}
transition={{
duration: pulse && !prefers ? 1.8 : 0.6,
repeat: pulse && !prefers ? Infinity : 0,
repeatType: "mirror",
ease: [0.22, 1, 0.36, 1],
}}
/>
</>
);
};
/** Moving packet along a path */
const Packet = ({
path,
delay = 0,
accent = "#00b8db",
}: {
path: string;
delay?: number;
accent?: string;
}) => {
const prefers = useReducedMotion();
return (
<motion.circle
r={5}
fill={accent}
initial={{ offsetDistance: "0%", opacity: 0 }}
animate={{
offsetDistance: ["0%", "100%"],
opacity: [0, 1, 0],
}}
transition={{
delay,
duration: 1.6,
repeat: !prefers ? Infinity : 0,
repeatType: "loop",
ease: "linear",
}}
style={{
offsetPath: `path('${path}')`,
}}
/>
);
};
export default function NoControl({
className,
accent = "#00b8db",
bg = "#0a0a0a",
}: Props) {
const center = { x: 380, y: 210 };
const outer = [
{ x: 160, y: 120 },
{ x: 600, y: 120 },
{ x: 160, y: 300 },
{ x: 600, y: 300 },
];
const link = (a: any, b: any) => `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="No single point of control illustration"
style={{ background: bg }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* Background grid */}
<defs>
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
<path d="M 28 0 L 0 0 0 28" fill="none" stroke="#0f0f0f" strokeWidth="1" />
</pattern>
</defs>
<rect width={W} height={H} fill="url(#grid-dark)" />
{/* Connections between outer nodes (decentralized ring) */}
<motion.path
d={`M ${outer[0].x} ${outer[0].y}
L ${outer[1].x} ${outer[1].y}
L ${outer[3].x} ${outer[3].y}
L ${outer[2].x} ${outer[2].y} Z`}
stroke="#1F2937"
strokeWidth={3}
strokeLinecap="round"
fill="none"
initial={{ pathLength: 0, opacity: 0.3 }}
animate={{ pathLength: 1, opacity: 0.6 }}
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
/>
{/* Cyan pulsing signals around the ring */}
{outer.map((o, i) => {
const next = outer[(i + 1) % outer.length];
const p = link(o, next);
return <Packet key={`p-${i}`} path={p} delay={i * 0.4} accent={accent} />;
})}
{/* Faded center node (represents the “former” single point) */}
<Node x={center.x} y={center.y} r={18} accent={accent} faded pulse={false} />
{/* Outer sovereign nodes */}
{outer.map((n, i) => (
<Node key={i} x={n.x} y={n.y} r={14} accent={accent} pulse />
))}
{/* Cross mark over center node (control denied) */}
<motion.path
d={`M ${center.x - 12} ${center.y - 12} L ${center.x + 12} ${center.y + 12} M ${center.x - 12} ${center.y + 12} L ${center.x + 12} ${center.y - 12}`}
stroke="#FF4D4D"
strokeWidth={3}
strokeLinecap="round"
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.8, duration: 0.6 }}
/>
</svg>
</div>
);
}