Compare commits
114 Commits
developmen
...
1ec9741283
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ec9741283 | |||
| 5d7272ff6c | |||
| c68b4e5de9 | |||
| c3b171e3b2 | |||
| ed995113df | |||
| 357b2f58c3 | |||
| 7d8ae1d26d | |||
| 3c4da26ecb | |||
| 023921a6cc | |||
| 8e033a3c21 | |||
| 01b1c20b60 | |||
| c962743737 | |||
| e62c4a5688 | |||
| 6b735b5a40 | |||
| 7b9cf135e4 | |||
| 544d7d541b | |||
| b21f874a05 | |||
| e6b194e8c7 | |||
| a8105a0551 | |||
| de5d990fc9 | |||
| 29d2b76db9 | |||
| a0beec808e | |||
| 96c445aa41 | |||
| ce4b4b3360 | |||
| c849c74a53 | |||
| 7bc895d8be | |||
| c0b84fd578 | |||
| 6d96ff9ea8 | |||
| f1e1721b25 | |||
| aed3e8ed25 | |||
| f126287018 | |||
| 9f1e78e116 | |||
| d469bb7b99 | |||
| 97fdf3aa4f | |||
| c1ca5e35f0 | |||
| e5ed88b676 | |||
| 6beb9c1432 | |||
| a9f53224cf | |||
| 3df73ec418 | |||
| c6efc29ab6 | |||
| bec52e7185 | |||
| 29f713cd2f | |||
| d8a7a7331a | |||
| 3f89d1c441 | |||
| fa30aeff43 | |||
| cbbc87cc29 | |||
| 8dfc46cabe | |||
| 00c5f7f880 | |||
| 2dbcda4997 | |||
| 979d151537 | |||
| 252e2335c2 | |||
| 9b2c406e9e | |||
| 791216433c | |||
| a4a6c95612 | |||
| da5cf2d4a2 | |||
| d2e2e87a0c | |||
| a8d91a1624 | |||
| f9ee299362 | |||
| 8509b80f47 | |||
| 7d6bbc2763 | |||
| 4cf6f63139 | |||
| ef9865862b | |||
| 8fdcf1777d | |||
| 503fe26303 | |||
| 39b20f30a4 | |||
| d53f2d943f | |||
| 3213179ff6 | |||
| c289c63856 | |||
| 6329d2dcac | |||
| 3604b47137 | |||
| fe0e231204 | |||
| da539a5e55 | |||
| b7e003a680 | |||
| 0324b2e8ad | |||
| 49d9c22de3 | |||
| 828fe93f46 | |||
| 8b04b668b6 | |||
| 618e1b0f5b | |||
| e46848b98d | |||
| 1fb08e83f2 | |||
| fb35ede79d | |||
| 1a34508699 | |||
| 97da7ba907 | |||
| 2b7559ab47 | |||
| 3ab559aa84 | |||
| 57c39a8b2b | |||
| 6ff539b3fc | |||
| 29e2d942de | |||
| def0972762 | |||
| 1c37cc08ee | |||
| 0eef2cd013 | |||
| 3cd41ab1d9 | |||
| 3121251272 | |||
| 56ceac1319 | |||
| a3028ff448 | |||
| 33821987aa | |||
| 678da4b66c | |||
| 326efc9fbd | |||
| 3a656ef5e9 | |||
| cf32cd081c | |||
| 1950342b7a | |||
| d1fc11ce80 | |||
| 48954151c9 | |||
| 61e368e27e | |||
| d8524ef181 | |||
| c44d9158f2 | |||
| 359afc3360 | |||
| 96a17a668a | |||
| c93974ea3e | |||
| f1f8f50871 | |||
| e4b2d66a76 | |||
| b208fe7f2a | |||
| 7b80ab84c9 | |||
| aa6f475050 |
1
google5dd3a8b700455c0e.html
Normal file
@@ -0,0 +1 @@
|
||||
google-site-verification: google5dd3a8b700455c0e.html
|
||||
@@ -4,8 +4,10 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Project Mycelium - Unleash the Power of Decentralized Networks</title>
|
||||
<meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." />
|
||||
<meta name="google-site-verification" content="rRrZkMEhdC4yFe_BrENEzYmy2bRfD-VE6RTRiDJNLkg" />
|
||||
<title>Project Mycelium - Built for Digital Sovereignty</title>
|
||||
<meta name="description" content="Discover Project Mycelium. A sovereign peer-to-peer network for private communication, storage, and compute. Build and run your digital environment on infrastructure you control." />
|
||||
<meta name="keywords" content="Project Mycelium, Mycelium, digital sovereignty, decentralized network, peer-to-peer infrastructure, private storage, secure compute, sovereign cloud, edge cloud" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/images/6b080b431e628e15126de0893db49e1a.webp
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
public/images/Modern Metallic Home Symbol.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/images/agent1.png
Normal file
|
After Width: | Height: | Size: 618 KiB |
BIN
public/images/ainode.png
Normal file
|
After Width: | Height: | Size: 508 KiB |
BIN
public/images/autonomous.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
public/images/bento-03-mobile-friendly.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/bento-cloud2.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
public/images/bento-network.jpg
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
public/images/cons.png
Normal file
|
After Width: | Height: | Size: 793 KiB |
BIN
public/images/dev.png
Normal file
|
After Width: | Height: | Size: 801 KiB |
BIN
public/images/edge.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
public/images/edgenode.png
Normal file
|
After Width: | Height: | Size: 792 KiB |
BIN
public/images/encryptednode.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
public/images/energy.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
public/images/fullstack.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
public/images/mainlogo.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/images/mobile/agents.jpg
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/images/mobile/cloudpage.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/mobile/homepage.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/images/mobile/nodes.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/images/mobile/pods.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/pod1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/images/podsimg.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/seekers.png
Normal file
|
After Width: | Height: | Size: 990 KiB |
BIN
public/images/uptime.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 156 KiB |
26
src/App.tsx
@@ -1,6 +1,6 @@
|
||||
import { HashRouter, Routes, Route } from 'react-router-dom';
|
||||
import { HashRouter, Routes, Route, useLocation } from 'react-router-dom';
|
||||
import { Layout } from './components/Layout';
|
||||
import { lazy, Suspense } from 'react';
|
||||
import { lazy, Suspense, useEffect } from 'react';
|
||||
|
||||
const HomePage = lazy(() => import('./pages/home/HomePage'));
|
||||
const CloudPage = lazy(() => import('./pages/cloud/CloudPage'));
|
||||
@@ -11,10 +11,31 @@ const ComputePage = lazy(() => import('./pages/compute/ComputePage'));
|
||||
const StoragePage = lazy(() => import('./pages/storage/StoragePage'));
|
||||
const GpuPage = lazy(() => import('./pages/gpu/GpuPage'));
|
||||
const PodsPage = lazy(() => import('./pages/pods/PodsPage'));
|
||||
const NodesPage = lazy(() => import('./pages/nodes/NodesPage'));
|
||||
|
||||
function ScrollToTop() {
|
||||
const { pathname, hash } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
if (hash) {
|
||||
const id = hash.replace('#', '');
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
||||
}, [pathname, hash]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<HashRouter>
|
||||
<ScrollToTop />
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Layout />}>
|
||||
@@ -27,6 +48,7 @@ function App() {
|
||||
<Route path="storage" element={<StoragePage />} />
|
||||
<Route path="gpu" element={<GpuPage />} />
|
||||
<Route path="pods" element={<PodsPage />} />
|
||||
<Route path="nodes" element={<NodesPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
||||
@@ -3,23 +3,30 @@ import clsx from 'clsx'
|
||||
|
||||
const baseStyles = {
|
||||
solid:
|
||||
'inline-flex justify-center rounded-full py-2 px-5 text-base font-semibold transition-colors',
|
||||
'inline-flex justify-center rounded-full py-2 px-5 text-sm md:text-base font-semibold transition-colors',
|
||||
outline:
|
||||
'inline-flex justify-center bg-transparent rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-base transition-colors',
|
||||
'inline-flex justify-center bg-transparent font-semibold rounded-full border border-2 py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-sm md:text-base transition-colors',
|
||||
link:
|
||||
'inline-flex items-center text-sm md:text-base font-semibold transition-colors',
|
||||
}
|
||||
|
||||
const variantStyles = {
|
||||
solid: {
|
||||
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors',
|
||||
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 hover:bg-cyan-400 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-500 active:text-white/80 before:transition-colors',
|
||||
white:
|
||||
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
|
||||
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
|
||||
green: 'bg-green-500 text-white hover:bg-green-600',
|
||||
},
|
||||
outline: {
|
||||
cyan: 'border-cyan-500 text-cyan-500',
|
||||
gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500',
|
||||
white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500',
|
||||
cyan: 'border-cyan-500 text-cyan-500 hover:border-cyan-400 hover:text-cyan-400 active:border-cyan-400',
|
||||
gray: 'border-gray-200 text-gray-600 hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400',
|
||||
white: 'border-gray-300 text-white hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400',
|
||||
},
|
||||
link: {
|
||||
cyan: 'text-cyan-400 underline hover:text-cyan-300',
|
||||
white: 'text-white underline hover:text-cyan-300',
|
||||
dark: 'text-gray-900 underline hover:text-cyan-400',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -30,7 +37,11 @@ type ButtonProps = (
|
||||
}
|
||||
| {
|
||||
variant: 'outline'
|
||||
color?: (keyof typeof variantStyles.outline) | 'cyan'
|
||||
color?: keyof typeof variantStyles.outline
|
||||
}
|
||||
| {
|
||||
variant: 'link'
|
||||
color?: keyof typeof variantStyles.link
|
||||
}
|
||||
) &
|
||||
(
|
||||
@@ -43,16 +54,33 @@ type ButtonProps = (
|
||||
)
|
||||
|
||||
export function Button({ className, as, ...props }: ButtonProps) {
|
||||
props.variant ??= 'solid'
|
||||
props.color ??= 'gray'
|
||||
// Set safe defaults per variant so color always matches a valid palette key
|
||||
if (!props.variant) {
|
||||
props.variant = 'solid'
|
||||
}
|
||||
|
||||
if (!props.color) {
|
||||
if (props.variant === 'solid') {
|
||||
props.color = 'gray'
|
||||
} else if (props.variant === 'outline') {
|
||||
props.color = 'gray'
|
||||
} else if (props.variant === 'link') {
|
||||
props.color = 'cyan'
|
||||
}
|
||||
}
|
||||
|
||||
const variant = props.variant
|
||||
const color = props.color as string
|
||||
|
||||
className = clsx(
|
||||
baseStyles[props.variant],
|
||||
props.variant === 'outline'
|
||||
? variantStyles.outline[props.color]
|
||||
: props.variant === 'solid'
|
||||
? variantStyles.solid[props.color]
|
||||
: undefined,
|
||||
baseStyles[variant],
|
||||
variant === 'outline'
|
||||
? variantStyles.outline[color as keyof typeof variantStyles.outline]
|
||||
: variant === 'solid'
|
||||
? variantStyles.solid[color as keyof typeof variantStyles.solid]
|
||||
: variant === 'link'
|
||||
? variantStyles.link[color as keyof typeof variantStyles.link]
|
||||
: undefined,
|
||||
className,
|
||||
)
|
||||
|
||||
|
||||
@@ -8,27 +8,27 @@ export function Footer() {
|
||||
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8">
|
||||
<div>
|
||||
<div className="flex items-center text-gray-900">
|
||||
<img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-15 w-15 flex-none" />
|
||||
<div className="ml-4">
|
||||
<p className="text-base font-semibold">Project Mycelium</p>
|
||||
<p className="mt-1 text-sm">Unleash the Power of Decentralization</p>
|
||||
<img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-20 w-20 flex-none" />
|
||||
<div className="">
|
||||
<p className="text-base lg:text-lg font-semibold">Project Mycelium</p>
|
||||
<p className="mt-1 text-sm">Built for Digital Sovereignty</p>
|
||||
</div>
|
||||
</div>
|
||||
<nav className="mt-10 flex gap-8">
|
||||
<Link to="/" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Home
|
||||
<Link to="/network" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Network
|
||||
</Link>
|
||||
<Link to="/cloud" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Cloud
|
||||
</Link>
|
||||
<Link to="/network" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Network
|
||||
<Link to="/pods" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Pods
|
||||
</Link>
|
||||
<Link to="/agents" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Agents
|
||||
</Link>
|
||||
<Link to="/pods" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Pods
|
||||
<Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
Nodes
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -49,11 +49,11 @@ export function Footer() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center border-t border-gray-100 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6">
|
||||
<div className="flex flex-col items-center border-t border-gray-100 py-8 md:flex-row-reverse md:justify-between md:pt-6">
|
||||
<p className="mt-6 text-sm text-gray-500 md:mt-0">
|
||||
© Copyright{' '}
|
||||
<a href="https://www.threefold.io" target="_blank" rel="noopener noreferrer" className="hover:text-cyan-500 transition-colors">
|
||||
ThreeFold
|
||||
<a href="https://ourworld.tf/" target="_blank" rel="noopener noreferrer" className="font-semibold hover:text-cyan-500 transition-colors">
|
||||
OurWorld
|
||||
</a>{' '}
|
||||
{new Date().getFullYear()}. All rights reserved.
|
||||
</p>
|
||||
|
||||
@@ -1,32 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { Dropdown } from './ui/Dropdown'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Container } from './Container'
|
||||
import { Button } from './Button'
|
||||
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||
import pmyceliumLogo from '../images/logos/mainlogo.svg'
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const cloudNavItems = [
|
||||
{ name: 'Cloud', href: '/cloud' },
|
||||
{ name: 'Compute', href: '/compute' },
|
||||
{ name: 'Storage', href: '/storage' },
|
||||
{ name: 'GPU', href: '/gpu' },
|
||||
]
|
||||
|
||||
export function Header() {
|
||||
const location = useLocation()
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
const getCurrentPageName = () => {
|
||||
const currentPath = location.pathname;
|
||||
if (currentPath.startsWith('/compute')) return 'Compute';
|
||||
if (currentPath.startsWith('/storage')) return 'Storage';
|
||||
if (currentPath.startsWith('/gpu')) return 'GPU';
|
||||
return 'Cloud';
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-white">
|
||||
<nav className="border-b border-gray-100">
|
||||
@@ -36,28 +18,24 @@ export function Header() {
|
||||
<img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<Dropdown
|
||||
buttonContent={
|
||||
<>
|
||||
{['Compute', 'Storage', 'GPU'].includes(getCurrentPageName()) ? (
|
||||
<>
|
||||
<span className="text-gray-500">Cloud {' >'} </span>
|
||||
<span>{getCurrentPageName()}</span>
|
||||
</>
|
||||
) : (
|
||||
'Cloud'
|
||||
)}
|
||||
<ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</>
|
||||
}
|
||||
items={cloudNavItems}
|
||||
/>
|
||||
<Link
|
||||
to="/network"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/cloud"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
@@ -65,10 +43,10 @@ export function Header() {
|
||||
Agents
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
to="/nodes"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Pods
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,16 +102,6 @@ export function Header() {
|
||||
<div className="mt-6 flow-root">
|
||||
<div className="-my-6 divide-y divide-gray-500/10">
|
||||
<div className="space-y-2 py-6">
|
||||
{cloudNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
to="/network"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
@@ -141,6 +109,20 @@ export function Header() {
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/cloud"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
@@ -149,11 +131,11 @@ export function Header() {
|
||||
Agents
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
to="/nodes"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Pods
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
<div className="py-6">
|
||||
|
||||
@@ -1,32 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { Dropdown } from './ui/Dropdown'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Container } from './Container'
|
||||
import { Button } from './Button'
|
||||
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||
import pmyceliumLogo from '../images/logos/mainlogo.svg'
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const cloudNavItems = [
|
||||
{ name: 'Cloud', href: '/cloud' },
|
||||
{ name: 'Compute', href: '/compute' },
|
||||
{ name: 'Storage', href: '/storage' },
|
||||
{ name: 'GPU', href: '/gpu' },
|
||||
]
|
||||
|
||||
export function HeaderDark() {
|
||||
const location = useLocation()
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
const getCurrentPageName = () => {
|
||||
const currentPath = location.pathname;
|
||||
if (currentPath.startsWith('/compute')) return 'Compute';
|
||||
if (currentPath.startsWith('/storage')) return 'Storage';
|
||||
if (currentPath.startsWith('/gpu')) return 'GPU';
|
||||
return 'Cloud';
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-[#111111]">
|
||||
<nav className="border-b border-gray-800">
|
||||
@@ -36,28 +18,24 @@ export function HeaderDark() {
|
||||
<img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<Dropdown
|
||||
buttonContent={
|
||||
<>
|
||||
{['Compute', 'Storage', 'GPU'].includes(getCurrentPageName()) ? (
|
||||
<>
|
||||
<span className="text-gray-400">Cloud {' >'} </span>
|
||||
<span className="text-white">{getCurrentPageName()}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="text-white">Cloud</span>
|
||||
)}
|
||||
<ChevronDownIcon className="h-5 w-5 text-white" aria-hidden="true" />
|
||||
</>
|
||||
}
|
||||
items={cloudNavItems}
|
||||
/>
|
||||
<Link
|
||||
to="/network"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/cloud"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
@@ -65,10 +43,10 @@ export function HeaderDark() {
|
||||
Agents
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
to="/nodes"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Pods
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,16 +102,6 @@ export function HeaderDark() {
|
||||
<div className="mt-6 flow-root">
|
||||
<div className="-my-6 divide-y divide-gray-500/20">
|
||||
<div className="space-y-2 py-6">
|
||||
{cloudNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
to="/network"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
@@ -141,6 +109,20 @@ export function HeaderDark() {
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/cloud"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
@@ -149,11 +131,11 @@ export function HeaderDark() {
|
||||
Agents
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
to="/nodes"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-white hover:bg-gray-800"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Pods
|
||||
Nodes
|
||||
</Link>
|
||||
</div>
|
||||
<div className="py-6">
|
||||
|
||||
@@ -1,8 +1,42 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import { Footer } from './Footer'
|
||||
import { Header } from './Header'
|
||||
|
||||
export function Layout() {
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
|
||||
if (document.getElementById('mailerlite-universal')) return
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.id = 'mailerlite-universal'
|
||||
script.innerHTML = `
|
||||
(function(m,a,i,l,e,r){
|
||||
m['MailerLiteObject']=e;
|
||||
function f(){
|
||||
var c={a:arguments,q:[]};
|
||||
var r=this.push(c);
|
||||
return "number"!=typeof r?r:f.bind(c.q);
|
||||
}
|
||||
f.q=f.q||[];
|
||||
m[e]=m[e]||f.bind(f.q);
|
||||
m[e].q=m[e].q||f.q;
|
||||
r=a.createElement(i);
|
||||
var _=a.getElementsByTagName(i)[0];
|
||||
r.async=1;
|
||||
r.src=l+'?v'+(~~(new Date().getTime()/1000000));
|
||||
_.parentNode.insertBefore(r,_);
|
||||
})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
|
||||
window.ml_account = ml('accounts', '1778010', 'x2d3d9f8n1', 'load');
|
||||
`
|
||||
|
||||
document.body.appendChild(script)
|
||||
|
||||
return () => {
|
||||
script.remove()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||
|
||||
@@ -12,8 +12,8 @@ const colorVariants = {
|
||||
primary: 'text-gray-900',
|
||||
secondary: 'text-gray-600',
|
||||
light: 'text-gray-50',
|
||||
accent: 'text-cyan-500',
|
||||
cyan: 'text-cyan-50',
|
||||
accent: 'text-cyan-400',
|
||||
cyan: 'text-cyan-400',
|
||||
white: 'text-white',
|
||||
dark: 'text-gray-950',
|
||||
tertiary: 'text-gray-700',
|
||||
@@ -162,5 +162,5 @@ export const DownloadCardDescription = createTextComponent(
|
||||
'text-base/7 leading-normal tracking-normal'
|
||||
)
|
||||
|
||||
export const CT = createTextComponent('span', 'text-base lg:text-lg font-medium')
|
||||
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-tight font-light')
|
||||
export const CT = createTextComponent('span', 'text-base lg:text-lg leading-normal font-medium')
|
||||
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-normal font-light')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
export const InfiniteMovingCards = ({
|
||||
items,
|
||||
direction = "left",
|
||||
speed = "fast",
|
||||
speed = "slow",
|
||||
pauseOnHover = true,
|
||||
className,
|
||||
}: {
|
||||
@@ -15,43 +15,39 @@ export const InfiniteMovingCards = ({
|
||||
speed?: "fast" | "normal" | "slow";
|
||||
pauseOnHover?: boolean;
|
||||
className?: string;
|
||||
}): JSX.Element => {
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
const scrollerRef = React.useRef<HTMLUListElement>(null);
|
||||
const [start, setStart] = useState(false);
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
getSpeed();
|
||||
setStart(true);
|
||||
}
|
||||
}, [getSpeed]);
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const scrollerRef = useRef<HTMLUListElement>(null);
|
||||
const [isReady, setIsReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
addAnimation();
|
||||
}, [addAnimation]);
|
||||
if (!scrollerRef.current) return;
|
||||
|
||||
const children = Array.from(scrollerRef.current.children);
|
||||
|
||||
// duplicate each item ONCE
|
||||
children.forEach((item) => {
|
||||
const clone = item.cloneNode(true);
|
||||
scrollerRef.current!.appendChild(clone);
|
||||
});
|
||||
|
||||
// set speed variable
|
||||
const duration =
|
||||
speed === "fast" ? "20s" : speed === "normal" ? "40s" : "80s";
|
||||
|
||||
containerRef.current?.style.setProperty(
|
||||
"--animation-duration",
|
||||
duration
|
||||
);
|
||||
|
||||
// set direction variable
|
||||
containerRef.current?.style.setProperty(
|
||||
"--animation-direction",
|
||||
direction === "left" ? "forwards" : "reverse"
|
||||
);
|
||||
|
||||
setIsReady(true);
|
||||
}, [direction, speed]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -61,16 +57,16 @@ export const InfiniteMovingCards = ({
|
||||
<ul
|
||||
ref={scrollerRef}
|
||||
className={cn(
|
||||
"flex min-w-full shrink-0 gap-16 py-4 w-max flex-nowrap",
|
||||
start && "animate-scroll",
|
||||
pauseOnHover && "hover:[animation-play-state:paused]",
|
||||
"flex w-max shrink-0 gap-16 py-4",
|
||||
isReady &&
|
||||
(direction === "left"
|
||||
? "animate-infinite-scroll"
|
||||
: "animate-infinite-scroll-right"),
|
||||
pauseOnHover && "hover:[animation-play-state:paused]"
|
||||
)}
|
||||
style={{
|
||||
"--animation-direction": direction === "left" ? "forwards" : "reverse",
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{items.map((item, idx) => (
|
||||
<li className="relative flex-shrink-0" key={idx}>
|
||||
{items.map((item, i) => (
|
||||
<li key={i} className="flex-shrink-0">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
|
||||
122
src/components/ui/DynamicMapContainer.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import WorldMap from './world-map';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// Interface for the simplified data passed to WorldMap
|
||||
interface GeoNode {
|
||||
lat: number;
|
||||
lng: number;
|
||||
label?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
// Interface for the raw data structure expected from the gridproxy API
|
||||
interface RawNode {
|
||||
node_id: number;
|
||||
location: {
|
||||
latitude: string; // API often returns these as strings
|
||||
longitude: string; // API often returns these as strings
|
||||
city: string;
|
||||
country: string;
|
||||
};
|
||||
// ... other raw fields you don't need
|
||||
}
|
||||
|
||||
const clusterNodes = (nodeList: GeoNode[], cellSize = 2) => {
|
||||
const buckets = new Map<
|
||||
string,
|
||||
{ latSum: number; lngSum: number; count: number }
|
||||
>();
|
||||
|
||||
nodeList.forEach((node) => {
|
||||
const latBucket = Math.round(node.lat / cellSize) * cellSize;
|
||||
const lngBucket = Math.round(node.lng / cellSize) * cellSize;
|
||||
const key = `${latBucket}|${lngBucket}`;
|
||||
|
||||
const bucket = buckets.get(key);
|
||||
if (bucket) {
|
||||
bucket.latSum += node.lat;
|
||||
bucket.lngSum += node.lng;
|
||||
bucket.count += 1;
|
||||
} else {
|
||||
buckets.set(key, {
|
||||
latSum: node.lat,
|
||||
lngSum: node.lng,
|
||||
count: 1,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(buckets.values()).map((bucket) => {
|
||||
const avgLat = bucket.latSum / bucket.count;
|
||||
const avgLng = bucket.lngSum / bucket.count;
|
||||
const count = bucket.count;
|
||||
|
||||
let color = "#06b6d4";
|
||||
if (count > 20) {
|
||||
color = "#0891b2";
|
||||
} else if (count > 5) {
|
||||
color = "#22d3ee";
|
||||
}
|
||||
|
||||
return {
|
||||
lat: avgLat,
|
||||
lng: avgLng,
|
||||
color,
|
||||
label: `${count} nodes`,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function DynamicMapContainer() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [nodes, setNodes] = useState<GeoNode[]>([]);
|
||||
const API_URL = "https://gridproxy.grid.tf/nodes?healthy=true&size=500";
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchNodeData() {
|
||||
try {
|
||||
const response = await fetch(API_URL);
|
||||
const data: RawNode[] = await response.json();
|
||||
|
||||
const geoNodes: GeoNode[] = data
|
||||
.filter((node: RawNode) => node.location && node.location.latitude && node.location.longitude)
|
||||
.map((node: RawNode) => ({
|
||||
lat: parseFloat(node.location.latitude),
|
||||
lng: parseFloat(node.location.longitude),
|
||||
label: `${node.location.city}, ${node.location.country} (${node.node_id})`,
|
||||
}));
|
||||
|
||||
const clusteredNodes = clusterNodes(geoNodes);
|
||||
setNodes(clusteredNodes);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch node data:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchNodeData();
|
||||
}, []);
|
||||
|
||||
// While fetching, show a loading state
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center w-full aspect-[2/1] bg-[#111111] rounded-lg text-cyan-500">
|
||||
<motion.span
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
||||
className="text-4xl"
|
||||
>
|
||||
🌎
|
||||
</motion.span>
|
||||
<p className="ml-4">Loading nodes...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// After data is loaded, render the map
|
||||
return <WorldMap nodes={nodes} />;
|
||||
}
|
||||
|
||||
export default DynamicMapContainer;
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { motion } from "framer-motion";
|
||||
import DottedMap from "dotted-map";
|
||||
|
||||
interface MapProps {
|
||||
@@ -9,34 +9,39 @@ interface MapProps {
|
||||
start: { lat: number; lng: number; label?: string };
|
||||
end: { lat: number; lng: number; label?: string };
|
||||
}>;
|
||||
// New prop for dynamic standalone nodes
|
||||
nodes?: Array<{ lat: number; lng: number; label?: string; color?: string }>;
|
||||
lineColor?: string;
|
||||
}
|
||||
|
||||
export default function WorldMap({
|
||||
dots = [],
|
||||
lineColor = "#06b6d4", // cyan-500
|
||||
nodes = [],
|
||||
lineColor = "#06b6d4",
|
||||
}: MapProps) {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
|
||||
// ✅ Force dark-dotted map theme
|
||||
// ✅ Force dark-dotted map theme
|
||||
const map = new DottedMap({ height: 100, grid: "diagonal" });
|
||||
const svgMap = map.getSVG({
|
||||
radius: 0.22,
|
||||
color: "#06b6d480", // cyan-500 at 50% opacity
|
||||
color: "#06b6d480",
|
||||
shape: "circle",
|
||||
backgroundColor: "#111111",
|
||||
});
|
||||
|
||||
// ✅ Point projection stays the same
|
||||
// ✅ Point projection stays the same
|
||||
// Projects lat/lng to the SVG's 800x400 viewBox coordinates
|
||||
const projectPoint = (lat: number, lng: number) => {
|
||||
const x = (lng + 180) * (800 / 360);
|
||||
const y = (90 - lat) * (400 / 180);
|
||||
const y = (90 - lat) * (400 / 180) + 45;
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
const createCurvedPath = (start: any, end: any) => {
|
||||
const midX = (start.x + end.x) / 2;
|
||||
const midY = Math.min(start.y, end.y) - 50;
|
||||
// Creates an arc that bows upward by 50 units
|
||||
const midY = Math.min(start.y, end.y) - 50;
|
||||
return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;
|
||||
};
|
||||
|
||||
@@ -49,13 +54,53 @@ export default function WorldMap({
|
||||
draggable={false}
|
||||
/>
|
||||
|
||||
{/* ✅ Lines + points */}
|
||||
{/* ✅ Lines + points + new standalone nodes */}
|
||||
<svg
|
||||
ref={svgRef}
|
||||
viewBox="0 0 800 400"
|
||||
className="w-full h-full absolute inset-0 pointer-events-none select-none"
|
||||
>
|
||||
{/* ✅ animated curved travel lines */}
|
||||
{/* Glowing path gradient DEFS */}
|
||||
<defs>
|
||||
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="black" stopOpacity="0" />
|
||||
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
|
||||
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
|
||||
<stop offset="100%" stopColor="black" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{/* ✅ DYNAMIC STANDALONE NODE DOTS (New Section) */}
|
||||
{nodes.map((node, i) => {
|
||||
const p = projectPoint(node.lat, node.lng);
|
||||
const dotColor = node.color || lineColor;
|
||||
|
||||
return (
|
||||
<g key={`node-${i}`}>
|
||||
{/* Outer pulsing circle */}
|
||||
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} opacity="0.5">
|
||||
<animate
|
||||
attributeName="r"
|
||||
from="2"
|
||||
to="7"
|
||||
dur="1.4s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
from="0.6"
|
||||
to="0"
|
||||
dur="1.4s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</circle>
|
||||
{/* Inner fixed circle */}
|
||||
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} />
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* ✅ Animated curved travel lines (Existing Logic) */}
|
||||
{dots.map((dot, i) => {
|
||||
const startPoint = projectPoint(dot.start.lat, dot.start.lng);
|
||||
const endPoint = projectPoint(dot.end.lat, dot.end.lng);
|
||||
@@ -78,17 +123,7 @@ export default function WorldMap({
|
||||
);
|
||||
})}
|
||||
|
||||
{/* ✅ glowing path gradient */}
|
||||
<defs>
|
||||
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="black" stopOpacity="0" />
|
||||
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
|
||||
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
|
||||
<stop offset="100%" stopColor="black" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
{/* ✅ start & end points with pulsing cyan glow */}
|
||||
{/* ✅ Start & end points with pulsing cyan glow (Existing Logic) */}
|
||||
{dots.map((dot, i) => {
|
||||
const s = projectPoint(dot.start.lat, dot.start.lng);
|
||||
const e = projectPoint(dot.end.lat, dot.end.lng);
|
||||
@@ -122,4 +157,4 @@ export default function WorldMap({
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
1
src/images/logos/logomark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="77" zoomAndPan="magnify" viewBox="0 0 57.75 54" height="72" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="6b820d2194"><path d="M 0.402344 0 L 57.101562 0 L 57.101562 6 L 0.402344 6 Z M 0.402344 0 " clip-rule="nonzero"/></clipPath><clipPath id="3794aac157"><path d="M 0.402344 23 L 57 23 L 57 30 L 0.402344 30 Z M 0.402344 23 " clip-rule="nonzero"/></clipPath><clipPath id="a8068b094c"><path d="M 0.402344 46 L 57.101562 46 L 57.101562 53 L 0.402344 53 Z M 0.402344 46 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#6b820d2194)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 0.00000196364)" fill="none" stroke-linejoin="miter" d="M 0.000096737 3.999805 L 76.537522 3.999805 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><g clip-path="url(#3794aac157)"><path fill="#43d7ff" d="M 0.402344 23.136719 L 35.746094 23.136719 L 35.746094 29.027344 L 0.402344 29.027344 M 41.636719 23.136719 L 56.761719 23.136719 L 56.761719 29.027344 L 41.636719 29.027344 " fill-opacity="1" fill-rule="nonzero"/></g><g clip-path="url(#a8068b094c)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 46.951043)" fill="none" stroke-linejoin="miter" d="M 0.000096737 4.002635 L 76.537522 4.002635 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
src/images/logos/mainlogo.svg
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
@@ -1,15 +1,32 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import AgentCoordination from "./animations/AgentCoordination";
|
||||
import DeterministicExecution from "./animations/DeterministicExecution";
|
||||
import Fungistor from "./animations/Fungistor";
|
||||
import Herodb from "./animations/Herodb";
|
||||
import MOSSandboxes from "./animations/MOSSandboxes";
|
||||
import MyceliumMesh from "./animations/MyceliumMesh";
|
||||
|
||||
const bentos = [
|
||||
const bentos: {
|
||||
id: string;
|
||||
eyebrow?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
description: string;
|
||||
animation: React.ComponentType | null;
|
||||
colSpan: string;
|
||||
rowSpan: string;
|
||||
custom?: boolean;
|
||||
noBorder?: boolean;
|
||||
}[] = [
|
||||
{
|
||||
id: "core",
|
||||
eyebrow: "ARCHITECTURE",
|
||||
title: "Deterministic by Design",
|
||||
title: "Intelligence Fabric",
|
||||
description:
|
||||
"Every workload runs exactly as declared: no drift, no hidden state, no surprises.",
|
||||
video: null,
|
||||
"The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.",
|
||||
animation: null,
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
custom: true,
|
||||
@@ -23,7 +40,7 @@ const bentos = [
|
||||
subtitle: "Long-Term AI Memory",
|
||||
description:
|
||||
"Erasure coding + compression slash storage bloat by up to 10× vs basic replication. Source-encrypted shards are geo-dispersed—lose pieces, rebuild perfectly from a quorum.",
|
||||
video: "/videos/fungistor.mp4",
|
||||
animation: Fungistor,
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -33,7 +50,7 @@ const bentos = [
|
||||
subtitle: "Active AI Memory",
|
||||
description:
|
||||
"Multimodal vector+keyword retrieval makes RAG feel instant across text, image, audio. Time-aware, policy-guarded context keeps results fresh while access stays governed.",
|
||||
video: "/videos/herodb.mp4",
|
||||
animation: Herodb,
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -43,7 +60,7 @@ const bentos = [
|
||||
subtitle: "Secure Agent Workspaces",
|
||||
description:
|
||||
"Attested, signed workspaces spin up ≈5s worldwide—ready to execute. Hardware isolation and scoped egress: run hard, tear down clean, zero residue.",
|
||||
video: "/videos/herodb.mp4",
|
||||
animation: MOSSandboxes,
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -53,7 +70,7 @@ const bentos = [
|
||||
subtitle: "Secure Communication Network",
|
||||
description:
|
||||
"A private, public-key fabric with self-healing multi-path routing. Glides through NATs and firewalls—direct, low-latency, no middlemen.",
|
||||
video: "/videos/mesh.mp4",
|
||||
animation: MyceliumMesh,
|
||||
colSpan: "lg:col-span-2",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -63,7 +80,7 @@ const bentos = [
|
||||
subtitle: "Verifiable Code Execution",
|
||||
description:
|
||||
"Declare intent, get a hash; remote attestation proves that is what runs. Reproducible builds, signed artifacts, immutable logs—supply chain, sealed.",
|
||||
video: "/videos/deterministic.mp4",
|
||||
animation: DeterministicExecution,
|
||||
colSpan: "lg:col-span-2",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -73,7 +90,7 @@ const bentos = [
|
||||
subtitle: "Sovereign Workflow Management",
|
||||
description:
|
||||
"Your private agent conducts swarms of specialists in parallel. Policies fan out work; human checkpoints keep you in command.",
|
||||
video: "/videos/agent.mp4",
|
||||
animation: AgentCoordination,
|
||||
colSpan: "lg:col-span-2",
|
||||
rowSpan: "lg:row-span-1",
|
||||
},
|
||||
@@ -101,20 +118,12 @@ export function AgentBento() {
|
||||
<div
|
||||
className={`relative flex lg:h-90 flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] `}
|
||||
>
|
||||
{/* ✅ VIDEO instead of animation */}
|
||||
{card.video ? (
|
||||
<div className="lg:h-64 h-48 w-full overflow-hidden bg-transparent flex items-center">
|
||||
<video
|
||||
src={card.video}
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
{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" />
|
||||
<div className="hidden md:flex md:h-48 w-full items-center justify-center bg-transparent" />
|
||||
)}
|
||||
|
||||
<div className="px-8 pt-4 pb-6">
|
||||
@@ -127,7 +136,7 @@ export function AgentBento() {
|
||||
) : (
|
||||
<>
|
||||
{/* ✅ NEW SUBTITLE */}
|
||||
<p className="text-sm text-cyan-400">{card.subtitle}</p>
|
||||
<p className="text-sm text-cyan-500">{card.subtitle}</p>
|
||||
|
||||
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
|
||||
{card.title}
|
||||
@@ -152,6 +161,9 @@ export function AgentBento() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
56
src/pages/agents/AgentDesign.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
ComputerDesktopIcon,
|
||||
ArrowsRightLeftIcon,
|
||||
CircleStackIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Each agent operates entirely inside your environment',
|
||||
icon: ComputerDesktopIcon,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'They communicate peer-to-peer across trusted nodes',
|
||||
icon: ArrowsRightLeftIcon,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'They access data locally without exposing it to external providers',
|
||||
icon: CircleStackIcon,
|
||||
},
|
||||
]
|
||||
|
||||
export function AgentDesign() {
|
||||
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" />
|
||||
|
||||
{/* ✅ Main content */}
|
||||
<div className="mx-auto max-w-7xl border-gray-100">
|
||||
<dl className="grid grid-cols-1 gap-4 lg:gap-6 lg:grid-cols-3 text-center ">
|
||||
{benefits.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="flex flex-col items-center bg-white py-10 px-4 border border-gray-100 lg:border-t-0 lg:border-b-0"
|
||||
>
|
||||
<item.icon className="h-10 w-10 text-cyan-500 mb-4" />
|
||||
<h3 className="text-base font-medium text-black max-w-xs">
|
||||
{item.title}
|
||||
</h3>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border 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>
|
||||
|
||||
)
|
||||
}
|
||||
@@ -1,36 +1,38 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Eyebrow, H3 } from '@/components/Texts'
|
||||
import { Eyebrow, H3, P } from '@/components/Texts'
|
||||
|
||||
export function AgentHeroAlt() {
|
||||
return (
|
||||
<div className="">
|
||||
{/* Boxed container */}
|
||||
<div
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden bg-contain bg-right bg-no-repeat"
|
||||
style={{ backgroundImage: "url('/images/agents.webp')", backgroundSize: "contain" }}
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden md:bg-[url('/images/agents.webp')] md:bg-contain md:bg-right md:bg-no-repeat"
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM AGENTS</Eyebrow>
|
||||
<div className="px-6 pt-4 pb-12 lg:py-24">
|
||||
{/* Mobile-only hero image */}
|
||||
<img
|
||||
src="/images/mobile/agents.jpg"
|
||||
alt="Mycelium Agents visual"
|
||||
className="mb-8 w-full object-cover md:hidden"
|
||||
/>
|
||||
|
||||
<div className="max-w-xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow>
|
||||
<H3 as="h1" className="mt-4">
|
||||
Sovereign AI Agents, Coming Soon.
|
||||
Private, Sovereign and Distributed AI You Control
|
||||
</H3>
|
||||
<p className="mt-6 text-lg">
|
||||
The Agent layer will allow you to run autonomous, policy-governed AI that operates on infrastructure you control, with private memory, verifiable execution, and coordination across your personal or organizational environment.
|
||||
</p>
|
||||
<p className="mt-4 lg:text-base italic text-gray-600 text-sm">
|
||||
Works Alone. Works Together. Use Agents on top of any Mycelium Cloud deployment or pair them with the Mycelium Network for private, encrypted collaboration across users and systems.
|
||||
</p>
|
||||
<P className="mt-6 text-gray-600">
|
||||
Mycelium Agents let you deploy and run intelligent systems on your own infrastructure.
|
||||
</P>
|
||||
<P className="mt-4 text-gray-600">
|
||||
Private, local, and autonomous by design, they give you everything you need to build, host, and connect AI agents without relying on centralized clouds.
|
||||
</P>
|
||||
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<Button href="#" variant="solid" color="cyan">
|
||||
Follow Deployment
|
||||
</Button>
|
||||
<Button href="#" variant="outline">
|
||||
Explore Docs <span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
{/* TODO: Hero CTAs (Follow Development / Explore Docs) to be added when links are ready.
|
||||
Previously two Buttons here with href="#". */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
90
src/pages/agents/AgentPro.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
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-white 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="text-cyan-500">
|
||||
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-base 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>
|
||||
);
|
||||
}
|
||||
120
src/pages/agents/AgentUseCase.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, P, CT, CP, H3} from "@/components/Texts";
|
||||
import {
|
||||
CpuChipIcon,
|
||||
GlobeAltIcon,
|
||||
LockClosedIcon,
|
||||
ArrowPathIcon,
|
||||
ShieldCheckIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
|
||||
const networkUseCases = [
|
||||
{
|
||||
isIntro: true,
|
||||
eyebrow: "WHAT IT ENABLES",
|
||||
title: "What It Enables",
|
||||
description:
|
||||
"The framework gives you full control over where agents live, how they connect, and what data they use.",
|
||||
},
|
||||
{
|
||||
title: "Run agents on your own hardware",
|
||||
description:
|
||||
"Deploy AI processes on laptops, homelabs, edge nodes, or full clusters with no cloud dependency.",
|
||||
icon: CpuChipIcon,
|
||||
},
|
||||
{
|
||||
title: "Connect them over the secure Mycelium network",
|
||||
description:
|
||||
"Agents communicate privately across homes, clouds, countries, and environments in one address space.",
|
||||
icon: GlobeAltIcon,
|
||||
},
|
||||
{
|
||||
title: "Keep data and memory private by default",
|
||||
description:
|
||||
"Your datasets, tools, prompts, embeddings, and memory stay local unless you choose otherwise.",
|
||||
icon: LockClosedIcon,
|
||||
},
|
||||
{
|
||||
title: "Build workflows across cloud + edge",
|
||||
description:
|
||||
"Orchestrate multi-node jobs, pipelines, and real-time systems that live anywhere in your infrastructure.",
|
||||
icon: ArrowPathIcon,
|
||||
},
|
||||
{
|
||||
title: "Operate securely in regulated contexts",
|
||||
description:
|
||||
"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() {
|
||||
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>
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* Main framed section */}
|
||||
<div className="max-w-7xl bg-white mx-auto py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-3xl sm:text-center px-6 lg:px-8">
|
||||
{/* Intro block (from isIntro item) */}
|
||||
{networkUseCases[0].isIntro && (
|
||||
<>
|
||||
<Eyebrow className="text-cyan-500">{networkUseCases[0].eyebrow}</Eyebrow>
|
||||
<H3
|
||||
className="mt-4 text-gray-900"
|
||||
>
|
||||
{networkUseCases[0].title}
|
||||
</H3>
|
||||
<P className="mt-4 text-lg text-gray-600">
|
||||
{networkUseCases[0].description}
|
||||
</P>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Grid of features (excluding intro) */}
|
||||
<ul
|
||||
role="list"
|
||||
className="mx-auto mt-8 max-w-6xl grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-y-10 px-6"
|
||||
>
|
||||
{networkUseCases.slice(1).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"
|
||||
>
|
||||
{/* Icon */}
|
||||
{item.icon && (
|
||||
<div className="h-10 w-10 mb-2 flex items-center justify-center rounded-xl bg-gray-100">
|
||||
<item.icon className="h-6 w-6 text-cyan-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title */}
|
||||
<CT className="leading-normal text-gray-900">
|
||||
{item.title}
|
||||
</CT>
|
||||
|
||||
{/* Description */}
|
||||
<CP className="mt-2 text-gray-600 text-sm leading-snug">
|
||||
{item.description}
|
||||
</CP>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Bottom horizontal 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>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { DeploySection } from './DeploySection'
|
||||
import { GallerySection } from './GallerySection'
|
||||
import { Companies } from './Companies'
|
||||
import { AgentBento } from './AgentBento'
|
||||
import { AgentHeroAlt } from './AgentHeroAlt'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { AgentUsecase } from './AgentUseCase'
|
||||
|
||||
import { AgentPro } from './AgentPro'
|
||||
|
||||
export default function AgentsPage() {
|
||||
return (
|
||||
@@ -22,13 +24,17 @@ export default function AgentsPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<GallerySection />
|
||||
<AgentUsecase />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<AgentBento />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<AgentPro />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CallToAction />
|
||||
</AnimatedSection>
|
||||
|
||||
@@ -13,37 +13,70 @@ export function CallToAction() {
|
||||
{/* ✅ Main boxed area */}
|
||||
<div
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
className="relative py-18 max-w-7xl mx-auto overflow-hidden bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
>
|
||||
{/* ✅ Cyan Radial Glow */}
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
aria-hidden="true"
|
||||
className="absolute top-full left-1/2 w-7xl h-320 -translate-x-1/2 -translate-y-1/2 mask-image mask-[radial-gradient(circle,white,transparent)]"
|
||||
>
|
||||
<circle
|
||||
r={512}
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#mycelium-cyan-glow)"
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<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">
|
||||
Start Building the Future of Sovereign AI
|
||||
Start with Mycelium Today
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Use today’s components — models, storage, compute, mesh — and step into agents as they arrive.
|
||||
<p className="mt-6 lg:text-lg text-base leading-normal text-gray-300">
|
||||
The Agent Framework launches in H1 2026, but the foundation is ready now.
|
||||
</p>
|
||||
<p className="mt-2 lg:text-lg text-base leading-normal 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-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
|
||||
</Button>
|
||||
</div>
|
||||
{/* ✅ Button row – same structure as homepage CTA */}
|
||||
<div className="mt-10 flex flex-wrap justify-center items-center gap-x-6 gap-y-4">
|
||||
<Button
|
||||
as="a"
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Deploy a Model
|
||||
</Button>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="/host" as="a" variant="outline" color="white" className="mt-4">
|
||||
Host a Node
|
||||
</Button>
|
||||
</div>
|
||||
<Button to="/nodes" variant="outline" color="white">
|
||||
Host a Node
|
||||
</Button>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="https://threefold.info/mycelium_network/docs/" as="a" target="_blank" variant="outline" color="white" className="mt-4">
|
||||
Follow Development
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
as="a"
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
variant="link"
|
||||
color="white"
|
||||
className="inline-flex items-center gap-1.5"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Follow Development
|
||||
<span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -37,7 +37,7 @@ const row2 = logos.slice(6);
|
||||
|
||||
export function Companies() {
|
||||
return (
|
||||
<div id="companies" className="relative bg-[#121212] flex flex-col items-center justify-center w-full overflow-hidden antialiased py-4 mb-12">
|
||||
<div id="companies" className="relative bg-[#121212] flex flex-col items-center justify-center w-full overflow-hidden antialiased py-4">
|
||||
<div className="relative z-10 mx-auto w-full max-w-7xl p-4">
|
||||
{/* Logos grid */}
|
||||
<div className="flex flex-col items-center gap-y-6 text-white ">
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useEffect, useMemo, useState } from 'react'
|
||||
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
|
||||
import { motion, AnimatePresence } from 'framer-motion'
|
||||
import { wrap } from 'popmotion'
|
||||
import { Button } from '@/components/Button';
|
||||
import { SectionHeader, P, Eyebrow, CP } from '@/components/Texts';
|
||||
import { TypeAnimation } from 'react-type-animation'
|
||||
import { FadeIn } from '@/components/FadeIn';
|
||||
@@ -147,9 +146,8 @@ export function GallerySection() {
|
||||
repeat={0}
|
||||
/>
|
||||
</CP>
|
||||
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base whitespace-nowrap">
|
||||
Start
|
||||
</Button>
|
||||
{/* TODO: Desktop CTA (Start) button to be added when link target is defined.
|
||||
Previously: <Button href="#" color="cyan">Start</Button> */}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -166,9 +164,8 @@ export function GallerySection() {
|
||||
repeat={0}
|
||||
/>
|
||||
</CP>
|
||||
<Button href="#" color="cyan" className="text-xs px-3 py-1.5 whitespace-nowrap">
|
||||
Start
|
||||
</Button>
|
||||
{/* TODO: Mobile CTA (Start) button to be added when link target is defined.
|
||||
Previously: <Button href="#" color="cyan">Start</Button> */}
|
||||
</div>
|
||||
</div>
|
||||
</FadeIn>
|
||||
|
||||
264
src/pages/agents/animations/AgentCoordination.tsx
Normal file
@@ -0,0 +1,264 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const AgentNode = ({
|
||||
x,
|
||||
y,
|
||||
r = 12,
|
||||
accent = "#00b8db",
|
||||
pulse = false,
|
||||
delay = 0,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
r?: number;
|
||||
accent?: string;
|
||||
pulse?: boolean;
|
||||
delay?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<>
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r + 10}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.7 }}
|
||||
transition={{ delay, duration: 0.6 }}
|
||||
/>
|
||||
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: pulse && !prefers ? [1, 1.12, 1] : 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: pulse ? 1.8 : 0.6,
|
||||
repeat: pulse && !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const Packet = ({
|
||||
path,
|
||||
delay = 0,
|
||||
accent = "#00b8db",
|
||||
duration = 2.2,
|
||||
reverse = false,
|
||||
}: {
|
||||
path: string;
|
||||
delay?: number;
|
||||
accent?: string;
|
||||
duration?: number;
|
||||
reverse?: boolean;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
r={4}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: reverse ? "100%" : "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: reverse ? ["100%", "0%"] : ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{
|
||||
offsetPath: `path('${path}')`,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default function AgentCoordination({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const center = { x: 380, y: 210 };
|
||||
|
||||
// Specialist agents placed in a controlled orbit
|
||||
const agents = [
|
||||
{ x: 200, y: 120 },
|
||||
{ x: 560, y: 120 },
|
||||
{ x: 620, y: 260 },
|
||||
{ x: 500, y: 330 },
|
||||
{ x: 260, y: 330 },
|
||||
{ x: 140, y: 260 },
|
||||
];
|
||||
|
||||
const paths = agents.map(
|
||||
(a) => `M ${center.x} ${center.y} Q ${(center.x + a.x) / 2} ${(center.y + a.y) / 2 - 40} ${a.x} ${a.y}`
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Agent coordination and sovereign workflow management"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-6" preserveAspectRatio="xMidYMid slice">
|
||||
|
||||
{/* background */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.20" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* global glow */}
|
||||
<circle cx={center.x} cy={center.y} r={240} fill="url(#glow)" opacity={0.45} />
|
||||
|
||||
{/* POLICY RING */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={110}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
strokeDasharray="14 10"
|
||||
animate={{ opacity: [0.25, 0.7, 0.25] }}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* CHECKPOINT GATE */}
|
||||
<motion.rect
|
||||
x={center.x - 50}
|
||||
y={center.y - 130}
|
||||
width={100}
|
||||
height={22}
|
||||
rx={6}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
animate={{ opacity: [0.2, 0.7, 0.2] }}
|
||||
transition={{
|
||||
duration: 2.4,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* central conductor agent */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={26}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: [1, 1.06, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* inward → outward routing lines */}
|
||||
{paths.map((p, i) => (
|
||||
<motion.path
|
||||
key={`p-${i}`}
|
||||
d={p}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay: i * 0.1,
|
||||
duration: 0.8,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* OUTBOUND tasks */}
|
||||
{paths.map((p, i) => (
|
||||
<Packet
|
||||
key={`out-${i}`}
|
||||
path={p}
|
||||
delay={0.3 * i}
|
||||
accent={accent}
|
||||
duration={2}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* INBOUND results */}
|
||||
{paths.map((p, i) => (
|
||||
<Packet
|
||||
key={`in-${i}`}
|
||||
path={p}
|
||||
reverse
|
||||
delay={0.5 * i}
|
||||
accent={accent}
|
||||
duration={2.2}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* specialist agents */}
|
||||
{agents.map((a, i) => (
|
||||
<AgentNode
|
||||
key={`agent-${i}`}
|
||||
x={a.x}
|
||||
y={a.y}
|
||||
pulse={i % 2 === 0}
|
||||
delay={i * 0.1}
|
||||
accent={accent}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
263
src/pages/agents/animations/DeterministicExecution.tsx
Normal file
@@ -0,0 +1,263 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const PulseCircle = ({
|
||||
x,
|
||||
y,
|
||||
r,
|
||||
accent,
|
||||
delay = 0,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
r: number;
|
||||
accent: string;
|
||||
delay?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ scale: 0.8, opacity: 0 }}
|
||||
animate={{
|
||||
scale: !prefers ? [1, 1.25, 1] : 1,
|
||||
opacity: !prefers ? [0.4, 0.9, 0.4] : 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 2.2,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Packet = ({
|
||||
path,
|
||||
delay = 0,
|
||||
duration = 2,
|
||||
accent = "#00b8db",
|
||||
}: {
|
||||
path: string;
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
accent?: string;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
r={4}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{ offsetPath: `path('${path}')` }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function DeterministicExecution({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const left = { x: 200, y: 210 };
|
||||
const center = { x: 380, y: 210 };
|
||||
const right = { x: 560, y: 210 };
|
||||
|
||||
const leftToCenter = `M ${left.x} ${left.y} L ${center.x} ${center.y}`;
|
||||
const centerToRight = `M ${center.x} ${center.y} L ${right.x} ${right.y}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Deterministic deployment and verifiable code execution"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-6" preserveAspectRatio="xMidYMid slice">
|
||||
|
||||
{/* background grid */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.18" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* global glow */}
|
||||
<circle cx={center.x} cy={center.y} r={230} fill="url(#glow)" opacity={0.4} />
|
||||
|
||||
{/* LEFT: Declare Intent block */}
|
||||
<motion.rect
|
||||
x={left.x - 60}
|
||||
y={left.y - 40}
|
||||
width={120}
|
||||
height={80}
|
||||
rx={10}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.8 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
/>
|
||||
<motion.rect
|
||||
x={left.x - 45}
|
||||
y={left.y - 25}
|
||||
width={90}
|
||||
height={50}
|
||||
rx={6}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
/>
|
||||
|
||||
{/* CENTER: Cryptographic hash */}
|
||||
<motion.rect
|
||||
x={center.x - 40}
|
||||
y={center.y - 40}
|
||||
width={80}
|
||||
height={80}
|
||||
rx={12}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 6"
|
||||
fill="none"
|
||||
animate={{ opacity: [0.4, 0.9, 0.4] }}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* hash print */}
|
||||
<motion.text
|
||||
x={center.x}
|
||||
y={center.y + 5}
|
||||
textAnchor="middle"
|
||||
fill={accent}
|
||||
fontFamily="monospace"
|
||||
fontSize="14"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.9 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
>
|
||||
9f1a..42c7
|
||||
</motion.text>
|
||||
|
||||
{/* signature stamp */}
|
||||
<motion.circle
|
||||
cx={center.x + 55}
|
||||
cy={center.y - 55}
|
||||
r={14}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ scale: 0, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
transition={{ delay: 0.4, duration: 0.6 }}
|
||||
/>
|
||||
|
||||
{/* RIGHT: Verified Execution Node */}
|
||||
<motion.circle
|
||||
cx={right.x}
|
||||
cy={right.y}
|
||||
r={24}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: [1, 1.06, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* outer attestation ring */}
|
||||
<PulseCircle x={right.x} y={right.y} r={40} accent={accent} delay={0.2} />
|
||||
|
||||
{/* immutable logs ring */}
|
||||
<motion.circle
|
||||
cx={right.x}
|
||||
cy={right.y}
|
||||
r={70}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="14 12"
|
||||
fill="none"
|
||||
animate={{ opacity: [0.2, 0.6, 0.2] }}
|
||||
transition={{
|
||||
duration: 3,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* deterministic path lines */}
|
||||
<motion.path
|
||||
d={leftToCenter}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
/>
|
||||
<motion.path
|
||||
d={centerToRight}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
initial={{ pathLength: 0 }}
|
||||
animate={{ pathLength: 1 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
/>
|
||||
|
||||
{/* active cryptographic pulses */}
|
||||
<Packet path={leftToCenter} delay={0.4} accent={accent} duration={1.8} />
|
||||
<Packet path={centerToRight} delay={0.8} accent={accent} duration={1.8} />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
238
src/pages/agents/animations/Fungistor.tsx
Normal file
@@ -0,0 +1,238 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const Node = ({
|
||||
x,
|
||||
y,
|
||||
r = 10,
|
||||
accent = "#00b8db",
|
||||
pulse = false,
|
||||
delay = 0,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
r?: number;
|
||||
accent?: string;
|
||||
pulse?: boolean;
|
||||
delay?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<>
|
||||
{/* outer faint ring */}
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r + 10}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.6 }}
|
||||
transition={{ delay, duration: 0.6 }}
|
||||
/>
|
||||
{/* glowing shard node */}
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: pulse && !prefers ? [1, 1.12, 1] : 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: pulse && !prefers ? 1.8 : 0.6,
|
||||
repeat: pulse && !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Shard = ({
|
||||
path,
|
||||
delay = 0,
|
||||
accent = "#00b8db",
|
||||
duration = 2,
|
||||
}: {
|
||||
path: string;
|
||||
delay?: number;
|
||||
accent?: string;
|
||||
duration?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
r={4}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{ offsetPath: `path('${path}')` }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function FungiStor({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const center = { x: 380, y: 210 };
|
||||
const shardNodes = [
|
||||
{ x: 160, y: 100 },
|
||||
{ x: 260, y: 70 },
|
||||
{ x: 580, y: 100 },
|
||||
{ x: 620, y: 250 },
|
||||
{ x: 500, y: 330 },
|
||||
{ x: 240, y: 320 },
|
||||
];
|
||||
|
||||
// outgoing shard paths
|
||||
const paths = shardNodes.map(
|
||||
(n) => `M ${center.x} ${center.y} Q ${(n.x + center.x) / 2} ${(n.y + center.y) / 2 - 40} ${n.x} ${n.y}`
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="FungiStor, a distributed long-term AI memory"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-16" preserveAspectRatio="xMidYMid slice">
|
||||
{/* Background grid */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.15" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* Soft global network glow */}
|
||||
<circle cx={center.x} cy={center.y} r={180} fill="url(#glow)" opacity={0.4} />
|
||||
|
||||
{/* Source data core */}
|
||||
<motion.rect
|
||||
x={center.x - 40}
|
||||
y={center.y - 40}
|
||||
width={80}
|
||||
height={80}
|
||||
rx={12}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 6"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: [0.4, 0.9, 0.4] }}
|
||||
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
||||
/>
|
||||
<motion.rect
|
||||
x={center.x - 20}
|
||||
y={center.y - 20}
|
||||
width={40}
|
||||
height={40}
|
||||
rx={6}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: [1, 1.08, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Outgoing shard connections */}
|
||||
{paths.map((p, i) => (
|
||||
<motion.path
|
||||
key={`path-${i}`}
|
||||
d={p}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay: 0.05 * i,
|
||||
duration: 0.8,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Animated data shards traveling */}
|
||||
{paths.map((p, i) => (
|
||||
<Shard key={`shard-${i}`} path={p} delay={i * 0.4} accent={accent} duration={2.6} />
|
||||
))}
|
||||
|
||||
{/* Destination storage nodes */}
|
||||
{shardNodes.map((n, i) => (
|
||||
<Node
|
||||
key={`node-${i}`}
|
||||
x={n.x}
|
||||
y={n.y}
|
||||
accent={accent}
|
||||
pulse={i % 2 === 0}
|
||||
delay={i * 0.1}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* “Reconstruction glow” pulse ring */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={110}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ scale: 0.9, opacity: 0 }}
|
||||
animate={{
|
||||
scale: [0.9, 1.2, 0.9],
|
||||
opacity: [0.2, 0.6, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 3.2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
286
src/pages/agents/animations/Herodb.tsx
Normal file
@@ -0,0 +1,286 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const Node = ({
|
||||
x,
|
||||
y,
|
||||
r = 12,
|
||||
accent = "#00b8db",
|
||||
pulse = false,
|
||||
delay = 0,
|
||||
type = "dot",
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
r?: number;
|
||||
accent?: string;
|
||||
pulse?: boolean;
|
||||
delay?: number;
|
||||
type?: "dot" | "text" | "image" | "audio";
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* faint ring */}
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r + 10}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.7 }}
|
||||
transition={{ delay, duration: 0.6 }}
|
||||
/>
|
||||
|
||||
{/* inner icon shape to represent modality */}
|
||||
{type === "text" && (
|
||||
<motion.rect
|
||||
x={x - r}
|
||||
y={y - r / 2}
|
||||
width={r * 2}
|
||||
height={r}
|
||||
rx={3}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay }}
|
||||
/>
|
||||
)}
|
||||
{type === "image" && (
|
||||
<motion.path
|
||||
d={`M ${x - r} ${y + r/2} L ${x - r} ${y - r/2} L ${x + r} ${y - r/2} L ${x + r} ${y + r/2} Z`}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay }}
|
||||
/>
|
||||
)}
|
||||
{type === "audio" && (
|
||||
<motion.path
|
||||
d={`
|
||||
M ${x - r/2} ${y - r/2}
|
||||
L ${x - r/2} ${y + r/2}
|
||||
M ${x} ${y - r/3}
|
||||
L ${x} ${y + r/3}
|
||||
M ${x + r/2} ${y - r/4}
|
||||
L ${x + r/2} ${y + r/4}
|
||||
`}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* standard pulsing circle fallback */}
|
||||
{type === "dot" && (
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: pulse && !prefers ? [1, 1.12, 1] : 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: pulse && !prefers ? 1.6 : 0.6,
|
||||
repeat: pulse && !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// inward pulse particle
|
||||
const Packet = ({
|
||||
path,
|
||||
delay = 0,
|
||||
accent = "#00b8db",
|
||||
duration = 1.8,
|
||||
}: {
|
||||
path: string;
|
||||
delay?: number;
|
||||
accent?: string;
|
||||
duration?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
r={4}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: "100%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["100%", "0%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{ offsetPath: `path('${path}')` }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Herodb({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const center = { x: 380, y: 210 };
|
||||
|
||||
const shardNodes = [
|
||||
{ x: 160, y: 100, type: "text" },
|
||||
{ x: 580, y: 120, type: "image" },
|
||||
{ x: 620, y: 280, type: "audio" },
|
||||
{ x: 420, y: 330, type: "text" },
|
||||
{ x: 240, y: 320, type: "image" },
|
||||
{ x: 150, y: 220, type: "dot" },
|
||||
];
|
||||
|
||||
const paths = shardNodes.map(
|
||||
(n) =>
|
||||
`M ${n.x} ${n.y} Q ${(n.x + center.x) / 2} ${(n.y + center.y) / 2 - 40} ${center.x} ${center.y}`
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="HeroDB, active AI memory retrieval"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-18" preserveAspectRatio="xMidYMid slice">
|
||||
{/* Background grid */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.18" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* global halo */}
|
||||
<circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={200}
|
||||
fill="url(#glow)"
|
||||
opacity={0.45}
|
||||
/>
|
||||
|
||||
{/* core retrieval sphere */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={22}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: [1, 1.05, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.6,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* core aura ring */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={40}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
animate={{
|
||||
opacity: [0.2, 0.7, 0.2],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* inward paths */}
|
||||
{paths.map((p, i) => (
|
||||
<motion.path
|
||||
key={`path-${i}`}
|
||||
d={p}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay: 0.05 * i,
|
||||
duration: 0.7,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* packets flowing inward */}
|
||||
{paths.map((p, i) => (
|
||||
<Packet
|
||||
key={`pkt-${i}`}
|
||||
path={p}
|
||||
delay={i * 0.3}
|
||||
accent={accent}
|
||||
duration={1.8}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* modality nodes */}
|
||||
{shardNodes.map((n, i) => (
|
||||
<Node
|
||||
key={`node-${i}`}
|
||||
x={n.x}
|
||||
y={n.y}
|
||||
type={n.type as any}
|
||||
r={12}
|
||||
accent={accent}
|
||||
pulse={i % 2 === 0}
|
||||
delay={i * 0.1}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
231
src/pages/agents/animations/MOSSandboxes.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const PulseRing = ({
|
||||
x,
|
||||
y,
|
||||
accent,
|
||||
delay = 0,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
accent: string;
|
||||
delay?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={42}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ scale: 0.85, opacity: 0 }}
|
||||
animate={{
|
||||
scale: !prefers ? [1, 1.12, 1] : 1,
|
||||
opacity: !prefers ? [0.15, 0.6, 0.15] : 0.4,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 2,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Egress = ({
|
||||
from,
|
||||
to,
|
||||
delay = 0,
|
||||
accent = "#00b8db",
|
||||
}: {
|
||||
from: { x: number; y: number };
|
||||
to: { x: number; y: number };
|
||||
delay?: number;
|
||||
accent?: string;
|
||||
}) => {
|
||||
const path = `M ${from.x} ${from.y} L ${to.x} ${to.y}`;
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.path
|
||||
d={path}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 0.8,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
|
||||
<motion.circle
|
||||
r={4}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 1.6,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{
|
||||
offsetPath: `path('${path}')`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function MOSSandboxes({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const center = { x: 380, y: 210 };
|
||||
|
||||
// scoped egress ports
|
||||
const egress = [
|
||||
{ from: center, to: { x: 520, y: 140 } },
|
||||
{ from: center, to: { x: 520, y: 280 } },
|
||||
{ from: center, to: { x: 260, y: 320 } },
|
||||
];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="MOS Secure Agent Sandboxes"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-16" preserveAspectRatio="xMidYMid slice">
|
||||
{/* BACKGROUND GRID */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.20" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* GLOBAL GLOW */}
|
||||
<circle cx={center.x} cy={center.y} r={200} fill="url(#glow)" opacity={0.45} />
|
||||
|
||||
{/* SANDBOX OUTER ENCLAVE */}
|
||||
<motion.rect
|
||||
x={center.x - 90}
|
||||
y={center.y - 60}
|
||||
width={180}
|
||||
height={120}
|
||||
rx={16}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.7 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
/>
|
||||
|
||||
{/* ATTESTATION RING */}
|
||||
<PulseRing x={center.x} y={center.y} accent={accent} delay={0.3} />
|
||||
|
||||
{/* SIGNED WORKSPACE CORE */}
|
||||
<motion.rect
|
||||
x={center.x - 40}
|
||||
y={center.y - 30}
|
||||
width={80}
|
||||
height={60}
|
||||
rx={10}
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 6"
|
||||
fill="none"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: [0.4, 0.9, 0.4],
|
||||
scale: [1, 1.06, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* SANDBOX ACTIVE PAYLOAD */}
|
||||
<motion.circle
|
||||
cx={center.x}
|
||||
cy={center.y}
|
||||
r={18}
|
||||
fill={accent}
|
||||
initial={{ scale: 0.6, opacity: 0 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: [1, 1.1, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 1.8,
|
||||
repeat: Infinity,
|
||||
repeatType: "mirror",
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* EGRESS PATHS */}
|
||||
{egress.map((e, i) => (
|
||||
<Egress key={i} from={e.from} to={e.to} delay={i * 0.2} accent={accent} />
|
||||
))}
|
||||
|
||||
{/* “TEAR DOWN” FADE — ephemeral sandbox lifecycle */}
|
||||
<motion.rect
|
||||
x={center.x - 90}
|
||||
y={center.y - 60}
|
||||
width={180}
|
||||
height={120}
|
||||
rx={16}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{
|
||||
opacity: [0, 0, 0.12, 0],
|
||||
}}
|
||||
transition={{
|
||||
duration: 4,
|
||||
repeat: Infinity,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
231
src/pages/agents/animations/MyceliumMesh.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const Node = ({
|
||||
x,
|
||||
y,
|
||||
r = 12,
|
||||
accent = "#00b8db",
|
||||
pulse = false,
|
||||
delay = 0,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
r?: number;
|
||||
accent?: string;
|
||||
pulse?: boolean;
|
||||
delay?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Outer ring */}
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r + 8}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={2}
|
||||
fill="none"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 0.8 }}
|
||||
transition={{ duration: 0.6, delay }}
|
||||
/>
|
||||
|
||||
{/* Core node */}
|
||||
<motion.circle
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={r}
|
||||
fill={accent}
|
||||
initial={{ opacity: 0, scale: 0.85 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: pulse && !prefers ? [1, 1.1, 1] : 1,
|
||||
}}
|
||||
transition={{
|
||||
duration: pulse ? 1.6 : 0.6,
|
||||
repeat: pulse && !prefers ? Infinity : 0,
|
||||
repeatType: "mirror",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
delay,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/* Animated encrypted packet */
|
||||
const Packet = ({
|
||||
path,
|
||||
delay = 0,
|
||||
accent = "#00b8db",
|
||||
duration = 2.4,
|
||||
}: {
|
||||
path: string;
|
||||
delay?: number;
|
||||
accent?: string;
|
||||
duration?: number;
|
||||
}) => {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
return (
|
||||
<motion.circle
|
||||
r={5}
|
||||
fill={accent}
|
||||
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0, 1, 0],
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration,
|
||||
repeat: !prefers ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: "linear",
|
||||
}}
|
||||
style={{ offsetPath: `path('${path}')` }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function MyceliumMesh({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0a0a0a",
|
||||
}: Props) {
|
||||
const center = { x: 380, y: 210 };
|
||||
|
||||
// Peer nodes forming a mesh
|
||||
const nodes = [
|
||||
{ x: 180, y: 120 },
|
||||
{ x: 580, y: 100 },
|
||||
{ x: 620, y: 260 },
|
||||
{ x: 460, y: 330 },
|
||||
{ x: 240, y: 320 },
|
||||
{ x: 140, y: 240 },
|
||||
];
|
||||
|
||||
// Multi-path routing (3 routes to illustrate "self-healing")
|
||||
const routes = [
|
||||
[nodes[0], nodes[1], nodes[2]], // path A→B→C
|
||||
[nodes[0], nodes[5], nodes[4], nodes[3]], // path A→F→E→D
|
||||
[nodes[1], nodes[4], nodes[3]], // path B→E→D
|
||||
];
|
||||
|
||||
// Convert list of nodes → SVG path
|
||||
const toPath = (list: any[]) =>
|
||||
list
|
||||
.map((p: any, i: number) =>
|
||||
i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`
|
||||
)
|
||||
.join(" ");
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"relative overflow-hidden",
|
||||
className
|
||||
)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Mycelium Mesh, a secure communication network"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:-translate-y-4" preserveAspectRatio="xMidYMid slice">
|
||||
{/* Background grid */}
|
||||
<defs>
|
||||
<pattern id="grid-dark" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" stroke="#0f0f0f" strokeWidth="1" />
|
||||
</pattern>
|
||||
|
||||
<radialGradient id="glow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor={accent} stopOpacity="0.18" />
|
||||
<stop offset="100%" stopColor={accent} stopOpacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect width={W} height={H} fill="url(#grid-dark)" />
|
||||
|
||||
{/* Wide ambient glow */}
|
||||
<circle cx={center.x} cy={center.y} r={240} fill="url(#glow)" opacity={0.45} />
|
||||
|
||||
{/* Multi-path routing lines */}
|
||||
{routes.map((pathNodes, i) => (
|
||||
<motion.path
|
||||
key={`line-${i}`}
|
||||
d={toPath(pathNodes)}
|
||||
stroke="#1F2937"
|
||||
strokeWidth={3}
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.4 }}
|
||||
transition={{
|
||||
delay: 0.2 * i,
|
||||
duration: 1,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Cyan “active” encrypted routing */}
|
||||
{routes.map((pathNodes, i) => (
|
||||
<motion.path
|
||||
key={`signal-${i}`}
|
||||
d={toPath(pathNodes)}
|
||||
stroke={accent}
|
||||
strokeWidth={2.5}
|
||||
strokeDasharray="12 10"
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
animate={{ opacity: [0.25, 0.8, 0.25] }}
|
||||
transition={{
|
||||
duration: 2.4,
|
||||
repeat: Infinity,
|
||||
delay: i * 0.3,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Moving encrypted packets */}
|
||||
{routes.map((pathNodes, i) => (
|
||||
<Packet
|
||||
key={`pkt-${i}`}
|
||||
path={toPath(pathNodes)}
|
||||
delay={i * 0.5}
|
||||
duration={2.6}
|
||||
accent={accent}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Pulse nodes */}
|
||||
{nodes.map((n, i) => (
|
||||
<Node
|
||||
key={`node-${i}`}
|
||||
x={n.x}
|
||||
y={n.y}
|
||||
r={12}
|
||||
accent={accent}
|
||||
pulse={i % 2 === 0}
|
||||
delay={i * 0.15}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,8 +13,28 @@ export function CallToAction() {
|
||||
{/* ✅ Main boxed area */}
|
||||
<div
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
className="relative py-18 max-w-7xl overflow-hidden mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
>
|
||||
{/* ✅ Cyan Radial Glow */}
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
aria-hidden="true"
|
||||
className="absolute top-full left-1/2 w-7xl h-320 -translate-x-1/2 -translate-y-1/2 mask-image mask-[radial-gradient(circle,white,transparent)]"
|
||||
>
|
||||
<circle
|
||||
r={512}
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#mycelium-cyan-glow)"
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
@@ -32,13 +52,21 @@ export function CallToAction() {
|
||||
{/* ✅ Two cards, stacked center with spacing */}
|
||||
<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="/host" variant="solid" color="cyan" className="mt-4">
|
||||
<Button to="/nodes" variant="solid" color="cyan" className="mt-4">
|
||||
Host a Node
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="/cloud" variant="outline" color="white" className="mt-4">
|
||||
<Button
|
||||
as="a"
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
color="white"
|
||||
className="mt-4"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Start Deploying
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export function CloudArchitecture() {
|
||||
<div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-800 bg-[#111111] py-12">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<Eyebrow className="text-cyan-400">ARCHITECTURE</Eyebrow>
|
||||
<Eyebrow className="">ARCHITECTURE</Eyebrow>
|
||||
|
||||
<H3 className="text-3xl lg:text-4xl font-medium tracking-tight text-white">
|
||||
How Mycelium Cloud Works
|
||||
|
||||
@@ -20,14 +20,14 @@ export function CloudBluePrint() {
|
||||
<div className="max-w-7xl bg-white mx-auto py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<Eyebrow className="text-cyan-500">Featured Blueprint</Eyebrow>
|
||||
<Eyebrow className="text-cyan-400">Featured Blueprint</Eyebrow>
|
||||
|
||||
<H3 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900 mt-2">
|
||||
Your Personal Sovereign Cloud Workspace
|
||||
</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 — 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, 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.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
|
||||
112
src/pages/cloud/CloudCodeTabs.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
const files = [
|
||||
{
|
||||
id: "kube",
|
||||
label: "kubernetes.yaml",
|
||||
code: `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mycelium-app
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mycelium-app
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mycelium-app`,
|
||||
},
|
||||
|
||||
{
|
||||
id: "vdc",
|
||||
label: "vdc.tf",
|
||||
code: `provider "mycelium" {
|
||||
identity = "~/.mycelium/id"
|
||||
}
|
||||
|
||||
resource "mycelium_vdc" "production" {
|
||||
name = "prod-vdc"
|
||||
region = "eu-central"
|
||||
nodes = 6
|
||||
cpu_cores = 24
|
||||
ram_gb = 128
|
||||
storage = "10TB"
|
||||
|
||||
network_policies = ["private", "encrypted"]
|
||||
}`,
|
||||
},
|
||||
|
||||
{
|
||||
id: "qsfs",
|
||||
label: "qsfs.py",
|
||||
code: `from qsfs import QSFS
|
||||
|
||||
# mount encrypted distributed filesystem
|
||||
fs = QSFS.mount("/mnt/secure", key="my-private-key")
|
||||
|
||||
# write protected research data
|
||||
with fs.open("dataset/raw-images/img001.png", "wb") as f:
|
||||
f.write(b"...binary data...")
|
||||
|
||||
# list stored files via S3/IPFS/WebDAV compatibility layer
|
||||
files = fs.list("dataset/raw-images/")
|
||||
print("Stored files:", files)`,
|
||||
},
|
||||
];
|
||||
|
||||
export function CloudCodeTabs() {
|
||||
const [active, setActive] = useState("kube");
|
||||
const file = files.find((f) => f.id === active)!;
|
||||
|
||||
return (
|
||||
<div className="sm:px-6 lg:px-0">
|
||||
<div className="relative isolate overflow-hidden bg-cyan-600 px-6 pt-8 sm:mx-auto sm:max-w-2xl sm:rounded-md sm:pt-16 sm:pr-0 sm:pl-16 lg:mx-0 lg:max-w-none">
|
||||
|
||||
{/* Cyan skew background */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute -inset-y-px -left-3 -z-10 w-full origin-bottom-left skew-x-[-30deg] bg-cyan-500 opacity-20 ring-1 ring-white ring-inset"
|
||||
/>
|
||||
|
||||
<div className="mx-auto max-w-2xl sm:mx-0 sm:max-w-none">
|
||||
<div className="w-screen overflow-hidden rounded-tl-xl bg-[#121212] ring-1 ring-white/10">
|
||||
|
||||
{/* File Tabs */}
|
||||
<div className="flex bg-gray-800/40 ring-1 ring-white/5">
|
||||
<div className="-mb-px flex text-sm font-medium text-gray-400">
|
||||
{files.map((f) => (
|
||||
<button
|
||||
key={f.id}
|
||||
onClick={() => setActive(f.id)}
|
||||
className={`px-4 py-2 border-r border-white/10 transition ${
|
||||
active === f.id
|
||||
? "border-b border-b-white/20 bg-white/5 text-white"
|
||||
: "hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{f.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Code Block */}
|
||||
<div className="px-6 pt-6 pb-14 font-mono text-xs leading-relaxed text-gray-200 whitespace-pre overflow-x-auto">
|
||||
{file.code}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Outer ring */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute inset-0 ring-1 ring-white/10 ring-inset sm:rounded-3xl"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -254,8 +254,8 @@ function CloudFeaturesDesktop() {
|
||||
className={clsx(
|
||||
'relative rounded-2xl outline-2 transition-all duration-300 ease-in-out hover:scale-105 hover:bg-gray-800/30 ml-16',
|
||||
selectedIndex === featureIndex
|
||||
? 'outline-cyan-500'
|
||||
: 'outline-transparent hover:outline-cyan-500',
|
||||
? 'outline-cyan-400'
|
||||
: 'outline-transparent hover:outline-cyan-400',
|
||||
)}
|
||||
>
|
||||
{featureIndex === selectedIndex && (
|
||||
|
||||
@@ -153,8 +153,8 @@ function CloudFeaturesDesktop() {
|
||||
className={clsx(
|
||||
'relative rounded-2xl outline-2 transition-all duration-300 ease-in-out hover:scale-105 hover:bg-gray-100',
|
||||
selectedIndex === featureIndex
|
||||
? 'outline-cyan-500'
|
||||
: 'outline-transparent hover:outline-cyan-500',
|
||||
? 'outline-cyan-400'
|
||||
: 'outline-transparent hover:outline-cyan-400',
|
||||
)}
|
||||
>
|
||||
{featureIndex === selectedIndex && (
|
||||
@@ -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>
|
||||
|
||||
@@ -1,49 +1,59 @@
|
||||
import { H3, Eyebrow } from "@/components/Texts"
|
||||
import { H3, Eyebrow, P } from "@/components/Texts"
|
||||
import { Button } from "@/components/Button"
|
||||
|
||||
export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedClick?: () => void }) {
|
||||
export function CloudHeroNew() {
|
||||
return (
|
||||
<div className="">
|
||||
{/* Boxed container */}
|
||||
<div
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden bg-contain bg-right bg-no-repeat"
|
||||
style={{ backgroundImage: "url('/images/cloudhero4.webp')", backgroundSize: "contain" }}
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden md:bg-[url('/images/cloudhero4.webp')] md:bg-contain md:bg-right md:bg-no-repeat"
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="px-6 pt-4 pb-12 lg:py-24">
|
||||
{/* Mobile-only hero image */}
|
||||
<img
|
||||
src="/images/mobile/cloudpage.jpg"
|
||||
alt="Mycelium Cloud page visual"
|
||||
className="mb-8 w-full object-cover md:hidden"
|
||||
/>
|
||||
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>
|
||||
Mycelium Cloud
|
||||
MYCELIUM CLOUD
|
||||
</Eyebrow>
|
||||
<H3 className="mt-4">
|
||||
Run Kubernetes on the Sovereign Agentic Cloud
|
||||
Sovereign Edge Cloud Infrastructure
|
||||
</H3>
|
||||
<p className="mt-6 text-lg">
|
||||
Deploy K3s clusters on a global, self-healing mesh network.
|
||||
Your workloads run on sovereign, encrypted infrastructure, without centralized cloud control.
|
||||
</p>
|
||||
<p className="mt-4 lg:text-base italic text-gray-600 text-sm">
|
||||
Works Alone. Works Together.
|
||||
Mycelium Cloud can run on any network fabric, or pair with Mycelium Network
|
||||
for sovereign connectivity.
|
||||
</p>
|
||||
<P className="mt-6 text-gray-600">
|
||||
Run compute, storage, and AI resources on infrastructure you control.
|
||||
The Mycelium Cloud runs on a distributed grid of independent nodes,
|
||||
delivering secure, scalable performance wherever your users or data live.
|
||||
</P>
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<Button
|
||||
onClick={onGetStartedClick}
|
||||
as="a"
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Get started
|
||||
Deploy Workloads
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Documentation <span aria-hidden="true">→</span>
|
||||
<Button
|
||||
as="a"
|
||||
to="https://myceliumcloud.tf/docs"
|
||||
variant="outline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Explore Docs <span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
{/* 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>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,6 @@ import { Eyebrow, H3, H4 } from "@/components/Texts"
|
||||
const product = {
|
||||
subtitle: 'capabilities',
|
||||
name: 'What You Can Run on Mycelium Cloud',
|
||||
description: '<p>Host nodes, deploy workloads, or build private AI systems, all on infrastructure you own and control.</p>',
|
||||
details: [
|
||||
{
|
||||
name: 'Kubernetes Clusters',
|
||||
@@ -64,9 +63,6 @@ export function CloudHostingNew() {
|
||||
|
||||
|
||||
|
||||
<div className="mt-4 text-gray-300 text-xl"
|
||||
dangerouslySetInnerHTML={{ __html: product.description }}
|
||||
/>
|
||||
|
||||
|
||||
{/* ✅ Details accordion */}
|
||||
|
||||
214
src/pages/cloud/CloudIntro.tsx
Normal file
@@ -0,0 +1,214 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: "kubernetes",
|
||||
label: "Managed Kubernetes",
|
||||
content: {
|
||||
item: "Managed Kubernetes",
|
||||
desc:
|
||||
"Create and manage clusters across distributed environments using standard Kubernetes tools.",
|
||||
|
||||
bullets: [
|
||||
"Create and manage clusters on distributed nodes",
|
||||
"Run workloads at the edge or across enterprise sites",
|
||||
"Keep full ownership of data and orchestration",
|
||||
"Use the Kubernetes ecosystem without modification",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: "vdc",
|
||||
label: "Virtual Data Centers",
|
||||
content: {
|
||||
item: "Virtual Data Centers",
|
||||
desc:
|
||||
"Provision and manage full cloud environments without owning or maintaining servers.",
|
||||
|
||||
bullets: [
|
||||
"Create dedicated environments for applications, databases, and internal services",
|
||||
"Add or remove compute and storage resources instantly",
|
||||
"Migrate workloads from cloud or on-prem systems",
|
||||
"Meet compliance requirements by selecting where data resides",
|
||||
"Benefit from continuous monitoring and automated recovery",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
id: "qsfs",
|
||||
label: "Quantum Safe File System (QSFS)",
|
||||
content: {
|
||||
item: "Quantum Safe File System (QSFS)",
|
||||
desc:
|
||||
"Encrypted, redundant storage designed for high-security and high-availability workloads. Data is distributed across independent nodes and remains accessible even during failures or outages.",
|
||||
|
||||
bullets: [
|
||||
"Secure file storage with quantum-safe encryption",
|
||||
"Distributed replication for durability",
|
||||
"Standard protocol support: S3, IPFS, WebDAV",
|
||||
"Automatic scaling as data grows",
|
||||
"Consistent performance for research, enterprise, and AI workloads",
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
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];
|
||||
|
||||
const primaryLinks: Record<string, string | undefined> = {
|
||||
kubernetes: "https://myceliumcloud.tf",
|
||||
vdc: undefined,
|
||||
qsfs: undefined,
|
||||
};
|
||||
|
||||
const secondaryLinks: Record<string, string | undefined> = {
|
||||
kubernetes: "https://myceliumcloud.tf/docs",
|
||||
vdc: "https://threefold.info/mycelium_vdc/docs/",
|
||||
qsfs: undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="relative w-full bg-[#121212] overflow-hidden">
|
||||
{/* Top Spacing Border */}
|
||||
<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">
|
||||
|
||||
{/* ================================
|
||||
Header
|
||||
================================= */}
|
||||
<div className="mb-16">
|
||||
<Eyebrow color="accent">Capabilities</Eyebrow>
|
||||
|
||||
<H3 color="white">What You Can Run on Mycelium Cloud</H3>
|
||||
|
||||
<P className="max-w-3xl text-gray-200 mt-6">
|
||||
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.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* ================================
|
||||
Two-column layout
|
||||
================================= */}
|
||||
<div className="flex flex-col lg:flex-row gap-16">
|
||||
|
||||
{/* 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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right: Tabs */}
|
||||
<div className="w-full lg:w-1/2 text-white">
|
||||
|
||||
{/* Tabs Navigation */}
|
||||
<div className="flex gap-6 border-b border-white/10 pb-2">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActive(tab.id)}
|
||||
className={`text-sm font-medium tracking-wide leading-tight pb-2 ${
|
||||
active === tab.id
|
||||
? "border-b-2 border-cyan-500 text-white"
|
||||
: "text-gray-400 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab Content */}
|
||||
<div className="mt-6 space-y-6">
|
||||
<div>
|
||||
<p className="text-lg lg:text-xl font-medium text-white">{current.item}</p>
|
||||
<p className="mt-2 text-base text-gray-300 leading-relaxed">
|
||||
{current.desc}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="text-sm uppercase tracking-wide text-cyan-500 font-semibold">
|
||||
Key capabilities
|
||||
</p>
|
||||
|
||||
<ul className="space-y-2">
|
||||
{current.bullets.map((b, i) => (
|
||||
<li key={i} className="text-base text-gray-400 flex gap-2">
|
||||
<span className="text-cyan-500">•</span>
|
||||
{b}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{currentButtons && (
|
||||
<div className="mt-8 flex flex-wrap gap-4">
|
||||
<Button
|
||||
as={primaryLinks[active] ? "a" : undefined}
|
||||
to={primaryLinks[active] ?? "#"}
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
{...(primaryLinks[active]
|
||||
? { target: "_blank", rel: "noopener noreferrer" }
|
||||
: {})}
|
||||
>
|
||||
{currentButtons.primary}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
as={secondaryLinks[active] ? "a" : undefined}
|
||||
to={secondaryLinks[active] ?? "#"}
|
||||
variant="outline"
|
||||
color="white"
|
||||
{...(secondaryLinks[active]
|
||||
? { target: "_blank", rel: "noopener noreferrer" }
|
||||
: {})}
|
||||
>
|
||||
{currentButtons.secondary}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Borders */}
|
||||
<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,11 +1,8 @@
|
||||
import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { CloudArchitecture } from './CloudArchitecture'
|
||||
import { CloudUseCases } from './CloudUseCases'
|
||||
import { CloudHeroNew } from './CloudHeroNew'
|
||||
import { CloudBluePrint } from './CloudBluePrint'
|
||||
import { CallToAction } from './CalltoAction'
|
||||
import { CloudHostingNew } from './CloudHostingNew'
|
||||
import { CloudFeaturesLight } from './CloudFeaturesLight'
|
||||
import { CloudIntro } from './CloudIntro'
|
||||
import { CloudPros } from './CloudPros'
|
||||
|
||||
|
||||
export default function CloudPage() {
|
||||
@@ -17,23 +14,11 @@ export default function CloudPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudHostingNew />
|
||||
<CloudIntro />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudFeaturesLight />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudArchitecture />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudUseCases />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<CloudBluePrint />
|
||||
<CloudPros />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
|
||||
89
src/pages/cloud/CloudPros.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { H3, P, Eyebrow, Small } 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 CloudPros() {
|
||||
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-white 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-400">
|
||||
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 */}
|
||||
<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-base 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>
|
||||
);
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export function CloudUseCases() {
|
||||
<div className="max-w-7xl bg-white mx-auto py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<Eyebrow className="text-cyan-500">USE CASES</Eyebrow>
|
||||
<Eyebrow className="text-cyan-400">USE CASES</Eyebrow>
|
||||
|
||||
<H3 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
|
||||
Built for intelligent workloads across every edge.
|
||||
|
||||
@@ -13,8 +13,28 @@ export function CallToAction() {
|
||||
{/* ✅ Main boxed area */}
|
||||
<div
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
className="relative py-18 max-w-7xl overflow-hidden mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
>
|
||||
{/* ✅ Cyan Radial Glow */}
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
aria-hidden="true"
|
||||
className="absolute top-full left-1/2 w-7xl h-320 -translate-x-1/2 -translate-y-1/2 mask-image mask-[radial-gradient(circle,white,transparent)]"
|
||||
>
|
||||
<circle
|
||||
r={512}
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#mycelium-cyan-glow)"
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
@@ -23,7 +43,7 @@ export function CallToAction() {
|
||||
Choose How You Want to Start
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
<p className="mt-6 lg:text-lg text-base leading-normal text-gray-300">
|
||||
Host your own node to contribute capacity or deploy workloads using the Mycelium Cloud.
|
||||
You don’t need to host before deploying, and you don’t need to deploy before hosting.
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import { Eyebrow, H3, P } from '@/components/Texts'
|
||||
import { Button } from '@/components/Button'
|
||||
|
||||
const capabilities = [
|
||||
{
|
||||
@@ -76,14 +75,8 @@ export function ComputeCapabilities() {
|
||||
</ul>
|
||||
|
||||
{/* Button section */}
|
||||
<div className="mx-auto mt-12 flex justify-center gap-6">
|
||||
<Button variant="solid" color="cyan" href="#">
|
||||
Get Started
|
||||
</Button>
|
||||
<Button variant="outline" color="white" href="#">
|
||||
Explore Docs
|
||||
</Button>
|
||||
</div>
|
||||
{/* TODO: CTA buttons (Get Started / Explore Docs) to be re-enabled when real links are available.
|
||||
Previously rendered here as two Buttons with href="#". */}
|
||||
</Container>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -78,12 +78,8 @@ export function ComputeCapabilitiesNew() {
|
||||
|
||||
{/* Arrows inside first card */}
|
||||
<div className="flex items-center gap-x-4 mt-2">
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center gap-1 text-cyan-400 hover:text-cyan-300 text-sm font-medium mr-auto"
|
||||
>
|
||||
Learn more →
|
||||
</a>
|
||||
{/* TODO: Intro card CTA (Learn more) to be added here when destination is defined.
|
||||
Previously: <a href="#" className="inline-flex items-center ...">Learn more →</a> */}
|
||||
<button
|
||||
onClick={scrollLeft}
|
||||
className="h-8 w-8 flex items-center justify-center border border-gray-800 rounded-md hover:border-cyan-500 transition-colors"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -35,9 +35,8 @@ export function ComputeHero({ onGetStartedClick = () => {} }: { onGetStartedClic
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Documentation <span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
{/* TODO: Secondary CTA (Documentation) link to be wired when docs route is ready.
|
||||
Previously: <Button to="#" variant="outline">Documentation →</Button> */}
|
||||
</div>
|
||||
</div>
|
||||
</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 — no VPNs, no exposed ports.",
|
||||
desc: "All services communicate through Mycelium Mesh without 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>
|
||||
|
||||
@@ -36,8 +36,12 @@ const features = [
|
||||
|
||||
export function DevHub() {
|
||||
return (
|
||||
<div className="bg-[#121212] py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<section className="bg-[#121212] w-full max-w-8xl mx-auto">
|
||||
{/* Top spacing + border */}
|
||||
<div className="max-w-7xl mx-auto py-6 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="relative px-6 lg:px-6 py-12 bg-[#111111] border border-t-0 border-b-0 border-gray-800 max-w-7xl mx-auto">
|
||||
<div className="mx-auto grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-5">
|
||||
<div className="col-span-2">
|
||||
<h2 className="mb-2 text-base font-semibold leading-7 text-cyan-500">Get Started</h2>
|
||||
@@ -63,7 +67,11 @@ export function DevHub() {
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom horizontal line with spacing (match NetworkDownload) */}
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,14 +40,14 @@ const features = [
|
||||
|
||||
export function DownloadHero() {
|
||||
return (
|
||||
<div className="py-16 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<section id="download" className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8 bg-white pt-24 pb-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-5xl font-medium tracking-tight text-gray-900 lg:text-6xl"
|
||||
className="text-4xl font-medium tracking-tight text-gray-900 lg:text-5xl"
|
||||
>
|
||||
Download Mycelium Network
|
||||
</motion.h2>
|
||||
@@ -55,7 +55,7 @@ export function DownloadHero() {
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mt-6 text-lg text-gray-600 lg:leading-8"
|
||||
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
|
||||
@@ -68,8 +68,9 @@ export function DownloadHero() {
|
||||
</a>
|
||||
</motion.p>
|
||||
</div>
|
||||
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 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">
|
||||
|
||||
<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 lg:gap-y-16 gap-y-8 md:grid-cols-2 lg:max-w-none lg:grid-cols-4">
|
||||
{features.map((feature) => (
|
||||
<div
|
||||
key={feature.name}
|
||||
@@ -99,6 +100,10 @@ export function DownloadHero() {
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom horizontal line with spacing (match NetworkDownload) */}
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,28 @@ export function CallToAction() {
|
||||
{/* ✅ Main boxed area */}
|
||||
<div
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
className="relative py-18 max-w-7xl overflow-hidden mx-auto bg-[#111111] border border-t-0 border-b-0 border-gray-800"
|
||||
>
|
||||
{/* ✅ Cyan Radial Glow */}
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
aria-hidden="true"
|
||||
className="absolute top-full left-1/2 w-7xl h-320 -translate-x-1/2 -translate-y-1/2 mask-image mask-[radial-gradient(circle,white,transparent)]"
|
||||
>
|
||||
<circle
|
||||
r={512}
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#mycelium-cyan-glow)"
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
@@ -23,7 +43,7 @@ export function CallToAction() {
|
||||
Choose How You Want to Start
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
<p className="mt-6 lg:text-lg text-base leading-normal text-gray-300">
|
||||
Use GPUs through Mycelium Cloud, or contribute GPU nodes to the mesh and run your own workloads.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -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 workloads — on sovereign hardware.",
|
||||
"GPU acceleration for inference, training, rendering, and agent workload on sovereign hardware.",
|
||||
},
|
||||
{
|
||||
name: "AI / ML Inference & Training",
|
||||
@@ -84,7 +84,7 @@ export function GpuCapabilities() {
|
||||
<div className="flex items-center gap-x-4 mt-3">
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center gap-1 text-cyan-400 hover:text-cyan-300 text-sm font-medium mr-auto"
|
||||
className="inline-flex items-center gap-1 text-cyan-500 hover:text-cyan-400 text-sm font-medium mr-auto"
|
||||
>
|
||||
Learn more →
|
||||
</a>
|
||||
|
||||
@@ -42,7 +42,7 @@ export function GpuOverview() {
|
||||
<div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-800 bg-[#111111] py-12">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<Eyebrow className="text-cyan-400 tracking-[0.32em] uppercase">
|
||||
<Eyebrow className="text-cyan-500 tracking-[0.32em] uppercase">
|
||||
PLATFORM OVERVIEW
|
||||
</Eyebrow>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export function CallToAction() {
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#mycelium-cyan-glow)"
|
||||
fillOpacity="0.2"
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
@@ -37,33 +37,37 @@ export function CallToAction() {
|
||||
</svg>
|
||||
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<H3 className=" text-white ">
|
||||
A Living Network
|
||||
</H3>
|
||||
Use the Mycelium Stack Your Way
|
||||
</H3>
|
||||
|
||||
<P className="mt-6 text-gray-300">
|
||||
Mycelium isn’t a platform.
|
||||
It’s the soil where a new internet grows — open, resilient, and alive.
|
||||
<P className="mt-6 lg:text-lg text-base leading-normal text-gray-300">
|
||||
Deploy infrastructure, run workloads, connect environments, and build distributed AI systems, all on one network designed for autonomy and control.
|
||||
</P>
|
||||
|
||||
<P className="mt-4 text-gray-300">
|
||||
The self-sovereign network powering the next internet.
|
||||
<P className="mt-4 lg:text-lg text-base leading-normal text-gray-300">
|
||||
Start wherever you are. Scale on your own terms.
|
||||
</P>
|
||||
|
||||
<div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
|
||||
<Button to="/cloud" variant="solid" color="cyan">
|
||||
Join Mycelium
|
||||
<div className="mt-10 flex flex-wrap justify-center items-center gap-x-6 gap-y-4">
|
||||
<Button to="/network#download" variant="solid" color="cyan">
|
||||
Join the Network
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
as="a"
|
||||
target="_blank"
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
color="white"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Join Early Cloud Access
|
||||
Deploy in Cloud
|
||||
</Button>
|
||||
|
||||
<Button to="/nodes" variant="link" color="white">
|
||||
Host a Node →
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
import NoExtraction from "./animations/NoExtraction";
|
||||
import NoControl from "./animations/NoControl";
|
||||
import NoCentral from "./animations/NoCentral";
|
||||
import NoSinglePoint from "./animations/NoSinglePoint";
|
||||
|
||||
const deterministicCards = [
|
||||
{
|
||||
@@ -11,7 +12,7 @@ const deterministicCards = [
|
||||
eyebrow: "Why It Matters",
|
||||
title: "Built for a Sovereign Digital World",
|
||||
description:
|
||||
"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 modern internet still runs on centralized platforms that own the servers, shape the rules, and extract the data. Mycelium gives you a way out. You operate the infrastructure. You keep the data. You decide the boundaries.",
|
||||
animation: null,
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
@@ -23,7 +24,7 @@ const deterministicCards = [
|
||||
title: "No central servers.",
|
||||
description:
|
||||
"Your devices form a distributed network, eliminating reliance on centralized data centers.",
|
||||
animation: <NoCentral className="lg:-mt-12" />, // ✅ NEW
|
||||
animation: <NoCentral />, // ✅ NEW
|
||||
colSpan: "lg:col-span-3",
|
||||
rowSpan: "lg:row-span-1",
|
||||
rounded: "lg:rounded-tr-4xl max-lg:rounded-t-4xl",
|
||||
@@ -34,19 +35,30 @@ const deterministicCards = [
|
||||
title: "No data extraction.",
|
||||
description:
|
||||
"You own your data. Run services and AI models on your own devices, ensuring privacy and control.",
|
||||
animation: <NoExtraction className="lg:-mt-12" />, // ✅ NEW
|
||||
colSpan: "lg:col-span-3",
|
||||
animation: <NoExtraction />, // ✅ NEW
|
||||
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: "healing",
|
||||
title: "No single point of control.",
|
||||
title: "No single point of failure.",
|
||||
description:
|
||||
"No single entity can dictate or censor your online experience.",
|
||||
animation: <NoSinglePoint />, // ✅ NEW
|
||||
colSpan: "lg:col-span-2",
|
||||
rowSpan: "lg:row-span-1",
|
||||
rounded: "",
|
||||
innerRounded: "",
|
||||
},
|
||||
{
|
||||
id: "control",
|
||||
title: "No single point of control.",
|
||||
description:
|
||||
"Infrastructure that moves with its operators, not a corporation.",
|
||||
animation: <NoControl />, // ✅ NEW
|
||||
colSpan: "lg:col-span-3",
|
||||
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)]",
|
||||
@@ -65,7 +77,7 @@ export function HomeArchitecture() {
|
||||
{deterministicCards.map((card) => (
|
||||
<div
|
||||
key={card.id}
|
||||
className={`relative ${card.colSpan} ${card.rowSpan} transition-transform duration-300 hover:scale-102 group`}
|
||||
className={`relative flex flex-col ${card.colSpan} ${card.rowSpan} transition-transform duration-300 hover:scale-102 group`}
|
||||
>
|
||||
{/* ✅ Disable wrapper on first card */}
|
||||
{!card.noBorder && (
|
||||
@@ -79,13 +91,13 @@ export function HomeArchitecture() {
|
||||
>
|
||||
{/* ✅ SVG Animation instead of images */}
|
||||
{card.animation ? (
|
||||
<div className="lg:h-64 h-48 w-full overflow-hidden bg-transparent flex items-center">
|
||||
<div className="w-full h-full">
|
||||
<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" />
|
||||
<div className="h-48 w-full items-center justify-center bg-transparent hidden md:flex" />
|
||||
)}
|
||||
|
||||
<div className="px-8 pt-4 pb-6">
|
||||
|
||||
@@ -72,7 +72,10 @@ export function HomeAudience() {
|
||||
|
||||
<P className="text-gray-800 mt-4">
|
||||
The internet wasn’t built for sovereignty. Today, data, AI models, and identity
|
||||
live on centralized clouds — owned by a few. Mycelium brings infrastructure back
|
||||
live on centralized clouds and owned by a few.
|
||||
</P>
|
||||
<P className="text-gray-800 mt-4">
|
||||
Mycelium brings infrastructure back
|
||||
to people, communities, and nations: private, resilient, and cryptographically yours.
|
||||
</P>
|
||||
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
import { H1, H4, H5 } from "@/components/Texts"
|
||||
import { H3, H5, Eyebrow } from "@/components/Texts"
|
||||
import { Button } from "@/components/Button"
|
||||
|
||||
export function HomeAurora({ onGetStartedClick }: { onGetStartedClick: () => void }) {
|
||||
export function HomeAurora() {
|
||||
return (
|
||||
<div className="px-4">
|
||||
{/* Boxed container */}
|
||||
<div
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-gray-100 bg-white overflow-hidden bg-contain bg-right bg-no-repeat"
|
||||
style={{ backgroundImage: "url('/images/hero11.webp')" }}
|
||||
className="relative mx-auto max-w-7xl border border-t-0 border-gray-100 bg-white overflow-hidden bg-size-[65%] bg-right bg-no-repeat bg-none md:bg-[url('/images/hero11.webp')]"
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-32 ">
|
||||
<div className="px-6 pb-12 pt-8 lg:py-32 ">
|
||||
{/* Mobile-only hero image */}
|
||||
<img
|
||||
src="/images/mobile/homepage.jpg"
|
||||
alt="Mycelium homepage visual"
|
||||
className="mb-8 w-full object-cover md:hidden"
|
||||
/>
|
||||
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<div className="hidden sm:flex">
|
||||
<div className="relative rounded-full px-3 py-1 text-sm/6 text-gray-500 ring-1 ring-gray-900/10 hover:ring-gray-900/20">
|
||||
Deploying at scale?{' '}
|
||||
<a href="#" className="font-semibold whitespace-nowrap text-cyan-600">
|
||||
<span aria-hidden="true" className="absolute inset-0" />
|
||||
Book a call <span>→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<Eyebrow> Project MYCELIUM</Eyebrow>
|
||||
<H3 className="mt-4">
|
||||
Secure, Distributed Infrastructure Built
|
||||
<br />
|
||||
for Digital Sovereignty
|
||||
</H3>
|
||||
|
||||
<H1 className="mt-8">
|
||||
MYCELIUM
|
||||
</H1>
|
||||
|
||||
<H4 className="mt-8">
|
||||
The Living Network of the Next Internet
|
||||
</H4>
|
||||
|
||||
<H5 className="mt-8 text-lg text-gray-600">
|
||||
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.
|
||||
<H5 className="mt-6 text-lg text-gray-600 max-w-xl">
|
||||
Run your apps, data, and intelligence on infrastructure that belongs to you
|
||||
</H5>
|
||||
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<div className="mt-8 flex items-center gap-x-6">
|
||||
<Button
|
||||
to="/nodes"
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
onClick={onGetStartedClick}
|
||||
>
|
||||
Start Hosting
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Deploy in Cloud →
|
||||
<Button
|
||||
as="a"
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Deploy in Cloud
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 — private, distributed, and self-sovereign.
|
||||
A new internet is emerging, a 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">
|
||||
|
||||
85
src/pages/home/HomeDesign.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
const benefits = [
|
||||
{
|
||||
id: 1,
|
||||
title: "For Integrators & Builders",
|
||||
description:
|
||||
"Deploy sovereign infrastructure for organizations, governments, and large-scale systems.",
|
||||
image: "/images/dev.png",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "For Enterprises & Institutions",
|
||||
description:
|
||||
"Protect data, meet local compliance, and unlock new AI capabilities across distributed environments.",
|
||||
image: "/images/cons.png",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "For Sovereignty Seekers",
|
||||
description:
|
||||
"Run nodes, build applications, and connect directly without relying on centralized platforms.",
|
||||
image: "/images/seekers.png",
|
||||
},
|
||||
];
|
||||
|
||||
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-100" />
|
||||
<div className="w-full border border-l border-r border-gray-100" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="mx-auto max-w-7xl border border-t-0 border-b-0 bg-white border-gray-100">
|
||||
{/* Centered intro */}
|
||||
<div className="px-6 pt-12 pb-4 text-center max-w-4xl mx-auto ">
|
||||
<Eyebrow className="text-cyan-500">
|
||||
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>
|
||||
|
||||
<dl className="grid grid-cols-1 lg:grid-cols-3 gap-2 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-100 lg:border-t lg:border-b border-l-0.5 border-r-0.5 transition-all duration-300 ease-in-out hover:scale-[1.02] hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
|
||||
>
|
||||
{/* Image on the LEFT */}
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
className="h-30 w-30 object-contain opacity-90"
|
||||
/>
|
||||
|
||||
{/* Text on the RIGHT */}
|
||||
<div className="text-left">
|
||||
<h3 className="text-base font-semibold tracking-wide text-gray-900 mb-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 leading-relaxed max-w-xs">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-t 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>
|
||||
);
|
||||
}
|
||||