62 Commits

Author SHA1 Message Date
d469bb7b99 add wishlist 2025-11-19 16:23:53 +02:00
3f89d1c441 add some padding at mobile 2025-11-19 11:20:12 +02:00
fa30aeff43 make map load fast 2025-11-19 10:56:29 +02:00
cbbc87cc29 update map with nodes dynamic 2025-11-19 10:24:15 +02:00
8dfc46cabe Replace static actual grid data 2025-11-18 16:43:23 +02:00
8fdcf1777d update statics to be dynamic 2025-11-18 13:28:20 +02:00
6329d2dcac refactor: convert NodeSpecs from image-based bento grid to icon-based horizontal layout
- Replaced placeholder Tailwind images with local image assets (/images/*.png)
- Changed card layout from vertical image-over-text to horizontal icon-beside-text
- Reduced image size from h-80 full-width to h-16 w-16 icon size with object-contain
- Updated flex direction from flex-col to flex-row with items-center alignment
- Adjusted padding and spacing (p-8 on card, reduced mt-2 to mt-1 on title)
- Removed "
2025-11-17 17:36:44 +01:00
3604b47137 refactor: convert NodeSpecs to bento grid layout with updated typography components
- Replaced Heroicons with typography components (Eyebrow, H3, P, Small, CT, CP)
- Changed from card grid to bento grid layout with image-based cards
- Added category labels and placeholder images to each specification card
- Removed hover effects and icon-based design in favor of image-focused layout
- Updated container structure from Container component to standard section with max-w-7xl
- Removed border decorations an
2025-11-17 17:10:47 +01:00
fe0e231204 refactor: add Small import to CloudPros typography components
- Added Small to existing Eyebrow, H3, and P imports for component availability
2025-11-17 16:12:47 +01:00
da539a5e55 refactor: replace Small with Eyebrow component in CloudPros intro section
- Changed import from Small to Eyebrow for consistent typography
- Updated "Cloud Advantages" text to use Eyebrow component instead of Small
2025-11-17 16:12:31 +01:00
b7e003a680 refactor: update intro section width and eyebrow text in CloudPros
- Changed max-width from max-w-3xl to max-w-4xl for wider content area
- Updated eyebrow text from "Agent Advantage" to "Cloud Advantages" for consistency
2025-11-17 16:11:41 +01:00
0324b2e8ad refactor: add infinite-scroll animation keyframes to Tailwind config
- Added infinite-scroll animation with translateX from 0 to -50%
- Animation supports horizontal scrolling effects for carousel/ticker components
2025-11-17 16:07:39 +01:00
49d9c22de3 refactor: remove unused imports and variables in CloudPros and DataControl
- Removed unused Eyebrow import from CloudPros
- Removed unused loop index variable 'i' from gates mapping in DataControl
2025-11-17 16:07:20 +01:00
828fe93f46 refactor: reduce spacing and adjust typography in CallToAction and NetworkDownload
- Changed paragraph spacing from mt-6 to mt-2 in CallToAction for tighter layout
- Reduced top margin on cards container from mt-10 to mt-8
- Replaced "Follow Development" Button with styled anchor link featuring underline and arrow
- Added hover effects with cyan color transition to new anchor link
- Reduced heading size in NetworkDownload from text-5xl/6xl to text-4xl/5xl
2025-11-17 16:05:54 +01:00
8b04b668b6 refactor: update text color in NodeBenefits feature descriptions
- Changed CP component color prop from "light" to direct gray-200 class for consistent styling
2025-11-17 15:58:14 +01:00
618e1b0f5b refactor: improve punctuation consistency and add spacing in multiple components
- Replaced em dashes with commas in AgentPro descriptions for "Local Execution" and "Private Data Access"
- Changed em dash to comma in CloudPros description for better readability
- Removed em dash in NetworkUsecases "Service-to-Service Networking" description
- Added bottom spacing elements to NodeBenefits with border styling and padding
2025-11-17 15:56:40 +01:00
e46848b98d refactor: increase vertical padding on large screens in NodeHero
- Changed lg:py-16 to lg:py-24 for consistent spacing with other hero sections
2025-11-17 15:52:37 +01:00
1fb08e83f2 refactor: add centered intro sections and update typography imports in CloudPros and NetworkPros
- Added Eyebrow, H3, and P imports to CloudPros and NetworkPros
- Replaced raw h3 and p tags with typography components in CloudPros
- Added centered "Why It's Revolutionary" intro section to NetworkPros with eyebrow, heading, and description
- Updated NetworkPros grid with top margin (mt-8) for spacing
- Changed Homepod eyebrow text from "MYCELIUM PODS" to "Mycelium Pods - Coming Soon"
2025-11-17 15:51:37 +01:00
fb35ede79d feat: add centered intro section to HomeDesign and hover effects to HomeTab cards
- Added centered "Who's it For" intro section to HomeDesign with eyebrow, heading, and description
- Imported Eyebrow, H3, and P components for consistent typography
- Updated HomeDesign grid item styling with top margin and adjusted border classes
- Added hover effects to all HomeTab cards with scale, border color, and shadow transitions
- Applied consistent hover styling (scale-105, cyan border, and cyan shadow)
2025-11-17 15:46:02 +01:00
1a34508699 refactor: rename and update pod animations with improved visuals and consistency
- Renamed NoCentral to DataControl with enhanced data ownership visualization
- Renamed NoExtraction to Connectivity with full mesh P2P topology
- Renamed NoSinglePoint to Security with improved styling
- Renamed NoControl to Resilience with consistent styling
- Added lg:-mt-12 margin to Security and Resilience animations for alignment
- Updated DataControl to show explicit permission gates and bi-directional replication
2025-11-17 15:12:55 +01:00
97da7ba907 refactor: reduce horizontal padding on large screens in PodsBento
- Changed lg:px-10 to lg:px-8 for more consistent spacing with other sections
2025-11-17 15:00:53 +01:00
2b7559ab47 refactor: redesign pods page with bento grid layout and improved benefits sections
- Increased CloudHeroNew vertical padding on large screens (lg:py-16 to lg:py-24)
- Reduced spacing between description paragraphs in CloudHeroNew (mt-4 to mt-2)
- Created PodsBento component with animated bento grid showcasing pod benefits
- Added animations for data control, connectivity, security, and resilience features
- Refactored PodsDesign from accordion layout to centered intro with 4-column grid
- Create
2025-11-17 15:00:41 +01:00
3ab559aa84 refactor: remove unused imports across multiple components
- Fixed CloudPros export name from AgentPro to CloudPros
- Removed unused H1, H4, and H3Icon imports from HomeAurora
- Removed unused H3, P, and Eyebrow imports from NetworkDownload
- Changed motion.H3 and motion.P to lowercase motion.h3 and motion.p in NetworkDownload
- Removed unused Small import from PodsFeatures
- Removed unused IconClockHour5 import from PodsHow
2025-11-17 14:43:39 +01:00
57c39a8b2b feat: refactor pods page with simplified content and improved animations
- Added agent1.png image asset
- Refactored InfiniteMovingCards component with cleaner animation logic and improved duplication handling
- Changed default speed from "fast" to "slow" and simplified animation setup
- Updated AgentBento title from "Augmented Intelligence Fabric" to "Intelligence Fabric"
- Increased Homepod vertical padding on large screens (lg:py-16 to lg:py-24)
- Removed "Feature" label from PodsFeatures use
2025-11-17 14:39:23 +01:00
6ff539b3fc feat: add AgentPro section and refactor AgentUsecase layout
- Created AgentPro component highlighting local execution, mesh connectivity, private data access, and portability advantages
- Replaced horizontal scrolling carousel in AgentUsecase with responsive grid layout
- Added "Blend local + remote intelligence" use case to AgentUsecase
- Removed slider navigation buttons and replaced with static grid display
- Replaced AgentDesign with AgentPro in AgentsPage component order
- Increased hero section padding on
2025-11-17 14:12:17 +01:00
29e2d942de refactor: improve punctuation and grammar consistency across components
- Replaced em dashes with commas for better readability in descriptions
- Standardized punctuation in aria-labels (changed "—" to ",")
- Removed unnecessary em dashes in favor of commas or removed punctuation
- Fixed inconsistent spacing around punctuation marks
- Improved sentence flow in multiple component descriptions
2025-11-17 13:52:42 +01:00
def0972762 feat: streamline cloud and pods pages with improved CTAs and content
- Replaced CloudCodeTabs with static reserve image in CloudIntro
- Added dynamic action buttons (Deploy, Follow Development, View Docs) to CloudIntro tabs
- Removed CloudFeaturesLight section from CloudPage
- Enhanced Pods CallToAction with benefits checklist and expanded early adopter messaging
- Added CTA buttons to Homepod component with waitlist and docs links
- Removed PodsBenefits section from PodsPage
- Updated Homepod description
2025-11-17 13:01:56 +01:00
1c37cc08ee feat: add network download section and update network page styling
- Created NetworkDownload component with platform-specific download cards for iOS, macOS, Windows, Android, and Linux
- Added hover effects and external links to app stores and GitHub releases
- Replaced NetworkUsecases with NetworkDownload in NetworkPage component order
- Renamed CloudPros to NetworkPros for consistency
- Updated NetworkPros styling from light theme to dark theme with improved contrast and hover states
- Change
2025-11-17 12:44:23 +01:00
0eef2cd013 feat: add scroll-to-top behavior and hash anchor navigation
- Added ScrollToTop component to handle navigation scroll behavior
- Implemented smooth scrolling to hash anchors when present in URL
- Added automatic scroll to top on route changes
- Imported useLocation and useEffect hooks for scroll management
2025-11-17 12:27:24 +01:00
3cd41ab1d9 refactor: simplify hero section with updated typography and spacing
- Replaced H1 with H3 and added Eyebrow component for "Project MYCELIUM" label
- Removed promotional banner about booking a call
- Improved text hierarchy and reduced vertical spacing (mt-8 to mt-4)
- Added max-width constraint to description text for better readability
- Removed unused H3Icon import
2025-11-17 12:23:27 +01:00
3121251272 refactor: remove unused imports and add icon null check
- Removed unused ShieldCheckIcon import from NodeProducts
- Removed unused Small component import from NodeSpecs
- Added conditional rendering for Icon in PodsWhat to prevent errors when icon is undefined
2025-11-14 22:59:24 +01:00
56ceac1319 refactor: remove dark mode styling from agent and home pages
- Simplified styling by removing dark mode classes (dark:bg-*, dark:text-*, dark:border-*)
- Added explicit text color classes for better consistency
- Fixed animation container layout with proper centering and sizing
2025-11-14 22:51:19 +01:00
a3028ff448 feat: add cyan glow effects and new network resilience animation
- Added cyan radial glow SVG to CallToAction components across all product pages for visual consistency
- Created NoSinglePoint animation demonstrating redundant network paths and resilience against single point failures
- Updated HomeArchitecture layout to better center and display animations with improved flex positioning
2025-11-14 22:23:11 +01:00
33821987aa refactor: streamline page components and simplify UI elements
- Removed unused HomeAudience component from HomePage
- Renamed NetworkPros to CloudPros for clarity
- Removed bullet point lists from PodsFeatures to simplify presentation
- Replaced PodCapabilities with PodsWhat and removed PodsComing section
2025-11-14 21:53:21 +01:00
678da4b66c feat: redesign cloud page with improved content structure and visual hierarchy
- Replaced CloudHostingNew with new CloudIntro component featuring tabbed interface for Kubernetes, VDC, and QSFS capabilities
- Added CloudCodeTabs component with interactive code examples and syntax highlighting
- Created CloudPros section highlighting platform architecture, reliability, compatibility, and scalability
- Updated hero section copy to emphasize sovereign edge infrastructure and simplified messaging
- Remove
2025-11-14 18:15:04 +01:00
326efc9fbd refactor: rename Node page to Nodes and reorganize network page sections
- Renamed NodePage component and directory to NodesPage/nodes for consistency
- Updated all navigation links from "Node" to "Nodes" across headers and footer
- Replaced anchor tags with React Router Link components for proper SPA navigation
- Reorganized NetworkPage component order and added NetworkPros section
- Converted NetworkUsecases from horizontal slider to responsive grid layout
- Added new use cases for adaptive mesh and compute fabric
- Update
2025-11-14 17:01:29 +01:00
3a656ef5e9 fix: correct node route path from /node to /nodes 2025-11-14 16:20:26 +01:00
cf32cd081c feat: update network CTA with improved messaging and navigation
- Changed heading from "Start" to "Connect" and refined description for clarity
- Replaced two-button layout with three-option navigation (Join Network, Deploy in Cloud, Host a Node)
- Updated button destinations and styling to better guide user journey
2025-11-14 16:16:08 +01:00
1950342b7a feat: simplify network page messaging and update feature descriptions
- Streamlined hero and features sections with clearer, more direct value propositions
- Updated network capabilities to emphasize unified connectivity over technical details
- Replaced technical feature descriptions with user-focused benefits (DNS, VPN, publishing, AI)
2025-11-14 16:16:00 +01:00
d1fc11ce80 refactor: simplify navigation structure and update homepage content
- Removed Cloud dropdown menu and flattened navigation to direct links (Network, Cloud, Pods, Agents, Node)
- Reordered navigation items for better flow and renamed "Nodes" to "Node" for consistency
- Updated homepage hero and CTA sections with clearer messaging focused on digital sovereignty and infrastructure control
- Added new bento grid images for visual refresh
2025-11-14 15:41:21 +01:00
48954151c9 Mycelium unifies everything 2025-11-14 13:39:25 +01:00
61e368e27e refactor: add type assertion for node selection 2025-11-14 13:07:25 +01:00
d8524ef181 feat: redesign node products section with interactive configuration selector
- Replaced static grid layout with dynamic two-column design featuring live product switching
- Added configuration selector allowing users to toggle between AI Node and Compute Node options
- Enhanced product information with detailed features, descriptions, and direct purchase/learn more CTAs
2025-11-14 13:03:06 +01:00
c44d9158f2 feat: add node setup guide and product recommendations
- Created NodeSteps component with 4-step horizontal stepper showing node hosting process
- Added NodeProducts component displaying recommended Edge AI and Edge Compute nodes
- Integrated new components into NodePage with supporting product images
2025-11-14 12:43:43 +01:00
359afc3360 feat: add nodes page with hosting information and benefits
- Created new NodePage with hero section explaining the Mycelium node network
- Added NodeBenefits component showcasing three key advantages of hosting nodes
- Integrated nodes route into navigation (header, footer, and routing configuration)
2025-11-14 12:14:28 +01:00
96a17a668a fix: improve text contrast in hero sections 2025-11-13 15:28:39 +01:00
c93974ea3e style: remove trailing whitespace and adjust background opacity 2025-11-13 15:27:33 +01:00
f1f8f50871 refactor: replace paragraph tag with P component in AgentHeroAlt 2025-11-13 15:27:00 +01:00
e4b2d66a76 fix: update homepage pod description text styling 2025-11-13 15:25:59 +01:00
b208fe7f2a refactor: convert agent bento cards from videos to React animation components
- Replaced video paths with imported animation components (Fungistor, Herodb, MOSSandboxes, etc.)
- Added new AgentDesign and AgentUseCase sections to agents page
- Updated hero copy to emphasize private, sovereign AI and 2026 timeline
- Reorganized page layout with new sections between existing components
2025-11-13 15:24:46 +01:00
7b80ab84c9 feat: add comprehensive Pods landing page sections
- Created new sections including benefits grid, federation design, upcoming features, and call-to-action
- Added pricing tiers component with three Pod subscription levels
- Updated existing sections with refined messaging and improved visual hierarchy
2025-11-13 14:52:24 +01:00
aa6f475050 feat: redesign pods page with enhanced UI and animations
- Replaced HomeBlink with HomeAurora component for improved visual experience
- Added PodCapabilities horizontal slider with interactive navigation controls
- Created PodsFeatures section showcasing use cases with hover animations
- Updated PodsHow layout with bullet points and new PodsFlow animation component
2025-11-13 14:04:13 +01:00
5c2fcecbd9 feat: add dark theme header component with navigation
- Created HeaderDark component with responsive navigation including Cloud dropdown menu with breadcrumb-style display
- Implemented mobile menu with slide-out panel using Headless UI Dialog
- Integrated HeaderDark into HomeHostingDark page for consistent dark theme experience
2025-11-12 16:53:04 +01:00
e5cf6ee362 refactor: remove unused imports and variables
- Removed unused React imports from components (now using named imports only)
- Removed unused cursor state and onMouseMove handler from text-hover-effect
- Removed unused H1 import from HomeBlink
2025-11-12 16:26:51 +01:00
e9c1fd795e refactor: remove unused Button import from HomeAudience 2025-11-12 16:24:22 +01:00
be95ea97c4 refactor: remove unused CTA buttons from HomeAudience component 2025-11-12 16:23:07 +01:00
9c47ea39fc refactor: adjust horizontal padding to vertical padding in HomeBlink container 2025-11-12 16:20:32 +01:00
7a0675a408 feat: add animated text hover effect to home headline
- Created TextHoverEffect component with animated gradient mask and stroke drawing
- Replaced static H1 title with interactive HomeHeadline component featuring auto-looping animation
- Enhanced visual appeal with cyan gradient glow and smooth reveal effects
2025-11-12 16:19:17 +01:00
37d4371288 feat: update CallToAction component styling and add cyan glow effect
- Replaced hardcoded heading and paragraph elements with reusable H3 and P components
- Added cyan radial glow SVG overlay to HomeBlink for enhanced visual atmosphere
- Updated audience images (7.jpg and 8.jpg)
2025-11-12 15:59:18 +01:00
dfe71dd4eb refactor: remove unused import and fix typo in HomePage 2025-11-12 15:51:00 +01:00
eba1bb7047 feat: add architecture section with animated illustrations
- Created HomeArchitecture component showcasing decentralized network principles with SVG animations
- Added animated illustrations for mesh networking, no extraction, no control, and no central servers
- Enhanced CallToAction with cyan radial glow effect for visual emphasis
- Fixed audience gallery image paths to use correct /images/audiences/ directory
2025-11-12 15:47:58 +01:00
0d9f357881 feat: add dark theme support and update homepage content
- Integrated next-themes for dark mode theming with default dark theme
- Added new UI components (GridBlink, Spotlight) and enhanced world map with cyan glow effects
- Updated homepage messaging to emphasize Mycelium as a living network with new audience imagery
2025-11-12 15:18:34 +01:00
146 changed files with 9496 additions and 648 deletions

11
package-lock.json generated
View File

@@ -28,6 +28,7 @@
"framer-motion": "^10.18.0",
"lucide-react": "^0.544.0",
"motion": "^12.23.24",
"next-themes": "^0.4.6",
"popmotion": "^11.0.5",
"react": "^18.3.1",
"react-countup": "^6.5.3",
@@ -9417,6 +9418,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/next-themes": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
"integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
}
},
"node_modules/node-releases": {
"version": "2.0.26",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz",

View File

@@ -30,6 +30,7 @@
"framer-motion": "^10.18.0",
"lucide-react": "^0.544.0",
"motion": "^12.23.24",
"next-themes": "^0.4.6",
"popmotion": "^11.0.5",
"react": "^18.3.1",
"react-countup": "^6.5.3",

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/images/agent1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 KiB

BIN
public/images/ainode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
public/images/cons.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 KiB

BIN
public/images/dev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB

BIN
public/images/edge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 KiB

BIN
public/images/edgenode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

BIN
public/images/energy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 KiB

BIN
public/images/fullstack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 KiB

BIN
public/images/pod1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/images/podsimg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/images/seekers.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 KiB

BIN
public/images/uptime.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

View File

@@ -14,6 +14,8 @@
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -0,0 +1,163 @@
import { useState } from 'react'
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'
export function HeaderDark() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
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">
<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"
>
Agents
</Link>
<Link
to="/nodes"
className="text-base/7 tracking-tight text-gray-300 hover:text-cyan-400 transition-colors"
>
Nodes
</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">
<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="/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"
onClick={() => setMobileMenuOpen(false)}
>
Agents
</Link>
<Link
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)}
>
Nodes
</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>
)
}

View File

@@ -0,0 +1,9 @@
import { TextHoverEffect } from "@/components/ui/text-hover-effect";
export function HomeHeadline() {
return (
<div className="flex items-center justify-center h-auto max-h-[200px]">
<TextHoverEffect text="MYCELIUM" />
</div>
);
}

View File

@@ -1,8 +1,42 @@
import { useEffect } from 'react'
import { Outlet } from 'react-router-dom'
import { Footer } from './Footer'
import { Header } from './Header'
export function Layout() {
useEffect(() => {
if (typeof window === 'undefined') return
if (document.getElementById('mailerlite-universal')) return
const script = document.createElement('script')
script.id = 'mailerlite-universal'
script.innerHTML = `
(function(m,a,i,l,e,r){
m['MailerLiteObject']=e;
function f(){
var c={a:arguments,q:[]};
var r=this.push(c);
return "number"!=typeof r?r:f.bind(c.q);
}
f.q=f.q||[];
m[e]=m[e]||f.bind(f.q);
m[e].q=m[e].q||f.q;
r=a.createElement(i);
var _=a.getElementsByTagName(i)[0];
r.async=1;
r.src=l+'?v'+(~~(new Date().getTime()/1000000));
_.parentNode.insertBefore(r,_);
})(window, document, 'script', 'https://static.mailerlite.com/js/universal.js', 'ml');
window.ml_account = ml('accounts', '1778010', 'x2d3d9f8n1', 'load');
`
document.body.appendChild(script)
return () => {
script.remove()
}
}, [])
return (
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>

View File

@@ -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>
))}

View 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;

View File

@@ -0,0 +1,64 @@
"use client";
import { useEffect, useRef } from "react";
export function GridBlink() {
const svgRef = useRef<SVGSVGElement>(null);
const maxActive = 3; // ✅ limit active squares
useEffect(() => {
const svg = svgRef.current;
if (!svg) return;
const squares = Array.from(svg.querySelectorAll<SVGRectElement>(".blink-square"));
function scheduleBlink() {
// ✅ only blink if we have too few active
const currentlyActive = squares.filter(sq => sq.classList.contains("active"));
if (currentlyActive.length < maxActive) {
const sq = squares[Math.floor(Math.random() * squares.length)];
sq.classList.add("active");
const duration = 800 + Math.random() * 1000; // ✅ slower fade-out
setTimeout(() => {
sq.classList.remove("active");
}, duration);
}
// ✅ slower scheduling
setTimeout(scheduleBlink, 300 + Math.random() * 600);
}
scheduleBlink();
}, []);
const rows = 20;
const cols = 32;
const size = 40;
return (
<svg
ref={svgRef}
className="pointer-events-none absolute inset-0 z-0"
style={{ width: "100%", height: "100%" }}
>
{Array.from({ length: rows * cols }).map((_, i) => {
const x = (i % cols) * size;
const y = Math.floor(i / cols) * size;
return (
<rect
key={i}
className="blink-square"
x={x}
y={y}
width={size}
height={size}
fill="transparent"
stroke="#efefef"
strokeWidth="1"
/>
);
})}
</svg>
);
}

View File

@@ -0,0 +1,63 @@
import { cn } from "@/lib/utils";
type SpotlightProps = {
className?: string;
fill?: string;
};
export const Spotlight = ({ className, fill }: SpotlightProps) => {
return (
<svg
className={cn(
"animate-spotlight pointer-events-none absolute z-[1] h-[169%] w-[138%] lg:w-[84%] opacity-100 mix-blend-screen",
className
)}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 3787 2842"
fill="none"
>
<g filter="url(#filter)">
<ellipse
cx="1924.71"
cy="273.501"
rx="1924.71"
ry="273.501"
transform="matrix(-0.822377 -0.568943 -0.568943 0.822377 3631.88 2291.09)"
fill={fill || "url(#spotlightGradient)"}
fillOpacity="1"
/>
</g>
<defs>
{/* ✅ Cyan radial gradient */}
<radialGradient
id="spotlightGradient"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(2200 1600) rotate(90) scale(1800 2800)"
>
<stop offset="0%" stopColor="#00eaff" stopOpacity="0.95" />
<stop offset="40%" stopColor="#00eaff" stopOpacity="0.35" />
<stop offset="100%" stopColor="#00eaff" stopOpacity="0" />
</radialGradient>
<filter
id="filter"
x="0.860352"
y="0.838989"
width="3785.16"
height="2840.26"
filterUnits="userSpaceOnUse"
colorInterpolationFilters="sRGB"
>
<feFlood floodOpacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="151" result="effect1_foregroundBlur" />
</filter>
</defs>
</svg>
);
};

