diff --git a/src/components/InfrastructureDiagram.tsx b/src/components/InfrastructureDiagram.tsx
new file mode 100644
index 0000000..832b680
--- /dev/null
+++ b/src/components/InfrastructureDiagram.tsx
@@ -0,0 +1,353 @@
+'use client';
+
+import { useState, useEffect, useRef } from 'react';
+
+interface NodeConfig {
+ id: string;
+ label: string;
+ description: string;
+ icon: string;
+ color: string;
+ tech: string[];
+ x: number;
+ y: number;
+ connections: string[];
+}
+
+const nodes: NodeConfig[] = [
+ {
+ id: 'whymyrating',
+ label: 'whymyrating',
+ description: 'Web + Mobile Apps',
+ icon: '📱',
+ color: '#8b5cf6',
+ tech: ['React', 'Next.js', 'React Native', 'TypeScript'],
+ x: 50,
+ y: 5,
+ connections: ['whymyrating-engine', 'whymyrating-brand', 'whymyrating-templates'],
+ },
+ {
+ id: 'whymyrating-engine',
+ label: 'whymyrating-engine',
+ description: 'AI Pipelines & Backend API',
+ icon: '⚙️',
+ color: '#ef4444',
+ tech: ['Python', 'FastAPI', 'PostgreSQL', 'Docker'],
+ x: 10,
+ y: 40,
+ connections: ['nuc-server'],
+ },
+ {
+ id: 'whymyrating-brand',
+ label: 'whymyrating-brand',
+ description: 'Brand Assets & Guidelines',
+ icon: '🎨',
+ color: '#f59e0b',
+ tech: ['SVG', 'Figma', 'CSS'],
+ x: 50,
+ y: 40,
+ connections: [],
+ },
+ {
+ id: 'whymyrating-templates',
+ label: 'whymyrating-templates',
+ description: 'Email & Doc Templates',
+ icon: '📄',
+ color: '#10b981',
+ tech: ['Next.js', 'React Email', 'TypeScript'],
+ x: 90,
+ y: 40,
+ connections: [],
+ },
+ {
+ id: 'whyrating-hub',
+ label: 'whyrating-hub',
+ description: 'Internal Dashboard (You are here)',
+ icon: '🏠',
+ color: '#3b82f6',
+ tech: ['Next.js', 'Tailwind', 'TypeScript'],
+ x: 50,
+ y: 75,
+ connections: [],
+ },
+ {
+ id: 'nuc-server',
+ label: 'NUC Server',
+ description: 'Self-hosted Infrastructure',
+ icon: '🖥️',
+ color: '#6366f1',
+ tech: ['Docker', 'PostgreSQL', 'Nginx', 'Gitea'],
+ x: 10,
+ y: 75,
+ connections: ['whyrating-hub'],
+ },
+];
+
+const DiagramNode = ({
+ node,
+ index,
+ onHover,
+ isHighlighted,
+}: {
+ node: NodeConfig;
+ index: number;
+ onHover: (id: string | null) => void;
+ isHighlighted: boolean;
+}) => {
+ const [isHovered, setIsHovered] = useState(false);
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setIsVisible(true), index * 100);
+ return () => clearTimeout(timer);
+ }, [index]);
+
+ return (
+
{
+ setIsHovered(true);
+ onHover(node.id);
+ }}
+ onMouseLeave={() => {
+ setIsHovered(false);
+ onHover(null);
+ }}
+ >
+
+ {/* Pulse animation for hovered node */}
+ {isHovered && (
+
+ )}
+
+ {/* Icon */}
+
{node.icon}
+
+ {/* Label */}
+
+ {node.label}
+
+
+ {/* Description */}
+
+ {node.description}
+
+
+ {/* Tech stack - shown on hover */}
+
+ {node.tech.map((t, i) => (
+
+ {t}
+
+ ))}
+
+
+
+ );
+};
+
+const ConnectionLine = ({
+ from,
+ to,
+ isHighlighted,
+ delay,
+}: {
+ from: NodeConfig;
+ to: NodeConfig;
+ isHighlighted: boolean;
+ delay: number;
+}) => {
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => setIsVisible(true), delay);
+ return () => clearTimeout(timer);
+ }, [delay]);
+
+ // Calculate line positions (center of nodes)
+ const x1 = from.x;
+ const y1 = from.y + 8; // Offset for node height
+ const x2 = to.x;
+ const y2 = to.y;
+
+ // Calculate control points for curved line
+ const midY = (y1 + y2) / 2;
+
+ return (
+
+ );
+};
+
+export default function InfrastructureDiagram() {
+ const [hoveredNode, setHoveredNode] = useState(null);
+ const [mounted, setMounted] = useState(false);
+
+ useEffect(() => {
+ setMounted(true);
+ }, []);
+
+ if (!mounted) {
+ return (
+
+ );
+ }
+
+ // Get all connections
+ const connections: { from: NodeConfig; to: NodeConfig }[] = [];
+ nodes.forEach((node) => {
+ node.connections.forEach((targetId) => {
+ const target = nodes.find((n) => n.id === targetId);
+ if (target) {
+ connections.push({ from: node, to: target });
+ }
+ });
+ });
+
+ // Determine which nodes/connections are highlighted
+ const getHighlightedNodes = () => {
+ if (!hoveredNode) return null;
+ const node = nodes.find((n) => n.id === hoveredNode);
+ if (!node) return null;
+ return new Set([hoveredNode, ...node.connections]);
+ };
+
+ const highlightedSet = getHighlightedNodes();
+
+ return (
+
+ {/* Background grid */}
+
+
+ {/* Diagram container */}
+
+ {/* Connection lines */}
+ {connections.map(({ from, to }, i) => (
+
+ ))}
+
+ {/* Nodes */}
+ {nodes.map((node, i) => (
+
+ ))}
+
+
+ {/* Legend */}
+
+ {[
+ { color: '#8b5cf6', label: 'Main Apps' },
+ { color: '#ef4444', label: 'Backend/AI' },
+ { color: '#f59e0b', label: 'Brand' },
+ { color: '#10b981', label: 'Templates' },
+ { color: '#3b82f6', label: 'Dashboard' },
+ { color: '#6366f1', label: 'Infrastructure' },
+ ].map((item) => (
+
+ ))}
+
+
+ {/* Global styles for animations */}
+
+
+ );
+}
diff --git a/src/components/index.ts b/src/components/index.ts
index 1c618ce..6b6cae1 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,2 +1,3 @@
export { Icon, icons } from './Icons';
export { WhyMyRatingLogo } from './WhyMyRatingLogo';
+export { default as InfrastructureDiagram } from './InfrastructureDiagram';