refactor: rename and update pod animations with improved visuals and consistency

- 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
This commit is contained in:
2025-11-17 15:12:55 +01:00
parent 97da7ba907
commit 1a34508699
9 changed files with 909 additions and 893 deletions

View File

@@ -0,0 +1,231 @@
"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>
);
}