View File

@@ -0,0 +1,139 @@
"use client";
import { useRef, useEffect, useState } from "react";
import { motion, useAnimation } from "motion/react";
export const TextHoverEffect = ({
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>
);
};

View File

@@ -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,158 +9,152 @@ 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 = [],
nodes = [],
lineColor = "#06b6d4",
}: MapProps) {
const svgRef = useRef<SVGSVGElement>(null);
const map = new DottedMap({ height: 100, grid: "diagonal" });
// ✅ Force dark-dotted map theme
const map = new DottedMap({ height: 100, grid: "diagonal" });
const svgMap = map.getSVG({
radius: 0.22,
color: "#FFFFFF40", // Hardcoded for dark theme
color: "#06b6d480",
shape: "circle",
backgroundColor: "black", // Hardcoded for dark theme
backgroundColor: "#111111",
});
// ✅ 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: { x: number; y: number },
end: { x: number; y: number }
) => {
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}`;
};
return (
<div className="w-full aspect-[2/1] dark:bg-black bg-white rounded-lg relative font-sans">
<div className="w-full aspect-[2/1] bg-[#111111] rounded-lg relative font-sans">
<img
src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`}
className="h-full w-full [mask-image:linear-gradient(to_bottom,transparent,white_10%,white_90%,transparent)] pointer-events-none select-none"
className="h-full w-full pointer-events-none select-none opacity-[0.6]"
alt="world map"
height="495"
width="1056"
draggable={false}
/>
{/* ✅ 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"
>
{dots.map((dot, i) => {
const startPoint = projectPoint(dot.start.lat, dot.start.lng);
const endPoint = projectPoint(dot.end.lat, dot.end.lng);
{/* 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={`path-group-${i}`}>
<motion.path
d={createCurvedPath(startPoint, endPoint)}
fill="none"
stroke="url(#path-gradient)"
strokeWidth="1"
initial={{
pathLength: 0,
}}
animate={{
pathLength: 1,
}}
transition={{
duration: 1,
delay: 0.5 * i,
ease: "easeOut",
}}
key={`start-upper-${i}`}
></motion.path>
<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>
);
})}
<defs>
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="white" stopOpacity="0" />
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
<stop offset="100%" stopColor="white" stopOpacity="0" />
</linearGradient>
</defs>
{/* ✅ 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);
{dots.map((dot, i) => (
<g key={`points-group-${i}`}>
<g key={`start-${i}`}>
<circle
cx={projectPoint(dot.start.lat, dot.start.lng).x}
cy={projectPoint(dot.start.lat, dot.start.lng).y}
r="2"
fill={lineColor}
/>
<circle
cx={projectPoint(dot.start.lat, dot.start.lng).x}
cy={projectPoint(dot.start.lat, dot.start.lng).y}
r="2"
fill={lineColor}
opacity="0.5"
>
<animate
attributeName="r"
from="2"
to="8"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.5"
to="0"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
</circle>
return (
<motion.path
key={`path-${i}`}
d={createCurvedPath(startPoint, endPoint)}
fill="none"
stroke="url(#path-gradient)"
strokeWidth="1"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{
duration: 1,
delay: 0.5 * i,
ease: "easeOut",
}}
/>
);
})}
{/* ✅ 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);
return (
<g key={`points-${i}`}>
{[s, e].map((p, idx) => (
<g key={idx}>
<circle cx={p.x} cy={p.y} r="2" fill={lineColor} />
<circle cx={p.x} cy={p.y} r="2" fill={lineColor} 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>
</g>
))}
</g>
<g key={`end-${i}`}>
<circle
cx={projectPoint(dot.end.lat, dot.end.lng).x}
cy={projectPoint(dot.end.lat, dot.end.lng).y}
r="2"
fill={lineColor}
/>
<circle
cx={projectPoint(dot.end.lat, dot.end.lng).x}
cy={projectPoint(dot.end.lat, dot.end.lng).y}
r="2"
fill={lineColor}
opacity="0.5"
>
<animate
attributeName="r"
from="2"
to="8"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.5"
to="0"
dur="1.5s"
begin="0s"
repeatCount="indefinite"
/>
</circle>
</g>
</g>
))}
);
})}
</svg>
</div>
);
}
}

View File

@@ -1,10 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ThemeProvider } from 'next-themes'
import './styles/tailwind.css'
import App from './App'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
<App />
</ThemeProvider>
</StrictMode>,
)

View File

@@ -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" />

View 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>
)
}

View File

@@ -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>

View 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 Its 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>
);
}

View 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>
);
}

View File

@@ -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>

View File

@@ -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 todays 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 todays 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>

View File

@@ -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 ">

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
);
}

View File

@@ -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">

View File

@@ -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 whats 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 whats 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>

View 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>
);
}

View File

@@ -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 dont need
with compute, storage, and networking built in, so you dont need
external cloud dependencies.
</P>
</div>

View File

@@ -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>

View File

@@ -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 */}

