From 2b7559ab477f6db377c3514b2eef33bb5fb16b54 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Mon, 17 Nov 2025 15:00:41 +0100 Subject: [PATCH] refactor: redesign pods page with bento grid layout and improved benefits sections - Increased CloudHeroNew vertical padding on large screens (lg:py-16 to lg:py-24) - Reduced spacing between description paragraphs in CloudHeroNew (mt-4 to mt-2) - Created PodsBento component with animated bento grid showcasing pod benefits - Added animations for data control, connectivity, security, and resilience features - Refactored PodsDesign from accordion layout to centered intro with 4-column grid - Create --- src/pages/cloud/CloudHeroNew.tsx | 4 +- src/pages/pods/PodsBento.tsx | 160 ++++++++++++ src/pages/pods/PodsDesign.tsx | 137 +++++----- src/pages/pods/PodsPage.tsx | 6 +- src/pages/pods/PodsPro.tsx | 111 ++++++++ src/pages/pods/animations/Deterministic.tsx | 189 ++++++++++++++ src/pages/pods/animations/Meshnetworking.tsx | 151 +++++++++++ src/pages/pods/animations/NoCentral.tsx | 238 +++++++++++++++++ src/pages/pods/animations/NoControl.tsx | 175 +++++++++++++ src/pages/pods/animations/NoExtraction.tsx | 247 ++++++++++++++++++ src/pages/pods/animations/NoSinglePoint.tsx | 225 ++++++++++++++++ .../pods/animations/SovereignCompute.tsx | 236 +++++++++++++++++ 12 files changed, 1799 insertions(+), 80 deletions(-) create mode 100644 src/pages/pods/PodsBento.tsx create mode 100644 src/pages/pods/PodsPro.tsx create mode 100644 src/pages/pods/animations/Deterministic.tsx create mode 100644 src/pages/pods/animations/Meshnetworking.tsx create mode 100644 src/pages/pods/animations/NoCentral.tsx create mode 100644 src/pages/pods/animations/NoControl.tsx create mode 100644 src/pages/pods/animations/NoExtraction.tsx create mode 100644 src/pages/pods/animations/NoSinglePoint.tsx create mode 100644 src/pages/pods/animations/SovereignCompute.tsx diff --git a/src/pages/cloud/CloudHeroNew.tsx b/src/pages/cloud/CloudHeroNew.tsx index 38b61dd..69f8a10 100644 --- a/src/pages/cloud/CloudHeroNew.tsx +++ b/src/pages/cloud/CloudHeroNew.tsx @@ -10,7 +10,7 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli style={{ backgroundImage: "url('/images/cloudhero4.webp')", backgroundSize: "contain" }} > {/* Inner padding */} -
+
MYCELIUM CLOUD @@ -21,7 +21,7 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli

Run compute, storage, and AI resources on infrastructure you control.

-

+

The Mycelium Cloud runs on a distributed grid of independent nodes, delivering secure, scalable performance wherever your users or data live.

