Compare commits
52 Commits
developmen
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| d469bb7b99 | |||
| 3f89d1c441 | |||
| fa30aeff43 | |||
| cbbc87cc29 | |||
| 8dfc46cabe | |||
| 8fdcf1777d | |||
| 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 |
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: 922 KiB |
BIN
public/images/ainode.png
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
public/images/autonomous.png
Normal file
|
After Width: | Height: | Size: 988 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: 780 KiB |
BIN
public/images/edgenode.png
Normal file
|
After Width: | Height: | Size: 796 KiB |
BIN
public/images/encryptednode.png
Normal file
|
After Width: | Height: | Size: 906 KiB |
BIN
public/images/energy.png
Normal file
|
After Width: | Height: | Size: 861 KiB |
BIN
public/images/fullstack.png
Normal file
|
After Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 44 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: 874 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>
|
||||
|
||||
@@ -18,7 +18,7 @@ const variantStyles = {
|
||||
},
|
||||
outline: {
|
||||
cyan: 'border-cyan-500 text-cyan-500',
|
||||
gray: 'border-gray-300 text-white hover:text-cyan-500 hover:border-cyan-500 active:border-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',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,20 +15,20 @@ export function Footer() {
|
||||
</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>
|
||||
|
||||
@@ -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 { 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,64 +1,41 @@
|
||||
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/logowhite.png'
|
||||
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||
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="">
|
||||
<nav className="border-b border-gray-800">
|
||||
<Container className="flex bg-transparent justify-between py-4">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link to="/" aria-label="Home">
|
||||
<img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<Dropdown
|
||||
buttonClassName="bg-transparent"
|
||||
buttonContent={
|
||||
<>
|
||||
{['Compute', 'Storage', 'GPU'].includes(getCurrentPageName()) ? (
|
||||
<>
|
||||
<span className="text-white bg-[#111111]">Cloud {' >'} </span>
|
||||
<span className="text-white bg-[#111111]">{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"
|
||||
@@ -66,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>
|
||||
@@ -125,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"
|
||||
@@ -142,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"
|
||||
@@ -150,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,9 +1,9 @@
|
||||
import { TextHoverEffects } from "@/components/ui/text-hover-effects";
|
||||
import { TextHoverEffect } from "@/components/ui/text-hover-effect";
|
||||
|
||||
export function HomeHeadline() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-auto max-h-[200px]">
|
||||
<TextHoverEffects text="MYCELIUM" />
|
||||
<TextHoverEffect text="MYCELIUM" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { TextHoverEffect } from "@/components/ui/text-hover-effect";
|
||||
|
||||
export function HomeHeadlineDark() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-auto max-h-[200px]">
|
||||
<TextHoverEffect text="MYCELIUM" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,47 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import { Footer } from './Footer'
|
||||
import { HeaderDark } from './HeaderDark'
|
||||
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)' }}>
|
||||
<div className="relative z-10">
|
||||
<HeaderDark />
|
||||
<Header />
|
||||
<main>
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -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,13 @@ 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 && "animate-infinite-scroll",
|
||||
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>
|
||||
))}
|
||||
|
||||
@@ -10,14 +10,13 @@ interface DropdownItem {
|
||||
interface DropdownProps {
|
||||
buttonContent: React.ReactNode
|
||||
items: DropdownItem[]
|
||||
buttonClassName?: string
|
||||
}
|
||||
|
||||
export function Dropdown({ buttonContent, items, buttonClassName }: DropdownProps) {
|
||||
export function Dropdown({ buttonContent, items }: DropdownProps) {
|
||||
return (
|
||||
<Menu as="div" className="relative inline-block text-left">
|
||||
<div>
|
||||
<Menu.Button className={`inline-flex w-full justify-center items-center gap-x-1.5 rounded-md text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors ${buttonClassName}`}>
|
||||
<Menu.Button className="inline-flex w-full justify-center items-center gap-x-1.5 rounded-md bg-white text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors">
|
||||
{buttonContent}
|
||||
</Menu.Button>
|
||||
</div>
|
||||
|
||||
131
src/components/ui/DynamicMapContainer.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
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=99999";
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchNodeData() {
|
||||
try {
|
||||
const response = await fetch(API_URL);
|
||||
const data: RawNode[] = await response.json(); // Type the incoming data
|
||||
|
||||
// 🚨 Map the API response to your component's expected GeoNode format
|
||||
const geoNodes: GeoNode[] = data
|
||||
.filter((node: RawNode) => node.location && node.location.latitude && node.location.longitude)
|
||||
.map((node: RawNode) => ({
|
||||
// Convert string coordinates to numbers
|
||||
lat: parseFloat(node.location.latitude),
|
||||
lng: parseFloat(node.location.longitude),
|
||||
label: `${node.location.city}, ${node.location.country} (${node.node_id})`,
|
||||
// Optionally set color based on some node property if available
|
||||
}));
|
||||
|
||||
const clusteredNodes = clusterNodes(geoNodes);
|
||||
setNodes(clusteredNodes);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch node data:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchNodeData();
|
||||
}, []);
|
||||
|
||||
// --- RENDERING ---
|
||||
|
||||
if (loading) {
|
||||
// Show a loading state while data is being fetched
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
// Pass the dynamically fetched nodes to your WorldMap component
|
||||
return (
|
||||
<WorldMap
|
||||
nodes={nodes}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default DynamicMapContainer;
|
||||
@@ -1,31 +1,35 @@
|
||||
"use client";
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
import { motion, useAnimation } from "motion/react";
|
||||
|
||||
export const TextHoverEffect = ({
|
||||
text,
|
||||
duration,
|
||||
duration = 6, // loop duration
|
||||
}: {
|
||||
text: string;
|
||||
duration?: number;
|
||||
automatic?: boolean;
|
||||
}) => {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const [cursor, setCursor] = useState({ x: 0, y: 0 });
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const [maskPosition, setMaskPosition] = useState({ cx: "50%", cy: "50%" });
|
||||
const controls = useAnimation();
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
// ✅ Animate mask looping automatically
|
||||
useEffect(() => {
|
||||
if (svgRef.current && cursor.x !== null && cursor.y !== null) {
|
||||
const svgRect = svgRef.current.getBoundingClientRect();
|
||||
const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;
|
||||
const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;
|
||||
setMaskPosition({
|
||||
cx: `${cxPercentage}%`,
|
||||
cy: `${cyPercentage}%`,
|
||||
});
|
||||
}
|
||||
}, [cursor]);
|
||||
const loop = async () => {
|
||||
while (true) {
|
||||
await controls.start({
|
||||
cx: ["20%", "80%", "50%"],
|
||||
cy: ["20%", "80%", "50%"],
|
||||
transition: {
|
||||
duration,
|
||||
ease: "easeInOut",
|
||||
repeat: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
loop();
|
||||
}, [controls, duration]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
@@ -36,96 +40,97 @@ export const TextHoverEffect = ({
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })}
|
||||
className="select-none"
|
||||
className="select-none"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="textGradient"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
r="25%"
|
||||
>
|
||||
{hovered && (
|
||||
{/* ✅ Softer cyan gradient */}
|
||||
<linearGradient id="textGradient" gradientUnits="userSpaceOnUse">
|
||||
{hovered ? (
|
||||
<>
|
||||
<stop offset="0%" stopColor="#eab308" />
|
||||
<stop offset="25%" stopColor="#ef4444" />
|
||||
<stop offset="50%" stopColor="#3b82f6" />
|
||||
<stop offset="75%" stopColor="#06b6d4" />
|
||||
<stop offset="100%" stopColor="#8b5cf6" />
|
||||
<stop offset="0%" stopColor="#7df3ff" />
|
||||
<stop offset="40%" stopColor="#4adffa" />
|
||||
<stop offset="70%" stopColor="#18c5e8" />
|
||||
<stop offset="100%" stopColor="#0aaecb" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<stop offset="0%" stopColor="#7df3ff33" />
|
||||
<stop offset="100%" stopColor="#0aaecb33" />
|
||||
</>
|
||||
)}
|
||||
</linearGradient>
|
||||
|
||||
{/* ✅ Mask with autoplay motion */}
|
||||
<motion.radialGradient
|
||||
id="revealMask"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="20%"
|
||||
r="45%"
|
||||
animate={controls}
|
||||
initial={{ cx: "50%", cy: "50%" }}
|
||||
animate={maskPosition}
|
||||
transition={{ duration: duration ?? 0, ease: "easeOut" }}
|
||||
|
||||
// example for a smoother animation below
|
||||
|
||||
// transition={{
|
||||
// type: "spring",
|
||||
// stiffness: 300,
|
||||
// damping: 50,
|
||||
// }}
|
||||
>
|
||||
<stop offset="0%" stopColor="white" />
|
||||
<stop offset="100%" stopColor="black" />
|
||||
</motion.radialGradient>
|
||||
|
||||
{/* ✅ Glow */}
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="3.2" result="blur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<mask id="textMask">
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
fill="url(#revealMask)"
|
||||
/>
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="url(#revealMask)" />
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
{/* ✅ Background faint stroke */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.3"
|
||||
className="fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800"
|
||||
style={{ opacity: hovered ? 0.7 : 0 }}
|
||||
strokeWidth="0.15"
|
||||
className="fill-transparent stroke-neutral-300 dark:stroke-neutral-800 font-[helvetica] text-7xl font-bold"
|
||||
style={{ opacity: hovered ? 0.25 : 0.1 }}
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
|
||||
{/* ✅ Line drawing animation always plays too */}
|
||||
<motion.text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.3"
|
||||
className="fill-transparent stroke-neutral-200 font-[helvetica] text-7xl font-bold dark:stroke-neutral-800"
|
||||
initial={{ strokeDashoffset: 1000, strokeDasharray: 1000 }}
|
||||
strokeWidth="0.25"
|
||||
className="fill-transparent stroke-cyan-300 font-[helvetica] text-7xl font-bold"
|
||||
initial={{ strokeDashoffset: 600, strokeDasharray: 600 }}
|
||||
animate={{
|
||||
strokeDashoffset: 0,
|
||||
strokeDasharray: 1000,
|
||||
strokeDasharray: 600,
|
||||
}}
|
||||
transition={{
|
||||
duration: 4,
|
||||
duration: 2.2,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</motion.text>
|
||||
|
||||
{/* ✅ Final filled glowing cyan text (mask reveals it) */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
stroke="url(#textGradient)"
|
||||
strokeWidth="0.3"
|
||||
strokeWidth="1.1"
|
||||
mask="url(#textMask)"
|
||||
className="fill-transparent font-[helvetica] text-7xl font-bold"
|
||||
filter="url(#glow)"
|
||||
className="font-[helvetica] text-7xl font-bold fill-[url(#textGradient)]"
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
"use client";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
import { motion, useAnimation } from "motion/react";
|
||||
|
||||
export const TextHoverEffects = ({
|
||||
text,
|
||||
duration = 6, // loop duration
|
||||
}: {
|
||||
text: string;
|
||||
duration?: number;
|
||||
}) => {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const controls = useAnimation();
|
||||
const [hovered, setHovered] = useState(false);
|
||||
|
||||
// ✅ Animate mask looping automatically
|
||||
useEffect(() => {
|
||||
const loop = async () => {
|
||||
while (true) {
|
||||
await controls.start({
|
||||
cx: ["20%", "80%", "50%"],
|
||||
cy: ["20%", "80%", "50%"],
|
||||
transition: {
|
||||
duration,
|
||||
ease: "easeInOut",
|
||||
repeat: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
loop();
|
||||
}, [controls, duration]);
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={svgRef}
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 300 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
className="select-none"
|
||||
>
|
||||
<defs>
|
||||
{/* ✅ Softer cyan gradient */}
|
||||
<linearGradient id="textGradient" gradientUnits="userSpaceOnUse">
|
||||
{hovered ? (
|
||||
<>
|
||||
<stop offset="0%" stopColor="#7df3ff" />
|
||||
<stop offset="40%" stopColor="#4adffa" />
|
||||
<stop offset="70%" stopColor="#18c5e8" />
|
||||
<stop offset="100%" stopColor="#0aaecb" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<stop offset="0%" stopColor="#7df3ff33" />
|
||||
<stop offset="100%" stopColor="#0aaecb33" />
|
||||
</>
|
||||
)}
|
||||
</linearGradient>
|
||||
|
||||
{/* ✅ Mask with autoplay motion */}
|
||||
<motion.radialGradient
|
||||
id="revealMask"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
r="45%"
|
||||
animate={controls}
|
||||
initial={{ cx: "50%", cy: "50%" }}
|
||||
>
|
||||
<stop offset="0%" stopColor="white" />
|
||||
<stop offset="100%" stopColor="black" />
|
||||
</motion.radialGradient>
|
||||
|
||||
{/* ✅ Glow */}
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="3.2" result="blur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<mask id="textMask">
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="url(#revealMask)" />
|
||||
</mask>
|
||||
</defs>
|
||||
|
||||
{/* ✅ Background faint stroke */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.15"
|
||||
className="fill-transparent stroke-neutral-300 dark:stroke-neutral-800 font-[helvetica] text-7xl font-bold"
|
||||
style={{ opacity: hovered ? 0.25 : 0.1 }}
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
|
||||
{/* ✅ Line drawing animation always plays too */}
|
||||
<motion.text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
strokeWidth="0.25"
|
||||
className="fill-transparent stroke-cyan-300 font-[helvetica] text-7xl font-bold"
|
||||
initial={{ strokeDashoffset: 600, strokeDasharray: 600 }}
|
||||
animate={{
|
||||
strokeDashoffset: 0,
|
||||
strokeDasharray: 600,
|
||||
}}
|
||||
transition={{
|
||||
duration: 2.2,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</motion.text>
|
||||
|
||||
{/* ✅ Final filled glowing cyan text (mask reveals it) */}
|
||||
<text
|
||||
x="50%"
|
||||
y="50%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
stroke="url(#textGradient)"
|
||||
strokeWidth="1.1"
|
||||
mask="url(#textMask)"
|
||||
filter="url(#glow)"
|
||||
className="font-[helvetica] text-7xl font-bold fill-[url(#textGradient)]"
|
||||
>
|
||||
{text}
|
||||
</text>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -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,181 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { Dropdown } from './ui/Dropdown'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { Container } from './Container'
|
||||
import { Button } from './Button'
|
||||
import pmyceliumLogo from '../images/logos/logowhite.png'
|
||||
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">
|
||||
<Container className="flex bg-transparent justify-between py-4">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link to="/" aria-label="Home">
|
||||
<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="/agents"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Agents
|
||||
</Link>
|
||||
<Link
|
||||
to="/pods"
|
||||
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
>
|
||||
Pods
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-6 max-lg:hidden">
|
||||
<Button
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
as="a"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Deploy Now
|
||||
</Button>
|
||||
<Button to="/download" variant="solid" color="cyan">
|
||||
Get Mycelium Connector
|
||||
</Button>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
onClick={() => setMobileMenuOpen(true)}
|
||||
>
|
||||
<span className="sr-only">Open main menu</span>
|
||||
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</nav>
|
||||
<Dialog as="div" className="lg:hidden" open={mobileMenuOpen} onClose={setMobileMenuOpen}>
|
||||
<div className="fixed inset-0 z-10" />
|
||||
<Dialog.Panel className="fixed inset-y-0 right-0 z-10 w-full overflow-y-auto bg-[#111111] px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-200/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link to="#" className="-m-1.5 p-1.5">
|
||||
<span className="sr-only">Mycelium</span>
|
||||
<img
|
||||
className="h-8 w-auto"
|
||||
src={pmyceliumLogo}
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2.5 rounded-md p-2.5 text-gray-300 hover:text-cyan-400 transition-colors"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<span className="sr-only">Close menu</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<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"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Network
|
||||
</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"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Agents
|
||||
</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>
|
||||
</div>
|
||||
<div className="py-6">
|
||||
<Button
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
as="a"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
Start Deployment
|
||||
</Button>
|
||||
<Button to="/download" variant="solid" color="cyan" className="mt-4 w-full" onClick={() => setMobileMenuOpen(false)}>
|
||||
Get Mycelium Connector
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 44 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,17 +118,9 @@ 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" />
|
||||
|
||||
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,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Eyebrow, H3 } from '@/components/Texts'
|
||||
import { Eyebrow, H3, P } from '@/components/Texts'
|
||||
|
||||
export function AgentHeroAlt() {
|
||||
return (
|
||||
@@ -12,21 +12,20 @@ export function AgentHeroAlt() {
|
||||
style={{ backgroundImage: "url('/images/agents.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM AGENTS</Eyebrow>
|
||||
<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-800">
|
||||
Mycelium Agents let you deploy and run intelligent systems on your own infrastructure.
|
||||
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
|
||||
Follow Development
|
||||
</Button>
|
||||
<Button href="#" variant="outline">
|
||||
Explore Docs <span aria-hidden="true">→</span>
|
||||
|
||||
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-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl mx-auto flex flex-col items-center justify-center min-h-[220px] text-center">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Advantages
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Why It’s Different
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 text-base leading-relaxed">
|
||||
Most AI systems run on centralized clouds, where the models, data, and
|
||||
logic operate behind third-party APIs. Mycelium Agents flip that
|
||||
architecture, it runs entirely inside your environment so control,
|
||||
privacy, and autonomy stay with you.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
121
src/pages/agents/AgentUseCase.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, SectionHeader, P } 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="border border-t-0 border-b-0 border-gray-100 bg-white">
|
||||
<div className="mx-auto max-w-4xl sm:text-center py-12">
|
||||
{/* Intro block (from isIntro item) */}
|
||||
{networkUseCases[0].isIntro && (
|
||||
<>
|
||||
<Eyebrow className="text-cyan-600">{networkUseCases[0].eyebrow}</Eyebrow>
|
||||
<SectionHeader
|
||||
as="h3"
|
||||
className="mt-4 text-gray-900 text-3xl lg:text-4xl"
|
||||
>
|
||||
{networkUseCases[0].title}
|
||||
</SectionHeader>
|
||||
<P className="mt-6 text-lg text-gray-600">
|
||||
{networkUseCases[0].description}
|
||||
</P>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Grid of features (excluding intro) */}
|
||||
<ul
|
||||
role="list"
|
||||
className="mx-auto mt-6 max-w-6xl grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-y-10 px-6 pb-16"
|
||||
>
|
||||
{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 flex items-center justify-center rounded-xl bg-gray-100">
|
||||
<item.icon className="h-6 w-6 text-cyan-600" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Title */}
|
||||
<p className="mt-6 text-lg font-semibold text-gray-900">
|
||||
{item.title}
|
||||
</p>
|
||||
|
||||
{/* Description */}
|
||||
<p className="mt-2 text-gray-600 text-sm leading-snug">
|
||||
{item.description}
|
||||
</p>
|
||||
</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>
|
||||
|
||||
@@ -15,18 +15,41 @@ export function CallToAction() {
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<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.
|
||||
The Agent Framework launches in H1 2026, but the foundation is ready now.
|
||||
</p>
|
||||
<p className="mt-2 text-lg text-gray-300">
|
||||
Use today’s components —models, storage, compute, and network— to deploy workloads, connect nodes, and prepare for the next generation of distributed AI.
|
||||
</p>
|
||||
|
||||
{/* ✅ Two cards, stacked center with spacing */}
|
||||
<div className="mt-10 flex flex-wrap justify-center gap-x-10 gap-y-8">
|
||||
<div className="mt-8 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
|
||||
@@ -40,9 +63,15 @@ export function CallToAction() {
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-5 font-semibold text-white underline underline-offset-4 decoration-white/70 hover:text-cyan-200 hover:decoration-cyan-200 transition-colors inline-flex items-center gap-1.5"
|
||||
>
|
||||
Follow Development
|
||||
</Button>
|
||||
<span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 ">
|
||||
|
||||
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" 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" 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" 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" 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" 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" 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>
|
||||
);
|
||||
}
|
||||
@@ -15,6 +15,26 @@ export function CallToAction() {
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
|
||||
@@ -27,7 +27,7 @@ export function CloudBluePrint() {
|
||||
</H3>
|
||||
|
||||
<P className="mt-6 text-lg text-gray-600">
|
||||
Digital Me is an example environment built to demonstrate what’s possible on top of the Mycelium Stack — 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>
|
||||
);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ export function CloudFeaturesLight() {
|
||||
</SectionHeader>
|
||||
<P className="mt-6 text-gray-600">
|
||||
Mycelium Cloud runs Kubernetes on a sovereign, self-healing network
|
||||
with compute, storage, and networking built in — so you don’t need
|
||||
with compute, storage, and networking built in, so you don’t need
|
||||
external cloud dependencies.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -10,22 +10,20 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli
|
||||
style={{ backgroundImage: "url('/images/cloudhero4.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<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 className="mt-6 text-lg text-gray-600">
|
||||
Run compute, storage, and AI resources on infrastructure you 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 className="mt-2 text-lg text-gray-600">
|
||||
The Mycelium Cloud runs on a distributed grid of independent nodes,
|
||||
delivering secure, scalable performance wherever your users or data live.
|
||||
</p>
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<Button
|
||||
@@ -33,10 +31,10 @@ export function CloudHeroNew({ onGetStartedClick = () => {} }: { onGetStartedCli
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
>
|
||||
Get started
|
||||
Deploy Workloads
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Documentation <span aria-hidden="true">→</span>
|
||||
Explore Docs <span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
</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 */}
|
||||
|
||||
194
src/pages/cloud/CloudIntro.tsx
Normal file
@@ -0,0 +1,194 @@
|
||||
"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];
|
||||
|
||||
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-400 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 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 font-medium text-white">{current.item}</p>
|
||||
<p className="mt-2 text-base text-gray-400 leading-relaxed">
|
||||
{current.desc}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-2">
|
||||
<p className="text-sm uppercase tracking-wide text-cyan-400 font-semibold">
|
||||
Key capabilities
|
||||
</p>
|
||||
|
||||
<ul className="space-y-2">
|
||||
{current.bullets.map((b, i) => (
|
||||
<li key={i} className="text-base text-gray-300 flex gap-2">
|
||||
<span className="text-cyan-500">•</span>
|
||||
{b}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{currentButtons && (
|
||||
<div className="mt-8 flex flex-wrap gap-4">
|
||||
<Button
|
||||
to="#"
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
>
|
||||
{currentButtons.primary}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
to="#"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
{currentButtons.secondary}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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-[#FDFDFD] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="px-8 py-12 max-w-4xl">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Cloud Advantages
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Why It’s Different
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 leading-relaxed">
|
||||
Most AI systems run on centralized clouds, where the models, data, and
|
||||
logic operate behind third-party APIs. Mycelium Agents flip that
|
||||
architecture, running entirely inside your environment so control,
|
||||
privacy, and autonomy stay with you.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="grid lg:grid-cols-4">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-gray-100 bg-white p-8 transition hover:border-cyan-400/40 hover:bg-white"
|
||||
>
|
||||
{/* Glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-200/0 via-cyan-100/20 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-600">
|
||||
{item.label}
|
||||
</Small>
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold leading-tight text-black">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing */}
|
||||
<div className="w-full border-b border-gray-100 bg-[#FDFDFD]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -15,6 +15,26 @@ export function CallToAction() {
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
|
||||
@@ -33,7 +33,7 @@ export function ComputeFeatures() {
|
||||
</P>
|
||||
|
||||
<P className="mt-3 text-lg text-gray-600">
|
||||
Each component — from message passing to content distribution — works in harmony
|
||||
Each component, from message passing to content distribution — works in harmony
|
||||
to create a fully self-healing, self-optimizing data mesh.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -34,11 +34,11 @@ const tabs = [
|
||||
},
|
||||
{
|
||||
item: "Zero cloud lock-in",
|
||||
desc: "Deploy containers, VMs, or full Kubernetes clusters — migrate off AWS/GCP/Azure with no code changes.",
|
||||
desc: "Deploy containers, VMs, or full Kubernetes clusters, migrate off AWS/GCP/Azure with no code changes.",
|
||||
},
|
||||
{
|
||||
item: "Encrypted networking",
|
||||
desc: "All services communicate through Mycelium Mesh — 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>
|
||||
|
||||
@@ -15,6 +15,26 @@ export function CallToAction() {
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<Container className="relative">
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Container } from '@/components/Container'
|
||||
import { Button } from '@/components/Button'
|
||||
import { H3, P } from '@/components/Texts'
|
||||
@@ -37,34 +38,35 @@ 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.
|
||||
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.
|
||||
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" variant="solid" color="cyan">
|
||||
Join the Network
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
as="a"
|
||||
target="_blank"
|
||||
to="/cloud"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
Join Early Cloud Access
|
||||
Deploy in Cloud
|
||||
</Button>
|
||||
|
||||
<Link to="/nodes" className="text-cyan-400 hover:text-cyan-300 transition-colors">
|
||||
Host a Node →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -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",
|
||||
@@ -35,18 +36,29 @@ const deterministicCards = [
|
||||
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",
|
||||
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,8 +91,8 @@ 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>
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
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 }) {
|
||||
@@ -6,35 +6,23 @@ export function HomeAurora({ onGetStartedClick }: { onGetStartedClick: () => voi
|
||||
<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"
|
||||
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"
|
||||
style={{ backgroundImage: "url('/images/hero11.webp')" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-32 ">
|
||||
<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">
|
||||
Private, Distributed Infrastructure Built
|
||||
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-4 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
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { Spotlight } from "@/components/ui/spotlight";
|
||||
import { H4, H5 } from "@/components/Texts";
|
||||
import { HomeHeadline } from "@/components/HomeHeadline";
|
||||
|
||||
export function HomeBlinkDark({ onGetStartedClick }: { onGetStartedClick: () => void }) {
|
||||
return (
|
||||
<div className="">
|
||||
<div className="relative mx-auto max-w-8xl bg-[#111111] overflow-hidden py-24 lg:py-32 border border-t-0 border-l-0 border-r-0 border-b 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-720 -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.2"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="mycelium-cyan-glow">
|
||||
<stop stopColor="#00e5ff" />
|
||||
<stop offset="1" stopColor="transparent" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div className="relative z-10 mx-auto w-full max-w-8xl md:pt-0">
|
||||
<HomeHeadline />
|
||||
<H4 className="text-center max-w-3xl mx-auto text-white mt-12">Private, distributed infrastructure built for digital sovereignty</H4>
|
||||
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-gray-200">
|
||||
Build and deploy private, peer-to-peer systems with full control of your data, infrastructure, and intelligence.
|
||||
</H5>
|
||||
|
||||
<div className="mt-8 flex justify-center gap-6">
|
||||
<Button variant="solid" color="cyan" onClick={onGetStartedClick}>
|
||||
Enter the Network
|
||||
</Button>
|
||||
<Button variant="outline" color="gray" onClick={onGetStartedClick}>
|
||||
Explore Docs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
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-200" />
|
||||
<div className="w-full border border-l border-r border-gray-200" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-200">
|
||||
{/* Centered intro */}
|
||||
<div className="px-6 pt-12 pb-4 text-center max-w-4xl mx-auto ">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
Who's it For
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-black">
|
||||
Built for Real-World Impact
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-700 text-base leading-relaxed">
|
||||
Whether you’re deploying infrastructure, securing sensitive operations, or simply taking back control of your digital life, Mycelium provides the foundation to build confidently in a connected world.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
<dl className="grid grid-cols-1 lg:grid-cols-3 gap-4 lg:gap-0">
|
||||
{benefits.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="mt-8 group flex items-start gap-2 bg-white px-8 py-12 border border-gray-200 lg:border-t lg:border-b border-l-0.5 border-r-0.5"
|
||||
>
|
||||
{/* 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>
|
||||
);
|
||||
}
|
||||
@@ -1,42 +1,146 @@
|
||||
"use client";
|
||||
import WorldMap from "@/components/ui/world-map";
|
||||
import { useEffect, useState } from "react";
|
||||
import DynamicMapContainer from "@/components/ui/DynamicMapContainer";
|
||||
import { Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
type StatKey = "cores" | "nodes" | "ssd" | "countries";
|
||||
|
||||
type StatsData = Record<StatKey, string>;
|
||||
|
||||
const STAT_API_URL = "https://stats.grid.tf/api/stats-summary";
|
||||
|
||||
const DEFAULT_STATS: StatsData = {
|
||||
cores: "31,669",
|
||||
nodes: "1157",
|
||||
ssd: "4,199,303",
|
||||
countries: "41",
|
||||
};
|
||||
|
||||
const STAT_CARDS: Array<{ key: StatKey; title: string; description: string }> = [
|
||||
{
|
||||
key: "ssd",
|
||||
title: "SSD CAPACITY",
|
||||
description: "Total GB of storage (SSD, HDD, & RAM) on the grid.",
|
||||
},
|
||||
{
|
||||
key: "cores",
|
||||
title: "CORES",
|
||||
description: "Total Central Processing Unit cores available on the grid.",
|
||||
},
|
||||
{
|
||||
key: "nodes",
|
||||
title: "NODES",
|
||||
description: "Total number of nodes on the grid.",
|
||||
},
|
||||
|
||||
{
|
||||
key: "countries",
|
||||
title: "COUNTRIES",
|
||||
description: "Total number of countries with active nodes.",
|
||||
},
|
||||
];
|
||||
|
||||
export function HomeMap() {
|
||||
const [stats, setStats] = useState<StatsData>(DEFAULT_STATS);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
const formatValue = (value: unknown, fallback: string) => {
|
||||
if (typeof value === "number") {
|
||||
return value.toLocaleString();
|
||||
}
|
||||
if (typeof value === "string" && value.trim().length) {
|
||||
const numeric = Number(value);
|
||||
return Number.isNaN(numeric) ? value : numeric.toLocaleString();
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const response = await fetch(STAT_API_URL);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed with ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
setStats({
|
||||
cores: formatValue(data?.cores, DEFAULT_STATS.cores),
|
||||
nodes: formatValue(data?.nodes, DEFAULT_STATS.nodes),
|
||||
ssd: formatValue(data?.ssd, DEFAULT_STATS.ssd),
|
||||
countries: formatValue(data?.countries, DEFAULT_STATS.countries),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[HomeMap] Failed to load stats", error);
|
||||
} finally {
|
||||
if (isMounted) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetchStats();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="bg-[#121212] w-full">
|
||||
{/* ✅ 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-800"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
<div className="max-w-7xl mx-auto text-center py-12 border border-t-0 border-b-0 border-gray-800">
|
||||
<Eyebrow>Mycelium Nodes</Eyebrow>
|
||||
<div className="max-w-7xl mx-auto text-center pt-12 border border-t-0 border-b-0 border-gray-800 px-4">
|
||||
<Eyebrow>PROJECT MYCELIUM IS LIVE. </Eyebrow>
|
||||
<H3 className="text-white">Host a Node, Grow the Network</H3>
|
||||
<P className="text-sm md:text-lg text-gray-200 max-w-3xl mx-auto py-4">
|
||||
Mycelium runs on real nodes operated by individuals, communities,
|
||||
and organizations around the world. Each node provides compute, storage, and bandwidth to
|
||||
expand the network’s capacity and resilience.
|
||||
|
||||
Mycelium runs on nodes hosted by people and organizations around the world.
|
||||
Each node adds compute, storage, and bandwidth, expanding the network’s capacity and resilience.
|
||||
</P>
|
||||
<P className="text-sm md:text-lg text-gray-200 max-w-3xl mx-auto py-4">
|
||||
You can share your idle resources and earn rewards when they are used.
|
||||
Configure it once. Your node takes over from there.
|
||||
</P>
|
||||
<p className="text-sm italic md:text-base text-gray-200 max-w-3xl mx-auto py-4">
|
||||
Set it up once. It runs continuously, powering the network and rewarding your contribution.
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-800 ">
|
||||
{/* ✅ Match same side margins */}
|
||||
<div className="max-w-7xl mx-auto px-6 border border-t-0 border-b-0 border-gray-800">
|
||||
<WorldMap
|
||||
dots={[
|
||||
{ start: { lat: 64.2008, lng: -149.4937 }, end: { lat: 34.0522, lng: -118.2437 } }, // Alaska → LA
|
||||
{ start: { lat: 64.2008, lng: -149.4937 }, end: { lat: -15.7975, lng: -47.8919 } }, // Alaska → Brasília
|
||||
{ start: { lat: -15.7975, lng: -47.8919 }, end: { lat: 38.7223, lng: -9.1393 } }, // Brasília → Lisbon
|
||||
{ start: { lat: 51.5074, lng: -0.1278 }, end: { lat: 28.6139, lng: 77.209 } }, // London → New Delhi
|
||||
{ start: { lat: 28.6139, lng: 77.209 }, end: { lat: 43.1332, lng: 131.9113 } }, // New Delhi → Vladivostok
|
||||
{ start: { lat: 28.6139, lng: 77.209 }, end: { lat: -1.2921, lng: 36.8219 } }, // New Delhi → Nairobi
|
||||
]}
|
||||
/>
|
||||
<div className="max-w-5xl mx-auto px-6 ">
|
||||
<DynamicMapContainer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8 border border-t-0 border-b-0 border-gray-800 pb-12">
|
||||
|
||||
<dl className="pt-6 grid grid-cols-1 gap-0.5 overflow-hidden rounded-md text-center sm:grid-cols-2 lg:grid-cols-4">
|
||||
{STAT_CARDS.map(({ key, title, description }) => (
|
||||
<div
|
||||
key={key}
|
||||
className="flex flex-col bg-white/1 p-8"
|
||||
>
|
||||
<dt className="text-sm/6 font-semibold text-gray-300">
|
||||
{title}
|
||||
</dt>
|
||||
|
||||
<dd className="order-first text-3xl font-semibold tracking-tight text-white">
|
||||
{isLoading ? "…" : stats[key]}
|
||||
</dd>
|
||||
|
||||
<p className="mt-2 text-sm text-gray-400">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
</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>
|
||||
|
||||
@@ -4,10 +4,9 @@ import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { HomeTab } from './HomeTab'
|
||||
import { HomeMap } from './HomeMap'
|
||||
import { HomeAudience } from './HomeAudience'
|
||||
import { HomeBlinkDark } from './HomeBlinkDark'
|
||||
import { HomeAurora } from './HomeAurora'
|
||||
import { HomeArchitecture } from './HomeArchitecture';
|
||||
|
||||
import { HomeDesign } from './HomeDesign';
|
||||
|
||||
|
||||
|
||||
@@ -19,9 +18,12 @@ export default function HomePage() {
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<AnimatedSection>
|
||||
<HomeAurora onGetStartedClick={handleScrollToSlider} />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<HomeBlinkDark onGetStartedClick={handleScrollToSlider} />
|
||||
<HomeArchitecture/>
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
@@ -33,7 +35,7 @@ export default function HomePage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<HomeAudience />
|
||||
<HomeDesign />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
|
||||
@@ -34,10 +34,10 @@ export function HomeSpotlight({
|
||||
<H4 className="text-center mt-4">The Living Network of the Next Internet</H4>
|
||||
|
||||
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-neutral-500">
|
||||
A new internet is emerging — 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">
|
||||
|
||||
@@ -3,175 +3,136 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { CP, CT, Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
const bentoCards = [
|
||||
{
|
||||
id: 'network',
|
||||
title: 'Mycelium Network',
|
||||
eyebrow: 'Network',
|
||||
description: 'Encrypted peer-to-peer mesh networking across the globe.',
|
||||
image: '/images/bento-network.png',
|
||||
link: '/network',
|
||||
colSpan: 'lg:col-span-3',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-tl-4xl max-lg:rounded-t-4xl',
|
||||
innerRounded: 'lg:rounded-tl-[calc(2rem+1px)] max-lg:rounded-t-[calc(2rem+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'agents',
|
||||
title: 'Mycelium Agents',
|
||||
eyebrow: 'Agents',
|
||||
description: 'Private, programmable AI systems that run on your hardware.',
|
||||
image: '/images/bento-agent.jpg',
|
||||
link: '/agents',
|
||||
colSpan: 'lg:col-span-3',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-tr-4xl',
|
||||
innerRounded: 'lg:rounded-tr-[calc(2rem+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'cloud',
|
||||
title: 'Mycelium Cloud',
|
||||
eyebrow: 'Cloud',
|
||||
description: 'Deploy Kubernetes clusters on sovereign infrastructure.',
|
||||
image: '/images/bento-cloud.jpg',
|
||||
link: '/cloud',
|
||||
colSpan: 'lg:col-span-6',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'rounded-lg',
|
||||
innerRounded: 'rounded-[calc(var(--radius-lg)+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'compute',
|
||||
title: 'Mycelium Compute',
|
||||
eyebrow: 'Compute',
|
||||
description: 'The Compute resource layers powering the stack.',
|
||||
image: '/images/bento-compute.png',
|
||||
link: '/compute',
|
||||
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)]'
|
||||
},
|
||||
];
|
||||
|
||||
export function HomeTab() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
|
||||
{/* ✅ Top spacer + full-width line */}
|
||||
{/* Top section separators */}
|
||||
<div className="max-w-7xl mx-auto py-6 border-x border-gray-100 bg-white border-t-0 border-b-0" />
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
{/* ✅ Section with vertical borders */}
|
||||
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8 py-12 text-center">
|
||||
<Eyebrow color="accent">Components</Eyebrow>
|
||||
<H3 className="mx-auto mt-2 max-w-xl text-center ">
|
||||
Explore Mycelium Stack
|
||||
</H3>
|
||||
<P className="mx-auto mt-4 max-w-3xl text-center ">
|
||||
Mycelium’s technology stack gives you everything you need to build and run applications on a distributed network, from connectivity and compute to personal environments and AI.
|
||||
{/* Main content */}
|
||||
<div className="mx-auto bg-white max-w-2xl px-6 lg:max-w-7xl lg:px-10 border border-t-0 border-b-0 border-gray-100">
|
||||
<Eyebrow className="pt-12">Components</Eyebrow>
|
||||
<H3 className="mt-2">Explore the Stack</H3>
|
||||
<P className="mt-6 max-w-4xl">
|
||||
Mycelium’s technology stack gives you everything you need to build and run applications
|
||||
on a distributed network, from connectivity and compute to personal environments and AI.
|
||||
</P>
|
||||
<div className="mt-10 grid gap-4 sm:mt-16 lg:grid-cols-3 lg:grid-rows-2">
|
||||
<div className="relative lg:row-span-2">
|
||||
<div className="absolute inset-px rounded-lg bg-white lg:rounded-l-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-l-[calc(2rem+1px)]">
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950 max-lg:text-center">
|
||||
Mycelium Network
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600 max-lg:text-center">
|
||||
Peer-to-peer connectivity between users, nodes, and devices.
|
||||
The foundation for secure communication and collaboration.
|
||||
|
||||
</p>
|
||||
{/* BENTO GRID LAYOUT — EXACT MATCH */}
|
||||
<div className="mt-10 grid gap-4 sm:mt-16 lg:grid-cols-3 lg:grid-rows-2 pb-12">
|
||||
|
||||
{/* ------------------ LEFT TALL CARD ------------------ */}
|
||||
<Link to="/network" className="relative lg:row-span-2 cursor-pointer">
|
||||
<div className="absolute inset-px rounded-lg bg-white lg:rounded-l-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-l-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">LIVE</h3>
|
||||
<CT className="mt-2 text-lg font-medium tracking-tight text-gray-950">Mycelium Network</CT>
|
||||
<CP className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Peer-to-peer connectivity between users, nodes, and devices. The foundation for secure
|
||||
communication and collaboration.
|
||||
</CP>
|
||||
</div>
|
||||
|
||||
{/* Tall image container */}
|
||||
<div className="@container relative min-h-120 w-full grow max-lg:mx-auto max-lg:max-w-sm">
|
||||
<div className="absolute inset-x-10 top-10 bottom-0 overflow-hidden rounded-t-[12cqw] border-x-[3cqw] border-t-[3cqw] border-gray-700 bg-gray-900 shadow-2xl">
|
||||
<img
|
||||
alt=""
|
||||
src="https://tailwindcss.com/plus-assets/img/component-images/bento-03-mobile-friendly.png"
|
||||
<div className="absolute inset-x-10 top-10 bottom-0 overflow-hidden
|
||||
rounded-t-[12cqw] border-x-[3cqw] border-t-[3cqw]
|
||||
border-gray-700 bg-gray-900 shadow-2xl">
|
||||
<img
|
||||
src="/images/bento-network.jpg"
|
||||
className="size-full object-cover object-top"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5 lg:rounded-l-4xl" />
|
||||
</div>
|
||||
<div className="relative max-lg:row-start-1">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-t-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)]">
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950 max-lg:text-center">Mycelium Cloud</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600 max-lg:text-center">
|
||||
Decentralized compute, storage, and orchestration.
|
||||
Everything the cloud does, distributed across the network.
|
||||
|
||||
</p>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5 lg:rounded-l-4xl" />
|
||||
</Link>
|
||||
|
||||
{/* ------------------ MIDDLE TOP ------------------ */}
|
||||
<Link to="/pods" className="relative cursor-pointer max-lg:row-start-1">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-t-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Coming Soon</h3>
|
||||
<CT className="mt-2 text-lg font-medium tracking-tight text-gray-950">Mycelium Pods</CT>
|
||||
<CP className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Personal digital environments that never reset and stay under your control. Build, deploy,
|
||||
and connect on your own terms.
|
||||
</CP>
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-center px-8 max-lg:pt-10 max-lg:pb-12 sm:px-10 lg:pb-2">
|
||||
|
||||
<div className="flex flex-1 items-center justify-center px-8 max-lg:pt-10 sm:px-10 lg:pb-2">
|
||||
<img
|
||||
alt=""
|
||||
src="https://tailwindcss.com/plus-assets/img/component-images/bento-03-performance.png"
|
||||
src="/images/bento-gpu.jpg"
|
||||
className="w-full max-lg:max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-t-4xl" />
|
||||
</div>
|
||||
<div className="relative max-lg:row-start-3 lg:col-start-2 lg:row-start-2">
|
||||
<div className="absolute inset-px rounded-lg bg-white" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)]">
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950 max-lg:text-center">Mycelium Agents</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600 max-lg:text-center">
|
||||
Intelligent agents that operate across the network, not in centralized data centers.
|
||||
Train, deploy, and own your AI end-to-end.
|
||||
|
||||
</p>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-t-4xl" />
|
||||
</Link>
|
||||
|
||||
{/* ------------------ MIDDLE BOTTOM ------------------ */}
|
||||
<Link to="/agents" className="relative cursor-pointer max-lg:row-start-3 lg:col-start-2 lg:row-start-2">
|
||||
<div className="absolute inset-px rounded-lg bg-white" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
|
||||
<div className="px-8 pt-8 sm:px-10 sm:pt-10">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Q1 2026</h3>
|
||||
<CT className="mt-2 text-lg font-medium tracking-tight text-gray-950">Mycelium Agents</CT>
|
||||
<CP className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Peer-to-peer AI that serves you, not your data. Train, deploy, and own your AI end-to-end.
|
||||
</CP>
|
||||
</div>
|
||||
<div className="@container flex flex-1 items-center max-lg:py-6 lg:pb-2">
|
||||
|
||||
<div className="@container flex flex-1 items-center justify-center max-lg:py-6 lg:pb-2">
|
||||
<img
|
||||
alt=""
|
||||
src="https://tailwindcss.com/plus-assets/img/component-images/bento-03-security.png"
|
||||
className="h-[min(152px,40cqw)] object-cover"
|
||||
src="/images/bento-agent.jpg"
|
||||
className="h-[min(200px,40cqw)] object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5" />
|
||||
</div>
|
||||
<div className="relative lg:row-span-2">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-r-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-r-[calc(2rem+1px)]">
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950 max-lg:text-center">
|
||||
Mycelium Pods
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600 max-lg:text-center">
|
||||
Personal digital environments that stay persistent and under your control.
|
||||
Build, deploy, and connect on your own terms.
|
||||
|
||||
</p>
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5" />
|
||||
</Link>
|
||||
|
||||
{/* ------------------ RIGHT TALL ------------------ */}
|
||||
<Link to="/cloud" className="relative lg:row-span-2 cursor-pointer">
|
||||
<div className="absolute inset-px rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-r-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-r-[calc(2rem+1px)] transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20">
|
||||
|
||||
<div className="px-8 pt-8 pb-3 sm:px-10 sm:pt-10 sm:pb-0">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Early Access</h3>
|
||||
<CT className="mt-2 text-lg font-medium tracking-tight text-gray-950">Mycelium Cloud</CT>
|
||||
<CP className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Decentralized compute, storage, and orchestration. A full cloud service without a central operator.
|
||||
</CP>
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-120 w-full grow">
|
||||
<div className="absolute top-10 right-0 bottom-0 left-10 overflow-hidden rounded-tl-xl bg-gray-900 shadow-2xl outline outline-white/10">
|
||||
<div className="flex bg-gray-900 outline outline-white/5">
|
||||
<div className="-mb-px flex text-sm/6 font-medium text-gray-400">
|
||||
<div className="border-r border-b border-r-white/10 border-b-white/20 bg-white/5 px-4 py-2 text-white">
|
||||
NotificationSetting.jsx
|
||||
</div>
|
||||
<div className="border-r border-gray-600/10 px-4 py-2">App.jsx</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-6 pt-6 pb-14">{/* Your code example */}</div>
|
||||
<div className="absolute top-10 right-0 bottom-0 left-10 overflow-hidden
|
||||
rounded-tl-xl bg-gray-900 shadow-2xl outline outline-white/10">
|
||||
<img
|
||||
src="/images/cloud/reserve.png"
|
||||
className="size-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pointer-events-none absolute inset-px rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-b-4xl lg:rounded-r-4xl" />
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ✅ Bottom full-width line + spacer */}
|
||||
{/* Bottom separators */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl mx-auto py-6 border-x border-gray-100 border-t-0 border-b-0" />
|
||||
</section>
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
import { CP, CT, Eyebrow, H3, P } from "@/components/Texts";
|
||||
|
||||
const bentoCards = [
|
||||
{
|
||||
id: 'network',
|
||||
title: 'Mycelium Network',
|
||||
eyebrow: 'Network',
|
||||
description: 'Encrypted peer-to-peer mesh networking across the globe.',
|
||||
image: '/images/bento-network.png',
|
||||
link: '/network',
|
||||
colSpan: 'lg:col-span-3',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-tl-4xl max-lg:rounded-t-4xl',
|
||||
innerRounded: 'lg:rounded-tl-[calc(2rem+1px)] max-lg:rounded-t-[calc(2rem+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'agents',
|
||||
title: 'Mycelium Agents',
|
||||
eyebrow: 'Agents',
|
||||
description: 'Private, programmable AI systems that run on your hardware.',
|
||||
image: '/images/bento-agent.jpg',
|
||||
link: '/agents',
|
||||
colSpan: 'lg:col-span-3',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-tr-4xl',
|
||||
innerRounded: 'lg:rounded-tr-[calc(2rem+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'cloud',
|
||||
title: 'Mycelium Cloud',
|
||||
eyebrow: 'Cloud',
|
||||
description: 'Deploy Kubernetes clusters on sovereign infrastructure.',
|
||||
image: '/images/bento-cloud.jpg',
|
||||
link: '/cloud',
|
||||
colSpan: 'lg:col-span-6',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'rounded-lg',
|
||||
innerRounded: 'rounded-[calc(var(--radius-lg)+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'compute',
|
||||
title: 'Mycelium Compute',
|
||||
eyebrow: 'Compute',
|
||||
description: 'The Compute resource layers powering the stack.',
|
||||
image: '/images/bento-compute.png',
|
||||
link: '/compute',
|
||||
colSpan: 'lg:col-span-2',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-bl-4xl',
|
||||
innerRounded: 'lg:rounded-bl-[calc(2rem+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'storage',
|
||||
title: 'Mycelium Storage',
|
||||
eyebrow: 'Storage',
|
||||
description: 'The Storage resource layers powering the stack.',
|
||||
image: '/images/bento-storage.png',
|
||||
link: '/storage',
|
||||
colSpan: 'lg:col-span-2',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'rounded-lg',
|
||||
innerRounded: 'rounded-[calc(var(--radius-lg)+1px)]'
|
||||
},
|
||||
{
|
||||
id: 'gpu',
|
||||
title: 'Mycelium GPU',
|
||||
eyebrow: 'GPU',
|
||||
description: 'The GPU resource layers powering the stack.',
|
||||
image: '/images/bento-gpu.jpg',
|
||||
link: '/gpu',
|
||||
colSpan: 'lg:col-span-2',
|
||||
rowSpan: 'lg:row-span-1',
|
||||
rounded: 'lg:rounded-br-4xl max-lg:rounded-b-4xl',
|
||||
innerRounded: 'lg:rounded-br-[calc(2rem+1px)] max-lg:rounded-b-[calc(2rem+1px)]'
|
||||
},
|
||||
];
|
||||
|
||||
export function HomeTabDark() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-[#111111]">
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-[#111111] mx-auto border border-t-0 border-b-0 border-gray-800"></div>
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
|
||||
{/* ✅ Section with vertical borders */}
|
||||
<div className="mx-auto bg-[#121212] max-w-2xl px-6 lg:max-w-7xl lg:px-10 border border-t-0 border-b-0 border-gray-800">
|
||||
<Eyebrow className="pt-12 ">Components</Eyebrow>
|
||||
<H3 className="mt-2 text-white">Explore the Stack</H3>
|
||||
<P className="mt-6 max-w-4xl text-gray-200">
|
||||
Mycelium unifies everything the next generation internet needs — communication, cloud, and intelligence — into one seamless, privacy-first network anyone can join.
|
||||
From encrypted peer-to-peer communication to decentralized cloud and sovereign AI — everything runs on one seamless system.
|
||||
|
||||
</P>
|
||||
|
||||
<div className="mt-8 grid grid-cols-1 gap-6 sm:mt-10 lg:grid-cols-6 lg:grid-rows-3 pb-12 bg-[#121212]">
|
||||
{bentoCards.map((card) => (
|
||||
<Link to={card.link} key={card.id} className={`relative ${card.colSpan} ${card.rowSpan} transition-transform duration-300 hover:scale-102 cursor-pointer`}>
|
||||
<div className={`absolute inset-0 rounded-md bg-[#121212] ${card.rounded}`} />
|
||||
<div className={`relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] ${card.innerRounded}`}>
|
||||
<img
|
||||
alt={card.title}
|
||||
src={card.image}
|
||||
className="h-50 object-cover object-center bg-[#121212]"
|
||||
/>
|
||||
<div className="px-8 pt-4 pb-6 bg-[#121212]">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">{card.eyebrow}</h3>
|
||||
<CT className="mt-2 text-lg text-white lg:text-xl tracking-tight">{card.title}</CT>
|
||||
<CP className="mt-1 max-w-lg text-gray-200">
|
||||
{card.description}
|
||||
</CP>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 ${card.rounded}`} />
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ✅ Bottom full-width line + spacer */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl mx-auto py-6 border-x border-gray-100 border-t-0 border-b-0" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export function HomeWhy() {
|
||||
<Eyebrow>Why It Matters</Eyebrow>
|
||||
<H3 className="mt-2 text-gray-200">Why Mycelium?</H3>
|
||||
<P className="mx-auto mt-5 max-w-prose text-gray-400">
|
||||
The current internet is a rent-seeking one. Mycelium builds one that belongs to everyone — where infrastructure, data, and intelligence stay with the people and organizations who create them.
|
||||
The current internet is a rent-seeking one. Mycelium builds one that belongs to everyone where infrastructure, data, and intelligence stay with the people and organizations who create them.
|
||||
|
||||
</P>
|
||||
<div className="mt-16">
|
||||
|
||||
225
src/pages/home/animations/NoSinglePoint.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
"use client";
|
||||
|
||||
import { motion, useReducedMotion } from "framer-motion";
|
||||
import clsx from "clsx";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string; // cyan
|
||||
bg?: string; // solid dark background
|
||||
gridStroke?: string;
|
||||
};
|
||||
|
||||
const W = 720; // 4:3
|
||||
const H = 540; // 4:3
|
||||
|
||||
export default function NoSinglePoint({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
bg = "#0b0b0b",
|
||||
gridStroke = "#2b2a2a",
|
||||
}: Props) {
|
||||
const prefers = useReducedMotion();
|
||||
|
||||
// Nodes (left source, right dest, top hub, bottom hub, plus two relays)
|
||||
const nodes = {
|
||||
left: { x: 120, y: H / 2 },
|
||||
right: { x: W - 120, y: H / 2 },
|
||||
top: { x: W / 2, y: 160 },
|
||||
bot: { x: W / 2, y: H - 160 },
|
||||
tl: { x: 240, y: 200 },
|
||||
br: { x: W - 240, y: H - 200 },
|
||||
};
|
||||
|
||||
// Redundant paths from left → right
|
||||
const upperPath = `M ${nodes.left.x} ${nodes.left.y}
|
||||
L ${nodes.tl.x} ${nodes.tl.y}
|
||||
L ${nodes.top.x} ${nodes.top.y}
|
||||
L ${nodes.right.x} ${nodes.right.y}`;
|
||||
|
||||
const lowerPath = `M ${nodes.left.x} ${nodes.left.y}
|
||||
L ${nodes.bot.x} ${nodes.bot.y}
|
||||
L ${nodes.br.x} ${nodes.br.y}
|
||||
L ${nodes.right.x} ${nodes.right.y}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
{/* Subtle dark grid */}
|
||||
<defs>
|
||||
<pattern id="grid-dark-4x3" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.35" />
|
||||
</pattern>
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="3" result="b" />
|
||||
<feMerge>
|
||||
<feMergeNode in="b" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#grid-dark-4x3)" />
|
||||
|
||||
{/* Base links (all potential connectivity) */}
|
||||
{[
|
||||
`M ${nodes.left.x} ${nodes.left.y} L ${nodes.tl.x} ${nodes.tl.y}`,
|
||||
`M ${nodes.tl.x} ${nodes.tl.y} L ${nodes.top.x} ${nodes.top.y}`,
|
||||
`M ${nodes.top.x} ${nodes.top.y} L ${nodes.right.x} ${nodes.right.y}`,
|
||||
`M ${nodes.left.x} ${nodes.left.y} L ${nodes.bot.x} ${nodes.bot.y}`,
|
||||
`M ${nodes.bot.x} ${nodes.bot.y} L ${nodes.br.x} ${nodes.br.y}`,
|
||||
`M ${nodes.br.x} ${nodes.br.y} L ${nodes.right.x} ${nodes.right.y}`,
|
||||
// cross edges (mesh redundancy)
|
||||
`M ${nodes.tl.x} ${nodes.tl.y} L ${nodes.bot.x} ${nodes.bot.y}`,
|
||||
`M ${nodes.top.x} ${nodes.top.y} L ${nodes.br.x} ${nodes.br.y}`,
|
||||
].map((d, i) => (
|
||||
<motion.path
|
||||
key={`base-${i}`}
|
||||
d={d}
|
||||
stroke="#1e1e1e"
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
fill="none"
|
||||
initial={{ pathLength: 0, opacity: 0.2 }}
|
||||
animate={{ pathLength: 1, opacity: 0.5 }}
|
||||
transition={{ duration: 0.8, delay: i * 0.05, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Highlight the two redundant main routes */}
|
||||
<motion.path
|
||||
d={upperPath}
|
||||
fill="none"
|
||||
stroke="#3a3a3a"
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.6 }}
|
||||
animate={{ pathLength: 1, opacity: 0.6 }}
|
||||
transition={{ duration: 0.9 }}
|
||||
/>
|
||||
<motion.path
|
||||
d={lowerPath}
|
||||
fill="none"
|
||||
stroke="#3a3a3a"
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.6 }}
|
||||
animate={{ pathLength: 1, opacity: 0.6 }}
|
||||
transition={{ duration: 0.9, delay: 0.1 }}
|
||||
/>
|
||||
|
||||
{/* Cyan accent dash showing “preferred/active” path(s) */}
|
||||
<motion.path
|
||||
d={upperPath}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 8"
|
||||
initial={{ pathLength: 0, opacity: 0.8 }}
|
||||
animate={{ pathLength: 1, opacity: [0.8, 0.2, 0.8] }} // will fade as "blocked"
|
||||
transition={{ duration: 1.1, repeat: Infinity, repeatType: "reverse" }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
<motion.path
|
||||
d={lowerPath}
|
||||
fill="none"
|
||||
stroke={accent}
|
||||
strokeWidth={2}
|
||||
strokeDasharray="10 8"
|
||||
initial={{ pathLength: 0, opacity: 1 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{ duration: 1 }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
|
||||
{/* Moving packets: one tries upper (gets dimmed at top hub), one uses lower and continues */}
|
||||
{!prefers && (
|
||||
<>
|
||||
{/* Upper path packet (dims near top hub to suggest block/censor but NOT stopping overall flow) */}
|
||||
<motion.circle
|
||||
r={5}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${upperPath}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 0.9 }}
|
||||
animate={{
|
||||
offsetDistance: ["0%", "100%"],
|
||||
opacity: [0.9, 0.4, 0.9], // subtle dimming cycle
|
||||
}}
|
||||
transition={{ duration: 3.0, repeat: Infinity, ease: "linear" }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
|
||||
{/* Lower path packet (stable flow) */}
|
||||
<motion.circle
|
||||
r={6}
|
||||
fill={accent}
|
||||
style={{ offsetPath: `path('${lowerPath}')` }}
|
||||
initial={{ offsetDistance: "0%", opacity: 1 }}
|
||||
animate={{ offsetDistance: ["0%", "100%"] }}
|
||||
transition={{ duration: 2.4, repeat: Infinity, ease: "linear" }}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Nodes */}
|
||||
{Object.values(nodes).map((n, i) => (
|
||||
<g key={`node-${i}`}>
|
||||
<circle cx={n.x} cy={n.y} r={20} fill="#0f0f0f" stroke="#1f1f1f" strokeWidth={2} />
|
||||
<motion.circle
|
||||
cx={n.x}
|
||||
cy={n.y}
|
||||
r={8}
|
||||
fill={i === 2 ? "#0f0f0f" : accent} // top hub inner is dark (to hint “blocked” moment)
|
||||
stroke="#222"
|
||||
strokeWidth={2}
|
||||
animate={
|
||||
!prefers
|
||||
? i === 2 // top node (potential choke/attack point) pulses differently
|
||||
? { opacity: [0.5, 0.25, 0.5], scale: [1, 0.95, 1] }
|
||||
: { opacity: [0.9, 1, 0.9], scale: [1, 1.06, 1] }
|
||||
: { opacity: 1 }
|
||||
}
|
||||
transition={{
|
||||
duration: 2.2,
|
||||
repeat: prefers ? 0 : Infinity,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
delay: i * 0.05,
|
||||
}}
|
||||
filter="url(#glow)"
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
{/* A subtle “block” overlay on the top hub (appears/disappears) */}
|
||||
{!prefers && (
|
||||
<motion.g
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: [0, 0.7, 0] }}
|
||||
transition={{ duration: 3.2, repeat: Infinity, ease: "easeInOut", delay: 0.8 }}
|
||||
>
|
||||
<circle
|
||||
cx={nodes.top.x}
|
||||
cy={nodes.top.y}
|
||||
r={18}
|
||||
fill="none"
|
||||
stroke="#8b8b8b"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
<path
|
||||
d={`M ${nodes.top.x - 10} ${nodes.top.y - 10} L ${nodes.top.x + 10} ${nodes.top.y + 10}
|
||||
M ${nodes.top.x + 10} ${nodes.top.y - 10} L ${nodes.top.x - 10} ${nodes.top.y + 10}`}
|
||||
stroke="#8b8b8b"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
</motion.g>
|
||||
)}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export function HomeComparisonTable() {
|
||||
<Eyebrow>COMPARISON</Eyebrow>
|
||||
<H3 className="mt-2">Why People Choose Mycelium</H3>
|
||||
<P className="mx-auto mt-5 max-w-prose">
|
||||
Mycelium brings cloud-grade automation to infrastructure you control — without surrendering data, identity,
|
||||
Mycelium brings cloud-grade automation to infrastructure you control without surrendering data, identity,
|
||||
or uptime to centralized platforms.
|
||||
</P>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ const tabs = [
|
||||
label: "K8s Clusters",
|
||||
title: "K8s Clusters",
|
||||
description:
|
||||
"Deploy and scale containerized apps across your own hardware — enabling a world of possibilities.",
|
||||
"Deploy and scale containerized apps across your own hardware, enabling a world of possibilities.",
|
||||
link: "#",
|
||||
icon: CubeIcon,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Container } from "@/components/Container";
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Container } from '@/components/Container'
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
export function CallToAction() {
|
||||
@@ -15,31 +16,56 @@ export function CallToAction() {
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<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">
|
||||
Choose How You Want to Start
|
||||
Choose How You Want to Connect
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Use the network to connect workloads or host nodes to deepen mesh resilience and run your environments on your own hardware.
|
||||
Choose How You Want to Connect
|
||||
Use the network to link environments, deploy workloads, or host nodes to strengthen the mesh and run on your own hardware.
|
||||
</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="/download" variant="solid" color="cyan" className="mt-4">
|
||||
Get Mycelium Network
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mt-10 flex flex-wrap justify-center items-center gap-x-6 gap-y-4">
|
||||
<Button to="/network" variant="solid" color="cyan">
|
||||
Join the Network
|
||||
</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">
|
||||
Host a Node
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
to="/cloud"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
Deploy in Cloud
|
||||
</Button>
|
||||
|
||||
<Link to="/nodes" className="text-cyan-400 hover:text-cyan-300 transition-colors">
|
||||
Host a Node →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -18,10 +18,10 @@ export function Features() {
|
||||
<Eyebrow>Core Components</Eyebrow>
|
||||
<H3>Network Capabilities</H3>
|
||||
<P className="mt-4 max-w-4xl text-lg text-gray-600">
|
||||
Built for resilience and autonomy, the Mycelium Network dynamically connects nodes through intelligent routing, proxy discovery, and decentralized delivery.
|
||||
Built for resilience, performance, and autonomy.
|
||||
</P>
|
||||
<P className="mt-4 max-w-4xl text-lg text-gray-600">
|
||||
Each component — from message passing to content distribution — works in harmony to create a fully self-healing, self-optimizing data mesh.
|
||||
<P className="mt-4 max-w-3xl text-lg text-gray-600">
|
||||
Nodes connect through intelligent routing, peer discovery, and decentralized delivery to create a continuously optimized data mesh.
|
||||
</P>
|
||||
<div className="mt-8 grid grid-cols-1 gap-x-4 gap-y-8 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
|
||||
<div className="group relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useId } from 'react'
|
||||
import { Container } from '@/components/Container'
|
||||
import { Button } from '@/components/Button'
|
||||
import phoneFrame from '../../images/phoneframe.png'
|
||||
import { H3, P, CT } from "@/components/Texts";
|
||||
import { H3, P
|
||||
, Eyebrow } from "@/components/Texts";
|
||||
|
||||
function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) {
|
||||
let id = useId()
|
||||
@@ -79,23 +80,21 @@ export function Hero() {
|
||||
<Container>
|
||||
<div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20 px-6 lg:px-8">
|
||||
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
|
||||
<H3 className="mb-4">
|
||||
Mycelium Network
|
||||
<Eyebrow className="mb-4">
|
||||
MYCELIUM NETWORK
|
||||
</Eyebrow>
|
||||
<H3 className="mt-8 ">
|
||||
The Network Stack for Private, Autonomous Networking
|
||||
</H3>
|
||||
<CT className="mt-8 font-medium text-gray-600 ">
|
||||
Encrypted Peer-to-Peer Connectivity Across the Globe
|
||||
</CT>
|
||||
<P className="mt-6 text-lg leading-tight text-gray-600 lg:text-xl lg:leading-normal">
|
||||
Mycelium Network provides an unbreakable sovereign IPv6 mesh that connects people, nodes, and applications directly, with no central servers.
|
||||
Connect once. Get private networking, censorship-resistant publishing, and on-network AI in one encrypted fabric.
|
||||
</P>
|
||||
<P className="mt-6 text-lg leading-tight text-gray-600 lg:text-xl lg:leading-normal">
|
||||
Works Alone. Works Together.
|
||||
Mycelium Network can be used standalone, or together with Mycelium Cloud
|
||||
for full-stack sovereignty.
|
||||
Your Pod is your personal gateway to the network.
|
||||
</P>
|
||||
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-4">
|
||||
<Button to="/download" variant="solid" color="cyan">
|
||||
Get Mycelium Connector
|
||||
Get Started
|
||||
</Button>
|
||||
<Button to="/download" variant="outline" color="cyan">
|
||||
Explore Docs
|
||||
|
||||
@@ -23,17 +23,15 @@ export function NetworkCapabilities() {
|
||||
<Eyebrow>WHAT IT ENABLES</Eyebrow>
|
||||
|
||||
<H4 className="mt-6 text-white">
|
||||
A Private Networking Layer for Everything You Run
|
||||
One Network. Many Capabilities.
|
||||
</H4>
|
||||
|
||||
<P className="mt-6 text-gray-200">
|
||||
Mycelium Network is the backbone for secure, autonomous connectivity
|
||||
across devices, data centers, clusters, and self-hosted environments — anywhere in the world.
|
||||
One network for all your connectivity needs.
|
||||
</P>
|
||||
|
||||
<P className="mt-3 text-lg text-gray-200">
|
||||
Every node gets its own encrypted identity and address space, forming a
|
||||
zero-trust mesh without any centralized controller.
|
||||
Mycelium replaces separate layers like VPNs, hosting, and DNS with a single encrypted, peer-to-peer system that links devices, apps, and environments directly.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +45,7 @@ export function NetworkCapabilities() {
|
||||
End-to-End Encrypted Mesh
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-200 max-w-2xl">
|
||||
Every packet is encrypted and routed peer-to-peer — no intermediaries, no sniffing, no compromise.
|
||||
Every packet is encrypted and routed peer-to-peer. No intermediaries, no data sniffing, no compromise.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
@@ -59,7 +57,7 @@ export function NetworkCapabilities() {
|
||||
Global IPv6 Address Space
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-200 max-w-2xl">
|
||||
Every node, app, and microservice gets an address — solving discovery, routing, and NAT issues forever.
|
||||
Every node, app, and microservice gets a unique global address. Permanently solves discovery, routing, and NAT issues.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
@@ -68,10 +66,10 @@ export function NetworkCapabilities() {
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white flex items-center">
|
||||
<ArrowPathRoundedSquareIcon className="h-6 w-6 text-cyan-500 mr-3" />
|
||||
Self-Healing Routing
|
||||
Dynamic Pathfinding
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-200 max-w-2xl">
|
||||
Traffic automatically finds the fastest path, dynamically re-routing around failures or congestion.
|
||||
Traffic automatically finds the fastest route, re-routing around failures or congestion in real time.
|
||||
</p>
|
||||
<div className="mt-8 h-px w-full bg-cyan-500/50" />
|
||||
</div>
|
||||
@@ -83,7 +81,7 @@ export function NetworkCapabilities() {
|
||||
No Central Control
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-200 max-w-2xl">
|
||||
No servers to trust, no corporate choke points, and no authority that can turn you off.
|
||||
No corporate servers, no authority that can disable or censor the network.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
114
src/pages/network/NetworkDownload.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
|
||||
import appleIcon from '@/images/apple.svg'
|
||||
import windowsIcon from '@/images/windows.svg'
|
||||
import androidIcon from '@/images/android.svg'
|
||||
import linuxIcon from '@/images/linux.svg'
|
||||
|
||||
import { CT, CP } from '@/components/Texts'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Download for iOS & MacOS',
|
||||
description: 'Download Mycelium App from the Apple Store.',
|
||||
href: 'https://apps.apple.com/us/app/mycelium-network/id6504277565',
|
||||
icon: appleIcon,
|
||||
alt: 'Apple logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Windows',
|
||||
description: 'Download the Mycelium App for Windows directly from its Github repository.',
|
||||
href: 'https://github.com/threefoldtech/myceliumflut/releases',
|
||||
icon: windowsIcon,
|
||||
alt: 'Windows logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Android',
|
||||
description: 'Download Mycelium from the Google Play Store.',
|
||||
href: 'https://play.google.com/store/apps/details?id=tech.threefold.mycelium&pli=1',
|
||||
icon: androidIcon,
|
||||
alt: 'Android logo',
|
||||
},
|
||||
{
|
||||
name: 'Download for Linux',
|
||||
description: 'Download the Mycelium binary for Linux directly from its Github repository.',
|
||||
href: 'https://github.com/threefoldtech/mycelium/releases',
|
||||
icon: linuxIcon,
|
||||
alt: 'Linux logo',
|
||||
},
|
||||
]
|
||||
|
||||
export function NetworkDownload() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
<div className="w-full border border-l border-r border-gray-100" />
|
||||
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8 bg-white py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<motion.h3
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-4xl font-medium tracking-tight text-gray-900 lg:text-5xl"
|
||||
>
|
||||
Download Mycelium Network
|
||||
</motion.h3>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mt-8 text-lg text-gray-600 lg:leading-8"
|
||||
>
|
||||
Get Mycelium Network for Android, Windows, macOS, and iOS to securely connect, store, and interact with the decentralized network—seamlessly and efficiently. Not sure how it works?{' '}
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
className="font-semibold text-gray-900 underline transition-colors hover:text-cyan-500"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read the manual.
|
||||
</a>
|
||||
</motion.p>
|
||||
</div>
|
||||
<div className="mx-auto mt-8 max-w-2xl lg:mt-12 lg:max-w-none">
|
||||
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 md:grid-cols-2 lg:max-w-none lg:grid-cols-4">
|
||||
{features.map((feature) => (
|
||||
<div
|
||||
key={feature.name}
|
||||
className="flex flex-col rounded-lg border border-gray-100 p-8 shadow-sm transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
|
||||
>
|
||||
<dt className="text-base font-semibold leading-7 text-gray-900">
|
||||
<div className="mb-6 flex h-10 w-10 items-center justify-center">
|
||||
<img src={feature.icon} alt={feature.alt} className="h-10 w-10" />
|
||||
</div>
|
||||
{feature.name}
|
||||
</dt>
|
||||
<dd className="mt-1 flex flex-auto flex-col text-base leading-7 text-gray-600">
|
||||
<CP className="flex-auto mt-2">{feature.description}</CP>
|
||||
<CT className="mt-4">
|
||||
<a
|
||||
href={feature.href}
|
||||
className="text-sm font-semibold leading-6 text-cyan-500 transition-colors hover:text-cyan-400"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Download Now <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</CT>
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import { PrimaryFeatures } from './PrimaryFeatures'
|
||||
import { SecondaryFeatures } from './SecondaryFeatures'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { NetworkCapabilities } from './NetworkCapabilities'
|
||||
import { NetworkUsecases } from './NetworkUsecases'
|
||||
import { NetworkDownload } from './NetworkDownload'
|
||||
import { NetworkPros } from './NetworkPros'
|
||||
|
||||
|
||||
export default function NetworkPage() {
|
||||
return (
|
||||
@@ -19,7 +21,7 @@ export default function NetworkPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<Features />
|
||||
<SecondaryFeatures />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
@@ -27,11 +29,16 @@ export default function NetworkPage() {
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<NetworkUsecases />
|
||||
<Features />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<SecondaryFeatures />
|
||||
<NetworkPros />
|
||||
</AnimatedSection>
|
||||
|
||||
|
||||
<AnimatedSection>
|
||||
<NetworkDownload />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
|
||||
85
src/pages/network/NetworkPros.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Eyebrow, H3, P, Small } from '@/components/Texts'
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
label: 'Network Advantage',
|
||||
title: 'Fully peer-to-peer, no logins, no central cloud.',
|
||||
description:
|
||||
'Connectivity flows directly between users, nodes, and services without platform ownership.',
|
||||
},
|
||||
{
|
||||
label: 'Platform',
|
||||
title: 'One unified platform for compute, storage, and orchestration.',
|
||||
description:
|
||||
'Runs reliably across distributed environments. Works with your existing tools and workflows. Scales from small projects to full environments.',
|
||||
},
|
||||
{
|
||||
label: 'Scale',
|
||||
title: 'Scales instantly from one POD to thousands.',
|
||||
description:
|
||||
'Deploy locally or expand globally, the mesh routes and balances itself automatically.',
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
title: 'Secure, quantum-safe, and edge-ready for the next decade.',
|
||||
description:
|
||||
'Next-gen encryption, multipath routing, and attested nodes protect workloads everywhere.',
|
||||
},
|
||||
]
|
||||
|
||||
export function NetworkPros() {
|
||||
return (
|
||||
<section className="bg-[#121212] w-full max-w-8xl mx-auto">
|
||||
{/* Top spacing line */}
|
||||
<div className="max-w-7xl py-6 mx-auto border border-t-0 border-b-0 border-gray-800" />
|
||||
<div className="w-full border-t border-l border-r border-gray-800" />
|
||||
|
||||
{/* Main framed content */}
|
||||
<div className="bg-[#121212] w-full max-w-7xl mx-auto border border-t-0 border-b-0 border-gray-800">
|
||||
{/* Centered intro */}
|
||||
<div className="px-6 pt-12 pb-4 text-center max-w-4xl mx-auto ">
|
||||
<Eyebrow className="uppercase tracking-[0.16em] text-cyan-600">
|
||||
WHY
|
||||
</Eyebrow>
|
||||
|
||||
<H3 className="mt-4 text-white">
|
||||
Why It’s Revolutionary
|
||||
</H3>
|
||||
|
||||
<P className="mt-4 text-gray-300 text-base leading-relaxed">
|
||||
Mycelium redefines how digital systems connect, scale, and stay secure in a world moving beyond the limits of traditional cloud
|
||||
</P>
|
||||
</div>
|
||||
<div className="grid lg:grid-cols-4 mt-8">
|
||||
{highlights.map((item) => (
|
||||
<div
|
||||
key={item.title}
|
||||
className="group relative overflow-hidden border border-white/10 bg-white/4 p-8 backdrop-blur-sm transition hover:border-cyan-300/50 hover:bg-white/8"
|
||||
>
|
||||
{/* Hover glow */}
|
||||
<div className="absolute inset-0 bg-linear-to-br from-cyan-500/0 via-white/5 to-cyan-300/20 opacity-0 transition group-hover:opacity-100" />
|
||||
|
||||
<div className="relative">
|
||||
<Small className="text-xs uppercase tracking-[0.16em] text-cyan-200">
|
||||
{item.label}
|
||||
</Small>
|
||||
|
||||
<h3 className="mt-4 text-lg font-semibold text-white">
|
||||
{item.title}
|
||||
</h3>
|
||||
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-300">
|
||||
{item.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom spacing line */}
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import { Eyebrow, SectionHeader, P } from "@/components/Texts";
|
||||
import { IoArrowBackOutline, IoArrowForwardOutline } from "react-icons/io5";
|
||||
import {
|
||||
LockClosedIcon,
|
||||
ArrowPathIcon,
|
||||
GlobeAltIcon,
|
||||
SignalIcon,
|
||||
CpuChipIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
|
||||
const networkUseCases = [
|
||||
@@ -27,46 +27,52 @@ const networkUseCases = [
|
||||
{
|
||||
title: "Service-to-Service Networking Across Environments",
|
||||
description:
|
||||
"Connect applications running across home labs, cloud regions, edge nodes, and data centers all on one address space.",
|
||||
"Connect apps running across home labs, cloud regions, edge nodes, and datacenters all on one address space.",
|
||||
ideal: "Ideal for: dev teams, distributed apps, container + K3s workloads",
|
||||
icon: GlobeAltIcon,
|
||||
},
|
||||
{
|
||||
title: "Resilient Connectivity Across Regions & Outages",
|
||||
description:
|
||||
"Connect systems across countries, datacenters, edge locations, and remote deployments — with routing that automatically heals around ISP failures, censorship, and regional outages.",
|
||||
"Automatically routes around ISP failures, censorship, and regional outages using multipath encrypted relays.",
|
||||
ideal:
|
||||
"Ideal for: research networks, cross-border orgs, distributed compute, off-grid / rural deployments",
|
||||
"Ideal for: cross-border orgs, distributed compute, remote/off-grid deployments",
|
||||
icon: ArrowPathIcon,
|
||||
},
|
||||
{
|
||||
title: "Adaptive Mesh for Mobile & Edge Movement",
|
||||
description:
|
||||
"Devices moving between networks, cities, or countries maintain continuous secure connectivity with no reconnection steps.",
|
||||
ideal: "Ideal for: mobile agents, field teams, robotics, vehicles, IoT",
|
||||
icon: SignalIcon,
|
||||
},
|
||||
{
|
||||
title: "Compute Fabric Linking Nodes & Agents",
|
||||
description:
|
||||
"Agents, jobs, and tasks can interact across nodes without exposing private IPs or opening firewalls.",
|
||||
ideal: "Ideal for: agent networks, edge AI workloads, distributed computation",
|
||||
icon: CpuChipIcon,
|
||||
},
|
||||
];
|
||||
|
||||
export function NetworkUsecases() {
|
||||
const sliderRef = useRef<HTMLUListElement>(null);
|
||||
|
||||
const scrollLeft = () =>
|
||||
sliderRef.current?.scrollBy({ left: -400, behavior: "smooth" });
|
||||
|
||||
const scrollRight = () =>
|
||||
sliderRef.current?.scrollBy({ left: 400, behavior: "smooth" });
|
||||
|
||||
return (
|
||||
<section className="bg-white w-full max-w-8xl mx-auto">
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-slate-200" />
|
||||
<div className="w-full border-t border-l border-r border-slate-200" />
|
||||
|
||||
<div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-slate-200 bg-white overflow-hidden">
|
||||
<ul
|
||||
ref={sliderRef}
|
||||
className="flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar"
|
||||
>
|
||||
<div className="mx-auto max-w-7xl border border-t-0 border-b-0 border-slate-200 bg-white">
|
||||
{/* GRID 3 x 3 */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{networkUseCases.map((item, idx) => (
|
||||
<li
|
||||
<div
|
||||
key={idx}
|
||||
className={`snap-start shrink-0 w-[85%] sm:w-[50%] lg:w-[33%] border border-slate-200 p-10 relative ${
|
||||
item.isIntro ? "bg-gray-50/80" : "bg-white"
|
||||
}`}
|
||||
className={`
|
||||
border border-slate-200 p-10 relative
|
||||
${item.isIntro ? "bg-gray-50/80" : "bg-white"}
|
||||
`}
|
||||
>
|
||||
{/* Intro Card */}
|
||||
{item.isIntro ? (
|
||||
<div className="flex flex-col justify-between h-full">
|
||||
<div>
|
||||
@@ -81,47 +87,39 @@ export function NetworkUsecases() {
|
||||
{item.description}
|
||||
</P>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-x-4 mt-2">
|
||||
<button
|
||||
onClick={scrollLeft}
|
||||
className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowBackOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={scrollRight}
|
||||
className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowForwardOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* ✅ Icon above title */}
|
||||
{item.icon && (
|
||||
{/* Icon */}
|
||||
{item.icon && (
|
||||
<div className="h-10 w-10 flex items-center justify-center rounded-xl bg-gray-100 mb-4">
|
||||
<item.icon className="h-6 w-6 text-cyan-600" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
{/* Title */}
|
||||
<p className="text-lg font-semibold text-gray-900">
|
||||
{item.title}
|
||||
</p>
|
||||
|
||||
<p className="mt-2 text-gray-600 leading-snug">
|
||||
{/* Description */}
|
||||
<p className="mt-2 text-gray-600 leading-snug">
|
||||
{item.description}
|
||||
</p>
|
||||
<p className="mt-3 text-xs font-medium text-cyan-700">
|
||||
|
||||
{/* Ideal for */}
|
||||
<p className="mt-3 text-xs font-medium text-cyan-700">
|
||||
{item.ideal}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full border-b border-slate-200 bg-white" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-slate-200" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ function FeaturesDesktop() {
|
||||
{feature.name}
|
||||
</Tab>
|
||||
</FeatureTitle>
|
||||
<FeatureDescription color="secondary" className="mt-2">
|
||||
<FeatureDescription color="white" className="mt-2">
|
||||
{feature.description}
|
||||
</FeatureDescription>
|
||||
</div>
|
||||
@@ -419,9 +419,7 @@ export function PrimaryFeatures() {
|
||||
How Mycelium Operates
|
||||
</SectionHeader>
|
||||
<P color="light" className="mt-6">
|
||||
Mycelium, like its natural namesake, thrives on decentralization,
|
||||
efficiency, and security, making it a truly powerful force in the world
|
||||
of decentralized networks.
|
||||
Your device connects to an encrypted peer-to-peer mesh that automatically handles routing, discovery, and communication.
|
||||
</P>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
@@ -4,41 +4,41 @@ import { CP } from '@/components/Texts'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Quantum Safe Storage Functionality',
|
||||
name: 'Sovereign DNS',
|
||||
description:
|
||||
"Mycelium's quantum safe storage enables flexible, scalable, and efficient data distribution across a decentralized network, ensuring redundancy and security.",
|
||||
icon: DeviceArrowIcon,
|
||||
},
|
||||
{
|
||||
name: 'Entry and Exit Points for AI Workloads',
|
||||
description:
|
||||
'Seamlessly connect AI applications to Mycelium, providing optimized and secured data pipelines for training, inference, and real-time processing.',
|
||||
icon: DeviceCardsIcon,
|
||||
},
|
||||
{
|
||||
name: 'Data Storage and Retrieval Mechanisms',
|
||||
description:
|
||||
'Users can choose between storing data locally for quick access or utilizing the distributed grid for enhanced scalability and resilience.',
|
||||
icon: DeviceClockIcon,
|
||||
},
|
||||
{
|
||||
name: 'Integrated Name Services (DNS)',
|
||||
description:
|
||||
'The Integrated DNS system efficiently finds the shortest path between users and websites, automatically balancing loads and identifying alternative routes in case of internet issues.',
|
||||
'Human-readable names secured by your keypair, not a registrar. Unblockable discovery, private namespaces, and instant resolution across PODs.',
|
||||
icon: DeviceListIcon,
|
||||
},
|
||||
{
|
||||
name: 'Frontend/Backend Integration',
|
||||
name: 'Integrated VPN (Desktop)',
|
||||
description:
|
||||
'Mycelium provides seamless integration with existing applications, enabling developers to leverage decentralized storage across both frontend and backend architectures.',
|
||||
'Connect securely from anywhere. No setup and no servers needed. Peer-to-peer routing delivers local speed and global reach.',
|
||||
icon: DeviceLockIcon,
|
||||
},
|
||||
{
|
||||
name: 'CDN (Content Delivery Network)',
|
||||
name: 'Unstoppable Publishing',
|
||||
description:
|
||||
'Mycelium accelerates data distribution by acting as a decentralized CDN, ensuring fast, secure, and efficient content delivery across global nodes with minimal latency.',
|
||||
'Host sites or services directly from your POD. Signed content, geo-aware delivery, and built-in resistance to takedowns.',
|
||||
icon: DeviceChartIcon,
|
||||
},
|
||||
{
|
||||
name: 'AI-Driven Search',
|
||||
description:
|
||||
'Private, semantic search across your own data and sites. Indexes locally, shares results securely, and retrieves instantly on-edge.',
|
||||
icon: DeviceArrowIcon,
|
||||
},
|
||||
{
|
||||
name: 'Private Chat',
|
||||
description:
|
||||
'Peer-to-peer messaging with zero metadata and zero servers. Works offline through nearby peers and is encrypted end-to-end.',
|
||||
icon: DeviceCardsIcon,
|
||||
},
|
||||
{
|
||||
name: 'On-Network AI',
|
||||
description:
|
||||
'Run LLMs and AI tools directly inside the network. Keep data local, distribute workloads, and build your own autonomous agents.',
|
||||
icon: DeviceClockIcon,
|
||||
},
|
||||
]
|
||||
|
||||
function DeviceArrowIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
@@ -197,12 +197,12 @@ export function SecondaryFeatures() {
|
||||
|
||||
<Container className="py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">IN ACTIVE EVOLUTION</h2>
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">FEATURES</h2>
|
||||
<p className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
|
||||
Expanding the Network Layer
|
||||
Core Features
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
The Mycelium Network is evolving to support richer data movement, identity, and application connectivity across the mesh. These enhancements deepen autonomy and improve real-world usability.
|
||||
The Mycelium Network is evolving with new features to support richer data movement, identity, and application connectivity across the mesh.
|
||||
</p>
|
||||
</div>
|
||||
<ul
|
||||
|
||||
69
src/pages/nodes/CallToAction.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import { Container } from "@/components/Container";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
export function CallToAction() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-[#121212]">
|
||||
<div className="max-w-7xl bg-[#111111] 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
|
||||
id="get-started"
|
||||
className="relative py-18 max-w-7xl 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.2"
|
||||
/>
|
||||
<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">
|
||||
Join Mycelium Grid
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Be part of a global network that powers private, distributed
|
||||
infrastructure. Host a node, contribute resources, and earn rewards
|
||||
while expanding the sovereign digital grid.
|
||||
</p>
|
||||
|
||||
<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">
|
||||
Join Mycelium
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="/docs" variant="outline" color="white" className="mt-4">
|
||||
Explore docs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800 bg-transparent" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
117
src/pages/nodes/NodeBenefits.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import { SectionHeader, P, Eyebrow, CT, CP } from "@/components/Texts";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
type CircleIconProps = ComponentPropsWithoutRef<"svg">;
|
||||
|
||||
const CircleNumber1Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm.994 5.886c-.83-.777-1.755-.394-1.701.404l-.006.114v8l.007.117a1 1 0 001.986 0l.007-.117V9l-.006-.114z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CircleNumber2Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2a10 10 0 110 20 10 10 0 010-20zm1 5h-3a1 1 0 100 2h3v2h-2a2 2 0 00-2 2v2a2 2 0 002 2h3a1 1 0 100-2h-3v-2h2a2 2 0 002-2V9a2 2 0 00-2-2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CircleNumber3Icon = (props: CircleIconProps) => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" {...props}>
|
||||
<path d="M12 2a10 10 0 110 20 10 10 0 010-20zm1 5h-2a2 2 0 00-2 2 1 1 0 102 0h2v2h-2c-1.39 0-2.103 1.67-1.11 2.588l.11.098h3v2h-2a1 1 0 100 2h2a2 2 0 002-2v-2a2 2 0 00-.25-.97l-.068-.115.068-.115A1.99 1.99 0 0015 11V9a2 2 0 00-2-2z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: "Build Your Private Setup",
|
||||
description:
|
||||
"Run a node in your home or office and use it to host private workloads, models, and data. Experiment, deploy, or tinker locally, then offset your costs by sharing unused capacity with the network.",
|
||||
icon: CircleNumber1Icon,
|
||||
},
|
||||
{
|
||||
name: "Strengthen the Network",
|
||||
description:
|
||||
"Every node adds bandwidth, compute, and resilience to the Mycelium Grid. Together, hosts form the physical foundation for decentralized communication, storage, and AI.",
|
||||
icon: CircleNumber2Icon,
|
||||
},
|
||||
{
|
||||
name: "Earn Rewards",
|
||||
description:
|
||||
"Share storage, CPU, GPU, and bandwidth. When your resources are used, you earn network rewards based on real demand.",
|
||||
icon: CircleNumber3Icon,
|
||||
},
|
||||
];
|
||||
|
||||
export function NodeBenefits() {
|
||||
return (
|
||||
<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">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.3 }}
|
||||
transition={{ duration: 0.6, ease: "easeOut", delay: 0.1 }}
|
||||
className="mx-auto max-w-5xl text-center"
|
||||
>
|
||||
<Eyebrow color="accent">Host</Eyebrow>
|
||||
<SectionHeader
|
||||
className="text-3xl font-medium tracking-tight"
|
||||
color="light"
|
||||
>
|
||||
Benefits of Hosting Nodes
|
||||
</SectionHeader>
|
||||
|
||||
<P className="mt-6" color="light">
|
||||
Hosting a node gives you private compute, contributes to the global
|
||||
Mycelium infrastructure, and unlocks ways to earn from real network
|
||||
usage, all while keeping sovereignty and control.
|
||||
</P>
|
||||
</motion.div>
|
||||
|
||||
{/* Features */}
|
||||
<motion.ul
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
className="mx-auto mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-base/7 sm:grid-cols-2 sm:gap-y-16 lg:mx-12 lg:mt-12 lg:max-w-7xl lg:grid-cols-3"
|
||||
>
|
||||
{features.map((feature, index) => (
|
||||
<motion.li
|
||||
key={feature.name}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{
|
||||
duration: 0.45,
|
||||
delay: 0.3 + index * 0.15,
|
||||
ease: "easeOut",
|
||||
}}
|
||||
className="rounded-2xl border border-gray-300 bg-white/5 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20 backdrop-blur-md"
|
||||
>
|
||||
<feature.icon className="mb-4 h-8 w-8 text-white" />
|
||||
<CT as="span" className="text-left font-semibold" color="light">
|
||||
{feature.name}
|
||||
</CT>
|
||||
<CP className="mt-2 text-left text-gray-200">
|
||||
{feature.description}
|
||||
</CP>
|
||||
</motion.li>
|
||||
))}
|
||||
</motion.ul>
|
||||
</div>
|
||||
{/* Bottom spacing line */}
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800" />
|
||||
</section>
|
||||
|
||||
);
|
||||
}
|
||||
40
src/pages/nodes/NodeHero.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Eyebrow, H3 } from '@/components/Texts'
|
||||
|
||||
export function NodeHero() {
|
||||
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/gpuhero2.png')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>MYCELIUM NODES</Eyebrow>
|
||||
<H3 as="h1" className="mt-4">
|
||||
Host a Node. Power the Network.
|
||||
</H3>
|
||||
<p className="mt-6 text-lg text-gray-800">
|
||||
The Mycelium Network runs on nodes hosted by people and organizations around the world. Each node adds capacity, resilience, and sovereignty, expanding a global network for private, distributed compute and AI.
|
||||
</p>
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<Button to="#node-getting-started" as="a" variant="solid" color="cyan">
|
||||
How it works
|
||||
</Button>
|
||||
<Button to="#node-architecture" as="a" variant="outline">
|
||||
Explore Docs <span aria-hidden="true">→</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
223
src/pages/nodes/NodeProducts.tsx
Normal file
@@ -0,0 +1,223 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Eyebrow, SectionHeader, P } from "@/components/Texts";
|
||||
import {
|
||||
QuestionMarkCircleIcon,
|
||||
CheckIcon,
|
||||
} from "@heroicons/react/20/solid";
|
||||
|
||||
/* ------------------------------------------ */
|
||||
/* PRODUCT DATA */
|
||||
/* ------------------------------------------ */
|
||||
|
||||
const nodes = {
|
||||
ainode: {
|
||||
id: "ainode",
|
||||
name: "Edge AI Node",
|
||||
subtitle: "Based on Ryzen AI MAX+ 395 platform",
|
||||
description:
|
||||
"A compact AI-ready node designed for local inference, agent hosting, and sovereign edge compute. Equipped with a dedicated AI accelerator and optimized thermals.",
|
||||
image: "/images/ainode.png",
|
||||
features: [
|
||||
"Run local AI and cloud workloads",
|
||||
"Host Mycelium Slices and earn SPORE",
|
||||
"Experiment with decentralized apps and LLM agents",
|
||||
"Participate in the global Mycelium Network",
|
||||
],
|
||||
buyUrl:
|
||||
"https://www.gmktec.com/products/amd-ryzen%E2%84%A2-ai-max-395-evo-x2-ai-mini-pc?variant=6f7af17b-b907-4a9d-9c7e-afecfb41ed98",
|
||||
learnUrl:
|
||||
"https://threefold.info/mycelium_economics/docs/recommended_nodes/edge_ai_node",
|
||||
},
|
||||
|
||||
edgenode: {
|
||||
id: "edgenode",
|
||||
name: "Edge Compute Node",
|
||||
subtitle: "Based on GMKtec NUCBox M6 Ultra (Ryzen 5 7640HS)",
|
||||
description:
|
||||
"High-performance edge compute for networking, local models, and multiple agents. Excellent balance of efficiency and compute density.",
|
||||
image: "/images/edgenode.png",
|
||||
features: [
|
||||
"Efficient 6-core / 12-thread Zen 4 architecture at up to 5.0 GHz boost. (CPU Monkey)",
|
||||
"Low power consumption (35 W class HS chip) conducive to continuous operation. (LaptopMedia)",
|
||||
"Compact size → easier placement, less cooling overhead.",
|
||||
"Full node owner flexibility: use it for private workloads, host slices, or a hybrid of both.",
|
||||
],
|
||||
buyUrl:
|
||||
"https://www.gmktec.com/products/amd-ryzen-5-7640hs-mini-pc-nucbox-m6-ultra?spm=..product_ba613c14-a120-431b-af10-c5c5ca575d55.header_1.1&variant=35ad4a9a-3f31-490c-a2d1-ef9ea3773fe9",
|
||||
learnUrl:
|
||||
"https://threefold.info/mycelium_economics/docs/recommended_nodes/edge_node",
|
||||
},
|
||||
};
|
||||
|
||||
const configOptions = [
|
||||
{
|
||||
id: "ainode",
|
||||
name: "EDGE AI Node",
|
||||
description: "Optimized for local inference + AI acceleration",
|
||||
},
|
||||
{
|
||||
id: "edgenode",
|
||||
name: "EDGE Compute Node",
|
||||
description: "Optimized for general-purpose compute + agent workloads",
|
||||
},
|
||||
];
|
||||
|
||||
/* ------------------------------------------ */
|
||||
/* MAIN COMPONENT */
|
||||
/* ------------------------------------------ */
|
||||
|
||||
export function NodeProducts() {
|
||||
const [selectedNode, setSelectedNode] = useState(nodes.ainode);
|
||||
|
||||
return (
|
||||
<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" />
|
||||
|
||||
{/* MAIN SECTION */}
|
||||
<div className="relative px-6 lg:px-12 py-16 bg-[#111111] border border-t-0 border-b-0 border-gray-800 max-w-7xl mx-auto">
|
||||
|
||||
{/* --------------------------------------- */}
|
||||
{/* SECTION TITLE + INTRO PARAGRAPH */}
|
||||
{/* --------------------------------------- */}
|
||||
<div className="text-center max-w-3xl mx-auto mb-16">
|
||||
<Eyebrow color="accent">Hardware</Eyebrow>
|
||||
<SectionHeader className="text-3xl font-medium" color="light">
|
||||
Recommended Nodes
|
||||
</SectionHeader>
|
||||
<P className="mt-4 text-gray-300">
|
||||
Below are some of the best-performing and most commonly recommended nodes
|
||||
for hosting agents, Mycelium workloads, and contributing compute to the network.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 lg:gap-24 max-w-6xl mx-auto">
|
||||
|
||||
{/* ------------------------------ */}
|
||||
{/* LEFT — TEXT AREA */}
|
||||
{/* ------------------------------ */}
|
||||
<motion.div
|
||||
key={selectedNode.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="flex flex-col justify-center"
|
||||
>
|
||||
<h1 className="text-3xl font-semibold text-white">
|
||||
{selectedNode.name}
|
||||
</h1>
|
||||
|
||||
<p className="mt-1 text-gray-400 text-base">
|
||||
{selectedNode.subtitle}
|
||||
</p>
|
||||
|
||||
{/* Description */}
|
||||
<p className="mt-6 text-gray-300 text-base leading-relaxed">
|
||||
{selectedNode.description}
|
||||
</p>
|
||||
|
||||
{/* FEATURES */}
|
||||
<ul className="mt-6 space-y-2">
|
||||
{selectedNode.features.map((f, i) => (
|
||||
<li key={i} className="flex items-start">
|
||||
<CheckIcon className="w-5 h-5 text-green-500 mt-0.5" />
|
||||
<p className="ml-2 text-sm text-gray-300 leading-relaxed">
|
||||
{f}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* CONFIG SELECTOR */}
|
||||
<fieldset className="mt-10">
|
||||
<legend className="text-sm font-medium text-gray-200">
|
||||
Choose Your Node Type
|
||||
</legend>
|
||||
|
||||
<div className="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{configOptions.map((opt) => (
|
||||
<label
|
||||
key={opt.id}
|
||||
className={`group relative flex flex-col border rounded-xl p-4 cursor-pointer transition
|
||||
${
|
||||
selectedNode.id === opt.id
|
||||
? "border-cyan-500 bg-white/5"
|
||||
: "border-gray-700 hover:border-gray-500"
|
||||
}`}
|
||||
onClick={() => setSelectedNode(nodes[opt.id as keyof typeof nodes])}
|
||||
>
|
||||
<span className="text-white font-medium">{opt.name}</span>
|
||||
<span className="mt-1 text-sm text-gray-400">{opt.description}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div className="mt-4">
|
||||
<a href="https://threefold.info/mycelium_economics/docs/category/recommended-nodes" target="_blank" rel="noopener noreferrer" className="inline-flex text-sm text-gray-500 hover:text-gray-300 transition">
|
||||
What config should I choose?
|
||||
<QuestionMarkCircleIcon className="ml-1 w-5 h-5 text-gray-500" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* ------------------------ */}
|
||||
{/* BUTTONS AREA */}
|
||||
{/* ------------------------ */}
|
||||
<div className="mt-10 flex flex-col sm:flex-row gap-4">
|
||||
|
||||
{/* Outline Button */}
|
||||
<a
|
||||
href={selectedNode.learnUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 sm:flex-none text-center border border-gray-700 hover:border-gray-500
|
||||
text-gray-300 hover:text-white px-8 py-3 rounded-lg transition"
|
||||
>
|
||||
Learn More
|
||||
</a>
|
||||
|
||||
{/* Solid Cyan Button */}
|
||||
<a
|
||||
href={selectedNode.buyUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex-1 sm:flex-none text-center bg-cyan-600 hover:bg-cyan-700
|
||||
text-white px-8 py-3 rounded-lg font-medium transition"
|
||||
>
|
||||
Purchase Node
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</motion.div>
|
||||
|
||||
{/* ------------------------------ */}
|
||||
{/* RIGHT — IMAGE */}
|
||||
{/* ------------------------------ */}
|
||||
<motion.div
|
||||
key={selectedNode.image}
|
||||
initial={{ opacity: 0, scale: 0.92 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.35 }}
|
||||
className="flex justify-center"
|
||||
>
|
||||
<img
|
||||
src={selectedNode.image}
|
||||
alt={selectedNode.name}
|
||||
className="max-w-md rounded-2xl "
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom border */}
|
||||
<div className="w-full border-b border-gray-800 bg-[#121212]" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
110
src/pages/nodes/NodeSpecs.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P, Small, CT, CP } from "@/components/Texts";
|
||||
|
||||
const cards = [
|
||||
{
|
||||
category: "Autonomous",
|
||||
title: "Autonomous Operation",
|
||||
description: "Runs autonomously with no central control.",
|
||||
image: "/images/autonomous.png",
|
||||
rounded: "lg:rounded-tl-4xl",
|
||||
innerRounded: "lg:rounded-tl-[calc(2rem+1px)]",
|
||||
},
|
||||
{
|
||||
category: "Security",
|
||||
title: "Encrypted by Default",
|
||||
description: "Fully encrypted and identity-based.",
|
||||
image: "/images/encryptednode.png",
|
||||
},
|
||||
{
|
||||
category: "Efficiency",
|
||||
title: "Energy Efficient",
|
||||
description: "Energy-efficient and quiet, designed for 24/7 uptime.",
|
||||
image: "/images/energy.png",
|
||||
rounded: "lg:rounded-tr-4xl",
|
||||
innerRounded: "lg:rounded-tr-[calc(2rem+1px)]",
|
||||
},
|
||||
{
|
||||
category: "Monitoring",
|
||||
title: "Measured Uptime",
|
||||
description: "Automatically measures uptime and contribution.",
|
||||
image: "/images/uptime.png",
|
||||
rounded: "lg:rounded-bl-4xl",
|
||||
innerRounded: "lg:rounded-bl-[calc(2rem+1px)]",
|
||||
},
|
||||
{
|
||||
category: "Full Stack",
|
||||
title: "Full Mycelium Stack Support",
|
||||
description: "Supports Mycelium Network, Cloud, Pods, and Agents.",
|
||||
image: "/images/fullstack.png",
|
||||
},
|
||||
{
|
||||
category: "Edge & Home",
|
||||
title: "Edge & Home Ready",
|
||||
description:
|
||||
"Runs seamlessly on compact hardware for edge, home, or micro-datacenter deployments.",
|
||||
image: "/images/edge.png",
|
||||
rounded: "lg:rounded-br-4xl",
|
||||
innerRounded: "lg:rounded-br-[calc(2rem+1px)]",
|
||||
},
|
||||
];
|
||||
|
||||
export function NodeSpecs() {
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
|
||||
<Eyebrow>Node Specifications</Eyebrow>
|
||||
<H3 className="mt-2">Built for Reliability and Control</H3>
|
||||
<P className="mt-4 max-w-2xl">
|
||||
Each node strengthens the network and helps build a more open,
|
||||
sovereign and distributed internet.
|
||||
</P>
|
||||
|
||||
{/* BENTO GRID */}
|
||||
<div className="mt-12 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-3 lg:grid-rows-2">
|
||||
{cards.map((item, index) => (
|
||||
<div key={index} className="relative">
|
||||
|
||||
{/* OUTER BACKPLATE */}
|
||||
<div
|
||||
className={`absolute inset-0 rounded-lg bg-white ${
|
||||
item.rounded || ""
|
||||
}`}
|
||||
/>
|
||||
|
||||
{/* INNER CARD */}
|
||||
<div
|
||||
className={`relative flex h-full flex-row items-center gap-6 overflow-hidden rounded-[calc(var(--radius-lg)+1px)] p-8 ${
|
||||
item.innerRounded || ""
|
||||
}`}
|
||||
>
|
||||
{/* LEFT IMAGE */}
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
className="h-16 w-16 object-contain"
|
||||
/>
|
||||
|
||||
{/* RIGHT TEXT */}
|
||||
<div className="flex flex-col">
|
||||
<Small className="text-gray-500">{item.category}</Small>
|
||||
<CT className="mt-1">{item.title}</CT>
|
||||
<CP className="mt-2 max-w-sm">{item.description}</CP>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* OUTLINE */}
|
||||
<div
|
||||
className={`pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 ${
|
||||
item.rounded || ""
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
87
src/pages/nodes/NodeSteps.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
|
||||
import { Eyebrow, H3, P, CT, CP } from "@/components/Texts";
|
||||
import {
|
||||
CloudArrowDownIcon,
|
||||
CpuChipIcon,
|
||||
WalletIcon,
|
||||
BoltIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
|
||||
const steps = [
|
||||
{
|
||||
name: "Buy a Node",
|
||||
description:
|
||||
"Choose your hardware setup. You can use your own device or one of the recommended models below.",
|
||||
icon: CpuChipIcon,
|
||||
},
|
||||
{
|
||||
name: "Download Mycelium OS",
|
||||
description:
|
||||
"Install the Mycelium OS to prepare your node for network access.",
|
||||
icon: CloudArrowDownIcon,
|
||||
},
|
||||
{
|
||||
name: "Link Your Wallet",
|
||||
description:
|
||||
"Connect your private key from your Mycelium account to enable rewards and authentication.",
|
||||
icon: WalletIcon,
|
||||
},
|
||||
{
|
||||
name: "Plug In and Go",
|
||||
description:
|
||||
"Connect to power and to the internet. Your node will join automatically and start contributing.",
|
||||
icon: BoltIcon,
|
||||
},
|
||||
];
|
||||
|
||||
export function NodeSteps() {
|
||||
return (
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
{/* Top line */}
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100" />
|
||||
<div className="w-full border-t border-l border-r border-gray-100" />
|
||||
|
||||
<div className="max-w-7xl bg-white mx-auto py-16 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<Eyebrow className="text-cyan-500">HOW IT WORKS</Eyebrow>
|
||||
|
||||
<H3 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
|
||||
Host a Node
|
||||
</H3>
|
||||
|
||||
<P className="mt-6 text-lg text-gray-600">
|
||||
Hosting a node takes minutes, and it runs automatically once online.
|
||||
</P>
|
||||
</div>
|
||||
|
||||
{/* 🔹 Horizontal Stepper */}
|
||||
<div className="relative mt-16 px-6 flex flex-col gap-8 lg:flex-row lg:items-start lg:justify-between">
|
||||
{steps.map((step, i) => (
|
||||
<div key={step.name} className="relative flex-1 px-4">
|
||||
{/* 🔹 Horizontal connector line (desktop only) */}
|
||||
{i < steps.length - 1 && (
|
||||
<div className="hidden lg:block absolute top-5 left-[55%] w-full h-[4px] bg-gray-400 -z-10" />
|
||||
)}
|
||||
|
||||
{/* 🔹 Step header with icon */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center w-8 h-8 rounded-full bg-cyan-100 text-cyan-600">
|
||||
<step.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<CT>{step.name}</CT>
|
||||
</div>
|
||||
|
||||
{/* 🔹 Description */}
|
||||
<CP className="mt-3">{step.description}</CP>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* bottom line */}
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
22
src/pages/nodes/NodesPage.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { NodeHero } from './NodeHero';
|
||||
import { NodeBenefits } from './NodeBenefits';
|
||||
import { NodeSteps } from './NodeSteps';
|
||||
import { NodeProducts } from './NodeProducts';
|
||||
import { NodeSpecs } from './NodeSpecs';
|
||||
import { CallToAction } from './CallToAction';
|
||||
|
||||
const NodesPage: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<NodeHero />
|
||||
<NodeBenefits />
|
||||
<NodeSteps />
|
||||
<NodeProducts />
|
||||
<NodeSpecs />
|
||||
<CallToAction />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NodesPage;
|
||||
106
src/pages/pods/CallToAction.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { Container } from "@/components/Container";
|
||||
import { CheckCircleIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
const benefits = [
|
||||
'Private chat and calls',
|
||||
'Calendar and file sync',
|
||||
'Secure team spaces',
|
||||
'Early AI Agent integration',
|
||||
]
|
||||
|
||||
|
||||
|
||||
export function CallToAction() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-[#121212] ">
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<div className="max-w-7xl bg-[#111111] 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" />
|
||||
|
||||
{/* ✅ 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"
|
||||
>
|
||||
{/* ✅ 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.2"
|
||||
/>
|
||||
<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">
|
||||
Be Among The First
|
||||
|
||||
</h2>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
The first Pods are launching soon.
|
||||
10,000 early Pods will be available for early adopters.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 flex items-center justify-center">
|
||||
<ul
|
||||
role="list"
|
||||
className="grid grid-cols-1 gap-x-8 gap-y-3 text-left text-base/7 text-gray-200 sm:grid-cols-2"
|
||||
>
|
||||
{benefits.map((benefit) => (
|
||||
<li key={benefit} className="flex items-start gap-x-3">
|
||||
<CheckCircleIcon
|
||||
aria-hidden="true"
|
||||
className="mt-1 h-7 w-5 flex-none text-gray-200"
|
||||
/>
|
||||
<span>{benefit}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Next, Pods will support peer-to-peer AI Agents that live inside your environment.
|
||||
Your own AI, powered by your data without any data leaks. Be among the first to claim
|
||||
your private space on the Mycelium Network.
|
||||
</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="#" variant="solid" color="cyan" className="mt-4">
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center text-center max-w-xs">
|
||||
<Button to="#" variant="outline" color="white" className="mt-4">
|
||||
Learn More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-800" />
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-800 bg-transparent" />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,34 +1,63 @@
|
||||
import { H3, Eyebrow } from "@/components/Texts"
|
||||
import { H3, Eyebrow, P } from "@/components/Texts"
|
||||
import { Button } from "@/components/Button"
|
||||
import React from 'react';
|
||||
|
||||
// NOTE: Ensure your global types file (global.d.ts) is set up
|
||||
// to avoid TypeScript errors on window.ml_account
|
||||
|
||||
export default function Homepod() {
|
||||
const onGetStartedClick = () => {
|
||||
// 1. Ensure we are in a browser environment (client-side)
|
||||
// and the ml_account function is available globally.
|
||||
if (typeof window !== 'undefined' && window.ml_account) {
|
||||
|
||||
// 2. Execute the MailerLite pop-up trigger function using the specific snippet you provided.
|
||||
// TypeScript is happy if you defined the global type in step 1.
|
||||
window.ml_account('webforms', '6108375', 'l9m8g1', 'show');
|
||||
|
||||
} else {
|
||||
console.log("MailerLite script (ml_account) not fully loaded or not in browser.");
|
||||
}
|
||||
};
|
||||
|
||||
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/pods.png')", backgroundSize: "contain" }}
|
||||
style={{ backgroundImage: "url('/images/computehero11.webp')", backgroundSize: "contain" }}
|
||||
>
|
||||
{/* Inner padding */}
|
||||
<div className="px-6 py-16 lg:py-16">
|
||||
<div className="px-6 py-16 lg:py-24">
|
||||
<div className="max-w-2xl lg:pl-6">
|
||||
<Eyebrow>
|
||||
MYCELIUM PODS
|
||||
Mycelium Pods - Coming Soon
|
||||
</Eyebrow>
|
||||
<H3 className="mt-4">
|
||||
Your Private Space in the New Internet
|
||||
</H3>
|
||||
<p className="mt-6 text-lg">
|
||||
Imagine having your own corner of the internet — private, secure, and always online.
|
||||
A Pod is your personal digital space on the Mycelium Network.
|
||||
It’s where your conversations, files, and digital tools live — owned by you, connected to others directly.
|
||||
</p>
|
||||
<P className="mt-6 text-gray-800">
|
||||
Pods are personal digital spaces on the Mycelium Network. They are private, persistent, and fully under your control. Run conversations, files, and tools directly on the network instead of through central servers
|
||||
</P>
|
||||
<div className="mt-8 flex items-center gap-x-6">
|
||||
<Button
|
||||
variant="solid"
|
||||
color="cyan"
|
||||
// The updated onClick handler calls the function
|
||||
onClick={onGetStartedClick}
|
||||
>
|
||||
Join the Waitlist
|
||||
</Button>
|
||||
<Button to="#" variant="outline">
|
||||
Explore Docs →
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{/* ✅ Bottom horizontal line with spacing */}
|
||||
<div className="w-full border-b border-gray-100" />
|
||||
{/* 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>
|
||||
)
|
||||
}
|
||||
}
|
||||