View 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>
);
}

View File

@@ -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>

View 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 Its 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>
);
}

View File

@@ -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">

View File

@@ -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>

View File

@@ -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 thats more resilient and private than the cloud.
</P>
</div>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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",

View File

@@ -1,47 +1,79 @@
import { Link } from 'react-router-dom';
import { Container } from '@/components/Container'
import { Button } from '@/components/Button'
import { H3, P } from '@/components/Texts'
export function CallToAction() {
return (
<section className='relative overflow-hidden bg-[#121212]'>
{/* ✅ Top horizontal line with spacing */}
<div className="max-w-7xl bg-[#121212] mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
{/* === Content === */}
<div className="w-full border-t border-l border-r border-gray-800 " />
<div
id="get-started"
className="py-18 max-w-7xl mx-auto border-t-0 border-b-0 border bg-[#111111] border-gray-800">
<Container className="relative">
<div className="mx-auto max-w-2xl text-center ">
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
Use the Mycelium Stack Your Way
</h2>
<p className="mt-6 text-lg text-gray-300">
Run workloads, connect environments, host nodes, and build agentic systems, all on one sovereign, self-healing network.
</p>
<p className="mt-4 text-lg text-gray-300">
Start wherever you are. Scale however you choose.
</p>
<div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
<Button to="/cloud" variant="solid" color="cyan">
Get Started
</Button>
<Button
to="https://threefold.info/mycelium_network/docs/"
as="a"
target="_blank"
variant="outline"
color="white"
>
Explore Docs
</Button>
<section className="relative overflow-hidden bg-[#121212]">
{/* ✅ Top horizontal line with spacing */}
<div className="max-w-7xl bg-[#121212] mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
{/* === Content === */}
<div className="w-full border-t border-l border-r border-gray-800" />
<div
id="get-started"
className="py-18 max-w-7xl mx-auto border-t-0 border-b-0 border bg-[#111111] border-gray-800 relative overflow-hidden"
>
{/* ✅ 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">
<H3 className=" text-white ">
Use the Mycelium Stack Your Way
</H3>
<P className="mt-6 text-gray-300">
Deploy infrastructure, run workloads, connect environments, and build distributed AI systems, all on one network designed for autonomy and control.
</P>
<P className="mt-4 text-gray-300">
Start wherever you are. Scale on your own terms.
</P>
<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="/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 &rarr;
</Link>
</div>
</div>
</div>
</Container>
</div>
{/* ✅ Bottom horizontal line with spacing */}
<div className="w-full border-b border-gray-800" />
</Container>
</div>
{/* ✅ Bottom horizontal line */}
<div className="w-full border-b border-gray-800" />
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
</section>
)

View File

@@ -0,0 +1,137 @@
"use client";
import { Eyebrow, H3, P } from "@/components/Texts";
import NoExtraction from "./animations/NoExtraction";
import NoControl from "./animations/NoControl";
import NoCentral from "./animations/NoCentral";
import NoSinglePoint from "./animations/NoSinglePoint";
const deterministicCards = [
{
id: "core",
eyebrow: "Why It Matters",
title: "Built for a Sovereign Digital World",
description:
"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",
custom: true,
noBorder: true,
},
{
id: "crypto",
title: "No central servers.",
description:
"Your devices form a distributed network, eliminating reliance on centralized data centers.",
animation: <NoCentral className="lg:-mt-12" />, // ✅ NEW
colSpan: "lg:col-span-3",
rowSpan: "lg:row-span-1",
rounded: "lg:rounded-tr-4xl max-lg:rounded-t-4xl",
innerRounded: "lg:rounded-tr-[calc(2rem+1px)] max-lg:rounded-t-[calc(2rem+1px)]",
},
{
id: "stateless",
title: "No data extraction.",
description:
"You own your data. Run services and AI models on your own devices, ensuring privacy and control.",
animation: <NoExtraction className="lg:-mt-12" />, // ✅ NEW
colSpan: "lg:col-span-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 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-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 HomeArchitecture() {
return (
<section className="relative w-full bg-[#121212] overflow-hidden">
{/* ✅ Top horizontal line */}
<div className="max-w-7xl bg-[#121212] mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
<div className="w-full border-t border-l border-r border-gray-800" />
<div className="mx-auto bg-[#111111] max-w-2xl px-6 lg:max-w-7xl lg:px-10 border border-t-0 border-b-0 border-gray-800">
<div className="grid grid-cols-1 gap-6 pt-6 lg:grid-cols-6 lg:grid-rows-2 pb-6">
{deterministicCards.map((card) => (
<div
key={card.id}
className={`relative flex flex-col ${card.colSpan} ${card.rowSpan} transition-transform duration-300 hover:scale-102 group`}
>
{/* ✅ Disable wrapper on first card */}
{!card.noBorder && (
<div
className={`absolute inset-0 rounded-md border border-gray-800 bg-[#111212] ${card.rounded} group-hover:bg-linear-to-br from-gray-900 to-gray-800`}
/>
)}
<div
className={`relative flex lg:h-90 flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] ${card.innerRounded}`}
>
{/* ✅ SVG Animation instead of images */}
{card.animation ? (
<div className="lg:h-64 h-48 w-full overflow-hidden bg-transparent flex items-center justify-center">
<div className="w-full h-full object-cover">
{card.animation}
</div>
</div>
) : (
<div className="h-48 w-full flex items-center justify-center bg-transparent" />
)}
<div className="px-8 pt-4 pb-6">
{card.custom ? (
<>
{card.eyebrow && <Eyebrow>{card.eyebrow}</Eyebrow>}
<H3 className="mt-2 text-white">{card.title}</H3>
<P className="mt-4 max-w-lg text-gray-200">{card.description}</P>
</>
) : (
<>
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
{card.title}
</p>
<p className="mt-1 max-w-lg text-sm/6 text-gray-200">
{card.description}
</p>
</>
)}
</div>
</div>
{!card.noBorder && (
<div
className={`pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 ${card.rounded}`}
/>
)}
</div>
))}
</div>
</div>
<div className="w-full border-b border-gray-800" />
<div className="max-w-7xl mx-auto py-6 border-x border-gray-800 border-t-0 border-b-0" />
</section>
);
}

View File

@@ -0,0 +1,109 @@
"use client";
import { useEffect, useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { H3, P } from "@/components/Texts";
const rotating = [
"Communities",
"Integrators",
"Builders",
"Enterprises",
"Institutions",
"Creators",
"Researchers",
"Individuals",
];
// ✅ Use local image files (18)
const gallery = [
"/images/audiences/1.jpg",
"/images/audiences/2.jpg",
"/images/audiences/3.jpg",
"/images/audiences/4.jpg",
"/images/audiences/5.jpg",
"/images/audiences/6.jpg",
"/images/audiences/7.jpg",
"/images/audiences/8.jpg",
];
export function HomeAudience() {
const [index, setIndex] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setIndex((prev) => (prev + 1) % rotating.length);
}, 3200);
return () => clearInterval(timer);
}, []);
return (
<div>
{/* ✅ Top horizontal line + container border */}
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
<div className="w-full border-t border-l border-r border-gray-100" />
{/* ✅ Main content */}
<div className="mx-auto max-w-7xl bg-white border border-t-0 border-b-0 border-gray-100">
<div className="grid grid-cols-1 lg:grid-cols-2">
{/* ✅ LEFT — Text & rotating headline */}
<div className="px-6 py-14 sm:px-10 lg:px-14 flex flex-col justify-center">
<H3 className="text-black">
Sovereign Infrastructure for{" "}
<span className="inline-block text-black font-semibold relative ml-2 h-[1.2em]">
<AnimatePresence mode="wait">
<motion.span
key={rotating[index]}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.35 }}
className="absolute left-0 top-0"
>
{rotating[index]}
</motion.span>
</AnimatePresence>
{/* Invisible placeholder to avoid layout jump */}
<span className="opacity-0">{rotating[index]}</span>
</span>
</H3>
<P className="text-gray-800 mt-4">
The internet wasnt built for sovereignty. Today, data, AI models, and identity
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>
</div>
{/* ✅ RIGHT — Landscape image gallery synced with title */}
<div className="relative h-64 sm:h-96 lg:h-full w-full overflow-hidden">
<AnimatePresence mode="wait">
<motion.img
key={gallery[index]}
src={gallery[index]}
alt=""
initial={{ opacity: 0, scale: 1.02 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 1.02 }}
transition={{ duration: 0.6 }}
className="absolute inset-0 w-full h-full object-cover"
/>
</AnimatePresence>
</div>
</div>
</div>
{/* ✅ Bottom border */}
<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>
);
}

