forked from emre/www_projectmycelium_com
Compare commits
2 Commits
developmen
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f63c20aa6 | |||
| 74dd974da5 |
Binary file not shown.
|
Before Width: | Height: | Size: 922 KiB |
@@ -0,0 +1 @@
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const XAILogo = () => (
|
||||
xmlSpace="preserve"
|
||||
width="30"
|
||||
height="30"
|
||||
fill="white"
|
||||
>
|
||||
<g>
|
||||
<polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18 " />
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
export const InfiniteMovingCards = ({
|
||||
items,
|
||||
direction = "left",
|
||||
speed = "slow",
|
||||
speed = "fast",
|
||||
pauseOnHover = true,
|
||||
className,
|
||||
}: {
|
||||
@@ -15,39 +15,43 @@ export const InfiniteMovingCards = ({
|
||||
speed?: "fast" | "normal" | "slow";
|
||||
pauseOnHover?: boolean;
|
||||
className?: string;
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const scrollerRef = useRef<HTMLUListElement>(null);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
}): JSX.Element => {
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const scrollerRef = React.useRef<HTMLUListElement>(null);
|
||||
const [start, setStart] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!scrollerRef.current) return;
|
||||
|
||||
const children = Array.from(scrollerRef.current.children);
|
||||
const getSpeed = useCallback(() => {
|
||||
if (containerRef.current) {
|
||||
if (speed === "fast") {
|
||||
containerRef.current.style.setProperty("--animation-duration", "20s");
|
||||
} else if (speed === "normal") {
|
||||
containerRef.current.style.setProperty("--animation-duration", "40s");
|
||||
} else {
|
||||
containerRef.current.style.setProperty("--animation-duration", "80s");
|
||||
}
|
||||
}
|
||||
}, [speed]);
|
||||
|
||||
// duplicate each item ONCE
|
||||
children.forEach((item) => {
|
||||
const clone = item.cloneNode(true);
|
||||
scrollerRef.current!.appendChild(clone);
|
||||
const addAnimation = useCallback(() => {
|
||||
if (containerRef.current && scrollerRef.current) {
|
||||
const scrollerContent = Array.from(scrollerRef.current.children);
|
||||
|
||||
scrollerContent.forEach((item) => {
|
||||
const duplicatedItem = item.cloneNode(true);
|
||||
if (scrollerRef.current) {
|
||||
scrollerRef.current.appendChild(duplicatedItem);
|
||||
}
|
||||
});
|
||||
|
||||
// set speed variable
|
||||
const duration =
|
||||
speed === "fast" ? "20s" : speed === "normal" ? "40s" : "80s";
|
||||
getSpeed();
|
||||
setStart(true);
|
||||
}
|
||||
}, [getSpeed]);
|
||||
|
||||
containerRef.current?.style.setProperty(
|
||||
"--animation-duration",
|
||||
duration
|
||||
);
|
||||
|
||||
// set direction variable
|
||||
containerRef.current?.style.setProperty(
|
||||
"--animation-direction",
|
||||
direction === "left" ? "forwards" : "reverse"
|
||||
);
|
||||
|
||||
setIsReady(true);
|
||||
}, [direction, speed]);
|
||||
useEffect(() => {
|
||||
addAnimation();
|
||||
}, [addAnimation]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -57,13 +61,13 @@ export const InfiniteMovingCards = ({
|
||||
<ul
|
||||
ref={scrollerRef}
|
||||
className={cn(
|
||||
"flex w-max shrink-0 gap-16 py-4",
|
||||
isReady && "animate-infinite-scroll",
|
||||
"flex min-w-full shrink-0 gap-16 py-4 w-max flex-nowrap",
|
||||
start && (direction === "left" ? "animate-scroll-left" : "animate-scroll-right"),
|
||||
pauseOnHover && "hover:[animation-play-state:paused]"
|
||||
)}
|
||||
>
|
||||
{items.map((item, i) => (
|
||||
<li key={i} className="flex-shrink-0">
|
||||
{items.map((item, idx) => (
|
||||
<li className="relative flex-shrink-0" key={idx}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -23,7 +23,7 @@ const bentos: {
|
||||
{
|
||||
id: "core",
|
||||
eyebrow: "ARCHITECTURE",
|
||||
title: "Intelligence Fabric",
|
||||
title: "Augmented Intelligence Fabric",
|
||||
description:
|
||||
"The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.",
|
||||
animation: null,
|
||||
|
||||
@@ -12,7 +12,7 @@ export function AgentHeroAlt() {
|
||||
style={{ backgroundImage: "url('/images/agents.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow>
|
||||
<H3 as="h1" className="mt-4">
|
||||
|
||||
65
src/pages/agents/AgentLogos.tsx
Normal file
65
src/pages/agents/AgentLogos.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { InfiniteMovingCards } from "@/components/magicui/infinite-moving-cards";
|
||||
import Ai21Logo from "@/components/logos/Ai21";
|
||||
import ClaudeLogo from "@/components/logos/Claude";
|
||||
import BaiduCloudLogo from "@/components/logos/BaiduCloud";
|
||||
import ByteDanceLogo from "@/components/logos/ByteDance";
|
||||
import DeepSeekLogo from "@/components/logos/DeepSeek";
|
||||
import DeepMindLogo from "@/components/logos/DeepMind";
|
||||
import MinimaxLogo from "@/components/logos/Minimax";
|
||||
import MistralLogo from "@/components/logos/Mistral";
|
||||
import MoonshotLogo from "@/components/logos/Moonshot";
|
||||
import TencentCloudLogo from "@/components/logos/TencentCloud";
|
||||
import OpenAILogo from "@/components/logos/OpenAI";
|
||||
import XAILogo from "@/components/logos/XAI";
|
||||
|
||||
const logos = [
|
||||
{ id: "ai21", Component: Ai21Logo, label: "AI21" },
|
||||
{ id: "claude", Component: ClaudeLogo, label: "Claude" },
|
||||
{ id: "baidu", Component: BaiduCloudLogo, label: "Baidu Cloud" },
|
||||
{ id: "bytedance", Component: ByteDanceLogo, label: "ByteDance" },
|
||||
{ id: "deepseek", Component: DeepSeekLogo, label: "DeepSeek" },
|
||||
{ id: "deepmind", Component: DeepMindLogo, label: "DeepMind" },
|
||||
{ id: "minimax", Component: MinimaxLogo, label: "Minimax" },
|
||||
{ id: "mistral", Component: MistralLogo, label: "Mistral" },
|
||||
{ id: "moonshot", Component: MoonshotLogo, label: "Moonshot" },
|
||||
{ id: "tencent", Component: TencentCloudLogo, label: "Tencent Cloud" },
|
||||
{ id: "openai", Component: OpenAILogo, label: "OpenAI" },
|
||||
{ id: "xai", Component: XAILogo, label: "xAI" },
|
||||
];
|
||||
|
||||
const splitLogoRows = () => {
|
||||
const midpoint = Math.ceil(logos.length / 2);
|
||||
return [logos.slice(0, midpoint), logos.slice(midpoint)];
|
||||
};
|
||||
|
||||
export const AgentLogos = () => {
|
||||
const rows = splitLogoRows();
|
||||
|
||||
return (
|
||||
<section className="relative isolate overflow-hidden bg-[#121212] py-4">
|
||||
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-black to-transparent" />
|
||||
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-black to-transparent" />
|
||||
<div className="flex w-full flex-col gap-1 px-0">
|
||||
{rows.map((row, idx) => (
|
||||
<InfiniteMovingCards
|
||||
key={`logos-row-${idx}`}
|
||||
items={row.map(({ id, Component, label }) => (
|
||||
<div
|
||||
key={id}
|
||||
className="flex h-24 w-36 items-center justify-center bg-transparent px-4"
|
||||
aria-label={label}
|
||||
>
|
||||
<Component />
|
||||
</div>
|
||||
))}
|
||||
direction={idx % 2 === 0 ? "left" : "right"}
|
||||
speed="slow"
|
||||
pauseOnHover={true}
|
||||
className="w-full"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import { Small } from "@/components/Texts";
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
label: "Local Execution",
|
||||
title: "Agents run entirely inside your environment.",
|
||||
description:
|
||||
"Models, logic, and memory stay within your own trusted hardware, never behind third-party APIs.",
|
||||
},
|
||||
{
|
||||
label: "Mesh Connectivity",
|
||||
title: "They communicate peer-to-peer across trusted nodes.",
|
||||
description:
|
||||
"Agents form direct encrypted paths between environments, without relays or central servers.",
|
||||
},
|
||||
{
|
||||
label: "Private Data Access",
|
||||
title: "They use your data without sending it elsewhere.",
|
||||
description:
|
||||
"Your datasets, embeddings, and context never leave your boundaries. Processing stays local.",
|
||||
},
|
||||
{
|
||||
label: "Portability",
|
||||
title: "They move with you, not with a cloud provider.",
|
||||
description:
|
||||
"Agents follow your devices, networks, and workflows, remaining sovereign across every location.",
|
||||
},
|
||||
];
|
||||
|
||||
export function AgentPro() {
|
||||
return (
|
||||
<section className="relative w-full bg-[#FDFDFD] overflow-hidden">
|
||||
{/* Top spacing line */}
|
||||
<div className="max-w-7xl bg-[#FDFDFD] mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* Intro Block */}
|
||||
<div className="bg-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl mx-auto flex flex-col items-center justify-center min-h-[220px] text-center">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Advantages
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Why It’s Different
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 text-base leading-relaxed">
|
||||
Most AI systems run on centralized clouds, where the models, data, and
|
||||
logic operate behind third-party APIs. Mycelium Agents flip that
|
||||
architecture, it runs entirely inside your environment so control,
|
||||
privacy, and autonomy stay with you.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import { Eyebrow, SectionHeader, P } from "@/components/Texts";
|
||||
import { IoArrowBackOutline, IoArrowForwardOutline } from "react-icons/io5";
|
||||
|
||||
import {
|
||||
CpuChipIcon,
|
||||
GlobeAltIcon,
|
||||
@@ -20,7 +23,7 @@ const networkUseCases = [
|
||||
{
|
||||
title: "Run agents on your own hardware",
|
||||
description:
|
||||
"Deploy AI processes on laptops, homelabs, edge nodes, or full clusters with no cloud dependency.",
|
||||
"Deploy AI processes on laptops, homelabs, edge nodes, or full clusters — no cloud dependency.",
|
||||
icon: CpuChipIcon,
|
||||
},
|
||||
{
|
||||
@@ -47,75 +50,98 @@ const networkUseCases = [
|
||||
"Run agents in sectors requiring strict data residency, verified identity, and controlled connectivity.",
|
||||
icon: ShieldCheckIcon,
|
||||
},
|
||||
{
|
||||
title: "Blend local + remote intelligence",
|
||||
description:
|
||||
"Let lightweight agents run locally while offloading heavy tasks to trusted nodes, maintaining privacy and performance balance.",
|
||||
icon: CpuChipIcon,
|
||||
},
|
||||
];
|
||||
|
||||
export function AgentUsecase() {
|
||||
const sliderRef = useRef<HTMLUListElement>(null);
|
||||
|
||||
const scrollLeft = () =>
|
||||
sliderRef.current?.scrollBy({ left: -400, behavior: "smooth" });
|
||||
|
||||
const scrollRight = () =>
|
||||
sliderRef.current?.scrollBy({ left: 400, behavior: "smooth" });
|
||||
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
{/* Top horizontal spacing line */}
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* Main framed section */}
|
||||
<div className="border border-t-0 border-b-0 border-gray-100 bg-white">
|
||||
<div className="mx-auto max-w-4xl sm:text-center py-12">
|
||||
{/* Intro block (from isIntro item) */}
|
||||
{networkUseCases[0].isIntro && (
|
||||
<>
|
||||
<Eyebrow className="text-cyan-600">{networkUseCases[0].eyebrow}</Eyebrow>
|
||||
<SectionHeader
|
||||
as="h3"
|
||||
className="mt-4 text-gray-900 text-3xl lg:text-4xl"
|
||||
>
|
||||
{networkUseCases[0].title}
|
||||
</SectionHeader>
|
||||
<P className="mt-6 text-lg text-gray-600">
|
||||
{networkUseCases[0].description}
|
||||
</P>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Grid of features (excluding intro) */}
|
||||
<div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-slate-200 bg-white overflow-hidden">
|
||||
<ul
|
||||
role="list"
|
||||
className="mx-auto mt-6 max-w-6xl grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-y-10 px-6 pb-16"
|
||||
ref={sliderRef}
|
||||
className="flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar"
|
||||
>
|
||||
{networkUseCases.slice(1).map((item, idx) => (
|
||||
{networkUseCases.map((item, idx) => (
|
||||
<li
|
||||
key={idx}
|
||||
className="rounded-2xl border border-gray-200 p-8 transition-all duration-300 ease-in-out hover:scale-[1.03] hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 bg-white"
|
||||
className={`snap-start shrink-0 w-[85%] sm:w-[50%] lg:w-[33%]
|
||||
border border-slate-200 px-10 py-12 relative
|
||||
${item.isIntro ? "bg-gray-50/80" : "bg-white"}`}
|
||||
>
|
||||
{/* Icon */}
|
||||
{/* INTRO CARD */}
|
||||
{item.isIntro ? (
|
||||
<div className="flex flex-col justify-between h-full">
|
||||
<div>
|
||||
<Eyebrow>{item.eyebrow}</Eyebrow>
|
||||
<SectionHeader
|
||||
as="h3"
|
||||
className="mt-4 text-gray-900 text-xl lg:text-2xl"
|
||||
>
|
||||
{item.title}
|
||||
</SectionHeader>
|
||||
<P className="mt-4 text-gray-600 text-sm lg:text-base">
|
||||
{item.description}
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* slider buttons */}
|
||||
<div className="flex items-center gap-x-4 mt-6">
|
||||
<button
|
||||
onClick={scrollLeft}
|
||||
className="h-8 w-8 flex items-center justify-center
|
||||
border border-slate-300 rounded-md
|
||||
hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowBackOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={scrollRight}
|
||||
className="h-8 w-8 flex items-center justify-center
|
||||
border border-slate-300 rounded-md
|
||||
hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowForwardOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* REGULAR CARD */
|
||||
<div className="flex flex-col h-full">
|
||||
{item.icon && (
|
||||
<div className="h-10 w-10 flex items-center justify-center rounded-xl bg-gray-100">
|
||||
<div className="h-12 w-12 flex items-center justify-center rounded-xl bg-gray-100 mb-6">
|
||||
<item.icon className="h-6 w-6 text-cyan-600" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title */}
|
||||
<p className="mt-6 text-lg font-semibold text-gray-900">
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
{item.title}
|
||||
</p>
|
||||
|
||||
{/* Description */}
|
||||
<p className="mt-2 text-gray-600 text-sm leading-snug">
|
||||
<p className="mt-2 text-gray-600 leading-snug">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Bottom horizontal line */}
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { DeploySection } from './DeploySection'
|
||||
import { Companies } from './Companies'
|
||||
import { AgentLogos } from './AgentLogos'
|
||||
import { AgentBento } from './AgentBento'
|
||||
import { AgentHeroAlt } from './AgentHeroAlt'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { AgentUsecase } from './AgentUseCase'
|
||||
|
||||
import { AgentPro } from './AgentPro'
|
||||
import { AgentDesign } from './AgentDesign'
|
||||
|
||||
export default function AgentsPage() {
|
||||
return (
|
||||
@@ -20,7 +19,7 @@ export default function AgentsPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<Companies />
|
||||
<AgentLogos />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
@@ -32,7 +31,7 @@ export default function AgentsPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<AgentPro />
|
||||
<AgentDesign />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
|
||||
@@ -44,12 +44,12 @@ export function CallToAction() {
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
The Agent Framework launches in H1 2026, but the foundation is ready now.
|
||||
</p>
|
||||
<p className="mt-2 text-lg text-gray-300">
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Use today’s components —models, storage, compute, and network— to deploy workloads, connect nodes, and prepare for the next generation of distributed AI.
|
||||
</p>
|
||||
|
||||
{/* ✅ Two cards, stacked center with spacing */}
|
||||
<div className="mt-8 flex flex-wrap justify-center gap-x-10 gap-y-8">
|
||||
<div className="mt-10 flex flex-wrap justify-center gap-x-10 gap-y-8">
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="/deploy" variant="solid" color="cyan" className="mt-4">
|
||||
Deploy a Model
|
||||
@@ -63,15 +63,9 @@ export function CallToAction() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-5 font-semibold text-white underline underline-offset-4 decoration-white/70 hover:text-cyan-200 hover:decoration-cyan-200 transition-colors inline-flex items-center gap-1.5"
|
||||
>
|
||||
<Button to="https://threefold.info/mycelium_network/docs/" as="a" target="_blank" variant="outline" color="white" className="mt-4">
|
||||
Follow Development
|
||||
<span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function FungiStor({
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="FungiStor, a distributed long-term AI memory"
|
||||
aria-label="FungiStor — distributed long-term AI memory"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full" preserveAspectRatio="xMidYMid slice">
|
||||
|
||||
@@ -175,7 +175,7 @@ export default function Herodb({
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="HeroDB, active AI memory retrieval"
|
||||
aria-label="HeroDB — active AI memory retrieval"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full" preserveAspectRatio="xMidYMid slice">
|
||||
|
||||
@@ -142,7 +142,7 @@ export default function MyceliumMesh({
|
||||
)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Mycelium Mesh, a secure communication network"
|
||||
aria-label="Mycelium Mesh — secure communication network"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full" preserveAspectRatio="xMidYMid slice">
|
||||
|
||||
@@ -27,7 +27,7 @@ export function CloudBluePrint() {
|
||||
</H3>
|
||||
|
||||
<P className="mt-6 text-lg text-gray-600">
|
||||
Digital Me is an example environment built to demonstrate what’s possible on top of the Mycelium Stack, which is a full personal cloud you can deploy, customize, or extend. Your files, communication, apps, and optional AI agent, all running privately on infrastructure you choose.
|
||||
Digital Me is an example environment built to demonstrate what’s possible on top of the Mycelium Stack — a full personal cloud you can deploy, customize, or extend. Your files, communication, apps, and optional AI agent, all running privately on infrastructure you choose.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ export function CloudFeaturesLight() {
|
||||
</SectionHeader>
|
||||
<P className="mt-6 text-gray-600">
|
||||
Mycelium Cloud runs Kubernetes on a sovereign, self-healing network
|
||||
with compute, storage, and networking built in, so you don’t need
|
||||
with compute, storage, and networking built in — so you don’t need
|
||||
external cloud dependencies.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli
|
||||
style={{ backgroundImage: "url('/images/cloudhero4.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>
|
||||
MYCELIUM CLOUD
|
||||
@@ -21,7 +21,7 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
Run compute, storage, and AI resources on infrastructure you control.
|
||||
</p>
|
||||
<p className="mt-2 text-lg text-gray-600">
|
||||
<p className="mt-4 text-lg text-gray-600">
|
||||
The Mycelium Cloud runs on a distributed grid of independent nodes,
|
||||
delivering secure, scalable performance wherever your users or data live.
|
||||
</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { CloudCodeTabs } from "./CloudCodeTabs";
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
@@ -59,25 +59,9 @@ const tabs = [
|
||||
},
|
||||
];
|
||||
|
||||
const tabButtons = {
|
||||
kubernetes: {
|
||||
primary: "Deploy a Cluster",
|
||||
secondary: "Learn More",
|
||||
},
|
||||
vdc: {
|
||||
primary: "Follow Development",
|
||||
secondary: "Learn More",
|
||||
},
|
||||
qsfs: {
|
||||
primary: "View Docs",
|
||||
secondary: "Explore Integration",
|
||||
},
|
||||
} as const;
|
||||
|
||||
export function CloudIntro() {
|
||||
const [active, setActive] = useState("kubernetes");
|
||||
const current = tabs.find((t) => t.id === active)!.content;
|
||||
const currentButtons = tabButtons[active as keyof typeof tabButtons];
|
||||
|
||||
return (
|
||||
<section className="relative w-full bg-[#121212] overflow-hidden">
|
||||
@@ -96,7 +80,7 @@ export function CloudIntro() {
|
||||
<H3 color="white">What You Can Run on Mycelium Cloud</H3>
|
||||
|
||||
<P className="max-w-3xl text-gray-400 mt-6">
|
||||
Host nodes, deploy workloads, or build private AI systems all on
|
||||
Host nodes, deploy workloads, or build private AI systems — all on
|
||||
infrastructure you own and control. Mycelium gives you scalable compute,
|
||||
secure storage, and sovereign orchestration without depending on
|
||||
hyperscalers.
|
||||
@@ -110,11 +94,7 @@ export function CloudIntro() {
|
||||
|
||||
{/* Left: Code UI */}
|
||||
<div className="w-full lg:w-1/2">
|
||||
<img
|
||||
src="/images/cloud/reserve.png"
|
||||
alt="Mycelium Cloud reserve"
|
||||
className="w-full h-auto rounded-xl border border-white/10 object-cover"
|
||||
/>
|
||||
<CloudCodeTabs />
|
||||
</div>
|
||||
|
||||
{/* Right: Tabs */}
|
||||
@@ -161,26 +141,6 @@ export function CloudIntro() {
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{currentButtons && (
|
||||
<div className="mt-8 flex flex-wrap gap-4">
|
||||
<Button
|
||||
to="#"
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
>
|
||||
{currentButtons.primary}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
to="#"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
{currentButtons.secondary}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { CloudHeroNew } from './CloudHeroNew'
|
||||
import { CallToAction } from './CalltoAction'
|
||||
import { CloudIntro } from './CloudIntro'
|
||||
import { CloudFeaturesLight } from './CloudFeaturesLight'
|
||||
import { CloudPros } from './CloudPros'
|
||||
|
||||
|
||||
@@ -17,6 +18,10 @@ export default function CloudPage() {
|
||||
<CloudIntro />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudFeaturesLight />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudPros />
|
||||
</AnimatedSection>
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import { H3, P, Eyebrow, Small } from "@/components/Texts";
|
||||
import { Small } from '@/components/Texts'
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
label: "Local Execution",
|
||||
title: "Agents run entirely inside your environment.",
|
||||
label: 'Platform Architecture',
|
||||
title: 'Unified compute, storage & orchestration.',
|
||||
description:
|
||||
"Models, logic, and memory stay within your own trusted hardware—never behind third-party APIs.",
|
||||
'One unified platform for compute, storage, and orchestration.',
|
||||
},
|
||||
{
|
||||
label: "Mesh Connectivity",
|
||||
title: "They communicate peer-to-peer across trusted nodes.",
|
||||
label: 'Reliability',
|
||||
title: 'Consistent performance everywhere.',
|
||||
description:
|
||||
"Agents form direct encrypted paths between environments, without relays or central servers.",
|
||||
'Runs reliably across distributed environments.',
|
||||
},
|
||||
{
|
||||
label: "Private Data Access",
|
||||
title: "They use your data without sending it elsewhere.",
|
||||
label: 'Compatibility',
|
||||
title: 'Works with your existing stack.',
|
||||
description:
|
||||
"Your datasets, embeddings, and context never leave your boundaries—processing stays local.",
|
||||
'Works with your existing tools and workflows.',
|
||||
},
|
||||
{
|
||||
label: "Portability",
|
||||
title: "They move with you, not with a cloud provider.",
|
||||
label: 'Scalability',
|
||||
title: 'Grows with your needs.',
|
||||
description:
|
||||
"Agents follow your devices, networks, and workflows, remaining sovereign across every location.",
|
||||
'Scales from small projects to full environments.',
|
||||
},
|
||||
];
|
||||
]
|
||||
|
||||
export function CloudPros() {
|
||||
return (
|
||||
@@ -34,33 +34,14 @@ export function CloudPros() {
|
||||
<div className="max-w-7xl bg-[#FDFDFD] mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* Intro Block */}
|
||||
<div className="bg-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Cloud Advantages
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Why It’s Different
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 leading-relaxed">
|
||||
Most AI systems run on centralized clouds, where the models, data, and
|
||||
logic operate behind third-party APIs. Mycelium Agents flip that
|
||||
architecture, running entirely inside your environment so control,
|
||||
privacy, and autonomy stay with you.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Glow */}
|
||||
{/* Hover glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
@@ -81,9 +62,8 @@ export function CloudPros() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function ComputeFeatures() {
|
||||
</P>
|
||||
|
||||
<P className="mt-3 text-lg text-gray-600">
|
||||
Each component, from message passing to content distribution — works in harmony
|
||||
Each component — from message passing to content distribution — works in harmony
|
||||
to create a fully self-healing, self-optimizing data mesh.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -34,11 +34,11 @@ const tabs = [
|
||||
},
|
||||
{
|
||||
item: "Zero cloud lock-in",
|
||||
desc: "Deploy containers, VMs, or full Kubernetes clusters, migrate off AWS/GCP/Azure with no code changes.",
|
||||
desc: "Deploy containers, VMs, or full Kubernetes clusters — migrate off AWS/GCP/Azure with no code changes.",
|
||||
},
|
||||
{
|
||||
item: "Encrypted networking",
|
||||
desc: "All services communicate through Mycelium Mesh without VPNs, no exposed ports.",
|
||||
desc: "All services communicate through Mycelium Mesh — no VPNs, no exposed ports.",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -48,11 +48,11 @@ const tabs = [
|
||||
content: [
|
||||
{
|
||||
item: "Distributed workloads",
|
||||
desc: "Run compute where data lives; homes, factories, hospitals, or remote regions.",
|
||||
desc: "Run compute where data lives — homes, factories, hospitals, or remote regions.",
|
||||
},
|
||||
{
|
||||
item: "Offline-first resilience",
|
||||
desc: "Nodes keep working even with weak internet or outages, ideal for mission-critical edge.",
|
||||
desc: "Nodes keep working even with weak internet or outages — ideal for mission-critical edge.",
|
||||
},
|
||||
{
|
||||
item: "Global deployment, local data",
|
||||
@@ -85,7 +85,7 @@ export function ComputeUseCases() {
|
||||
<P className="max-w-3xl text-gray-400 mt-6">
|
||||
Mycelium Compute is a decentralized physical infrastructure network
|
||||
(DePIN) for high-performance workloads. Run reproducible AI/ML
|
||||
pipelines, host self-healing applications, or deploy to the edge, all
|
||||
pipelines, host self-healing applications, or deploy to the edge — all
|
||||
on a fabric that’s more resilient and private than the cloud.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export function GpuArchitecture() {
|
||||
Sovereign Compute Nodes
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-600 max-w-2xl">
|
||||
GPUs run only on hardware you control, eliminating reliance on centralized clouds.
|
||||
GPUs run only on hardware you control — eliminating reliance on centralized clouds.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
@@ -52,7 +52,7 @@ export function GpuArchitecture() {
|
||||
Encrypted Mesh Networking
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-600 max-w-2xl">
|
||||
Nodes form private, encrypted tunnels to workloads, no public exposure required.
|
||||
Nodes form private, encrypted tunnels to workloads — no public exposure required.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@ const gpuCapabilities = [
|
||||
eyebrow: "CAPABILITIES",
|
||||
title: "What You Can Run on Mycelium Cloud",
|
||||
description:
|
||||
"GPU acceleration for inference, training, rendering, and agent workload on sovereign hardware.",
|
||||
"GPU acceleration for inference, training, rendering, and agent workloads — on sovereign hardware.",
|
||||
},
|
||||
{
|
||||
name: "AI / ML Inference & Training",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { H3, H5, Eyebrow } from "@/components/Texts"
|
||||
import { H1, H4, H3, H5, Eyebrow } from "@/components/Texts"
|
||||
import { Button } from "@/components/Button"
|
||||
import { H3Icon } from "@heroicons/react/20/solid"
|
||||
|
||||
export function HomeAurora({ onGetStartedClick }: { onGetStartedClick: () => void }) {
|
||||
return (
|
||||
|
||||
@@ -39,10 +39,10 @@ export function HomeBlink({ onGetStartedClick }: { onGetStartedClick: () => void
|
||||
<H4 className="text-center mt-8">The Living Network of the Next Internet</H4>
|
||||
|
||||
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-neutral-500">
|
||||
A new internet is emerging, a private, distributed, and self-sovereign.
|
||||
A new internet is emerging — private, distributed, and self-sovereign.
|
||||
Mycelium is the living network that makes it possible.
|
||||
A peer-to-peer foundation where people, data, and intelligence connect
|
||||
directly without intermediaries, without compromise.
|
||||
directly — without intermediaries, without compromise.
|
||||
</H5>
|
||||
|
||||
<div className="mt-8 flex justify-center gap-6">
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
id: 1,
|
||||
@@ -29,32 +27,18 @@ const benefits = [
|
||||
export function HomeDesign() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-white">
|
||||
|
||||
{/* Top spacing line */}
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-200" />
|
||||
<div className="w-full border border-l border-r border-gray-200" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-200">
|
||||
{/* Centered intro */}
|
||||
<div className="px-6 pt-12 pb-4 text-center max-w-4xl mx-auto ">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Who's it For
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Built for Real-World Impact
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 text-base leading-relaxed">
|
||||
Whether you’re deploying infrastructure, securing sensitive operations, or simply taking back control of your digital life, Mycelium provides the foundation to build confidently in a connected world.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-7xl border-gray-200">
|
||||
<dl className="grid grid-cols-1 lg:grid-cols-3 gap-4 lg:gap-0">
|
||||
{benefits.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="mt-8 group flex items-start gap-2 bg-white px-8 py-12 border border-gray-200 lg:border-t lg:border-b border-l-0.5 border-r-0.5"
|
||||
className="group flex items-start gap-2 bg-white px-8 py-12 border border-gray-200 0 lg:border-t-0 lg:border-b-0"
|
||||
>
|
||||
{/* Image on the LEFT */}
|
||||
<img
|
||||
|
||||
@@ -34,10 +34,10 @@ export function HomeSpotlight({
|
||||
<H4 className="text-center mt-4">The Living Network of the Next Internet</H4>
|
||||
|
||||
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-neutral-500">
|
||||
A new internet is emerging, a private, distributed, and self-sovereign.
|
||||
A new internet is emerging — private, distributed, and self-sovereign.
|
||||
Mycelium is the living network that makes it possible.
|
||||
A peer-to-peer foundation where people, data, and intelligence connect
|
||||
directly without intermediaries, without compromise.
|
||||
directly — without intermediaries, without compromise.
|
||||
</H5>
|
||||
|
||||
<div className="mt-8 flex justify-center gap-6">
|
||||
|
||||
@@ -26,7 +26,7 @@ export function HomeTab() {
|
||||
{/* ------------------ LEFT TALL CARD ------------------ */}
|
||||
<Link to="/network" className="relative lg:row-span-2 cursor-pointer">
|
||||
<div className="absolute inset-px rounded-lg bg-white lg:rounded-l-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-l-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-l-[calc(2rem+1px)]">
|
||||
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">LIVE</h3>
|
||||
@@ -56,7 +56,7 @@ export function HomeTab() {
|
||||
{/* ------------------ MIDDLE TOP ------------------ */}
|
||||
<Link to="/pods" className="relative cursor-pointer max-lg:row-start-1">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-t-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)]">
|
||||
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Coming Soon</h3>
|
||||
@@ -81,7 +81,7 @@ export function HomeTab() {
|
||||
{/* ------------------ MIDDLE BOTTOM ------------------ */}
|
||||
<Link to="/agents" className="relative cursor-pointer max-lg:row-start-3 lg:col-start-2 lg:row-start-2">
|
||||
<div className="absolute inset-px rounded-lg bg-white" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)]">
|
||||
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Q1 2026</h3>
|
||||
@@ -105,7 +105,7 @@ export function HomeTab() {
|
||||
{/* ------------------ RIGHT TALL ------------------ */}
|
||||
<Link to="/cloud" className="relative lg:row-span-2 cursor-pointer">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-r-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-r-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-r-[calc(2rem+1px)]">
|
||||
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Early Access</h3>
|
||||
|
||||
@@ -41,7 +41,7 @@ export function HomeWhy() {
|
||||
<Eyebrow>Why It Matters</Eyebrow>
|
||||
<H3 className="mt-2 text-gray-200">Why Mycelium?</H3>
|
||||
<P className="mx-auto mt-5 max-w-prose text-gray-400">
|
||||
The current internet is a rent-seeking one. Mycelium builds one that belongs to everyone where infrastructure, data, and intelligence stay with the people and organizations who create them.
|
||||
The current internet is a rent-seeking one. Mycelium builds one that belongs to everyone — where infrastructure, data, and intelligence stay with the people and organizations who create them.
|
||||
|
||||
</P>
|
||||
<div className="mt-16">
|
||||
|
||||
@@ -20,7 +20,7 @@ export function HomeComparisonTable() {
|
||||
<Eyebrow>COMPARISON</Eyebrow>
|
||||
<H3 className="mt-2">Why People Choose Mycelium</H3>
|
||||
<P className="mx-auto mt-5 max-w-prose">
|
||||
Mycelium brings cloud-grade automation to infrastructure you control without surrendering data, identity,
|
||||
Mycelium brings cloud-grade automation to infrastructure you control — without surrendering data, identity,
|
||||
or uptime to centralized platforms.
|
||||
</P>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const tabs = [
|
||||
label: "K8s Clusters",
|
||||
title: "K8s Clusters",
|
||||
description:
|
||||
"Deploy and scale containerized apps across your own hardware, enabling a world of possibilities.",
|
||||
"Deploy and scale containerized apps across your own hardware — enabling a world of possibilities.",
|
||||
link: "#",
|
||||
icon: CubeIcon,
|
||||
},
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
import appleIcon from '@/images/apple.svg'
|
||||
import windowsIcon from '@/images/windows.svg'
|
||||
import androidIcon from '@/images/android.svg'
|
||||
import linuxIcon from '@/images/linux.svg'
|
||||
|
||||
import { CT, CP } from '@/components/Texts'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Download for iOS & MacOS',
|
||||
description: 'Download Mycelium App from the Apple Store.',
|
||||
href: 'https://apps.apple.com/us/app/mycelium-network/id6504277565',
|
||||
icon: appleIcon,
|
||||
alt: 'Apple logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Windows',
|
||||
description: 'Download the Mycelium App for Windows directly from its Github repository.',
|
||||
href: 'https://github.com/threefoldtech/myceliumflut/releases',
|
||||
icon: windowsIcon,
|
||||
alt: 'Windows logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Android',
|
||||
description: 'Download Mycelium from the Google Play Store.',
|
||||
href: 'https://play.google.com/store/apps/details?id=tech.threefold.mycelium&pli=1',
|
||||
icon: androidIcon,
|
||||
alt: 'Android logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Linux',
|
||||
description: 'Download the Mycelium binary for Linux directly from its Github repository.',
|
||||
href: 'https://github.com/threefoldtech/mycelium/releases',
|
||||
icon: linuxIcon,
|
||||
alt: 'Linux logo',
|
||||
},
|
||||
]
|
||||
|
||||
export function NetworkDownload() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border border-l border-r border-gray-100" />
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8 bg-white py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<motion.h3
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-4xl font-medium tracking-tight text-gray-900 lg:text-5xl"
|
||||
>
|
||||
Download Mycelium Network
|
||||
</motion.h3>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mt-8 text-lg text-gray-600 lg:leading-8"
|
||||
>
|
||||
Get Mycelium Network for Android, Windows, macOS, and iOS to securely connect, store, and interact with the decentralized network—seamlessly and efficiently. Not sure how it works?{' '}
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
className="font-semibold text-gray-900 underline transition-colors hover:text-cyan-500"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read the manual.
|
||||
</a>
|
||||
</motion.p>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 max-w-2xl lg:mt-12 lg:max-w-none">
|
||||
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 md:grid-cols-2 lg:max-w-none lg:grid-cols-4">
|
||||
{features.map((feature) => (
|
||||
<div
|
||||
key={feature.name}
|
||||
className="flex flex-col rounded-lg border border-gray-100 p-8 shadow-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
|
||||
>
|
||||
<dt className="text-base font-semibold leading-7 text-gray-900">
|
||||
<div className="mb-6 flex h-10 w-10 items-center justify-center">
|
||||
<img src={feature.icon} alt={feature.alt} className="h-10 w-10" />
|
||||
</div>
|
||||
{feature.name}
|
||||
</dt>
|
||||
<dd className="mt-1 flex flex-auto flex-col text-base leading-7 text-gray-600">
|
||||
<CP className="flex-auto mt-2">{feature.description}</CP>
|
||||
<CT className="mt-4">
|
||||
<a
|
||||
href={feature.href}
|
||||
className="text-sm font-semibold leading-6 text-cyan-500 transition-colors hover:text-cyan-400"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Download Now <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</CT>
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import { PrimaryFeatures } from './PrimaryFeatures'
|
||||
import { SecondaryFeatures } from './SecondaryFeatures'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { NetworkCapabilities } from './NetworkCapabilities'
|
||||
import { NetworkDownload } from './NetworkDownload'
|
||||
import { NetworkPros } from './NetworkPros'
|
||||
import { NetworkUsecases } from './NetworkUsecases'
|
||||
import { CloudPros } from './NetworkPros'
|
||||
|
||||
|
||||
export default function NetworkPage() {
|
||||
@@ -33,14 +33,14 @@ export default function NetworkPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<NetworkPros />
|
||||
<CloudPros />
|
||||
</AnimatedSection>
|
||||
|
||||
|
||||
<AnimatedSection>
|
||||
<NetworkDownload />
|
||||
<NetworkUsecases />
|
||||
</AnimatedSection>
|
||||
|
||||
|
||||
<AnimatedSection>
|
||||
<CallToAction />
|
||||
</AnimatedSection>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Eyebrow, H3, P, Small } from '@/components/Texts'
|
||||
import { Small } from '@/components/Texts'
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
@@ -17,7 +17,7 @@ const highlights = [
|
||||
label: 'Scale',
|
||||
title: 'Scales instantly from one POD to thousands.',
|
||||
description:
|
||||
'Deploy locally or expand globally, the mesh routes and balances itself automatically.',
|
||||
'Deploy locally or expand globally — the mesh routes and balances itself automatically.',
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
@@ -27,48 +27,33 @@ const highlights = [
|
||||
},
|
||||
]
|
||||
|
||||
export function NetworkPros() {
|
||||
export function CloudPros() {
|
||||
return (
|
||||
<section className="bg-[#121212] w-full max-w-8xl mx-auto">
|
||||
<section className="relative w-full bg-[#FDFDFD] overflow-hidden">
|
||||
{/* Top spacing line */}
|
||||
<div className="max-w-7xl py-6 mx-auto border border-t-0 border-b-0 border-gray-800" />
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
<div className="max-w-7xl bg-[#FDFDFD] mx-auto py-6 border border-t-0 border-b-0 border-gray-200"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-200" />
|
||||
|
||||
{/* Main framed content */}
|
||||
<div className="bg-[#121212] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-800">
|
||||
{/* Centered intro */}
|
||||
<div className="px-6 pt-12 pb-4 text-center max-w-4xl mx-auto ">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
WHY
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-white">
|
||||
Why It’s Revolutionary
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-300 text-base leading-relaxed">
|
||||
Mycelium redefines how digital systems connect, scale, and stay secure in a world moving beyond the limits of traditional cloud
|
||||
</P>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-4 mt-8">
|
||||
<div className="bg-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-200">
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-white/10 bg-white/4 p-8 backdrop-blur-sm transition hover:border-cyan-300/50 hover:bg-white/8"
|
||||
className="group relative overflow-hidden border border-gray-200 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Hover glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-500/0 via-white/5 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-200">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold text-white">
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-300">
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -77,9 +62,8 @@ export function NetworkPros() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing line */}
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
<div className="w-full border-b border-gray-200 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-200" />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const networkUseCases = [
|
||||
{
|
||||
title: "Service-to-Service Networking Across Environments",
|
||||
description:
|
||||
"Connect apps running across home labs, cloud regions, edge nodes, and datacenters all on one address space.",
|
||||
"Connect apps running across home labs, cloud regions, edge nodes, and datacenters — all on one address space.",
|
||||
ideal: "Ideal for: dev teams, distributed apps, container + K3s workloads",
|
||||
icon: GlobeAltIcon,
|
||||
},
|
||||
|
||||
@@ -72,7 +72,7 @@ export function NodeBenefits() {
|
||||
<P className="mt-6" color="light">
|
||||
Hosting a node gives you private compute, contributes to the global
|
||||
Mycelium infrastructure, and unlocks ways to earn from real network
|
||||
usage, all while keeping sovereignty and control.
|
||||
usage — all while keeping sovereignty and control.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
@@ -101,17 +101,15 @@ export function NodeBenefits() {
|
||||
<CT as="span" className="text-left font-semibold" color="light">
|
||||
{feature.name}
|
||||
</CT>
|
||||
<CP className="mt-2 text-left text-gray-200">
|
||||
<CP className="mt-2 text-left text-sm" color="light">
|
||||
{feature.description}
|
||||
</CP>
|
||||
</motion.li>
|
||||
))}
|
||||
</motion.ul>
|
||||
</div>
|
||||
{/* Bottom spacing line */}
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
</section>
|
||||
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export function NodeHero() {
|
||||
style={{ backgroundImage: "url('/images/gpuhero2.png')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM NODES</Eyebrow>
|
||||
<H3 as="h1" className="mt-4">
|
||||
|
||||
@@ -2,16 +2,6 @@
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { Container } from "@/components/Container";
|
||||
import { CheckCircleIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
const benefits = [
|
||||
'Private chat and calls',
|
||||
'Calendar and file sync',
|
||||
'Secure team spaces',
|
||||
'Early AI Agent integration',
|
||||
]
|
||||
|
||||
|
||||
|
||||
export function CallToAction() {
|
||||
return (
|
||||
@@ -48,36 +38,11 @@ export function CallToAction() {
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
|
||||
Be Among The First
|
||||
|
||||
Join the First Wave
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
The first Pods are launching soon.
|
||||
10,000 early Pods will be available for early adopters.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 flex items-center justify-center">
|
||||
<ul
|
||||
role="list"
|
||||
className="grid grid-cols-1 gap-x-8 gap-y-3 text-left text-base/7 text-gray-200 sm:grid-cols-2"
|
||||
>
|
||||
{benefits.map((benefit) => (
|
||||
<li key={benefit} className="flex items-start gap-x-3">
|
||||
<CheckCircleIcon
|
||||
aria-hidden="true"
|
||||
className="mt-1 h-7 w-5 flex-none text-gray-200"
|
||||
/>
|
||||
<span>{benefit}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Next, Pods will support peer-to-peer AI Agents that live inside your environment.
|
||||
Your own AI, powered by your data without any data leaks. Be among the first to claim
|
||||
your private space on the Mycelium Network.
|
||||
Pods are launching soon. Be among the first to claim your private space in the new internet.
|
||||
</p>
|
||||
|
||||
{/* ✅ Two cards, stacked center with spacing */}
|
||||
@@ -90,7 +55,7 @@ export function CallToAction() {
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="#" variant="outline" color="white" className="mt-4">
|
||||
Learn More
|
||||
Deploy Pods in Your Community
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { H3, Eyebrow, P } from "@/components/Texts"
|
||||
import { Button } from "@/components/Button"
|
||||
|
||||
export default function Homepod() {
|
||||
const onGetStartedClick = () => {
|
||||
console.log("Get started clicked");
|
||||
};
|
||||
return (
|
||||
<div className="">
|
||||
{/* Boxed container */}
|
||||
@@ -13,29 +9,19 @@ export default function Homepod() {
|
||||
style={{ backgroundImage: "url('/images/computehero11.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>
|
||||
Mycelium Pods - Coming Soon
|
||||
MYCELIUM PODS
|
||||
</Eyebrow>
|
||||
<H3 className="mt-4">
|
||||
Your Private Space in the New Internet
|
||||
</H3>
|
||||
<P className="mt-6 text-gray-800">
|
||||
Pods are personal digital spaces on the Mycelium Network. They are private, persistent, and fully under your control. Run conversations, files, and tools directly on the network instead of through central servers
|
||||
Imagine having your own corner of the internet — private, secure, and always online.
|
||||
A Pod is your personal digital space on the Mycelium Network.
|
||||
It’s where your conversations, files, and digital tools live — owned by you, connected to others directly.
|
||||
</P>
|
||||
<div className="mt-8 flex items-center gap-x-6">
|
||||
<Button
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
onClick={onGetStartedClick}
|
||||
>
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Explore Docs →
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const capabilities = [
|
||||
{
|
||||
title: "An always-on space you fully control",
|
||||
description:
|
||||
"A dedicated, always-on environment you fully command, your own sovereign slice of the network that never goes offline.",
|
||||
"A dedicated, always-on environment you fully command — your own sovereign slice of the network that never goes offline.",
|
||||
icon: (
|
||||
<div className="flex items-start justify-start">
|
||||
<CpuChipIcon className="h-12 w-12 text-cyan-400" />
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import DataControl from "./animations/DataControl";
|
||||
import Connectivity from "./animations/Connectivity";
|
||||
import Security from "./animations/Security";
|
||||
import Resilience from "./animations/Resilience";
|
||||
|
||||
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: <DataControl className="lg:-mt-12" />,
|
||||
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: <Connectivity className="lg:-mt-12" />,
|
||||
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: <Security className="lg:-mt-12" />,
|
||||
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: <Resilience className="lg:-mt-12" />,
|
||||
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 (
|
||||
<section className="relative w-full bg-[#121212] overflow-hidden">
|
||||
{/* TOP LINE */}
|
||||
<div className="max-w-7xl bg-[#121212] mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
<div className="mx-auto bg-[#111111] max-w-2xl px-6 lg:max-w-7xl lg:px-8 border border-t-0 border-b-0 border-gray-800">
|
||||
<div className="grid grid-cols-1 gap-6 pt-6 lg:grid-cols-6 lg:grid-rows-2 pb-6">
|
||||
{deterministicCards.map((card) => (
|
||||
<div
|
||||
key={card.id}
|
||||
className={`relative flex flex-col ${card.colSpan} ${card.rowSpan} transition-transform duration-300 hover:scale-102 group`}
|
||||
>
|
||||
{/* DISABLE OUTER WRAPPER ON INTRO CARD */}
|
||||
{!card.noBorder && (
|
||||
<div
|
||||
className={`absolute inset-0 rounded-md border border-gray-800 bg-[#111111] ${card.rounded} group-hover:bg-linear-to-br from-gray-900 to-gray-800`}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`relative flex lg:h-90 flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] ${card.innerRounded}`}
|
||||
>
|
||||
{/* ANIMATION */}
|
||||
{card.animation ? (
|
||||
<div className="lg:h-64 h-48 w-full overflow-hidden bg-transparent flex items-center justify-center">
|
||||
<div className="w-full h-full object-cover">
|
||||
{card.animation}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-48 w-full flex items-center justify-center bg-transparent" />
|
||||
)}
|
||||
|
||||
{/* TEXT AREA */}
|
||||
<div className="px-8 pt-4 pb-6">
|
||||
{card.custom ? (
|
||||
<>
|
||||
<Eyebrow className="text-cyan-500">
|
||||
{card.eyebrow}
|
||||
</Eyebrow>
|
||||
<H3 className="mt-2 text-white">{card.title}</H3>
|
||||
<P className="mt-4 max-w-lg text-gray-200">
|
||||
{card.description}
|
||||
</P>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{card.label && (
|
||||
<p className="text-xs uppercase tracking-[0.16em] text-cyan-500">
|
||||
{card.label}
|
||||
</p>
|
||||
)}
|
||||
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
|
||||
{card.title}
|
||||
</p>
|
||||
<p className="mt-1 max-w-lg text-sm/6 text-gray-300">
|
||||
{card.description}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* OUTLINE SHADOW */}
|
||||
{!card.noBorder && (
|
||||
<div
|
||||
className={`pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 ${card.rounded}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* BOTTOM LINE */}
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
<div className="max-w-7xl mx-auto py-6 border-x border-gray-800 border-t-0 border-b-0" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export function PodsCapabilities() {
|
||||
</H3>
|
||||
|
||||
<P className="mt-6 text-gray-200">
|
||||
Access everything from any device, your data follows you, not the other way around.
|
||||
Access everything from any device — your data follows you, not the other way around.
|
||||
💡 It’s like having your own tiny cloud that belongs only to you.
|
||||
</P>
|
||||
</div>
|
||||
@@ -42,7 +42,7 @@ export function PodsCapabilities() {
|
||||
Communicate
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-200 max-w-2xl">
|
||||
Message, call, and share files privately with no tracking or ads.
|
||||
Message, call, and share files privately — no tracking or ads.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@ Your own AI, powered by your data without any data leaks.
|
||||
Calendar & File Sync
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-700 max-w-2xl">
|
||||
Your schedules, documents, and files synced across your Pods with no central cloud.
|
||||
Your schedules, documents, and files — synced across your Pods with no central cloud.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-200" />
|
||||
</div>
|
||||
@@ -66,7 +66,7 @@ Your own AI, powered by your data without any data leaks.
|
||||
Secure Team Spaces
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-700 max-w-2xl">
|
||||
Create shared Pods for teams, communities, or groups. Fully encrypted, fully sovereign.
|
||||
Create shared Pods for teams, communities, or groups — fully encrypted, fully sovereign.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-200" />
|
||||
</div>
|
||||
@@ -79,7 +79,7 @@ Your own AI, powered by your data without any data leaks.
|
||||
<span className="ml-2 text-xs text-gray-400">🕒</span>
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-700 max-w-2xl">
|
||||
Host your personal AI agent inside your Pod. Private, local-first, and fully under your control.
|
||||
Host your personal AI agent inside your Pod — private, local-first, and fully under your control.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
'use client'
|
||||
|
||||
import { Eyebrow, H3, P, Small } from "@/components/Texts"
|
||||
import {
|
||||
Disclosure,
|
||||
DisclosureButton,
|
||||
DisclosurePanel,
|
||||
} from '@headlessui/react'
|
||||
import { MinusIcon, PlusIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
import { Eyebrow, H3, H4 } from "@/components/Texts"
|
||||
|
||||
|
||||
const product = {
|
||||
subtitle: "BENEFITS",
|
||||
subtitle: "Federation",
|
||||
name: "Runs on Your Own Infrastructure",
|
||||
description: `
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
`,
|
||||
|
||||
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.",
|
||||
@@ -41,66 +44,78 @@ const product = {
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
export function PodsDesign() {
|
||||
return (
|
||||
<section className="relative w-full bg-[#FDFDFD] overflow-hidden">
|
||||
{/* ▸ Top Spacing Line */}
|
||||
<div className="max-w-7xl bg-[#FDFDFD] mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="bg-white text-gray-900">
|
||||
{/* TOP LINE */}
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* ▸ Intro Section */}
|
||||
<div className="bg-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl mx-auto flex flex-col items-center justify-center min-h-[220px] text-center">
|
||||
<main className="mx-auto max-w-7xl px-6 lg:px-12 py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-2xl lg:max-w-none">
|
||||
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
{product.subtitle}
|
||||
</Eyebrow>
|
||||
<div className="lg:grid lg:grid-cols-5 lg:items-start lg:gap-x-8">
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
{product.name}
|
||||
</H3>
|
||||
|
||||
<P
|
||||
className="mt-4 text-gray-700 text-base leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: product.description }}
|
||||
{/* IMAGE */}
|
||||
<div className="lg:col-span-2 lg:mt-8 mt-2">
|
||||
<img
|
||||
alt="Mycelium Federation"
|
||||
src="/images/pod1.png"
|
||||
className="aspect-square w-full object-cover rounded-md"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* ▸ 4-Column Highlights Grid */}
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{product.details.map((item) => (
|
||||
{/* PRODUCT INFO */}
|
||||
<div className="mt-8 px-4 sm:px-0 lg:mt-0 lg:col-span-3">
|
||||
|
||||
<Eyebrow className="text-cyan-600">
|
||||
{product.subtitle}
|
||||
</Eyebrow>
|
||||
|
||||
<H4 className="text-gray-900">
|
||||
{product.name}
|
||||
</H4>
|
||||
|
||||
<div
|
||||
key={item.name}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Hover Glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
className="mt-4 text-gray-700 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: product.description }}
|
||||
/>
|
||||
|
||||
<div className="relative">
|
||||
{/* DETAILS ACCORDION */}
|
||||
<section className="mt-6">
|
||||
<div className="divide-y divide-gray-200 border-t border-cyan-600/60">
|
||||
{product.details.map((detail) => (
|
||||
<Disclosure key={detail.name} as="div">
|
||||
<H3>
|
||||
<DisclosureButton className="group flex w-full items-center justify-between py-6 text-left">
|
||||
<span className="text-lg font-medium text-gray-900">
|
||||
{detail.name}
|
||||
</span>
|
||||
<span className="ml-6 flex items-center">
|
||||
<PlusIcon className="block h-6 w-6 text-gray-500 group-open:hidden" />
|
||||
<MinusIcon className="hidden h-6 w-6 text-cyan-600 group-open:block" />
|
||||
</span>
|
||||
</DisclosureButton>
|
||||
</H3>
|
||||
|
||||
{item.label && (
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
)}
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.name}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
<DisclosurePanel className="pb-6">
|
||||
<p className="text-gray-600 text-base">
|
||||
{detail.description}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ▸ Bottom Spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* BOTTOM LINE */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Container } from "@/components/Container";
|
||||
import { Small } from "@/components/Texts";
|
||||
|
||||
const useCases = [
|
||||
{
|
||||
@@ -9,7 +10,7 @@ const useCases = [
|
||||
"Communicate directly Pod-to-Pod with no centralized routing.",
|
||||
bullets: [
|
||||
"End-to-end encrypted messaging and voice calling.",
|
||||
"No intermediaries, connections flow directly between Pods.",
|
||||
"No intermediaries — connections flow directly between Pods.",
|
||||
"Zero metadata profiling, tracking, or data resale.",
|
||||
],
|
||||
},
|
||||
@@ -20,7 +21,7 @@ const useCases = [
|
||||
bullets: [
|
||||
"Files remain private with no platform-level scanning or analysis.",
|
||||
"Share documents and media directly with trusted contacts.",
|
||||
"Full ownership of your content, with no cloud vendor dependencies.",
|
||||
"Full ownership of your content — no cloud vendor dependencies.",
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -56,11 +57,11 @@ const useCases = [
|
||||
{
|
||||
title: "Access From Any Device",
|
||||
description:
|
||||
"Your Pod travels with you. Always accessible, always yours.",
|
||||
"Your Pod travels with you — always accessible, always yours.",
|
||||
bullets: [
|
||||
"Use your Pod from phones, laptops, tablets, or shared machines.",
|
||||
"Your identity, apps, and files follow you securely.",
|
||||
"No syncing or duplicated copies, direct access to your environment.",
|
||||
"No syncing or duplicated copies — direct access to your environment.",
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -105,6 +106,9 @@ export function PodsFeatures() {
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
{useCase.title}
|
||||
</h3>
|
||||
<Small className="uppercase tracking-[0.25em] text-cyan-500">
|
||||
Feature
|
||||
</Small>
|
||||
</div>
|
||||
|
||||
{/* Short description */}
|
||||
|
||||
@@ -1,69 +1,57 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H4, H5 } from "@/components/Texts";
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import PodsFlow from "./animations/PodsFlow";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const phrases = [
|
||||
"everything runs directly from your Pod.",
|
||||
"Messages travel from Pod to Pod, never through a server.",
|
||||
"Calls are hosted on your Pod, not in a data center.",
|
||||
"Files stay encrypted, available, and always yours.",
|
||||
];
|
||||
|
||||
export function PodsHow() {
|
||||
const [index, setIndex] = useState(0);
|
||||
const [fade, setFade] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setFade(false);
|
||||
setTimeout(() => {
|
||||
setIndex((prev) => (prev + 1) % phrases.length);
|
||||
setFade(true);
|
||||
}, 300);
|
||||
}, 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="relative w-full bg-[#121212] overflow-hidden">
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-[#121212] mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
<div className="max-w-7xl mx-auto px-6 lg:px-8 py-12 border border-t-0 border-b-0 border-gray-800 bg-[#111111] overflow-hidden">
|
||||
|
||||
{/* Two-column layout */}
|
||||
{/* ✅ Two-column layout */}
|
||||
<div className="flex flex-col lg:flex-row-reverse gap-8">
|
||||
|
||||
{/* Right: Animation */}
|
||||
{/* ✅ Right side animation */}
|
||||
<div className="w-full lg:w-4/9">
|
||||
<PodsFlow />
|
||||
</div>
|
||||
|
||||
{/* Left: JUST the H3 with auto-changing sentence */}
|
||||
<div className="w-full lg:w-5/9 text-white flex items-center">
|
||||
|
||||
<div>
|
||||
<Eyebrow color="accent">How it works</Eyebrow>
|
||||
<H4 color="white">A Pod in Action</H4>
|
||||
<H5 color="white" className="mt-4">
|
||||
When you use Mycelium,
|
||||
<span
|
||||
className={`inline-block transition-opacity duration-300 ${
|
||||
fade ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
{phrases[index]}
|
||||
</span>
|
||||
</H5>
|
||||
</div>
|
||||
|
||||
{/* ✅ Left side content */}
|
||||
<div className="w-full lg:w-5/9 text-white">
|
||||
<Eyebrow color="accent" className="">
|
||||
How it works
|
||||
</Eyebrow>
|
||||
<H3 color="white" className="mt-6">
|
||||
A Pod in Action
|
||||
</H3>
|
||||
<P className="max-w-4xl text-gray-400 mt-6">
|
||||
When you use Mycelium, everything runs directly from your Pod.
|
||||
</P>
|
||||
<ul className="max-w-4xl text-gray-400 mt-6 space-y-2 ml-6">
|
||||
<li className="flex items-start gap-2">
|
||||
<span className="mt-1 inline-block size-2 rounded-full bg-cyan-400" />
|
||||
<span>When you message someone, it goes Pod to Pod, not through a central server.</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="mt-1 inline-block size-2 rounded-full bg-cyan-400" />
|
||||
<span>When you host a call, it runs on your Pod — no third-party data centers.</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="mt-1 inline-block size-2 rounded-full bg-cyan-400" />
|
||||
<span>When you save a file, it stays on your Pod, encrypted and always available.</span>
|
||||
</li>
|
||||
</ul>
|
||||
<P className="max-w-3xl text-gray-400 mt-4">
|
||||
No one else can read it, rent it, or switch it off.
|
||||
You don’t log in to the internet — you are part of it.
|
||||
</P>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
</section>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Homepod from './Homepod';
|
||||
import { PodsHow } from './PodsHow';
|
||||
import { PodsFeatures } from './PodsFeatures';
|
||||
import { PodsDesign } from './PodsDesign';
|
||||
import { PodsBenefits } from './PodsBenefits';
|
||||
import { CallToAction } from './CallToAction';
|
||||
import { PodsWhat } from './PodsWhat';
|
||||
import { PodsPro } from './PodsPro';
|
||||
import { PodsBento } from './PodsBento';
|
||||
|
||||
const PodsPage = () => {
|
||||
return (
|
||||
@@ -13,8 +13,8 @@ const PodsPage = () => {
|
||||
<PodsWhat />
|
||||
<PodsFeatures />
|
||||
<PodsHow />
|
||||
<PodsBento />
|
||||
<PodsPro />
|
||||
<PodsDesign />
|
||||
<PodsBenefits />
|
||||
<CallToAction />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Eyebrow, H3, P, Small } from "@/components/Texts"
|
||||
|
||||
const product = {
|
||||
subtitle: "BENEFITS",
|
||||
name: "Why It’s Different",
|
||||
description: `
|
||||
<p>
|
||||
Pods combine self-hosting with the reach of the Mycelium Network.
|
||||
They run on the same system that powers Mycelium Cloud and AI Agents.
|
||||
</p>
|
||||
`,
|
||||
|
||||
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 (
|
||||
<section className="relative w-full bg-[#FDFDFD] overflow-hidden">
|
||||
{/* ▸ Top Spacing Line */}
|
||||
<div className="max-w-7xl bg-[#FDFDFD] mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* ▸ Intro Section */}
|
||||
<div className="bg-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl mx-auto flex flex-col items-center justify-center min-h-[220px] text-center">
|
||||
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
{product.subtitle}
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
{product.name}
|
||||
</H3>
|
||||
|
||||
<P
|
||||
className="mt-4 text-gray-700 text-base leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: product.description }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* ▸ Highlights Grid */}
|
||||
<div className="grid lg:grid-cols-5">
|
||||
{product.details.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Hover Glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
|
||||
{item.label && (
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
)}
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.name}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ▸ Bottom Spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -7,27 +7,45 @@ import {
|
||||
GlobeAltIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import { Eyebrow, H3 } from "@/components/Texts";
|
||||
|
||||
const podCards = [
|
||||
{
|
||||
id: "tools",
|
||||
title: "Runs communication, storage, and collaboration tools",
|
||||
id: "intro",
|
||||
eyebrow: "Capabilities",
|
||||
title: "What is a Pod?",
|
||||
description: null,
|
||||
icon: null,
|
||||
custom: true,
|
||||
noBorder: true,
|
||||
colSpan: "lg:col-span-4",
|
||||
},
|
||||
{
|
||||
id: "home",
|
||||
title: "Your private digital home on the decentralized internet",
|
||||
description:
|
||||
"Your Pod is a private digital home where apps, data, and identity live independently of Big Tech and central servers.",
|
||||
icon: ServerIcon,
|
||||
},
|
||||
{
|
||||
id: "p2p",
|
||||
title: "Operates peer to peer on the network",
|
||||
id: "control",
|
||||
title: "An always-on space you fully control",
|
||||
description:
|
||||
"A dedicated, always-on environment you fully command — your own sovereign slice of the network that never goes offline.",
|
||||
icon: ShieldCheckIcon,
|
||||
},
|
||||
{
|
||||
id: "encrypted",
|
||||
title: "Uses encrypted identities and storage",
|
||||
id: "tools",
|
||||
title: "Runs communication, storage, and collaboration tools",
|
||||
description:
|
||||
"Runs your communication, storage, and collaboration tools in a secure local environment without reliance on outside platforms.",
|
||||
icon: BoltIcon,
|
||||
},
|
||||
{
|
||||
id: "fabric",
|
||||
title: "Connects directly to other Pods through the network fabric",
|
||||
id: "networking",
|
||||
title: "Fully encrypted, federated peer-to-peer network",
|
||||
description:
|
||||
"Encrypted, federated peer-to-peer networking that links your Pod directly with trusted devices without intermediaries.",
|
||||
icon: GlobeAltIcon,
|
||||
},
|
||||
];
|
||||
@@ -42,31 +60,38 @@ export function PodsWhat() {
|
||||
{/* Content container */}
|
||||
<div className="mx-auto bg-[#111111] max-w-7xl px-6 lg:px-10 border border-t-0 border-b-0 border-gray-800">
|
||||
|
||||
{/* Intro heading */}
|
||||
<div className="pt-12 pb-10 max-w-3xl">
|
||||
<Eyebrow>What</Eyebrow>
|
||||
<H3 className="mt-2 text-white">What is a Pod?</H3>
|
||||
<P className="mt-4 text-gray-300">
|
||||
A Pod is an always-on digital environment that gives you a secure place to run your
|
||||
applications and data.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* 4-column grid */}
|
||||
<div className="grid grid-cols-1 gap-12 lg:grid-cols-4 pb-20">
|
||||
<div className="grid grid-cols-1 gap-12 pt-12 lg:grid-cols-4 pb-20">
|
||||
{podCards.map((card) => {
|
||||
const Icon = card.icon;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={card.id}
|
||||
className="flex flex-col transition-transform duration-300 hover:scale-[1.02]"
|
||||
className={`${card.colSpan || ""} flex flex-col ${
|
||||
card.custom ? "" : "transition-transform duration-300 hover:scale-[1.02]"
|
||||
}`}
|
||||
>
|
||||
{/* TITLE WITH ICON */}
|
||||
{/* Custom Intro Card */}
|
||||
{card.custom ? (
|
||||
<>
|
||||
<Eyebrow>{card.eyebrow}</Eyebrow>
|
||||
<H3 className="mt-2 text-white">{card.title}</H3>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* TITLE WITH ICON (matching the TL example) */}
|
||||
<dt className="flex items-center gap-x-3 text-base font-semibold text-white">
|
||||
{Icon && <Icon className="h-6 w-6 text-cyan-500" aria-hidden="true" />}
|
||||
{card.title}
|
||||
</dt>
|
||||
|
||||
{/* DESCRIPTION */}
|
||||
<dd className="mt-4 text-base text-gray-300">
|
||||
{card.description}
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
gridStroke?: string;
|
||||
};
|
||||
|
||||
const W = 720; // 4:3 width
|
||||
const H = 540; // 4:3 height
|
||||
|
||||
export default function Connectivity({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0b0b0b",
|
||||
gridStroke = "#2b2a2a",
|
||||
}: Props) {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
// Hex-like P2P topology — all peers connect *directly*
|
||||
const pods = [
|
||||
{ x: 250, y: 160 },
|
||||
{ x: 470, y: 160 },
|
||||
{ x: 580, y: 290 },
|
||||
{ x: 470, y: 420 },
|
||||
{ x: 250, y: 420 },
|
||||
{ x: 140, y: 290 },
|
||||
];
|
||||
|
||||
// Every pod connects to every other pod = full direct mesh
|
||||
const links: [number, number][] = [];
|
||||
for (let i = 0; i < pods.length; i++) {
|
||||
for (let j = i + 1; j < pods.length; j++) links.push([i, j]);
|
||||
}
|
||||
|
||||
const line = (i: number, j: number) => {
|
||||
const a = pods[i], b = pods[j];
|
||||
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("relative overflow-hidden", className)}>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full" style={{ background: bg }}>
|
||||
|
||||
{/* ========= GRID + FILTERS ========= */}
|
||||
<defs>
|
||||
<pattern id="conn-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path
|
||||
d="M 28 0 L 0 0 0 28"
|
||||
fill="none"
|
||||
stroke={gridStroke}
|
||||
strokeWidth="1"
|
||||
opacity="0.35"
|
||||
/>
|
||||
</pattern>
|
||||
|
||||
<filter id="conn-glow">
|
||||
<feGaussianBlur stdDeviation="3" result="b" />
|
||||
<feMerge>
|
||||
<feMergeNode in="b" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
{/* Cyan soft gradient for pods */}
|
||||
<radialGradient id="conn-pod" 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(#conn-grid)" />
|
||||
|
||||
{/* ========= BASE CONNECTION LINES (Full Mesh) ========= */}
|
||||
{links.map(([i, j], idx) => (
|
||||
<motion.path
|
||||
key={`base-${idx}`}
|
||||
d={line(i, j)}
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.2 }}
|
||||
animate={{ pathLength: 1, opacity: 0.5 }}
|
||||
transition={{ duration: 0.6, delay: idx * 0.03 }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ========= ACCENT DASH (Active P2P links) ========= */}
|
||||
{links.map(([i, j], idx) => (
|
||||
<motion.path
|
||||
key={`dash-${idx}`}
|
||||
d={line(i, j)}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
strokeDasharray="12 10"
|
||||
initial={{ pathLength: 0, opacity: 0.9 }}
|
||||
animate={{ pathLength: 1, opacity: [0.9, 0.6, 0.9] }}
|
||||
transition={{
|
||||
duration: 1,
|
||||
delay: idx * 0.04,
|
||||
repeat: Infinity,
|
||||
repeatType: "reverse",
|
||||
}}
|
||||
filter="url(#conn-glow)"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ========= MOVING SIGNALS (Direct P2P) ========= */}
|
||||
{!prefers &&
|
||||
links.map(([i, j], idx) => (
|
||||
<motion.circle
|
||||
key={`pulse-${idx}`}
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${line(i, j)}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0.2, 1, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.3,
|
||||
delay: idx * 0.2,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
filter="url(#conn-glow)"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ========= CLOUD PODS ========= */}
|
||||
{pods.map((p, i) => (
|
||||
<g key={`pod-${i}`}>
|
||||
{/* Outer soft cyan aura */}
|
||||
{!prefers && (
|
||||
<motion.ellipse
|
||||
cx={p.x}
|
||||
cy={p.y}
|
||||
rx={42}
|
||||
ry={28}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
opacity={0.15}
|
||||
animate={{ scale: [1, 1.1, 1] , opacity: [0.1, 0.25, 0.1] }}
|
||||
transition={{ duration: 2.3, delay: i * 0.15, repeat: Infinity }}
|
||||
filter="url(#conn-glow)"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Pod body */}
|
||||
<ellipse
|
||||
cx={p.x}
|
||||
cy={p.y}
|
||||
rx={36}
|
||||
ry={24}
|
||||
fill="url(#conn-pod)"
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={2}
|
||||
filter="url(#conn-glow)"
|
||||
/>
|
||||
|
||||
{/* Inner dark core */}
|
||||
<ellipse cx={p.x} cy={p.y} rx={18} ry={12} fill="#0f0f0f" stroke="#292929" strokeWidth={1.5} />
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string; // cyan
|
||||
bg?: string; // solid dark
|
||||
gridStroke?: string; // grid color
|
||||
};
|
||||
|
||||
const W = 720; // 4:3
|
||||
const H = 540; // 4:3
|
||||
|
||||
export default function DataControl({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0b0b0b",
|
||||
gridStroke = "#2b2a2a",
|
||||
}: Props) {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
// Central "Your Pod"
|
||||
const center = { x: W / 2, y: H / 2 + 10 };
|
||||
|
||||
// Personal pods (owned) — left-bottom cluster
|
||||
const owned = [
|
||||
{ x: 200, y: 380 },
|
||||
{ x: 260, y: 320 },
|
||||
{ x: 140, y: 320 },
|
||||
];
|
||||
|
||||
// External peers (share targets) — top-right cluster
|
||||
const peers = [
|
||||
{ x: 520, y: 180 },
|
||||
{ x: 600, y: 140 },
|
||||
{ x: 580, y: 240 },
|
||||
];
|
||||
|
||||
// helper
|
||||
const line = (a: {x:number;y:number}, b:{x:number;y:number}) => `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
||||
|
||||
// "Gates" sit near the center to symbolize explicit permission before data leaves your pod
|
||||
const gates = peers.map((p) => {
|
||||
// point 30% of the way from center to peer
|
||||
const gx = center.x + (p.x - center.x) * 0.28;
|
||||
const gy = center.y + (p.y - center.y) * 0.28;
|
||||
return { x: gx, y: gy };
|
||||
});
|
||||
|
||||
// Paths for outbound shares (center -> gate -> peer)
|
||||
const sharePaths = peers.map((p, i) =>
|
||||
`M ${center.x} ${center.y} L ${gates[i].x} ${gates[i].y} L ${p.x} ${p.y}`
|
||||
);
|
||||
|
||||
// Paths for local replication (center <-> owned pods)
|
||||
const localPaths = owned.map((o) => line(center, o));
|
||||
|
||||
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 + GLOW */}
|
||||
<defs>
|
||||
<pattern id="dc-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.35" />
|
||||
</pattern>
|
||||
<filter id="dc-glow">
|
||||
<feGaussianBlur stdDeviation="3" result="b"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="b"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
<radialGradient id="pod-cyan" 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(#dc-grid)" />
|
||||
|
||||
{/* ====== BASELINES ====== */}
|
||||
|
||||
{/* Local control links (owned replicas) */}
|
||||
{localPaths.map((d, i) => (
|
||||
<motion.path
|
||||
key={`local-${i}`}
|
||||
d={d}
|
||||
stroke="#1e1e1e"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.3 }}
|
||||
animate={{ pathLength: 1, opacity: 0.6 }}
|
||||
transition={{ duration: 0.7, delay: 0.05 * i, ease: [0.22,1,0.36,1] }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Outbound paths (to peers) */}
|
||||
{sharePaths.map((d, i) => (
|
||||
<motion.path
|
||||
key={`share-base-${i}`}
|
||||
d={d}
|
||||
stroke="#2a2a2a"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.25 }}
|
||||
animate={{ pathLength: 1, opacity: 0.5 }}
|
||||
transition={{ duration: 0.8, delay: 0.1 * i, ease: [0.22,1,0.36,1] }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Cyan dash overlays to show active/allowed routes */}
|
||||
{localPaths.map((d, i) => (
|
||||
<motion.path
|
||||
key={`local-accent-${i}`}
|
||||
d={d}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 8"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.9 }}
|
||||
animate={{ pathLength: 1, opacity: 0.9 }}
|
||||
transition={{ duration: 0.7, delay: 0.15 * i }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
))}
|
||||
{sharePaths.map((d, i) => (
|
||||
<motion.path
|
||||
key={`share-accent-${i}`}
|
||||
d={d}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 8"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.85 }}
|
||||
animate={{ pathLength: 1, opacity: [0.85, 0.6, 0.85] }}
|
||||
transition={{ duration: 0.9, delay: 0.2 * i, repeat: Infinity, repeatType: "reverse" }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ====== MOVING DATA (Packets) ====== */}
|
||||
{!prefers && (
|
||||
<>
|
||||
{/* Local replication packets (bi-directional) */}
|
||||
{localPaths.map((d, i) => (
|
||||
<motion.circle
|
||||
key={`localpkt-${i}`}
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${d}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0.2 }}
|
||||
animate={{ offsetDistance: ["0%", "100%"], opacity: [0.2, 1, 0.2] }}
|
||||
transition={{ duration: 2.2, delay: i * 0.2, repeat: Infinity, ease: "linear" }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Outbound share packets: center -> gate -> peer */}
|
||||
{sharePaths.map((d, i) => (
|
||||
<motion.circle
|
||||
key={`sharepkt-${i}`}
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${d}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{ offsetDistance: ["0%", "100%"], opacity: [0, 1, 0] }}
|
||||
transition={{ duration: 2.8, delay: 0.4 * i, repeat: Infinity, ease: "linear" }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* ====== GATES (permission points) ====== */}
|
||||
{gates.map((g, i) => (
|
||||
<g key={`gate-${i}`}>
|
||||
<circle cx={g.x} cy={g.y} r={12} fill="#0f0f0f" stroke="#232323" strokeWidth={2} />
|
||||
{!prefers && (
|
||||
<motion.circle
|
||||
cx={g.x} cy={g.y} r={8} fill="none" stroke={accent} strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: [0, 1, 0] }}
|
||||
transition={{ duration: 1.6, delay: 0.4 * i, repeat: Infinity, ease: [0.22,1,0.36,1] }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
)}
|
||||
{/* tiny "lock" glyph */}
|
||||
<path d={`M ${g.x-3} ${g.y+2} h 6 v 4 h -6 Z`} fill="#1d1d1d" stroke="#2b2b2b" strokeWidth={1}/>
|
||||
<path d={`M ${g.x-2} ${g.y+2} v -3 a 3 3 0 0 1 6 0 v 3`} fill="none" stroke="#2b2b2b" strokeWidth={1}/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
{/* ====== PODS ====== */}
|
||||
|
||||
{/* Center (Your Pod) */}
|
||||
<g>
|
||||
<ellipse cx={center.x} cy={center.y} rx={52} ry={34} fill="url(#pod-cyan)" stroke="#1f1f1f" strokeWidth={2} filter="url(#dc-glow)" />
|
||||
<ellipse cx={center.x} cy={center.y} rx={26} ry={18} fill="#0f0f0f" stroke="#232323" strokeWidth={2} />
|
||||
{!prefers && (
|
||||
<motion.ellipse
|
||||
cx={center.x} cy={center.y} rx={38} ry={26}
|
||||
fill="none" stroke={accent} strokeWidth={2} opacity={0.5}
|
||||
animate={{ scale: [1, 1.06, 1], opacity: [0.35, 0.7, 0.35] }}
|
||||
transition={{ duration: 2.2, repeat: Infinity, ease: [0.22,1,0.36,1] }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
|
||||
{/* Owned pods (left-bottom cluster) */}
|
||||
{owned.map((p, i) => (
|
||||
<g key={`owned-${i}`}>
|
||||
<ellipse cx={p.x} cy={p.y} rx={34} ry={22} fill="#0f0f0f" stroke="#232323" strokeWidth={2} />
|
||||
<ellipse cx={p.x} cy={p.y} rx={16} ry={11} fill="#121212" stroke="#272727" strokeWidth={1.5} />
|
||||
</g>
|
||||
))}
|
||||
|
||||
{/* External peers (top-right cluster) */}
|
||||
{peers.map((p, i) => (
|
||||
<g key={`peer-${i}`}>
|
||||
<ellipse cx={p.x} cy={p.y} rx={32} ry={21} fill="#0f0f0f" stroke="#232323" strokeWidth={2} />
|
||||
{/* subtle cyan rim = marked as “shared” endpoints */}
|
||||
<motion.ellipse
|
||||
cx={p.x} cy={p.y} rx={27} ry={18}
|
||||
fill="none" stroke={accent} strokeWidth={1.8} opacity={0.35}
|
||||
animate={!prefers ? { opacity: [0.2, 0.5, 0.2] } : { opacity: 0.35 }}
|
||||
transition={{ duration: 2.4, delay: i * 0.2, repeat: prefers ? 0 : Infinity }}
|
||||
filter="url(#dc-glow)"
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
"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 (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Deterministic orchestration: predictable deployments"
|
||||
style={{ background: "transparent" }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
|
||||
{/* BACKGROUND GRID */}
|
||||
<defs>
|
||||
<pattern id="grid-orch" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28"
|
||||
fill="none"
|
||||
stroke={gridStroke}
|
||||
strokeWidth="1"
|
||||
opacity="0.45"
|
||||
/>
|
||||
</pattern>
|
||||
|
||||
{/* Soft glow for highlight */}
|
||||
<filter id="orch-glow">
|
||||
<feGaussianBlur stdDeviation="4" result="blur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-orch)" />
|
||||
|
||||
{/* STAGE BOXES */}
|
||||
{stages.map((s, i) => (
|
||||
<motion.rect
|
||||
key={`stage-${i}`}
|
||||
x={s.x}
|
||||
y={s.y}
|
||||
width={s.w}
|
||||
height={s.h}
|
||||
rx={14}
|
||||
fill="#0d0d0d"
|
||||
stroke="#1a1a1a"
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
transition={{ duration: 0.6 + i * 0.1 }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Stage labels (subtle, not text-heavy) */}
|
||||
{stages.map((s, i) => (
|
||||
<motion.text
|
||||
key={`label-${i}`}
|
||||
x={s.x + s.w / 2}
|
||||
y={s.y + s.h / 2 + 6}
|
||||
fill="#9ca3af"
|
||||
textAnchor="middle"
|
||||
fontSize="14"
|
||||
fontFamily="Inter, sans-serif"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
transition={{ delay: 0.1 * i, duration: 0.6 }}
|
||||
>
|
||||
{s.label}
|
||||
</motion.text>
|
||||
))}
|
||||
|
||||
{/* CONSISTENT ORCHESTRATION LINES */}
|
||||
{arrows.map((d, i) => (
|
||||
<motion.path
|
||||
key={`arrow-${i}`}
|
||||
d={d}
|
||||
stroke="#3a3a3a"
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.8 }}
|
||||
transition={{ delay: 0.15 * i, duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* CYAN ACCENT OVERLAY ON LINES (predictable updates) */}
|
||||
{arrows.map((d, i) => (
|
||||
<motion.path
|
||||
key={`arrow-accent-${i}`}
|
||||
d={d}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
strokeDasharray="10"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{
|
||||
delay: 0.25 * i,
|
||||
duration: 0.8,
|
||||
ease: [0.22, 1, 0.36, 1]
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* MOVING PACKET SHOWING DETERMINISTIC FLOW */}
|
||||
{!prefers && (
|
||||
<motion.circle
|
||||
r={6}
|
||||
fill={accent}
|
||||
filter="url(#orch-glow)"
|
||||
style={{
|
||||
offsetPath: `path('${packetPath}')`,
|
||||
}}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0.2, 1, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.3,
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* FINAL CONFIRMATION PULSE AT DEPLOY STAGE */}
|
||||
{!prefers && (
|
||||
<motion.circle
|
||||
cx={stages[2].x + stages[2].w / 2}
|
||||
cy={stages[2].y + stages[2].h / 2}
|
||||
r={24}
|
||||
fill={accent}
|
||||
opacity={0.1}
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{ scale: [1, 1.15, 1], opacity: [0.05, 0.3, 0.05] }}
|
||||
transition={{
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
filter="url(#orch-glow)"
|
||||
/>
|
||||
)}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
"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 (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Mesh networking topology"
|
||||
style={{ background: "transparent" }} // ✅ transparent background
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
|
||||
{/* ✅ Subtle dark grid */}
|
||||
<defs>
|
||||
<pattern id="mesh-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke="#2b2a2a" strokeWidth="1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#mesh-grid)" />
|
||||
|
||||
{/* ✅ Gray baseline mesh connections */}
|
||||
{links.map(([i, j], idx) => (
|
||||
<motion.path
|
||||
key={`base-${idx}`}
|
||||
d={drawLine(i, j)}
|
||||
stroke={stroke}
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay: 0.05 * idx,
|
||||
duration: 0.6,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ✅ Cyan signal traveling across mesh diagonally */}
|
||||
{!prefersReduced &&
|
||||
links.map(([i, j], idx) => {
|
||||
const path = drawLine(i, j);
|
||||
return (
|
||||
<motion.circle
|
||||
key={`signal-${idx}`}
|
||||
r={4}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${path}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay: idx * 0.15,
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* ✅ Nodes with soft glow */}
|
||||
{nodes.map((n, idx) => (
|
||||
<g key={`node-${idx}`}>
|
||||
<motion.circle
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
r={18}
|
||||
fill="#0d0d0d"
|
||||
stroke="#111"
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.7 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
/>
|
||||
<motion.circle
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
r={10}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: prefersReduced ? 1 : [1, 1.08, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.6,
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -9,23 +9,22 @@ type Props = {
|
||||
gridStroke?: string;
|
||||
};
|
||||
|
||||
// ▸ NEW: Cropped dimensions
|
||||
const W = 760;
|
||||
const H = 300; // was 420
|
||||
const H = 420;
|
||||
|
||||
export default function PodsFlow({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
gridStroke = "#3a3a3a", // lighter grid stroke
|
||||
gridStroke = "#2b2a2a",
|
||||
}: Props) {
|
||||
const pods = [
|
||||
{ x: 100, y: 120, label: "Pod 1" }, // moved slightly up for new height
|
||||
{ x: 260, y: 120, label: "Pod 2" },
|
||||
{ x: 420, y: 120, label: "Pod 3" },
|
||||
{ x: 580, y: 120, label: "Pod 4" },
|
||||
{ x: 100, y: 180, label: "Pod 1" },
|
||||
{ x: 260, y: 180, label: "Pod 2" },
|
||||
{ x: 420, y: 180, label: "Pod 3" },
|
||||
{ x: 580, y: 180, label: "Pod 4" },
|
||||
];
|
||||
|
||||
// Pulse path (unchanged)
|
||||
// Pulse path
|
||||
const path = `
|
||||
M ${pods[0].x + 80} ${pods[0].y + 40}
|
||||
L ${pods[1].x - 10} ${pods[1].y + 40}
|
||||
@@ -35,7 +34,7 @@ export default function PodsFlow({
|
||||
L ${pods[3].x - 10} ${pods[3].y + 40}
|
||||
`;
|
||||
|
||||
// Line segments
|
||||
// Arrow segments
|
||||
const arrows = [
|
||||
{
|
||||
d: `M ${pods[0].x + 80} ${pods[0].y + 40} L ${pods[1].x - 6} ${pods[1].y + 40}`,
|
||||
@@ -55,13 +54,15 @@ export default function PodsFlow({
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Pod-to-Pod signal transfer animation"
|
||||
style={{ background: "transparent" }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
{/* GRID BACKGROUND */}
|
||||
{/* GRID BG */}
|
||||
<defs>
|
||||
<pattern id="pods-grid" width="26" height="26" patternUnits="userSpaceOnUse">
|
||||
<path d="M 26 0 L 0 0 0 26" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.9" />
|
||||
<pattern id="pods-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.6" />
|
||||
</pattern>
|
||||
|
||||
<filter id="pods-glow">
|
||||
@@ -84,8 +85,8 @@ export default function PodsFlow({
|
||||
width={80}
|
||||
height={80}
|
||||
rx={14}
|
||||
fill="#0f0f0f"
|
||||
stroke="#fff" // brighter stroke
|
||||
fill="#0d0d0d"
|
||||
stroke="#1a1a1a"
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
@@ -93,7 +94,7 @@ export default function PodsFlow({
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* POD TEXT */}
|
||||
{/* POD LABELS */}
|
||||
{pods.map((p, i) => (
|
||||
<motion.text
|
||||
key={i}
|
||||
@@ -102,26 +103,26 @@ export default function PodsFlow({
|
||||
textAnchor="middle"
|
||||
fontSize="14"
|
||||
fontFamily="Inter, sans-serif"
|
||||
fill="#fff" // lighter text
|
||||
fill="#9ca3af"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.95 }}
|
||||
transition={{ delay: 0.1 + i * 0.1 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
transition={{ delay: 0.1 + i * 0.1, duration: 0.6 }}
|
||||
>
|
||||
{p.label}
|
||||
</motion.text>
|
||||
))}
|
||||
|
||||
{/* GREY LINES (lighter) */}
|
||||
{/* GREY LINES */}
|
||||
{arrows.map((a, i) => (
|
||||
<motion.path
|
||||
key={`grey-${i}`}
|
||||
d={a.d}
|
||||
stroke="#444" // lighter grey
|
||||
stroke="#333"
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.9 }}
|
||||
animate={{ pathLength: 1, opacity: 0.8 }}
|
||||
transition={{ delay: 0.2 * i, duration: 0.7 }}
|
||||
/>
|
||||
))}
|
||||
@@ -142,7 +143,7 @@ export default function PodsFlow({
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ENDPOINT PULSES */}
|
||||
{/* NEW: CYAN ENDPOINT PULSES */}
|
||||
{arrows.map((a, i) => (
|
||||
<motion.circle
|
||||
key={`endpoint-${i}`}
|
||||
@@ -150,21 +151,23 @@ export default function PodsFlow({
|
||||
cy={a.end.y}
|
||||
r={10}
|
||||
fill={accent}
|
||||
opacity={0.16} // slightly stronger
|
||||
opacity={0.12}
|
||||
filter="url(#pods-glow)"
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{
|
||||
scale: [1, 1.2, 1],
|
||||
opacity: [0.08, 0.28, 0.08],
|
||||
opacity: [0.05, 0.25, 0.05],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.4,
|
||||
duration: 1.5,
|
||||
delay: i * 0.2,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* PULSE DOT */}
|
||||
{/* MAIN MOVING CYAN PULSE */}
|
||||
<motion.circle
|
||||
r={8}
|
||||
fill={accent}
|
||||
@@ -175,10 +178,10 @@ export default function PodsFlow({
|
||||
initial={{ offsetDistance: "0%", opacity: 0.4 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0.5, 1, 0.5],
|
||||
opacity: [0.4, 1, 0.4],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.4,
|
||||
duration: 2.6,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string; // cyan
|
||||
danger?: string; // failing node color
|
||||
bg?: string; // dark background
|
||||
gridStroke?: string; // grid
|
||||
};
|
||||
|
||||
const W = 720;
|
||||
const H = 540;
|
||||
|
||||
export default function Resilience({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
danger = "#ff4d4d",
|
||||
bg = "#0b0b0b",
|
||||
gridStroke = "#2b2a2a",
|
||||
}: Props) {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
// Primary healthy nodes
|
||||
const nodes = [
|
||||
{ x: 220, y: 180 },
|
||||
{ x: 500, y: 180 },
|
||||
{ x: 580, y: 330 },
|
||||
{ x: 360, y: 420 },
|
||||
{ x: 160, y: 330 },
|
||||
];
|
||||
|
||||
// One failing node
|
||||
const failing = { x: 360, y: 100 };
|
||||
|
||||
// Full mesh except failing node
|
||||
const links: [number, number][] = [];
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
for (let j = i + 1; j < nodes.length; j++) {
|
||||
links.push([i, j]);
|
||||
}
|
||||
}
|
||||
|
||||
// Paths for fallback reroute (detour link when failing node disappears)
|
||||
const reroute = `M ${nodes[0].x} ${nodes[0].y}
|
||||
L ${nodes[3].x} ${nodes[3].y}
|
||||
L ${nodes[2].x} ${nodes[2].y}`;
|
||||
|
||||
const line = (i: number, j: number) =>
|
||||
`M ${nodes[i].x} ${nodes[i].y} L ${nodes[j].x} ${nodes[j].y}`;
|
||||
|
||||
const failRay = `M ${failing.x} ${failing.y} L ${nodes[1].x} ${nodes[1].y}`;
|
||||
|
||||
return (
|
||||
<div className={clsx("relative overflow-hidden", className)}>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full" style={{ background: bg }}>
|
||||
{/* ===== GRID + FILTERS ===== */}
|
||||
<defs>
|
||||
<pattern id="res-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="res-glow">
|
||||
<feGaussianBlur stdDeviation="3" result="b" />
|
||||
<feMerge>
|
||||
<feMergeNode in="b" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<radialGradient id="res-pod" 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(#res-grid)" />
|
||||
|
||||
{/* ===== BASE LINKS (Healthy Mesh) ===== */}
|
||||
{links.map(([i, j], idx) => (
|
||||
<motion.path
|
||||
key={`mesh-${idx}`}
|
||||
d={line(i, j)}
|
||||
fill="none"
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.25 }}
|
||||
animate={{ pathLength: 1, opacity: 0.55 }}
|
||||
transition={{ duration: 0.6, delay: idx * 0.04 }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ===== FAILING NODE LINK (Fading Out) ===== */}
|
||||
<motion.path
|
||||
d={failRay}
|
||||
fill="none"
|
||||
stroke={danger}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 8"
|
||||
initial={{ pathLength: 0, opacity: 0.8 }}
|
||||
animate={{ pathLength: 1, opacity: [0.8, 0.15, 0.8] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity, repeatType: "reverse" }}
|
||||
filter="url(#res-glow)"
|
||||
/>
|
||||
|
||||
{/* ===== ACTIVE REROUTE (Compensating Path) ===== */}
|
||||
<motion.path
|
||||
d={reroute}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="12 10"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{
|
||||
opacity: [0, 1, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
filter="url(#res-glow)"
|
||||
/>
|
||||
|
||||
{/* ===== MOVING PACKETS ON HEALTHY LINKS ===== */}
|
||||
{!prefers &&
|
||||
links.map(([i, j], idx) => (
|
||||
<motion.circle
|
||||
key={`pkt-${idx}`}
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${line(i, j)}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{ offsetDistance: ["0%", "100%"], opacity: [0.15, 1, 0.15] }}
|
||||
transition={{
|
||||
duration: 2.4,
|
||||
delay: idx * 0.15,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
filter="url(#res-glow)"
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* ===== PACKETS ON REROUTE WHEN FAILURE OCCURS ===== */}
|
||||
{!prefers && (
|
||||
<motion.circle
|
||||
r={6}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${reroute}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0.2, 1, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.8,
|
||||
repeat: Infinity,
|
||||
ease: "linear",
|
||||
}}
|
||||
filter="url(#res-glow)"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* ===== HEALTHY NODES ===== */}
|
||||
{nodes.map((n, i) => (
|
||||
<g key={`node-${i}`}>
|
||||
{/* Aura */}
|
||||
{!prefers && (
|
||||
<motion.ellipse
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
rx={40}
|
||||
ry={26}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
opacity={0.18}
|
||||
animate={{
|
||||
scale: [1, 1.07, 1],
|
||||
opacity: [0.1, 0.25, 0.1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.2,
|
||||
delay: i * 0.1,
|
||||
repeat: Infinity,
|
||||
}}
|
||||
filter="url(#res-glow)"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Pod body */}
|
||||
<ellipse
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
rx={34}
|
||||
ry={22}
|
||||
fill="url(#res-pod)"
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
|
||||
{/* Core */}
|
||||
<ellipse
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
rx={16}
|
||||
ry={11}
|
||||
fill="#0f0f0f"
|
||||
stroke="#272727"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
{/* ===== FAILING NODE (Flickering / Disconnecting) ===== */}
|
||||
<g>
|
||||
<ellipse
|
||||
cx={failing.x}
|
||||
cy={failing.y}
|
||||
rx={30}
|
||||
ry={20}
|
||||
fill="#1a1a1a"
|
||||
stroke="#3a3a3a"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
|
||||
{!prefers && (
|
||||
<motion.ellipse
|
||||
cx={failing.x}
|
||||
cy={failing.y}
|
||||
rx={18}
|
||||
ry={12}
|
||||
fill={danger}
|
||||
opacity={0.5}
|
||||
animate={{ opacity: [0.5, 0.1, 0.5] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
"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 (
|
||||
<g>
|
||||
{/* chassis */}
|
||||
<rect x={x} y={y} width={w} height={h} rx={12} fill="#0d0d0d" stroke="#1a1a1a" strokeWidth={2} />
|
||||
{/* bays */}
|
||||
{Array.from({ length: rows }).map((_, i) => (
|
||||
<g key={i}>
|
||||
<rect
|
||||
x={x + 12}
|
||||
y={y + 12 + i * rowH}
|
||||
width={w - 24}
|
||||
height={rowH - 10}
|
||||
rx={8}
|
||||
fill="#111111"
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
{/* bay indicators */}
|
||||
<rect x={x + 20} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#16a34a" opacity={0.8} />
|
||||
<rect x={x + 36} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#9ca3af" opacity={0.6} />
|
||||
<rect x={x + 52} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#9ca3af" opacity={0.6} />
|
||||
</g>
|
||||
))}
|
||||
{/* subtle top highlight */}
|
||||
<rect x={x + 2} y={y + 2} width={w - 4} height={10} rx={5} fill="#0f0f0f" />
|
||||
</g>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Sovereign compute: execution only on hardware you control"
|
||||
style={{ background: "transparent" }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
{/* GRID (transparent bg, subtle dark grid) */}
|
||||
<defs>
|
||||
<pattern id="grid-secure" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.45" />
|
||||
</pattern>
|
||||
|
||||
{/* soft glow filter for shield */}
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="6" result="coloredBlur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-secure)" />
|
||||
|
||||
{/* RACKS (hardware you control) */}
|
||||
<Server x={left.x} y={left.y} />
|
||||
<Server x={mid.x} y={mid.y} />
|
||||
<Server x={right.x} y={right.y} />
|
||||
|
||||
{/* BASELINES for attestation links */}
|
||||
{[pathFromLeft, pathFromMid, pathFromRight].map((d, i) => (
|
||||
<motion.path
|
||||
key={`base-${i}`}
|
||||
d={d}
|
||||
fill="none"
|
||||
stroke="#303030"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.6 }}
|
||||
transition={{ delay: 0.15 * i, duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* MOVING ATTESTATION TOKENS (signatures/hashes) */}
|
||||
{!prefers && [pathFromLeft, pathFromMid, pathFromRight].map((d, i) => (
|
||||
<motion.circle
|
||||
key={`token-${i}`}
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${d}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{ offsetDistance: ["0%", "100%"], opacity: [0, 1, 0] }}
|
||||
transition={{
|
||||
delay: 0.25 * i,
|
||||
duration: 2.0,
|
||||
repeat: Infinity,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* TRUST BOUNDARY + SHIELD (hardware attestation target) */}
|
||||
<motion.circle
|
||||
cx={shield.cx}
|
||||
cy={shield.cy}
|
||||
r={shield.r + 18}
|
||||
fill="none"
|
||||
stroke="#1f1f1f"
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
/>
|
||||
|
||||
{/* cyan halo */}
|
||||
{!prefers && (
|
||||
<motion.circle
|
||||
cx={shield.cx}
|
||||
cy={shield.cy}
|
||||
r={shield.r + 6}
|
||||
fill={accent}
|
||||
opacity={0.12}
|
||||
initial={{ scale: 0.95, opacity: 0 }}
|
||||
animate={{ scale: [1, 1.12, 1], opacity: [0.1, 0.35, 0.1] }}
|
||||
transition={{ duration: 1.8, repeat: Infinity, repeatType: "mirror", ease: [0.22, 1, 0.36, 1] }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Shield outline */}
|
||||
<motion.path
|
||||
d={`M ${shield.cx} ${shield.cy - 30}
|
||||
L ${shield.cx + 28} ${shield.cy - 15}
|
||||
L ${shield.cx + 22} ${shield.cy + 24}
|
||||
L ${shield.cx} ${shield.cy + 34}
|
||||
L ${shield.cx - 22} ${shield.cy + 24}
|
||||
L ${shield.cx - 28} ${shield.cy - 15}
|
||||
Z`}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
|
||||
{/* Check mark (verified hardware) */}
|
||||
<motion.path
|
||||
d={`M ${shield.cx - 14} ${shield.cy + 6} L ${shield.cx - 2} ${shield.cy + 18} L ${shield.cx + 18} ${shield.cy - 6}`}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 0.9, delay: 0.2, ease: [0.22, 1, 0.36, 1] }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
|
||||
{/* LOCKED EXECUTION BOUNDARY (subtle arc) */}
|
||||
<motion.path
|
||||
d={`M ${shield.cx - 70} ${shield.cy + 46} Q ${shield.cx} ${shield.cy + 76} ${shield.cx + 70} ${shield.cy + 46}`}
|
||||
fill="none"
|
||||
stroke="#2e2e2e"
|
||||
strokeWidth={2}
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.6 }}
|
||||
transition={{ duration: 0.8, delay: 0.3 }}
|
||||
/>
|
||||
|
||||
{/* Cyan confirmation pulses emanating out (execution allowed) */}
|
||||
{!prefers && [0, 1].map((i) => (
|
||||
<motion.circle
|
||||
key={`emit-${i}`}
|
||||
cx={shield.cx}
|
||||
cy={shield.cy}
|
||||
r={shield.r + 12}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: [0.0, 0.5, 0.0], scale: [1, 1.15, 1.3] }}
|
||||
transition={{ duration: 1.8, delay: i * 0.3, repeat: Infinity, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function StorageArchitecture() {
|
||||
</H3>
|
||||
<P className="mt-6 text-gray-400">
|
||||
A layered design that encrypts, routes, and exposes storage through
|
||||
multiple protocols without duplicating data or compromising
|
||||
multiple protocols — without duplicating data or compromising
|
||||
sovereignty.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function StorageUseCases() {
|
||||
<P className="mt-6 text-gray-600">
|
||||
Mycelium Storage adapts to compliance-driven enterprise data,
|
||||
distributed application workloads, and global asset delivery
|
||||
without giving up sovereignty.
|
||||
— without giving up sovereignty.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export function StorageUseCasesNew() {
|
||||
<H3>Built for Real Data Workloads</H3>
|
||||
<P className="max-w-3xl text-gray-600 mt-4">
|
||||
Mycelium Storage adapts to compliance-driven enterprise data, distributed application workloads,
|
||||
and global asset delivery without giving up sovereignty.
|
||||
and global asset delivery — without giving up sovereignty.
|
||||
</P>
|
||||
|
||||
{/* Tabs */}
|
||||
|
||||
@@ -4,11 +4,24 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
--animate-scroll: scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite;
|
||||
--animate-scroll-left: scroll-left var(--animation-duration, 40s) linear infinite;
|
||||
--animate-scroll-right: scroll-right var(--animation-duration, 40s) linear infinite;
|
||||
|
||||
@keyframes scroll {
|
||||
@keyframes scroll-left {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
transform: translate(calc(-50% - 0.5rem));
|
||||
transform: translateX(calc(-50% - 0.5rem));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scroll-right {
|
||||
from {
|
||||
transform: translateX(calc(-50% - 0.5rem));
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,17 +241,3 @@
|
||||
filter: drop-shadow(0 0 6px #90f6ff);
|
||||
transition: opacity 1s ease-out;
|
||||
}
|
||||
|
||||
@keyframes infinite-scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-infinite-scroll {
|
||||
animation: infinite-scroll var(--animation-duration, 40s) linear infinite;
|
||||
animation-direction: var(--animation-direction, forwards);
|
||||
}
|
||||
|
||||
@@ -42,15 +42,6 @@ export default {
|
||||
'glitch-1': 'glitch-1 1s infinite',
|
||||
'glitch-2': 'glitch-2 1s infinite',
|
||||
},
|
||||
infinite-scroll {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
||||
Reference in New Issue
Block a user