forked from emre/www_projectmycelium_com
- Renamed NoCentral to DataControl with enhanced data ownership visualization - Renamed NoExtraction to Connectivity with full mesh P2P topology - Renamed NoSinglePoint to Security with improved styling - Renamed NoControl to Resilience with consistent styling - Added lg:-mt-12 margin to Security and Resilience animations for alignment - Updated DataControl to show explicit permission gates and bi-directional replication
232 lines
6.4 KiB
TypeScript
232 lines
6.4 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useReducedMotion } from "framer-motion";
|
|
import clsx from "clsx";
|
|
|
|
type Props = {
|
|
className?: string;
|
|
accent?: string; // cyan
|
|
danger?: string; // hostile color
|
|
bg?: string; // dark background
|
|
gridStroke?: string; // subtle grid
|
|
};
|
|
|
|
const W = 720;
|
|
const H = 540;
|
|
|
|
export default function Security({
|
|
className,
|
|
accent = "#00b8db",
|
|
danger = "#ff4d4d",
|
|
bg = "#0b0b0b",
|
|
gridStroke = "#2b2a2a",
|
|
}: Props) {
|
|
const prefers = useReducedMotion();
|
|
|
|
// Central protected pod
|
|
const center = { x: W / 2, y: H / 2 };
|
|
|
|
// External hostile/corporate nodes
|
|
const hostile = [
|
|
{ x: 160, y: 120 },
|
|
{ x: 560, y: 120 },
|
|
{ x: 120, y: 300 },
|
|
{ x: 600, y: 380 },
|
|
{ x: 350, y: 80 },
|
|
];
|
|
|
|
// Helper function for attack rays
|
|
const attackRay = (h: { x: number; y: number }) =>
|
|
`M ${h.x} ${h.y} L ${center.x} ${center.y}`;
|
|
|
|
return (
|
|
<div
|
|
className={clsx("relative overflow-hidden", className)}
|
|
aria-hidden="true"
|
|
role="img"
|
|
style={{ background: bg }}
|
|
>
|
|
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
|
{/* ====== GRID + FILTERS ====== */}
|
|
<defs>
|
|
<pattern id="sec-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
|
<path
|
|
d="M 28 0 L 0 0 0 28"
|
|
stroke={gridStroke}
|
|
strokeWidth="1"
|
|
opacity="0.35"
|
|
fill="none"
|
|
/>
|
|
</pattern>
|
|
|
|
<filter id="sec-glow">
|
|
<feGaussianBlur stdDeviation="3" result="b" />
|
|
<feMerge>
|
|
<feMergeNode in="b" />
|
|
<feMergeNode in="SourceGraphic" />
|
|
</feMerge>
|
|
</filter>
|
|
|
|
<radialGradient id="sec-core" cx="50%" cy="50%" r="60%">
|
|
<stop offset="0%" stopColor={accent} stopOpacity="0.9" />
|
|
<stop offset="100%" stopColor={accent} stopOpacity="0.06" />
|
|
</radialGradient>
|
|
</defs>
|
|
|
|
<rect width={W} height={H} fill="url(#sec-grid)" />
|
|
|
|
{/* ====== ATTACK LINES (blocked) ====== */}
|
|
{hostile.map((h, i) => (
|
|
<motion.path
|
|
key={`atk-${i}`}
|
|
d={attackRay(h)}
|
|
stroke={danger}
|
|
strokeWidth={2}
|
|
strokeDasharray="10 8"
|
|
strokeLinecap="round"
|
|
fill="none"
|
|
initial={{ pathLength: 0, opacity: 0.7 }}
|
|
animate={{
|
|
pathLength: 1,
|
|
opacity: [0.7, 0.2, 0.7], // attempt but weakened
|
|
}}
|
|
transition={{
|
|
duration: 1.5,
|
|
delay: i * 0.25,
|
|
repeat: Infinity,
|
|
repeatType: "reverse",
|
|
ease: "easeInOut",
|
|
}}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
))}
|
|
|
|
{/* ====== BLOCKING SHIELD ====== */}
|
|
{/* Outer shield */}
|
|
<motion.circle
|
|
cx={center.x}
|
|
cy={center.y}
|
|
r={90}
|
|
fill="none"
|
|
stroke={accent}
|
|
strokeWidth={2}
|
|
opacity={0.25}
|
|
animate={!prefers ? { scale: [1, 1.05, 1], opacity: [0.2, 0.4, 0.2] } : {}}
|
|
transition={{ duration: 2.6, repeat: Infinity, ease: [0.22, 1, 0.36, 1] }}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
|
|
{/* Impact pulses when attacks hit shield */}
|
|
{!prefers &&
|
|
hostile.map((h, i) => {
|
|
const midX = (h.x + center.x) / 2;
|
|
const midY = (h.y + center.y) / 2;
|
|
return (
|
|
<motion.circle
|
|
key={`impact-${i}`}
|
|
cx={midX}
|
|
cy={midY}
|
|
r={10}
|
|
fill="none"
|
|
stroke={danger}
|
|
strokeWidth={2}
|
|
initial={{ scale: 0.8, opacity: 0 }}
|
|
animate={{
|
|
scale: [0.8, 1.4, 1.8],
|
|
opacity: [0, 0.4, 0],
|
|
}}
|
|
transition={{
|
|
delay: i * 0.25 + 0.6,
|
|
duration: 1.2,
|
|
repeat: Infinity,
|
|
ease: "easeOut",
|
|
}}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
);
|
|
})}
|
|
|
|
{/* ====== EXTERNAL HOSTILE NODES (flickering / unstable) ====== */}
|
|
{hostile.map((h, i) => (
|
|
<g key={`hnode-${i}`}>
|
|
<circle
|
|
cx={h.x}
|
|
cy={h.y}
|
|
r={18}
|
|
fill="#1a1a1a"
|
|
stroke="#3a3a3a"
|
|
strokeWidth={2}
|
|
/>
|
|
|
|
{!prefers && (
|
|
<motion.circle
|
|
cx={h.x}
|
|
cy={h.y}
|
|
r={10}
|
|
fill={danger}
|
|
opacity={0.3}
|
|
animate={{ opacity: [0.3, 0.1, 0.3] }}
|
|
transition={{
|
|
duration: 1.8,
|
|
delay: i * 0.3,
|
|
repeat: Infinity,
|
|
ease: "easeInOut",
|
|
}}
|
|
/>
|
|
)}
|
|
</g>
|
|
))}
|
|
|
|
{/* ====== CENTRAL SECURE POD (always stable) ====== */}
|
|
<g>
|
|
{/* Glowing pod aura */}
|
|
{!prefers && (
|
|
<motion.circle
|
|
cx={center.x}
|
|
cy={center.y}
|
|
r={60}
|
|
fill="none"
|
|
stroke={accent}
|
|
strokeWidth={2}
|
|
opacity={0.2}
|
|
animate={{ scale: [1, 1.08, 1], opacity: [0.15, 0.35, 0.15] }}
|
|
transition={{ duration: 2.4, repeat: Infinity, ease: "easeInOut" }}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
)}
|
|
|
|
{/* Pod body */}
|
|
<ellipse
|
|
cx={center.x}
|
|
cy={center.y}
|
|
rx={48}
|
|
ry={30}
|
|
fill="url(#sec-core)"
|
|
stroke="#1f1f1f"
|
|
strokeWidth={2}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
|
|
{/* Inner dark core */}
|
|
<ellipse cx={center.x} cy={center.y} rx={22} ry={14} fill="#0f0f0f" stroke="#292929" strokeWidth={2} />
|
|
</g>
|
|
|
|
{/* Inner stable heartbeat */}
|
|
{!prefers && (
|
|
<motion.circle
|
|
cx={center.x}
|
|
cy={center.y}
|
|
r={20}
|
|
fill="none"
|
|
stroke={accent}
|
|
strokeWidth={2}
|
|
animate={{ scale: [1, 1.15, 1], opacity: [0.35, 1, 0.35] }}
|
|
transition={{ duration: 2.2, repeat: Infinity, ease: "easeInOut" }}
|
|
filter="url(#sec-glow)"
|
|
/>
|
|
)}
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|