View File

@@ -1,4 +1,4 @@
import { H1, H5 } from "@/components/Texts"
import { H3, H5, Eyebrow } from "@/components/Texts"
import { Button } from "@/components/Button"
export function HomeAurora({ onGetStartedClick }: { onGetStartedClick: () => void }) {
@@ -6,32 +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>&rarr;</span>
</a>
</div>
</div>
<Eyebrow> Project MYCELIUM</Eyebrow>
<H3 className="mt-4">
Private, Distributed Infrastructure Built
for Digital Sovereignty
</H3>
<H1 className="mt-8">
The Sovereign Agentic Cloud
</H1>
<H5 className="mt-8 text-lg text-gray-600">
Host nodes, deploy workloads, or build private AI systems,
all on infrastructure you own and control.
<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"

View File

@@ -0,0 +1,60 @@
"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 HomeBlink({ onGetStartedClick }: { onGetStartedClick: () => void }) {
return (
<div className="px-4">
<div className="relative mx-auto max-w-7xl border border-t-0 border-gray-100 bg-white overflow-hidden py-24 lg:py-32">
{/* ✅ 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>
{/* ✅ Cyan Spotlight */}
<Spotlight className="-top-40 left-0 md:-top-20 md:left-60" />
<div className="relative z-10 mx-auto w-full max-w-7xl py-4 md:pt-0">
<HomeHeadline />
<H4 className="text-center mt-8">The Living Network of the Next Internet</H4>
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-neutral-500">
A new internet is emerging, a private, distributed, and self-sovereign.
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>
<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>
);
}