diff --git a/src/pages/pods/PodsBento.tsx b/src/pages/pods/PodsBento.tsx new file mode 100644 index 0000000..b78a616 --- /dev/null +++ b/src/pages/pods/PodsBento.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { Eyebrow, H3, P } from "@/components/Texts"; +import NoExtraction from "./animations/NoExtraction"; +import NoControl from "./animations/NoControl"; +import NoCentral from "./animations/NoCentral"; +import NoSinglePoint from "./animations/NoSinglePoint"; + +const deterministicCards = [ + { + id: "intro", + eyebrow: "BENEFITS", + title: "Runs on Your Own Infrastructure", + description: + "Each Pod lives on your own hardware or on trusted local nodes in the Mycelium Network. There is no central cloud and no company in the middle. You are not uploading your life to the cloud. You are running it yourself.", + animation: null, + colSpan: "lg:col-span-3", + rowSpan: "lg:row-span-1", + custom: true, + noBorder: true, + }, + + { + id: "data", + label: "Data Control", + title: "Your Data Lives on Your Pods", + description: + "Full control of where your data is stored and how it’s shared.", + animation: , + colSpan: "lg:col-span-3", + rowSpan: "lg:row-span-1", + rounded: "lg:rounded-tr-4xl max-lg:rounded-t-4xl", + innerRounded: + "lg:rounded-tr-[calc(2rem+1px)] max-lg:rounded-t-[calc(2rem+1px)]", + }, + + { + id: "connectivity", + label: "Connectivity", + title: "Direct Pod-to-Pod Networking", + description: + "Direct connections between Pods for faster, private communication.", + animation: , + colSpan: "lg:col-span-2", + rowSpan: "lg:row-span-1", + rounded: "lg:rounded-bl-4xl max-lg:rounded-b-4xl", + innerRounded: + "lg:rounded-bl-[calc(2rem+1px)] max-lg:rounded-b-[calc(2rem+1px)]", + }, + + { + id: "security", + label: "Security", + title: "No One Can Spy or Shut You Down", + description: + "Independence from corporate servers or cloud outages.", + animation: , + colSpan: "lg:col-span-2", + rowSpan: "lg:row-span-1", + rounded: "", + innerRounded: "", + }, + + { + id: "resilience", + label: "Resilience", + title: "Resilient Even if Nodes Disconnect", + description: + "Continuous availability even if one node disconnects.", + animation: , + colSpan: "lg:col-span-2", + rowSpan: "lg:row-span-1", + rounded: "lg:rounded-br-4xl max-lg:rounded-b-4xl", + innerRounded: + "lg:rounded-br-[calc(2rem+1px)] max-lg:rounded-b-[calc(2rem+1px)]", + }, +]; + +export function PodsBento() { + return ( +
+ {/* TOP LINE */} +
+
+ +
+
+ {deterministicCards.map((card) => ( +
+ {/* DISABLE OUTER WRAPPER ON INTRO CARD */} + {!card.noBorder && ( +
+ )} + +
+ {/* ANIMATION */} + {card.animation ? ( +
+
+ {card.animation} +
+
+ ) : ( +
+ )} + + {/* TEXT AREA */} +
+ {card.custom ? ( + <> + + {card.eyebrow} + +

{card.title}

+

+ {card.description} +

+ + ) : ( + <> + {card.label && ( +

+ {card.label} +

+ )} +

+ {card.title} +

+

+ {card.description} +

+ + )} +
+
+ + {/* OUTLINE SHADOW */} + {!card.noBorder && ( +
+ )} +
+ ))} +
+
+ + {/* BOTTOM LINE */} +
+
+
+ ); +} diff --git a/src/pages/pods/PodsDesign.tsx b/src/pages/pods/PodsDesign.tsx index 551a031..56349a8 100644 --- a/src/pages/pods/PodsDesign.tsx +++ b/src/pages/pods/PodsDesign.tsx @@ -1,42 +1,39 @@ 'use client' -import { - Disclosure, - DisclosureButton, - DisclosurePanel, -} from '@headlessui/react' -import { MinusIcon, PlusIcon } from '@heroicons/react/24/outline' - -import { Eyebrow, H3, H4 } from "@/components/Texts" - +import { Eyebrow, H3, P, Small } from "@/components/Texts" const product = { - subtitle: "Federation", + subtitle: "BENEFITS", name: "Runs on Your Own Infrastructure", description: `

Each Pod lives on your own hardware or on trusted local nodes in the Mycelium Network. -There is no central cloud and no company in the middle. You are not uploading your life to the cloud. You are running it yourself. + There is no central cloud and no company in the middle. You are not uploading your life + to the cloud. You are running it yourself.

`, details: [ { + label: "Data Control", name: "Your Data Lives on Your Pods", description: "Full control of where your data is stored and how it’s shared.", }, { + label: "Connectivity", name: "Direct Pod-to-Pod Networking", description: "Direct connections between Pods for faster, private communication.", }, { + label: "Security", name: "No One Can Spy or Shut You Down", description: "Independence from corporate servers or cloud outages.", }, { + label: "Resilience", name: "Resilient Even if Nodes Disconnect", description: "Continuous availability even if one node disconnects.", @@ -44,78 +41,66 @@ There is no central cloud and no company in the middle. You are not uploading yo ], } - export function PodsDesign() { return ( -
- {/* TOP LINE */} -
+
+ {/* ▸ Top Spacing Line */} +
-
-
+ {/* ▸ Intro Section */} +
+
+ + + {product.subtitle} + -
+

+ {product.name} +

- {/* IMAGE */} -
- Mycelium Federation -
- - {/* PRODUCT INFO */} -
- - - {product.subtitle} - - -

- {product.name} -

- -
- - {/* DETAILS ACCORDION */} -
-
- {product.details.map((detail) => ( - -

- - - {detail.name} - - - - - - -

- - -

- {detail.description} -

-
-
- ))} -
-
-
-
+

-
- {/* BOTTOM LINE */} -
+ {/* ▸ 4-Column Highlights Grid */} +
+ {product.details.map((item) => ( +
+ {/* Hover Glow */} +
+ +
+ + {item.label && ( + + {item.label} + + )} + +

+ {item.name} +

+ +

+ {item.description} +

+ +
+
+ ))} +
+
+ + {/* ▸ Bottom Spacing */} +
-
- ) +
+ ); } diff --git a/src/pages/pods/PodsPage.tsx b/src/pages/pods/PodsPage.tsx index e48507b..8dafa59 100644 --- a/src/pages/pods/PodsPage.tsx +++ b/src/pages/pods/PodsPage.tsx @@ -1,9 +1,10 @@ import Homepod from './Homepod'; import { PodsHow } from './PodsHow'; import { PodsFeatures } from './PodsFeatures'; -import { PodsDesign } from './PodsDesign'; import { CallToAction } from './CallToAction'; import { PodsWhat } from './PodsWhat'; +import { PodsPro } from './PodsPro'; +import { PodsBento } from './PodsBento'; const PodsPage = () => { return ( @@ -12,7 +13,8 @@ const PodsPage = () => { - + + ); diff --git a/src/pages/pods/PodsPro.tsx b/src/pages/pods/PodsPro.tsx new file mode 100644 index 0000000..0d798bb --- /dev/null +++ b/src/pages/pods/PodsPro.tsx @@ -0,0 +1,111 @@ +'use client' + +import { Eyebrow, H3, P, Small } from "@/components/Texts" + +const product = { + subtitle: "BENEFITS", + name: "Why It’s Different", + description: ` +

+ Pods combine self-hosting with the reach of the Mycelium Network. + They run on the same system that powers Mycelium Cloud and AI Agents. +

+ `, + + details: [ + { + label: "Identity", + name: "Cryptographic identity with optional user logins", + description: + "Every Pod carries a verifiable identity, enabling secure interactions with optional login layers.", + }, + { + label: "Autonomy", + name: "No reliance on centralized cloud platforms", + description: + "Pods run independently without AWS, Google, or corporate cloud systems.", + }, + { + label: "Privacy", + name: "No data collection or hidden intermediaries", + description: + "Your data remains local—no telemetry, analytics pipelines, or behind-the-scenes tracking.", + }, + { + label: "Reliability", + name: "High availability through distributed routing", + description: + "Pods communicate through resilient, multi-path routing across the Mycelium Network.", + }, + { + label: "Future-Proof", + name: "Ready to host your personal AI Agent", + description: + "Pods are built to evolve as agent capabilities grow—deploy your own agent as the system expands.", + }, + ], +} + +export function PodsPro() { + return ( +
+ {/* ▸ Top Spacing Line */} +
+
+ + {/* ▸ Intro Section */} +
+
+ + + {product.subtitle} + + +

+ {product.name} +

+ +

+

+ + {/* ▸ Highlights Grid */} +
+ {product.details.map((item) => ( +
+ {/* Hover Glow */} +
+ +
+ + {item.label && ( + + {item.label} + + )} + +

+ {item.name} +

+ +

+ {item.description} +

+ +
+
+ ))} +
+
+ + {/* ▸ Bottom Spacing */} +
+
+
+ ); +} diff --git a/src/pages/pods/animations/Deterministic.tsx b/src/pages/pods/animations/Deterministic.tsx new file mode 100644 index 0000000..6e55526 --- /dev/null +++ b/src/pages/pods/animations/Deterministic.tsx @@ -0,0 +1,189 @@ +"use client"; + +import { motion, useReducedMotion } from "framer-motion"; +import clsx from "clsx"; + +type Props = { + className?: string; + accent?: string; + gridStroke?: string; +}; + +const W = 760; +const H = 420; + +export default function Deterministic({ + className, + accent = "#00b8db", + gridStroke = "#2b2a2a", +}: Props) { + const prefers = useReducedMotion(); + + const stages = [ + { x: 180, y: 180, w: 120, h: 80, label: "Build" }, + { x: 330, y: 180, w: 120, h: 80, label: "Package" }, + { x: 480, y: 180, w: 120, h: 80, label: "Deploy" }, + ]; + + // Packet path (deterministic flow) + const packetPath = `M ${stages[0].x + 120} ${stages[0].y + 40} + L ${stages[1].x + 0} ${stages[1].y + 40} + L ${stages[1].x + 120} ${stages[1].y + 40} + L ${stages[2].x + 0} ${stages[2].y + 40}`; + + // tiny arrow for each transition + const arrows = [ + `M ${stages[0].x + 120} ${stages[0].y + 40} L ${stages[1].x + 6} ${stages[1].y + 40}`, + `M ${stages[1].x + 120} ${stages[1].y + 40} L ${stages[2].x + 6} ${stages[2].y + 40}` + ]; + + return ( + + ); +} diff --git a/src/pages/pods/animations/Meshnetworking.tsx b/src/pages/pods/animations/Meshnetworking.tsx new file mode 100644 index 0000000..c55a2d8 --- /dev/null +++ b/src/pages/pods/animations/Meshnetworking.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { motion, useReducedMotion } from "framer-motion"; +import clsx from "clsx"; + +type Props = { + className?: string; + accent?: string; + stroke?: string; +}; + +const W = 760; +const H = 420; + +export default function MeshNetworking({ + className, + accent = "#00b8db", + stroke = "#4B5563", +}: Props) { + const prefersReduced = useReducedMotion(); + + // Nodes in a real mesh (hex pattern) + const nodes = [ + { x: 200, y: 120 }, + { x: 380, y: 100 }, + { x: 560, y: 120 }, + + { x: 130, y: 240 }, + { x: 320, y: 240 }, + { x: 540, y: 240 }, + { x: 630, y: 240 }, + + { x: 260, y: 340 }, + { x: 440, y: 340 }, + ]; + + // All connected pairs (mesh links) + const links = [ + [0,1],[1,2], + [0,3],[1,4],[2,5], + [3,4],[4,5],[5,6], + [3,7],[4,7],[4,8],[5,8], + [7,8] + ]; + + const drawLine = (i: number, j: number) => { + const a = nodes[i]; + const b = nodes[j]; + return `M ${a.x} ${a.y} L ${b.x} ${b.y}`; + }; + + return ( + + ); +} diff --git a/src/pages/pods/animations/NoCentral.tsx b/src/pages/pods/animations/NoCentral.tsx new file mode 100644 index 0000000..d5220f6 --- /dev/null +++ b/src/pages/pods/animations/NoCentral.tsx @@ -0,0 +1,238 @@ +"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; + +const Node = ({ + x, + y, + r = 10, + accent = "#00b8db", + pulse = false, + delay = 0, +}: { + x: number; + y: number; + r?: number; + accent?: string; + pulse?: boolean; + delay?: number; +}) => { + const prefers = useReducedMotion(); + return ( + <> + + + + ); +}; + +const Packet = ({ + path, + delay = 0, + accent = "#00b8db", + duration = 2.4, +}: { + path: string; + delay?: number; + accent?: string; + duration?: number; +}) => { + const prefers = useReducedMotion(); + return ( + + ); +}; + +export default function NoCentral({ + className, + accent = "#00b8db", + bg = "#0a0a0a", +}: Props) { + const center = { x: 380, y: 210 }; + const nodes = [ + { x: 160, y: 100 }, + { x: 270, y: 70 }, + { x: 500, y: 90 }, + { x: 620, y: 150 }, + { x: 220, y: 300 }, + { x: 360, y: 340 }, + { x: 530, y: 290 }, + ]; + + const links = [ + [nodes[0], nodes[1]], + [nodes[1], nodes[2]], + [nodes[2], nodes[3]], + [nodes[0], nodes[4]], + [nodes[4], nodes[5]], + [nodes[5], nodes[6]], + [nodes[1], nodes[5]], + [nodes[3], nodes[6]], + ]; + + return ( + + ); +} diff --git a/src/pages/pods/animations/NoControl.tsx b/src/pages/pods/animations/NoControl.tsx new file mode 100644 index 0000000..17d3be5 --- /dev/null +++ b/src/pages/pods/animations/NoControl.tsx @@ -0,0 +1,175 @@ +"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 ( + <> + + + + ); +}; + +/** Moving packet along a path */ +const Packet = ({ + path, + delay = 0, + accent = "#00b8db", +}: { + path: string; + delay?: number; + accent?: string; +}) => { + const prefers = useReducedMotion(); + return ( + + ); +}; + +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 ( + + ); +} diff --git a/src/pages/pods/animations/NoExtraction.tsx b/src/pages/pods/animations/NoExtraction.tsx new file mode 100644 index 0000000..52c044b --- /dev/null +++ b/src/pages/pods/animations/NoExtraction.tsx @@ -0,0 +1,247 @@ +"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 = local data cluster */ +const Node = ({ + x, + y, + r = 10, + accent = "#00b8db", + pulse = false, + delay = 0, +}: { + x: number; + y: number; + r?: number; + accent?: string; + pulse?: boolean; + delay?: number; +}) => { + const prefers = useReducedMotion(); + return ( + <> + {/* outer glow */} + + {/* data core */} + + + ); +}; + +/** A data particle traveling along a given path */ +const Particle = ({ + path, + delay = 0, + accent = "#00b8db", + duration = 2, + reverse = false, +}: { + path: string; + delay?: number; + accent?: string; + duration?: number; + reverse?: boolean; +}) => { + const prefers = useReducedMotion(); + return ( + + ); +}; + +export default function NoExtraction({ + className, + accent = "#00b8db", + bg = "#0a0a0a", +}: Props) { + const center = { x: 380, y: 210 }; + const WBOX = 360; + const HBOX = 220; + const boxX = center.x - WBOX / 2; + const boxY = center.y - HBOX / 2; + + // local nodes within boundary + const nodes = [ + { x: center.x - 80, y: center.y - 40 }, + { x: center.x + 60, y: center.y - 50 }, + { x: center.x, y: center.y + 50 }, + { x: center.x - 50, y: center.y + 30 }, + ]; + + // internal circulation paths + const internalPaths = [ + `M ${center.x - 80} ${center.y - 40} Q ${center.x} ${center.y - 80} ${center.x + 60} ${center.y - 50}`, + `M ${center.x - 50} ${center.y + 30} Q ${center.x} ${center.y + 70} ${center.x} ${center.y + 50}`, + ]; + + // escape attempt path + const attemptPath = `M ${center.x} ${center.y} Q ${center.x + 200} ${center.y - 50} ${center.x + 130} ${center.y}`; + + return ( + + ); +} diff --git a/src/pages/pods/animations/NoSinglePoint.tsx b/src/pages/pods/animations/NoSinglePoint.tsx new file mode 100644 index 0000000..8d16498 --- /dev/null +++ b/src/pages/pods/animations/NoSinglePoint.tsx @@ -0,0 +1,225 @@ +"use client"; + +import { motion, useReducedMotion } from "framer-motion"; +import clsx from "clsx"; + +type Props = { + className?: string; + accent?: string; // cyan + bg?: string; // solid dark background + gridStroke?: string; +}; + +const W = 720; // 4:3 +const H = 540; // 4:3 + +export default function NoSinglePoint({ + className, + accent = "#00b8db", + bg = "#0b0b0b", + gridStroke = "#2b2a2a", +}: Props) { + const prefers = useReducedMotion(); + + // Nodes (left source, right dest, top hub, bottom hub, plus two relays) + const nodes = { + left: { x: 120, y: H / 2 }, + right: { x: W - 120, y: H / 2 }, + top: { x: W / 2, y: 160 }, + bot: { x: W / 2, y: H - 160 }, + tl: { x: 240, y: 200 }, + br: { x: W - 240, y: H - 200 }, + }; + + // Redundant paths from left → right + const upperPath = `M ${nodes.left.x} ${nodes.left.y} + L ${nodes.tl.x} ${nodes.tl.y} + L ${nodes.top.x} ${nodes.top.y} + L ${nodes.right.x} ${nodes.right.y}`; + + const lowerPath = `M ${nodes.left.x} ${nodes.left.y} + L ${nodes.bot.x} ${nodes.bot.y} + L ${nodes.br.x} ${nodes.br.y} + L ${nodes.right.x} ${nodes.right.y}`; + + return ( + + ); +} diff --git a/src/pages/pods/animations/SovereignCompute.tsx b/src/pages/pods/animations/SovereignCompute.tsx new file mode 100644 index 0000000..77f5438 --- /dev/null +++ b/src/pages/pods/animations/SovereignCompute.tsx @@ -0,0 +1,236 @@ +"use client"; + +import { motion, useReducedMotion } from "framer-motion"; +import clsx from "clsx"; + +type Props = { + className?: string; + accent?: string; // cyan highlight + gridStroke?: string; // grid color (default #2b2a2a as requested) +}; + +const W = 760; +const H = 420; + +const Server = ({ + x, + y, + w = 140, + h = 90, + rows = 3, +}: { + x: number; + y: number; + w?: number; + h?: number; + rows?: number; +}) => { + const rowH = (h - 24) / rows; + + return ( + + {/* chassis */} + + {/* bays */} + {Array.from({ length: rows }).map((_, i) => ( + + + {/* bay indicators */} + + + + + ))} + {/* subtle top highlight */} + + + ); +}; + +export default function SovereignCompute({ + className, + accent = "#00b8db", + gridStroke = "#2b2a2a", +}: Props) { + const prefers = useReducedMotion(); + + // Positions + const left = { x: 140, y: 120 }; + const mid = { x: 310, y: 150 }; + const right= { x: 500, y: 120 }; + + // Shield position (trust boundary) + const shield = { cx: 600, cy: 250, r: 38 }; + + // Attestation paths from racks to shield + const pathFromLeft = `M ${left.x + 140} ${left.y + 45} C 330 150, 470 200, ${shield.cx - 50} ${shield.cy}`; + const pathFromMid = `M ${mid.x + 140} ${mid.y + 45} C 420 190, 500 215, ${shield.cx - 50} ${shield.cy}`; + const pathFromRight = `M ${right.x + 140} ${right.y + 45} C 520 180, 560 220, ${shield.cx - 50} ${shield.cy}`; + + return ( + + ); +}