2 Commits

Author SHA1 Message Date
4f63c20aa6 make 2 directions 2025-11-17 14:21:58 +02:00
74dd974da5 fix logo animation 2025-11-17 13:47:27 +02:00
202 changed files with 1005 additions and 4296 deletions

View File

@@ -1 +0,0 @@
google-site-verification: google5dd3a8b700455c0e.html

View File

@@ -2,13 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon-32.png" sizes="32x32" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="google-site-verification" content="rRrZkMEhdC4yFe_BrENEzYmy2bRfD-VE6RTRiDJNLkg" /> <title>Project Mycelium - Unleash the Power of Decentralized Networks</title>
<title>Project Mycelium - Built for Digital Sovereignty</title> <meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." />
<meta name="description" content="Discover Project Mycelium. A sovereign peer-to-peer network for private communication, storage, and compute. Build and run your digital environment on infrastructure you control." />
<meta name="keywords" content="Project Mycelium, Mycelium, digital sovereignty, decentralized network, peer-to-peer infrastructure, private storage, secure compute, sovereign cloud, edge cloud" />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 KiB

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.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" zoomAndPan="magnify" viewBox="0 0 75 74.999997" height="100" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="17cc7c511f"><path d="M 0.679688 0.28125 L 48.441406 0.28125 L 48.441406 5 L 0.679688 5 Z M 0.679688 0.28125 " clip-rule="nonzero"/></clipPath><clipPath id="871aafab52"><path d="M 0.679688 36 L 48.441406 36 L 48.441406 40.601562 L 0.679688 40.601562 Z M 0.679688 36 " clip-rule="nonzero"/></clipPath><clipPath id="08bb39558f"><rect x="0" width="49" y="0" height="41"/></clipPath></defs><g transform="matrix(1, 0, 0, 1, 13, 17)"><g clip-path="url(#08bb39558f)"><g clip-path="url(#17cc7c511f)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 0.434943)" fill="none" stroke-linejoin="miter" d="M -0.00342307 4.000082 L 76.539934 4.000082 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><path fill="#22d3ee" d="M 3.097656 18.003906 L 29.941406 18.003906 L 29.941406 22.480469 L 3.097656 22.480469 M 34.414062 18.003906 L 45.902344 18.003906 L 45.902344 22.480469 L 34.414062 22.480469 " fill-opacity="1" fill-rule="nonzero"/><g clip-path="url(#871aafab52)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 36.091279)" fill="none" stroke-linejoin="miter" d="M -0.00342307 3.999929 L 76.539934 3.999929 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" zoomAndPan="magnify" viewBox="0 0 75 74.999997" height="100" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="17cc7c511f"><path d="M 0.679688 0.28125 L 48.441406 0.28125 L 48.441406 5 L 0.679688 5 Z M 0.679688 0.28125 " clip-rule="nonzero"/></clipPath><clipPath id="871aafab52"><path d="M 0.679688 36 L 48.441406 36 L 48.441406 40.601562 L 0.679688 40.601562 Z M 0.679688 36 " clip-rule="nonzero"/></clipPath><clipPath id="08bb39558f"><rect x="0" width="49" y="0" height="41"/></clipPath></defs><g transform="matrix(1, 0, 0, 1, 13, 17)"><g clip-path="url(#08bb39558f)"><g clip-path="url(#17cc7c511f)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 0.434943)" fill="none" stroke-linejoin="miter" d="M -0.00342307 4.000082 L 76.539934 4.000082 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><path fill="#22d3ee" d="M 3.097656 18.003906 L 29.941406 18.003906 L 29.941406 22.480469 L 3.097656 22.480469 M 34.414062 18.003906 L 45.902344 18.003906 L 45.902344 22.480469 L 34.414062 22.480469 " fill-opacity="1" fill-rule="nonzero"/><g clip-path="url(#871aafab52)"><path stroke-linecap="butt" transform="matrix(0.559221, 0, 0, 0.559221, 3.099571, 36.091279)" fill="none" stroke-linejoin="miter" d="M -0.00342307 3.999929 L 76.539934 3.999929 " stroke="#22d3ee" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -1,6 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://www.projectmycelium.com/sitemap.xml

View File

@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.projectmycelium.com/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/cloud</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/network</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/agents</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/download</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/compute</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/storage</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/gpu</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/pods</loc>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/nodes</loc>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://www.projectmycelium.com/mediakit</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
</urlset>

BIN
public/videos/agent.mp4 Normal file

Binary file not shown.

BIN
public/videos/benefits.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
public/videos/benefits.mp4 Normal file

Binary file not shown.

BIN
public/videos/chip_vid.mp4 Normal file

Binary file not shown.

BIN
public/videos/cloud.mp4 Normal file

Binary file not shown.

BIN
public/videos/cta.mp4 Normal file

Binary file not shown.

Binary file not shown.

BIN
public/videos/fungistor.mp4 Normal file

Binary file not shown.

BIN
public/videos/herodb.mp4 Normal file

Binary file not shown.

BIN
public/videos/mesh.mp4 Normal file

Binary file not shown.

BIN
public/videos/mhero.mp4 Normal file

Binary file not shown.

BIN
public/videos/mycelium2.mp4 Normal file

Binary file not shown.

BIN
public/videos/sandbox.mp4 Normal file

Binary file not shown.

BIN
public/videos/universal.mp4 Normal file

Binary file not shown.

View File

@@ -12,7 +12,6 @@ const StoragePage = lazy(() => import('./pages/storage/StoragePage'));
const GpuPage = lazy(() => import('./pages/gpu/GpuPage')); const GpuPage = lazy(() => import('./pages/gpu/GpuPage'));
const PodsPage = lazy(() => import('./pages/pods/PodsPage')); const PodsPage = lazy(() => import('./pages/pods/PodsPage'));
const NodesPage = lazy(() => import('./pages/nodes/NodesPage')); const NodesPage = lazy(() => import('./pages/nodes/NodesPage'));
const MediaPage = lazy(() => import('./pages/mediakit/MediaPage'));
function ScrollToTop() { function ScrollToTop() {
const { pathname, hash } = useLocation(); const { pathname, hash } = useLocation();
@@ -50,7 +49,6 @@ function App() {
<Route path="gpu" element={<GpuPage />} /> <Route path="gpu" element={<GpuPage />} />
<Route path="pods" element={<PodsPage />} /> <Route path="pods" element={<PodsPage />} />
<Route path="nodes" element={<NodesPage />} /> <Route path="nodes" element={<NodesPage />} />
<Route path="mediakit" element={<MediaPage />} />
</Route> </Route>
</Routes> </Routes>
</Suspense> </Suspense>

Binary file not shown.

View File

@@ -1,93 +0,0 @@
Copyright 2016 The Mulish Project Authors (https://github.com/googlefonts/mulish)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,79 +0,0 @@
Mulish Variable Font
====================
This download contains Mulish as both variable fonts and static fonts.
Mulish is a variable font with this axis:
wght
This means all the styles are contained in these files:
Mulish/Mulish-VariableFont_wght.ttf
Mulish/Mulish-Italic-VariableFont_wght.ttf
If your app fully supports variable fonts, you can now pick intermediate styles
that arent available as static fonts. Not all apps support variable fonts, and
in those cases you can use the static font files for Mulish:
Mulish/static/Mulish-ExtraLight.ttf
Mulish/static/Mulish-Light.ttf
Mulish/static/Mulish-Regular.ttf
Mulish/static/Mulish-Medium.ttf
Mulish/static/Mulish-SemiBold.ttf
Mulish/static/Mulish-Bold.ttf
Mulish/static/Mulish-ExtraBold.ttf
Mulish/static/Mulish-Black.ttf
Mulish/static/Mulish-ExtraLightItalic.ttf
Mulish/static/Mulish-LightItalic.ttf
Mulish/static/Mulish-Italic.ttf
Mulish/static/Mulish-MediumItalic.ttf
Mulish/static/Mulish-SemiBoldItalic.ttf
Mulish/static/Mulish-BoldItalic.ttf
Mulish/static/Mulish-ExtraBoldItalic.ttf
Mulish/static/Mulish-BlackItalic.ttf
Get started
-----------
1. Install the font files you want to use
2. Use your app's font picker to view the font family and all the
available styles
Learn more about variable fonts
-------------------------------
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
https://variablefonts.typenetwork.com
https://medium.com/variable-fonts
In desktop apps
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
Online
https://developers.google.com/fonts/docs/getting_started
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
Installing fonts
MacOS: https://support.apple.com/en-us/HT201749
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
Android Apps
https://developers.google.com/fonts/docs/android
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
License
-------
Please read the full license text (OFL.txt) to understand the permissions,
restrictions and requirements for usage, redistribution, and modification.
You can use them in your products & projects print or digital,
commercial or otherwise.
This isn't legal advice, please consider consulting a lawyer and see the full
license for all details.

View File

@@ -3,30 +3,23 @@ import clsx from 'clsx'
const baseStyles = { const baseStyles = {
solid: solid:
'inline-flex justify-center rounded-full py-2 px-5 text-sm md:text-base font-semibold transition-colors', 'inline-flex justify-center rounded-full py-2 px-5 text-base font-semibold transition-colors',
outline: outline:
'inline-flex justify-center bg-transparent font-semibold rounded-full border border-2 py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-sm md:text-base transition-colors', 'inline-flex justify-center bg-transparent rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(5)-1px)] text-base transition-colors',
link:
'inline-flex items-center text-sm md:text-base font-semibold transition-colors',
} }
const variantStyles = { const variantStyles = {
solid: { solid: {
cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 hover:bg-cyan-400 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-500 active:text-white/80 before:transition-colors', cyan: 'relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors',
white: white:
'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70', 'bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70',
gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80', gray: 'bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80',
green: 'bg-green-500 text-white hover:bg-green-600', green: 'bg-green-500 text-white hover:bg-green-600',
}, },
outline: { outline: {
cyan: 'border-cyan-500 text-cyan-500 hover:border-cyan-400 hover:text-cyan-400 active:border-cyan-400', cyan: 'border-cyan-500 text-cyan-500',
gray: 'border-gray-200 text-gray-600 hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400', gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500',
white: 'border-gray-300 text-white hover:text-cyan-400 hover:border-cyan-400 active:border-cyan-400', white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500',
},
link: {
cyan: 'text-cyan-400 underline hover:text-cyan-300',
white: 'text-white underline hover:text-cyan-300',
dark: 'text-gray-900 underline hover:text-cyan-400',
}, },
} }
@@ -37,11 +30,7 @@ type ButtonProps = (
} }
| { | {
variant: 'outline' variant: 'outline'
color?: keyof typeof variantStyles.outline color?: (keyof typeof variantStyles.outline) | 'cyan'
}
| {
variant: 'link'
color?: keyof typeof variantStyles.link
} }
) & ) &
( (
@@ -54,33 +43,16 @@ type ButtonProps = (
) )
export function Button({ className, as, ...props }: ButtonProps) { export function Button({ className, as, ...props }: ButtonProps) {
// Set safe defaults per variant so color always matches a valid palette key props.variant ??= 'solid'
if (!props.variant) { props.color ??= 'gray'
props.variant = 'solid'
}
if (!props.color) {
if (props.variant === 'solid') {
props.color = 'gray'
} else if (props.variant === 'outline') {
props.color = 'gray'
} else if (props.variant === 'link') {
props.color = 'cyan'
}
}
const variant = props.variant
const color = props.color as string
className = clsx( className = clsx(
baseStyles[variant], baseStyles[props.variant],
variant === 'outline' props.variant === 'outline'
? variantStyles.outline[color as keyof typeof variantStyles.outline] ? variantStyles.outline[props.color]
: variant === 'solid' : props.variant === 'solid'
? variantStyles.solid[color as keyof typeof variantStyles.solid] ? variantStyles.solid[props.color]
: variant === 'link' : undefined,
? variantStyles.link[color as keyof typeof variantStyles.link]
: undefined,
className, className,
) )

View File

@@ -8,10 +8,10 @@ export function Footer() {
<div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8"> <div className="flex flex-col items-start justify-between gap-y-12 pt-16 pb-6 lg:flex-row lg:items-center lg:py-8">
<div> <div>
<div className="flex items-center text-gray-900"> <div className="flex items-center text-gray-900">
<img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-20 w-20 flex-none" /> <img src="/images/logomark.svg" alt="Mycelium Logomark" className="h-15 w-15 flex-none" />
<div className=""> <div className="ml-4">
<p className="text-base lg:text-lg font-semibold">Project Mycelium</p> <p className="text-base font-semibold">Project Mycelium</p>
<p className="mt-1 text-sm">Built for Digital Sovereignty</p> <p className="mt-1 text-sm">Unleash the Power of Decentralization</p>
</div> </div>
</div> </div>
<nav className="mt-10 flex gap-8"> <nav className="mt-10 flex gap-8">
@@ -30,9 +30,6 @@ export function Footer() {
<Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors"> <Link to="/nodes" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
Nodes Nodes
</Link> </Link>
<Link to="/mediakit" className="text-sm text-gray-700 hover:text-cyan-500 transition-colors">
Media Kit
</Link>
</nav> </nav>
</div> </div>
<div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-100 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6"> <div className="group relative -mx-4 flex items-center self-stretch p-4 transition-colors hover:bg-gray-100 sm:self-auto sm:rounded-2xl lg:mx-0 lg:self-auto lg:p-6">
@@ -52,11 +49,11 @@ export function Footer() {
</div> </div>
</div> </div>
</div> </div>
<div className="flex flex-col items-center border-t border-gray-100 py-8 md:flex-row-reverse md:justify-between md:pt-6"> <div className="flex flex-col items-center border-t border-gray-100 pt-8 pb-12 md:flex-row-reverse md:justify-between md:pt-6">
<p className="mt-6 text-sm text-gray-500 md:mt-0"> <p className="mt-6 text-sm text-gray-500 md:mt-0">
&copy; Copyright{' '} &copy; Copyright{' '}
<a href="https://ourworld.tf/" target="_blank" rel="noopener noreferrer" className="font-semibold hover:text-cyan-500 transition-colors"> <a href="https://www.threefold.io" target="_blank" rel="noopener noreferrer" className="hover:text-cyan-500 transition-colors">
OurWorld ThreeFold
</a>{' '} </a>{' '}
{new Date().getFullYear()}. All rights reserved. {new Date().getFullYear()}. All rights reserved.
</p> </p>

View File

@@ -2,7 +2,7 @@ import { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Container } from './Container' import { Container } from './Container'
import { Button } from './Button' import { Button } from './Button'
import pmyceliumLogo from '../images/logos/mainlogo.svg' import pmyceliumLogo from '../images/logos/logo_1.png'
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline' import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'

View File

@@ -2,7 +2,7 @@ import { useState } from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Container } from './Container' import { Container } from './Container'
import { Button } from './Button' import { Button } from './Button'
import pmyceliumLogo from '../images/logos/mainlogo.svg' import pmyceliumLogo from '../images/logos/logo_1.png'
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline' import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'

View File

@@ -1,42 +1,8 @@
import { useEffect } from 'react'
import { Outlet } from 'react-router-dom' import { Outlet } from 'react-router-dom'
import { Footer } from './Footer' import { Footer } from './Footer'
import { Header } from './Header' import { Header } from './Header'
export function Layout() { 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 ( return (
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}> <div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>

View File

@@ -12,8 +12,8 @@ const colorVariants = {
primary: 'text-gray-900', primary: 'text-gray-900',
secondary: 'text-gray-600', secondary: 'text-gray-600',
light: 'text-gray-50', light: 'text-gray-50',
accent: 'text-cyan-400', accent: 'text-cyan-500',
cyan: 'text-cyan-400', cyan: 'text-cyan-50',
white: 'text-white', white: 'text-white',
dark: 'text-gray-950', dark: 'text-gray-950',
tertiary: 'text-gray-700', tertiary: 'text-gray-700',
@@ -162,5 +162,5 @@ export const DownloadCardDescription = createTextComponent(
'text-base/7 leading-normal tracking-normal' 'text-base/7 leading-normal tracking-normal'
) )
export const CT = createTextComponent('span', 'text-base lg:text-lg leading-normal font-medium') export const CT = createTextComponent('span', 'text-base lg:text-lg font-medium')
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-normal font-light') export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-tight font-light')

View File

@@ -0,0 +1 @@

View File

@@ -10,6 +10,7 @@ const XAILogo = () => (
xmlSpace="preserve" xmlSpace="preserve"
width="30" width="30"
height="30" height="30"
fill="white"
> >
<g> <g>
<polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18 " /> <polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18 " />

View File

@@ -1,12 +1,12 @@
"use client"; "use client";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import React, { useEffect, useRef, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
export const InfiniteMovingCards = ({ export const InfiniteMovingCards = ({
items, items,
direction = "left", direction = "left",
speed = "slow", speed = "fast",
pauseOnHover = true, pauseOnHover = true,
className, className,
}: { }: {
@@ -15,39 +15,43 @@ export const InfiniteMovingCards = ({
speed?: "fast" | "normal" | "slow"; speed?: "fast" | "normal" | "slow";
pauseOnHover?: boolean; pauseOnHover?: boolean;
className?: string; className?: string;
}) => { }): JSX.Element => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = React.useRef<HTMLDivElement>(null);
const scrollerRef = useRef<HTMLUListElement>(null); const scrollerRef = React.useRef<HTMLUListElement>(null);
const [isReady, setIsReady] = useState(false); 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]);
useEffect(() => { useEffect(() => {
if (!scrollerRef.current) return; addAnimation();
}, [addAnimation]);
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 ( return (
<div <div
@@ -57,16 +61,13 @@ export const InfiniteMovingCards = ({
<ul <ul
ref={scrollerRef} ref={scrollerRef}
className={cn( className={cn(
"flex w-max shrink-0 gap-16 py-4", "flex min-w-full shrink-0 gap-16 py-4 w-max flex-nowrap",
isReady && start && (direction === "left" ? "animate-scroll-left" : "animate-scroll-right"),
(direction === "left"
? "animate-infinite-scroll"
: "animate-infinite-scroll-right"),
pauseOnHover && "hover:[animation-play-state:paused]" pauseOnHover && "hover:[animation-play-state:paused]"
)} )}
> >
{items.map((item, i) => ( {items.map((item, idx) => (
<li key={i} className="flex-shrink-0"> <li className="relative flex-shrink-0" key={idx}>
{item} {item}
</li> </li>
))} ))}

View File

@@ -1,122 +0,0 @@
import { useState, useEffect } from 'react';
import WorldMap from './world-map';
import { motion } from 'framer-motion';
// Interface for the simplified data passed to WorldMap
interface GeoNode {
lat: number;
lng: number;
label?: string;
color?: string;
}
// Interface for the raw data structure expected from the gridproxy API
interface RawNode {
node_id: number;
location: {
latitude: string; // API often returns these as strings
longitude: string; // API often returns these as strings
city: string;
country: string;
};
// ... other raw fields you don't need
}
const clusterNodes = (nodeList: GeoNode[], cellSize = 2) => {
const buckets = new Map<
string,
{ latSum: number; lngSum: number; count: number }
>();
nodeList.forEach((node) => {
const latBucket = Math.round(node.lat / cellSize) * cellSize;
const lngBucket = Math.round(node.lng / cellSize) * cellSize;
const key = `${latBucket}|${lngBucket}`;
const bucket = buckets.get(key);
if (bucket) {
bucket.latSum += node.lat;
bucket.lngSum += node.lng;
bucket.count += 1;
} else {
buckets.set(key, {
latSum: node.lat,
lngSum: node.lng,
count: 1,
});
}
});
return Array.from(buckets.values()).map((bucket) => {
const avgLat = bucket.latSum / bucket.count;
const avgLng = bucket.lngSum / bucket.count;
const count = bucket.count;
let color = "#06b6d4";
if (count > 20) {
color = "#0891b2";
} else if (count > 5) {
color = "#22d3ee";
}
return {
lat: avgLat,
lng: avgLng,
color,
label: `${count} nodes`,
};
});
};
function DynamicMapContainer() {
const [loading, setLoading] = useState(true);
const [nodes, setNodes] = useState<GeoNode[]>([]);
const API_URL = "https://gridproxy.grid.tf/nodes?healthy=true&size=500";
useEffect(() => {
async function fetchNodeData() {
try {
const response = await fetch(API_URL);
const data: RawNode[] = await response.json();
const geoNodes: GeoNode[] = data
.filter((node: RawNode) => node.location && node.location.latitude && node.location.longitude)
.map((node: RawNode) => ({
lat: parseFloat(node.location.latitude),
lng: parseFloat(node.location.longitude),
label: `${node.location.city}, ${node.location.country} (${node.node_id})`,
}));
const clusteredNodes = clusterNodes(geoNodes);
setNodes(clusteredNodes);
} catch (error) {
console.error("Failed to fetch node data:", error);
} finally {
setLoading(false);
}
}
fetchNodeData();
}, []);
// While fetching, show a loading state
if (loading) {
return (
<div className="flex justify-center items-center w-full aspect-[2/1] bg-[#111111] rounded-lg text-cyan-500">
<motion.span
animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
className="text-4xl"
>
🌎
</motion.span>
<p className="ml-4">Loading nodes...</p>
</div>
);
}
// After data is loaded, render the map
return <WorldMap nodes={nodes} />;
}
export default DynamicMapContainer;

View File

@@ -1,7 +1,7 @@
"use client"; "use client";
import { useRef } from "react"; import { useRef } from "react";
import { motion } from "framer-motion"; import { motion } from "motion/react";
import DottedMap from "dotted-map"; import DottedMap from "dotted-map";
interface MapProps { interface MapProps {
@@ -9,38 +9,33 @@ interface MapProps {
start: { lat: number; lng: number; label?: string }; start: { lat: number; lng: number; label?: string };
end: { 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; lineColor?: string;
} }
export default function WorldMap({ export default function WorldMap({
dots = [], dots = [],
nodes = [], lineColor = "#06b6d4", // cyan-500
lineColor = "#06b6d4",
}: MapProps) { }: MapProps) {
const svgRef = useRef<SVGSVGElement>(null); const svgRef = useRef<SVGSVGElement>(null);
// ✅ Force dark-dotted map theme // ✅ Force dark-dotted map theme
const map = new DottedMap({ height: 100, grid: "diagonal" }); const map = new DottedMap({ height: 100, grid: "diagonal" });
const svgMap = map.getSVG({ const svgMap = map.getSVG({
radius: 0.22, radius: 0.22,
color: "#06b6d480", color: "#06b6d480", // cyan-500 at 50% opacity
shape: "circle", shape: "circle",
backgroundColor: "#111111", backgroundColor: "#111111",
}); });
// ✅ Point projection stays the same // ✅ Point projection stays the same
// Projects lat/lng to the SVG's 800x400 viewBox coordinates
const projectPoint = (lat: number, lng: number) => { const projectPoint = (lat: number, lng: number) => {
const x = (lng + 180) * (800 / 360); const x = (lng + 180) * (800 / 360);
const y = (90 - lat) * (400 / 180) + 45; const y = (90 - lat) * (400 / 180);
return { x, y }; return { x, y };
}; };
const createCurvedPath = (start: any, end: any) => { const createCurvedPath = (start: any, end: any) => {
const midX = (start.x + end.x) / 2; const midX = (start.x + end.x) / 2;
// Creates an arc that bows upward by 50 units
const midY = Math.min(start.y, end.y) - 50; const midY = Math.min(start.y, end.y) - 50;
return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`; return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`;
}; };
@@ -54,53 +49,13 @@ export default function WorldMap({
draggable={false} draggable={false}
/> />
{/* ✅ Lines + points + new standalone nodes */} {/* ✅ Lines + points */}
<svg <svg
ref={svgRef} ref={svgRef}
viewBox="0 0 800 400" viewBox="0 0 800 400"
className="w-full h-full absolute inset-0 pointer-events-none select-none" className="w-full h-full absolute inset-0 pointer-events-none select-none"
> >
{/* Glowing path gradient DEFS */} {/* ✅ animated curved travel lines */}
<defs>
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="black" stopOpacity="0" />
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
<stop offset="100%" stopColor="black" stopOpacity="0" />
</linearGradient>
</defs>
{/* ✅ DYNAMIC STANDALONE NODE DOTS (New Section) */}
{nodes.map((node, i) => {
const p = projectPoint(node.lat, node.lng);
const dotColor = node.color || lineColor;
return (
<g key={`node-${i}`}>
{/* Outer pulsing circle */}
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} opacity="0.5">
<animate
attributeName="r"
from="2"
to="7"
dur="1.4s"
repeatCount="indefinite"
/>
<animate
attributeName="opacity"
from="0.6"
to="0"
dur="1.4s"
repeatCount="indefinite"
/>
</circle>
{/* Inner fixed circle */}
<circle cx={p.x} cy={p.y} r="2" fill={dotColor} />
</g>
);
})}
{/* ✅ Animated curved travel lines (Existing Logic) */}
{dots.map((dot, i) => { {dots.map((dot, i) => {
const startPoint = projectPoint(dot.start.lat, dot.start.lng); const startPoint = projectPoint(dot.start.lat, dot.start.lng);
const endPoint = projectPoint(dot.end.lat, dot.end.lng); const endPoint = projectPoint(dot.end.lat, dot.end.lng);
@@ -123,7 +78,17 @@ export default function WorldMap({
); );
})} })}
{/* ✅ Start & end points with pulsing cyan glow (Existing Logic) */} {/* ✅ glowing path gradient */}
<defs>
<linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="black" stopOpacity="0" />
<stop offset="5%" stopColor={lineColor} stopOpacity="1" />
<stop offset="95%" stopColor={lineColor} stopOpacity="1" />
<stop offset="100%" stopColor="black" stopOpacity="0" />
</linearGradient>
</defs>
{/* ✅ start & end points with pulsing cyan glow */}
{dots.map((dot, i) => { {dots.map((dot, i) => {
const s = projectPoint(dot.start.lat, dot.start.lng); const s = projectPoint(dot.start.lat, dot.start.lng);
const e = projectPoint(dot.end.lat, dot.end.lng); const e = projectPoint(dot.end.lat, dot.end.lng);

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="77" zoomAndPan="magnify" viewBox="0 0 57.75 54" height="72" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><clipPath id="6b820d2194"><path d="M 0.402344 0 L 57.101562 0 L 57.101562 6 L 0.402344 6 Z M 0.402344 0 " clip-rule="nonzero"/></clipPath><clipPath id="3794aac157"><path d="M 0.402344 23 L 57 23 L 57 30 L 0.402344 30 Z M 0.402344 23 " clip-rule="nonzero"/></clipPath><clipPath id="a8068b094c"><path d="M 0.402344 46 L 57.101562 46 L 57.101562 53 L 0.402344 53 Z M 0.402344 46 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#6b820d2194)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 0.00000196364)" fill="none" stroke-linejoin="miter" d="M 0.000096737 3.999805 L 76.537522 3.999805 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g><g clip-path="url(#3794aac157)"><path fill="#43d7ff" d="M 0.402344 23.136719 L 35.746094 23.136719 L 35.746094 29.027344 L 0.402344 29.027344 M 41.636719 23.136719 L 56.761719 23.136719 L 56.761719 29.027344 L 41.636719 29.027344 " fill-opacity="1" fill-rule="nonzero"/></g><g clip-path="url(#a8068b094c)"><path stroke-linecap="butt" transform="matrix(0.736364, 0, 0, 0.736364, 0.402273, 46.951043)" fill="none" stroke-linejoin="miter" d="M 0.000096737 4.002635 L 76.537522 4.002635 " stroke="#43d7ff" stroke-width="8" stroke-opacity="1" stroke-miterlimit="4"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -23,7 +23,7 @@ const bentos: {
{ {
id: "core", id: "core",
eyebrow: "ARCHITECTURE", eyebrow: "ARCHITECTURE",
title: "Intelligence Fabric", title: "Augmented Intelligence Fabric",
description: description:
"The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.", "The sovereign substrate for autonomous AI. Stateless, geo-aware, end-to-end encrypted—and verifiable from intent to execution.",
animation: null, animation: null,
@@ -123,7 +123,7 @@ export function AgentBento() {
<div className="w-full h-full object-cover"><card.animation /></div> <div className="w-full h-full object-cover"><card.animation /></div>
</div> </div>
) : ( ) : (
<div className="hidden md:flex md:h-48 w-full items-center justify-center bg-transparent" /> <div className="h-48 w-full flex items-center justify-center bg-transparent" />
)} )}
<div className="px-8 pt-4 pb-6"> <div className="px-8 pt-4 pb-6">
@@ -136,7 +136,7 @@ export function AgentBento() {
) : ( ) : (
<> <>
{/* ✅ NEW SUBTITLE */} {/* ✅ NEW SUBTITLE */}
<p className="text-sm text-cyan-500">{card.subtitle}</p> <p className="text-sm text-cyan-400">{card.subtitle}</p>
<p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white"> <p className="mt-1 text-lg font-medium lg:text-xl tracking-tight text-white">
{card.title} {card.title}
@@ -161,9 +161,6 @@ export function AgentBento() {
))} ))}
</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>
</section> </section>
); );
} }

View File

@@ -1,5 +1,6 @@
'use client' 'use client'
import { Button } from '@/components/Button'
import { Eyebrow, H3, P } from '@/components/Texts' import { Eyebrow, H3, P } from '@/components/Texts'
export function AgentHeroAlt() { export function AgentHeroAlt() {
@@ -7,32 +8,28 @@ export function AgentHeroAlt() {
<div className=""> <div className="">
{/* Boxed container */} {/* Boxed container */}
<div <div
className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden md:bg-[url('/images/agents.webp')] md:bg-contain md:bg-right md:bg-no-repeat" className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-gray-100 bg-white overflow-hidden bg-contain bg-right bg-no-repeat"
style={{ backgroundImage: "url('/images/agents.webp')", backgroundSize: "contain" }}
> >
{/* Inner padding */} {/* Inner padding */}
<div className="px-6 pt-4 pb-12 lg:py-24"> <div className="px-6 py-16 lg:py-16">
{/* Mobile-only hero image */} <div className="max-w-2xl lg:pl-6">
<img
src="/images/mobile/agents.jpg"
alt="Mycelium Agents visual"
className="mb-8 w-full object-cover md:hidden"
/>
<div className="max-w-xl lg:pl-6">
<Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow> <Eyebrow>MYCELIUM AGENTS - COMING IN 2026</Eyebrow>
<H3 as="h1" className="mt-4"> <H3 as="h1" className="mt-4">
Private, Sovereign and Distributed AI You Control Private, Sovereign and Distributed AI You Control
</H3> </H3>
<P className="mt-6 text-gray-600"> <P className="mt-6 text-gray-800">
Mycelium Agents let you deploy and run intelligent systems on your own infrastructure. Mycelium Agents let you deploy and run intelligent systems on your own infrastructure.
</P>
<P className="mt-4 text-gray-600">
Private, local, and autonomous by design, they give you everything you need to build, host, and connect AI agents without relying on centralized clouds. 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> </P>
<div className="mt-10 flex items-center gap-x-6"> <div className="mt-10 flex items-center gap-x-6">
{/* TODO: Hero CTAs (Follow Development / Explore Docs) to be added when links are ready. <Button href="#" variant="solid" color="cyan">
Previously two Buttons here with href="#". */} Follow Development
</Button>
<Button href="#" variant="outline">
Explore Docs <span aria-hidden="true"></span>
</Button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,65 @@
import { InfiniteMovingCards } from "@/components/magicui/infinite-moving-cards";
import Ai21Logo from "@/components/logos/Ai21";
import ClaudeLogo from "@/components/logos/Claude";
import BaiduCloudLogo from "@/components/logos/BaiduCloud";
import ByteDanceLogo from "@/components/logos/ByteDance";
import DeepSeekLogo from "@/components/logos/DeepSeek";
import DeepMindLogo from "@/components/logos/DeepMind";
import MinimaxLogo from "@/components/logos/Minimax";
import MistralLogo from "@/components/logos/Mistral";
import MoonshotLogo from "@/components/logos/Moonshot";
import TencentCloudLogo from "@/components/logos/TencentCloud";
import OpenAILogo from "@/components/logos/OpenAI";
import XAILogo from "@/components/logos/XAI";
const logos = [
{ id: "ai21", Component: Ai21Logo, label: "AI21" },
{ id: "claude", Component: ClaudeLogo, label: "Claude" },
{ id: "baidu", Component: BaiduCloudLogo, label: "Baidu Cloud" },
{ id: "bytedance", Component: ByteDanceLogo, label: "ByteDance" },
{ id: "deepseek", Component: DeepSeekLogo, label: "DeepSeek" },
{ id: "deepmind", Component: DeepMindLogo, label: "DeepMind" },
{ id: "minimax", Component: MinimaxLogo, label: "Minimax" },
{ id: "mistral", Component: MistralLogo, label: "Mistral" },
{ id: "moonshot", Component: MoonshotLogo, label: "Moonshot" },
{ id: "tencent", Component: TencentCloudLogo, label: "Tencent Cloud" },
{ id: "openai", Component: OpenAILogo, label: "OpenAI" },
{ id: "xai", Component: XAILogo, label: "xAI" },
];
const splitLogoRows = () => {
const midpoint = Math.ceil(logos.length / 2);
return [logos.slice(0, midpoint), logos.slice(midpoint)];
};
export const AgentLogos = () => {
const rows = splitLogoRows();
return (
<section className="relative isolate overflow-hidden bg-[#121212] py-4">
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-black to-transparent" />
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-black to-transparent" />
<div className="flex w-full flex-col gap-1 px-0">
{rows.map((row, idx) => (
<InfiniteMovingCards
key={`logos-row-${idx}`}
items={row.map(({ id, Component, label }) => (
<div
key={id}
className="flex h-24 w-36 items-center justify-center bg-transparent px-4"
aria-label={label}
>
<Component />
</div>
))}
direction={idx % 2 === 0 ? "left" : "right"}
speed="slow"
pauseOnHover={true}
className="w-full"
/>
))}
</div>
</section>
);
};

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