View 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 youre 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>
);
}

View File

@@ -6,6 +6,7 @@ import {
} from '@heroicons/react/24/outline'
import { CP, CT, Eyebrow, H3, P } from '@/components/Texts'
import { DarkCard } from '@/components/ui/cards'
import { HeaderDark } from '@/components/HeaderDark'
const features = [
{
@@ -32,7 +33,9 @@ const features = [
export function HomeHostingDark() {
return (
<div className="relative py-24 bg-[#111111] lg:py-32">
<>
<HeaderDark />
<div className="relative py-24 bg-[#111111] lg:py-32">
<div className="mx-auto max-w-md px-6 text-center sm:max-w-3xl lg:max-w-7xl lg:px-8">
<Eyebrow>DEPLOY</Eyebrow>
<H3 className="mt-2 text-gray-200">Run Real Infrastructure on Your Own Hardware</H3>
@@ -60,5 +63,6 @@ export function HomeHostingDark() {
</div>
</div>
</div>
</>
)
}

149
src/pages/home/HomeMap.tsx Normal file
View File

@@ -0,0 +1,149 @@
"use client";
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 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 nodes hosted by people and organizations around the world.
Each node adds compute, storage, and bandwidth, expanding the networks 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>
</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-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>
</div>
);
}

View File

@@ -1,13 +1,13 @@
import { useRef } from 'react'
import { AnimatedSection } from '../../components/AnimatedSection'
import { StackSectionDark } from './StackSectionDark'
import { WorldMap } from './HomeGlobe'
import { CallToAction } from './CallToAction'
import { HomeHosting } from './HomeHosting'
import { HomeAurora } from './HomeAurora'
import { HomeTab } from './HomeTab'
import { HomeBenefits } from './HomeBenefits'
import { HomeMap } from './HomeMap'
import { HomeAurora } from './HomeAurora'
import { HomeArchitecture } from './HomeArchitecture';
import { HomeDesign } from './HomeDesign';
export default function HomePage() {
@@ -22,23 +22,20 @@ export default function HomePage() {
<HomeAurora onGetStartedClick={handleScrollToSlider} />
</AnimatedSection>
<AnimatedSection id="next-section">
<WorldMap />
</AnimatedSection>
<AnimatedSection>
<HomeHosting />
</AnimatedSection>
<AnimatedSection>
<StackSectionDark />
<HomeArchitecture/>
</AnimatedSection>
<AnimatedSection>
<HomeTab />
</AnimatedSection>
<AnimatedSection>
<HomeBenefits />
<HomeMap />
</AnimatedSection>
<AnimatedSection>
<HomeDesign />
</AnimatedSection>
<AnimatedSection>

View File

@@ -0,0 +1,55 @@
"use client";
import { Button } from "@/components/Button";
import { Spotlight } from "@/components/ui/spotlight";
import { cn } from "@/lib/utils";
import { H1, H4, H5 } from "@/components/Texts";
export function HomeSpotlight({
onGetStartedClick,
}: {
onGetStartedClick: () => void;
}) {
return (
<div className="px-4">
{/* Boxed container */}
<div className="relative mx-auto max-w-7xl border border-t-0 border-gray-100 bg-white overflow-hidden py-24 lg:py-32">
{/* ✅ Grid background */}
<div
className={cn(
"pointer-events-none absolute inset-0 select-none [background-size:40px_40px]",
"[background-image:linear-gradient(to_right,#f4f4f4_1px,transparent_1px),linear-gradient(to_bottom,#f4f4f4_1px,transparent_1px)]"
)}
/>
{/* ✅ Cyan Spotlight */}
<Spotlight className="-top-40 left-0 md:-top-20 md:left-60" />
{/* ✅ Foreground content */}
<div className="relative z-10 mx-auto w-full max-w-7xl p-4 md:pt-0">
<H1 className="bg-opacity-50 bg-gradient-to-b from-neutral-50 to-black bg-clip-text text-center font-bold text-transparent ">
MYCELIUM
</H1>
<H4 className="text-center mt-4">The Living Network of the Next Internet</H4>
<H5 className="mx-auto mt-6 max-w-4xl text-center font-normal text-neutral-500">
A new internet is emerging, a private, distributed, and self-sovereign.
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>
<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>
);
}

View File

@@ -3,123 +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',
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 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 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 ">Deploy faster</Eyebrow>
<H3 className="mt-2">Mycelium Components</H3>
<P className="mt-6 max-w-lg">
Each component can be used on its own or combined into a fully sovereign cloud.
{/* 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">
Myceliums 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-8 grid grid-cols-1 gap-6 sm:mt-10 lg:grid-cols-6 lg:grid-rows-3 pb-12">
{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-white ${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"
/>
<div className="px-8 pt-4 pb-6">
<h3 className="text-sm/4 font-semibold text-cyan-500">{card.eyebrow}</h3>
<CT className="mt-2 text-lg lg:text-xl tracking-tight text-gray-950">{card.title}</CT>
<CP className="mt-1 max-w-lg text-gray-600">
{card.description}
</CP>
{/* 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
src="/images/bento-network.jpg"
className="size-full object-cover object-top"
/>
</div>
</div>
<div className={`pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 ${card.rounded}`} />
</Link>
))}
</div>
<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 sm:px-10 lg:pb-2">
<img
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" />
</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 justify-center max-lg:py-6 lg:pb-2">
<img
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" />
</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">
<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" />
</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>

View File

@@ -0,0 +1,72 @@
import {
GlobeAltIcon,
KeyIcon,
ServerStackIcon,
ShieldCheckIcon,
} from '@heroicons/react/24/outline'
import { CP, CT, Eyebrow, H3, P } from '@/components/Texts'
import { DarkCard } from '@/components/ui/cards'
const features = [
{
name: 'No central servers.',
description: 'Your devices form a distributed network, eliminating reliance on centralized data centers.',
icon: GlobeAltIcon,
},
{
name: 'No data extraction.',
description: 'You own your data. Run services and AI models on your own devices, ensuring privacy and control.',
icon: KeyIcon,
},
{
name: 'No single point of control.',
description: 'A decentralized architecture means no single entity can dictate or censor your online experience.',
icon: ServerStackIcon,
},
{
name: 'Mesh VPN & Zero-Trust Networking',
description: 'Create a secure, private network between your devices, accessible from anywhere.',
icon: ShieldCheckIcon,
},
]
export function HomeWhy() {
return (
<div className="relative bg-[#121212]">
{/* ✅ 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=" py-12 mx-auto bg-[#111111] max-w-md px-6 text-center sm:max-w-3xl lg:max-w-7xl lg:px-8 border border-t-0 border-b-0 border-gray-800">
<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.
</P>
<div className="mt-16">
<div className="grid grid-cols-1 gap-6 lg:grid-cols-4">
{features.map((feature) => (
<div key={feature.name} className="relative">
<DarkCard className="flex h-full flex-col pt-8">
<span className="absolute -top-6 left-1/2 -translate-x-1/2 transform rounded-md bg-cyan-600 hover:bg-cyan-500 p-2 shadow-lg">
<feature.icon aria-hidden="true" className="size-8 text-white" />
</span>
<CT as="h3" className="mt-4 text-gray-200">
{feature.name}
</CT>
<CP color="secondary" className="mt-2 text-gray-400">
{feature.description}
</CP>
</DarkCard>
</div>
))}
</div>
</div>
</div>
{/* ✅ Bottom horizontal line with spacing */}
<div className="w-full border-b border-gray-800" />
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-800"></div>
</div>
)
}

View File

@@ -0,0 +1,189 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string;
gridStroke?: string;
};
const W = 760;
const H = 420;
export default function Deterministic({
className,
accent = "#00b8db",
gridStroke = "#2b2a2a",
}: Props) {
const prefers = useReducedMotion();
const stages = [
{ x: 180, y: 180, w: 120, h: 80, label: "Build" },
{ x: 330, y: 180, w: 120, h: 80, label: "Package" },
{ x: 480, y: 180, w: 120, h: 80, label: "Deploy" },
];
// Packet path (deterministic flow)
const packetPath = `M ${stages[0].x + 120} ${stages[0].y + 40}
L ${stages[1].x + 0} ${stages[1].y + 40}
L ${stages[1].x + 120} ${stages[1].y + 40}
L ${stages[2].x + 0} ${stages[2].y + 40}`;
// tiny arrow for each transition
const arrows = [
`M ${stages[0].x + 120} ${stages[0].y + 40} L ${stages[1].x + 6} ${stages[1].y + 40}`,
`M ${stages[1].x + 120} ${stages[1].y + 40} L ${stages[2].x + 6} ${stages[2].y + 40}`
];
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="Deterministic orchestration: predictable deployments"
style={{ background: "transparent" }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* BACKGROUND GRID */}
<defs>
<pattern id="grid-orch" width="28" height="28" patternUnits="userSpaceOnUse">
<path d="M 28 0 L 0 0 0 28"
fill="none"
stroke={gridStroke}
strokeWidth="1"
opacity="0.45"
/>
</pattern>
{/* Soft glow for highlight */}
<filter id="orch-glow">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<rect width={W} height={H} fill="url(#grid-orch)" />
{/* STAGE BOXES */}
{stages.map((s, i) => (
<motion.rect
key={`stage-${i}`}
x={s.x}
y={s.y}
width={s.w}
height={s.h}
rx={14}
fill="#0d0d0d"
stroke="#1a1a1a"
strokeWidth={2}
initial={{ opacity: 0 }}
animate={{ opacity: 0.9 }}
transition={{ duration: 0.6 + i * 0.1 }}
/>
))}
{/* Stage labels (subtle, not text-heavy) */}
{stages.map((s, i) => (
<motion.text
key={`label-${i}`}
x={s.x + s.w / 2}
y={s.y + s.h / 2 + 6}
fill="#9ca3af"
textAnchor="middle"
fontSize="14"
fontFamily="Inter, sans-serif"
initial={{ opacity: 0 }}
animate={{ opacity: 0.9 }}
transition={{ delay: 0.1 * i, duration: 0.6 }}
>
{s.label}
</motion.text>
))}
{/* CONSISTENT ORCHESTRATION LINES */}
{arrows.map((d, i) => (
<motion.path
key={`arrow-${i}`}
d={d}
stroke="#3a3a3a"
strokeWidth={4}
strokeLinecap="round"
fill="none"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 0.8 }}
transition={{ delay: 0.15 * i, duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
/>
))}
{/* CYAN ACCENT OVERLAY ON LINES (predictable updates) */}
{arrows.map((d, i) => (
<motion.path
key={`arrow-accent-${i}`}
d={d}
stroke={accent}
strokeWidth={2}
strokeLinecap="round"
strokeDasharray="10"
fill="none"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{
delay: 0.25 * i,
duration: 0.8,
ease: [0.22, 1, 0.36, 1]
}}
/>
))}
{/* MOVING PACKET SHOWING DETERMINISTIC FLOW */}
{!prefers && (
<motion.circle
r={6}
fill={accent}
filter="url(#orch-glow)"
style={{
offsetPath: `path('${packetPath}')`,
}}
initial={{ offsetDistance: "0%", opacity: 0 }}
animate={{
offsetDistance: ["0%", "100%"],
opacity: [0.2, 1, 0.2],
}}
transition={{
duration: 2.3,
repeat: Infinity,
repeatType: "loop",
ease: "linear",
}}
/>
)}
{/* FINAL CONFIRMATION PULSE AT DEPLOY STAGE */}
{!prefers && (
<motion.circle
cx={stages[2].x + stages[2].w / 2}
cy={stages[2].y + stages[2].h / 2}
r={24}
fill={accent}
opacity={0.1}
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: [1, 1.15, 1], opacity: [0.05, 0.3, 0.05] }}
transition={{
duration: 1.8,
repeat: Infinity,
repeatType: "mirror",
ease: [0.22, 1, 0.36, 1],
}}
filter="url(#orch-glow)"
/>
)}
</svg>
</div>
);
}

View File

@@ -0,0 +1,151 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string;
stroke?: string;
};
const W = 760;
const H = 420;
export default function MeshNetworking({
className,
accent = "#00b8db",
stroke = "#4B5563",
}: Props) {
const prefersReduced = useReducedMotion();
// Nodes in a real mesh (hex pattern)
const nodes = [
{ x: 200, y: 120 },
{ x: 380, y: 100 },
{ x: 560, y: 120 },
{ x: 130, y: 240 },
{ x: 320, y: 240 },
{ x: 540, y: 240 },
{ x: 630, y: 240 },
{ x: 260, y: 340 },
{ x: 440, y: 340 },
];
// All connected pairs (mesh links)
const links = [
[0,1],[1,2],
[0,3],[1,4],[2,5],
[3,4],[4,5],[5,6],
[3,7],[4,7],[4,8],[5,8],
[7,8]
];
const drawLine = (i: number, j: number) => {
const a = nodes[i];
const b = nodes[j];
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
};
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="Mesh networking topology"
style={{ background: "transparent" }} // ✅ transparent background
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* ✅ Subtle dark grid */}
<defs>
<pattern id="mesh-grid" width="28" height="28" patternUnits="userSpaceOnUse">
<path d="M 28 0 L 0 0 0 28" fill="none" stroke="#2b2a2a" strokeWidth="1" />
</pattern>
</defs>
<rect width={W} height={H} fill="url(#mesh-grid)" />
{/* ✅ Gray baseline mesh connections */}
{links.map(([i, j], idx) => (
<motion.path
key={`base-${idx}`}
d={drawLine(i, j)}
stroke={stroke}
strokeWidth={2}
strokeLinecap="round"
fill="none"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1, opacity: 0.4 }}
transition={{
delay: 0.05 * idx,
duration: 0.6,
ease: [0.22, 1, 0.36, 1],
}}
/>
))}
{/* ✅ Cyan signal traveling across mesh diagonally */}
{!prefersReduced &&
links.map(([i, j], idx) => {
const path = drawLine(i, j);
return (
<motion.circle
key={`signal-${idx}`}
r={4}
fill={accent}
style={{ offsetPath: `path('${path}')` }}
initial={{ offsetDistance: "0%", opacity: 0 }}
animate={{
offsetDistance: ["0%", "100%"],
opacity: [0, 1, 0],
}}
transition={{
delay: idx * 0.15,
duration: 1.8,
repeat: Infinity,
repeatType: "loop",
ease: "linear",
}}
/>
);
})}
{/* ✅ Nodes with soft glow */}
{nodes.map((n, idx) => (
<g key={`node-${idx}`}>
<motion.circle
cx={n.x}
cy={n.y}
r={18}
fill="#0d0d0d"
stroke="#111"
strokeWidth={2}
initial={{ opacity: 0 }}
animate={{ opacity: 0.7 }}
transition={{ duration: 0.5 }}
/>
<motion.circle
cx={n.x}
cy={n.y}
r={10}
fill={accent}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: prefersReduced ? 1 : [1, 1.08, 1],
}}
transition={{
duration: 1.6,
repeat: prefersReduced ? 0 : Infinity,
repeatType: "mirror",
ease: [0.22, 1, 0.36, 1],
}}
/>
</g>
))}
</svg>
</div>
);
}

View 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 (
<>
<motion.circle
cx={x}
cy={y}
r={r + 8}
stroke="#1F2937"
strokeWidth={2}
fill="none"
initial={{ opacity: 0 }}
animate={{ opacity: 0.8 }}
transition={{ delay, duration: 0.6 }}
/>
<motion.circle
cx={x}
cy={y}
r={r}
fill={accent}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: pulse && !prefers ? [1, 1.1, 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],
}}
/>
</>
);
};
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={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 NoCentral({
className,
accent = "#00b8db",
bg = "#0a0a0a",
}: Props) {
const center = { x: 380, y: 210 };
const nodes = [
{ x: 160, y: 100 },
{ x: 270, y: 70 },
{ x: 500, y: 90 },
{ x: 620, y: 150 },
{ x: 220, y: 300 },
{ x: 360, y: 340 },
{ x: 530, y: 290 },
];
const links = [
[nodes[0], nodes[1]],
[nodes[1], nodes[2]],
[nodes[2], nodes[3]],
[nodes[0], nodes[4]],
[nodes[4], nodes[5]],
[nodes[5], nodes[6]],
[nodes[1], nodes[5]],
[nodes[3], nodes[6]],
];
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="Distributed network illustration"
style={{ background: bg }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* 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.12" />
<stop offset="100%" stopColor={accent} stopOpacity="0" />
</radialGradient>
</defs>
<rect width={W} height={H} fill="url(#grid-dark)" />
{/* Cyan glow field */}
<circle cx={center.x} cy={center.y} r={200} fill="url(#glow)" opacity={0.4} />
{/* Static connection lines */}
{links.map(([a, b], i) => (
<motion.path
key={`line-${i}`}
d={`M ${a.x} ${a.y} L ${b.x} ${b.y}`}
stroke="#1F2937"
strokeWidth={2}
strokeLinecap="round"
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],
}}
/>
))}
{/* Cyan animated signal lines */}
{links.slice(0, 5).map(([a, b], i) => (
<motion.path
key={`signal-${i}`}
d={`M ${a.x} ${a.y} L ${b.x} ${b.y}`}
stroke={accent}
strokeWidth={2.5}
strokeDasharray="10 10"
strokeLinecap="round"
fill="none"
animate={{ opacity: [0.3, 0.8, 0.3] }}
transition={{
duration: 2,
repeat: Infinity,
delay: i * 0.2,
ease: "easeInOut",
}}
/>
))}
{/* Circulating packets on random links */}
{links.map(([a, b], i) => (
<Packet
key={`pkt-${i}`}
path={`M ${a.x} ${a.y} L ${b.x} ${b.y}`}
delay={i * 0.4}
accent={accent}
duration={3}
/>
))}
{/* Pulsing nodes */}
{nodes.map((n, i) => (
<Node
key={`node-${i}`}
x={n.x}
y={n.y}
r={10}
accent={accent}
pulse={i % 2 === 0}
delay={i * 0.1}
/>
))}
{/* Faded red “no central” mark at middle */}
<motion.circle
cx={center.x}
cy={center.y}
r={18}
fill="none"
stroke="#FF4D4D"
strokeWidth={3}
strokeDasharray="6 4"
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 0.7 }}
transition={{ delay: 1, duration: 0.8 }}
/>
<motion.path
d={`M ${center.x - 10} ${center.y - 10} L ${center.x + 10} ${center.y + 10} M ${center.x - 10} ${center.y + 10} L ${center.x + 10} ${center.y - 10}`}
stroke="#FF4D4D"
strokeWidth={3}
strokeLinecap="round"
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 1.2, duration: 0.6 }}
/>
</svg>
</div>
);
}

View File

@@ -0,0 +1,175 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string;
bg?: string;
};
const W = 760;
const H = 420;
/** Node component */
const Node = ({
x,
y,
r = 14,
accent = "#00b8db",
pulse = false,
faded = false,
}: {
x: number;
y: number;
r?: number;
accent?: string;
pulse?: boolean;
faded?: boolean;
}) => {
const prefers = useReducedMotion();
return (
<>
<motion.circle
cx={x}
cy={y}
r={r + 10}
stroke="#1F2937"
strokeWidth={2}
fill="none"
initial={{ opacity: 0 }}
animate={{ opacity: faded ? 0.3 : 0.9 }}
transition={{ duration: 0.6 }}
/>
<motion.circle
cx={x}
cy={y}
r={r}
fill={accent}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: faded ? 0.3 : 1,
scale: pulse && !prefers ? [1, 1.12, 1] : 1,
}}
transition={{
duration: pulse && !prefers ? 1.8 : 0.6,
repeat: pulse && !prefers ? Infinity : 0,
repeatType: "mirror",
ease: [0.22, 1, 0.36, 1],
}}
/>
</>
);
};
/** Moving packet along a path */
const Packet = ({
path,
delay = 0,
accent = "#00b8db",
}: {
path: string;
delay?: number;
accent?: string;
}) => {
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: 1.6,
repeat: !prefers ? Infinity : 0,
repeatType: "loop",
ease: "linear",
}}
style={{
offsetPath: `path('${path}')`,
}}
/>
);
};
export default function NoControl({
className,
accent = "#00b8db",
bg = "#0a0a0a",
}: Props) {
const center = { x: 380, y: 210 };
const outer = [
{ x: 160, y: 120 },
{ x: 600, y: 120 },
{ x: 160, y: 300 },
{ x: 600, y: 300 },
];
const link = (a: any, b: any) => `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="No single point of control illustration"
style={{ background: bg }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* 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>
</defs>
<rect width={W} height={H} fill="url(#grid-dark)" />
{/* Connections between outer nodes (decentralized ring) */}
<motion.path
d={`M ${outer[0].x} ${outer[0].y}
L ${outer[1].x} ${outer[1].y}
L ${outer[3].x} ${outer[3].y}
L ${outer[2].x} ${outer[2].y} Z`}
stroke="#1F2937"
strokeWidth={3}
strokeLinecap="round"
fill="none"
initial={{ pathLength: 0, opacity: 0.3 }}
animate={{ pathLength: 1, opacity: 0.6 }}
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
/>
{/* Cyan pulsing signals around the ring */}
{outer.map((o, i) => {
const next = outer[(i + 1) % outer.length];
const p = link(o, next);
return <Packet key={`p-${i}`} path={p} delay={i * 0.4} accent={accent} />;
})}
{/* Faded center node (represents the “former” single point) */}
<Node x={center.x} y={center.y} r={18} accent={accent} faded pulse={false} />
{/* Outer sovereign nodes */}
{outer.map((n, i) => (
<Node key={i} x={n.x} y={n.y} r={14} accent={accent} pulse />
))}
{/* Cross mark over center node (control denied) */}
<motion.path
d={`M ${center.x - 12} ${center.y - 12} L ${center.x + 12} ${center.y + 12} M ${center.x - 12} ${center.y + 12} L ${center.x + 12} ${center.y - 12}`}
stroke="#FF4D4D"
strokeWidth={3}
strokeLinecap="round"
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.8, duration: 0.6 }}
/>
</svg>
</div>
);
}

View File

@@ -0,0 +1,247 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string;
bg?: string;
};
const W = 760;
const H = 420;
/** Node = local data cluster */
const Node = ({
x,
y,
r = 10,
accent = "#00b8db",
pulse = false,
delay = 0,
}: {
x: number;
y: number;
r?: number;
accent?: string;
pulse?: boolean;
delay?: number;
}) => {
const prefers = useReducedMotion();
return (
<>
{/* outer glow */}
<motion.circle
cx={x}
cy={y}
r={r + 8}
stroke="#1F2937"
strokeWidth={2}
fill="none"
initial={{ opacity: 0 }}
animate={{ opacity: 0.9 }}
transition={{ delay, duration: 0.6 }}
/>
{/* data core */}
<motion.circle
cx={x}
cy={y}
r={r}
fill={accent}
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: pulse && !prefers ? [1, 1.15, 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],
}}
/>
</>
);
};
/** A data particle traveling along a given path */
const Particle = ({
path,
delay = 0,
accent = "#00b8db",
duration = 2,
reverse = false,
}: {
path: string;
delay?: number;
accent?: string;
duration?: number;
reverse?: boolean;
}) => {
const prefers = useReducedMotion();
return (
<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: "mirror",
ease: "easeInOut",
}}
style={{
offsetPath: `path('${path}')`,
}}
/>
);
};
export default function NoExtraction({
className,
accent = "#00b8db",
bg = "#0a0a0a",
}: Props) {
const center = { x: 380, y: 210 };
const WBOX = 360;
const HBOX = 220;
const boxX = center.x - WBOX / 2;
const boxY = center.y - HBOX / 2;
// local nodes within boundary
const nodes = [
{ x: center.x - 80, y: center.y - 40 },
{ x: center.x + 60, y: center.y - 50 },
{ x: center.x, y: center.y + 50 },
{ x: center.x - 50, y: center.y + 30 },
];
// internal circulation paths
const internalPaths = [
`M ${center.x - 80} ${center.y - 40} Q ${center.x} ${center.y - 80} ${center.x + 60} ${center.y - 50}`,
`M ${center.x - 50} ${center.y + 30} Q ${center.x} ${center.y + 70} ${center.x} ${center.y + 50}`,
];
// escape attempt path
const attemptPath = `M ${center.x} ${center.y} Q ${center.x + 200} ${center.y - 50} ${center.x + 130} ${center.y}`;
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="No data extraction illustration"
style={{ background: bg }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* 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>
{/* Cyan glow gradient */}
<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)" />
{/* Cyan ambient glow field */}
<circle
cx={center.x}
cy={center.y}
r={160}
fill="url(#glow)"
opacity={0.3}
/>
{/* Secure boundary box */}
<motion.rect
x={boxX}
y={boxY}
width={WBOX}
height={HBOX}
rx={16}
stroke="#1F2937"
strokeWidth={3}
fill="none"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}
/>
{/* Cyan encryption border */}
<motion.rect
x={boxX + 4}
y={boxY + 4}
width={WBOX - 8}
height={HBOX - 8}
rx={14}
stroke={accent}
strokeWidth={2}
strokeDasharray="10 6"
fill="none"
animate={{ opacity: [0.4, 0.9, 0.4] }}
transition={{
duration: 2,
repeat: Infinity,
ease: "easeInOut",
}}
/>
{/* Internal data circulation */}
{internalPaths.map((p, i) => (
<Particle
key={`int-${i}`}
path={p}
delay={i * 0.6}
accent={accent}
duration={3}
/>
))}
{/* Local data nodes */}
{nodes.map((n, i) => (
<Node
key={i}
x={n.x}
y={n.y}
r={10}
accent={accent}
pulse={i === 2}
delay={i * 0.2}
/>
))}
{/* Data packet attempting to leave */}
<Particle path={attemptPath} delay={0.5} accent={accent} duration={2.2} />
{/* Impact glow at boundary */}
<motion.circle
cx={center.x + 130}
cy={center.y - 10}
r={10}
fill="#FF4D4D"
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: [0, 1.2, 0.8], opacity: [0, 1, 0] }}
transition={{
delay: 0.9,
duration: 1.4,
repeat: Infinity,
repeatDelay: 1.5,
ease: "easeOut",
}}
/>
</svg>
</div>
);
}

View 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>
);
}

View File

@@ -0,0 +1,236 @@
"use client";
import { motion, useReducedMotion } from "framer-motion";
import clsx from "clsx";
type Props = {
className?: string;
accent?: string; // cyan highlight
gridStroke?: string; // grid color (default #2b2a2a as requested)
};
const W = 760;
const H = 420;
const Server = ({
x,
y,
w = 140,
h = 90,
rows = 3,
}: {
x: number;
y: number;
w?: number;
h?: number;
rows?: number;
}) => {
const rowH = (h - 24) / rows;
return (
<g>
{/* chassis */}
<rect x={x} y={y} width={w} height={h} rx={12} fill="#0d0d0d" stroke="#1a1a1a" strokeWidth={2} />
{/* bays */}
{Array.from({ length: rows }).map((_, i) => (
<g key={i}>
<rect
x={x + 12}
y={y + 12 + i * rowH}
width={w - 24}
height={rowH - 10}
rx={8}
fill="#111111"
stroke="#1f1f1f"
strokeWidth={1}
/>
{/* bay indicators */}
<rect x={x + 20} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#16a34a" opacity={0.8} />
<rect x={x + 36} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#9ca3af" opacity={0.6} />
<rect x={x + 52} y={y + 22 + i * rowH} width={10} height={6} rx={2} fill="#9ca3af" opacity={0.6} />
</g>
))}
{/* subtle top highlight */}
<rect x={x + 2} y={y + 2} width={w - 4} height={10} rx={5} fill="#0f0f0f" />
</g>
);
};
export default function SovereignCompute({
className,
accent = "#00b8db",
gridStroke = "#2b2a2a",
}: Props) {
const prefers = useReducedMotion();
// Positions
const left = { x: 140, y: 120 };
const mid = { x: 310, y: 150 };
const right= { x: 500, y: 120 };
// Shield position (trust boundary)
const shield = { cx: 600, cy: 250, r: 38 };
// Attestation paths from racks to shield
const pathFromLeft = `M ${left.x + 140} ${left.y + 45} C 330 150, 470 200, ${shield.cx - 50} ${shield.cy}`;
const pathFromMid = `M ${mid.x + 140} ${mid.y + 45} C 420 190, 500 215, ${shield.cx - 50} ${shield.cy}`;
const pathFromRight = `M ${right.x + 140} ${right.y + 45} C 520 180, 560 220, ${shield.cx - 50} ${shield.cy}`;
return (
<div
className={clsx("relative overflow-hidden", className)}
aria-hidden="true"
role="img"
aria-label="Sovereign compute: execution only on hardware you control"
style={{ background: "transparent" }}
>
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
{/* GRID (transparent bg, subtle dark grid) */}
<defs>
<pattern id="grid-secure" width="28" height="28" patternUnits="userSpaceOnUse">
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.45" />
</pattern>
{/* soft glow filter for shield */}
<filter id="glow">
<feGaussianBlur stdDeviation="6" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<rect width={W} height={H} fill="url(#grid-secure)" />
{/* RACKS (hardware you control) */}
<Server x={left.x} y={left.y} />
<Server x={mid.x} y={mid.y} />
<Server x={right.x} y={right.y} />
{/* BASELINES for attestation links */}
{[pathFromLeft, pathFromMid, pathFromRight].map((d, i) => (
<motion.path
key={`base-${i}`}
d={d}
fill="none"
stroke="#303030"
strokeWidth={3}
strokeLinecap="round"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 0.6 }}
transition={{ delay: 0.15 * i, duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
/>
))}
{/* MOVING ATTESTATION TOKENS (signatures/hashes) */}
{!prefers && [pathFromLeft, pathFromMid, pathFromRight].map((d, i) => (
<motion.circle
key={`token-${i}`}
r={5}
fill={accent}
style={{ offsetPath: `path('${d}')` }}
initial={{ offsetDistance: "0%", opacity: 0 }}
animate={{ offsetDistance: ["0%", "100%"], opacity: [0, 1, 0] }}
transition={{
delay: 0.25 * i,
duration: 2.0,
repeat: Infinity,
repeatType: "loop",
ease: "linear",
}}
/>
))}
{/* TRUST BOUNDARY + SHIELD (hardware attestation target) */}
<motion.circle
cx={shield.cx}
cy={shield.cy}
r={shield.r + 18}
fill="none"
stroke="#1f1f1f"
strokeWidth={2}
initial={{ opacity: 0 }}
animate={{ opacity: 0.9 }}
transition={{ duration: 0.6 }}
/>
{/* cyan halo */}
{!prefers && (
<motion.circle
cx={shield.cx}
cy={shield.cy}
r={shield.r + 6}
fill={accent}
opacity={0.12}
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: [1, 1.12, 1], opacity: [0.1, 0.35, 0.1] }}
transition={{ duration: 1.8, repeat: Infinity, repeatType: "mirror", ease: [0.22, 1, 0.36, 1] }}
filter="url(#glow)"
/>
)}
{/* Shield outline */}
<motion.path
d={`M ${shield.cx} ${shield.cy - 30}
L ${shield.cx + 28} ${shield.cy - 15}
L ${shield.cx + 22} ${shield.cy + 24}
L ${shield.cx} ${shield.cy + 34}
L ${shield.cx - 22} ${shield.cy + 24}
L ${shield.cx - 28} ${shield.cy - 15}
Z`}
fill="none"
stroke={accent}
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 1 }}
transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
filter="url(#glow)"
/>
{/* Check mark (verified hardware) */}
<motion.path
d={`M ${shield.cx - 14} ${shield.cy + 6} L ${shield.cx - 2} ${shield.cy + 18} L ${shield.cx + 18} ${shield.cy - 6}`}
fill="none"
stroke={accent}
strokeWidth={4}
strokeLinecap="round"
strokeLinejoin="round"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 0.9, delay: 0.2, ease: [0.22, 1, 0.36, 1] }}
filter="url(#glow)"
/>
{/* LOCKED EXECUTION BOUNDARY (subtle arc) */}
<motion.path
d={`M ${shield.cx - 70} ${shield.cy + 46} Q ${shield.cx} ${shield.cy + 76} ${shield.cx + 70} ${shield.cy + 46}`}
fill="none"
stroke="#2e2e2e"
strokeWidth={2}
initial={{ pathLength: 0, opacity: 0 }}
animate={{ pathLength: 1, opacity: 0.6 }}
transition={{ duration: 0.8, delay: 0.3 }}
/>
{/* Cyan confirmation pulses emanating out (execution allowed) */}
{!prefers && [0, 1].map((i) => (
<motion.circle
key={`emit-${i}`}
cx={shield.cx}
cy={shield.cy}
r={shield.r + 12}
fill="none"
stroke={accent}
strokeWidth={2}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: [0.0, 0.5, 0.0], scale: [1, 1.15, 1.3] }}
transition={{ duration: 1.8, delay: i * 0.3, repeat: Infinity, ease: [0.22, 1, 0.36, 1] }}
/>
))}
</svg>
</div>
);
}

View File

@@ -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>

Some files were not shown because too many files have changed in this diff Show More