Compare commits
8 Commits
main_backu
...
main_sasha
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f8d45c7ff | |||
| f2cb22ef66 | |||
| 82e730346e | |||
| c9129c5c66 | |||
| 8f1df965f2 | |||
| af77948679 | |||
| ce33e0f7c3 | |||
| 1f0f18105e |
25
components.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "src/styles/tailwind.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true,
|
||||
"prefix": ""
|
||||
},
|
||||
"iconLibrary": "lucide",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils",
|
||||
"ui": "@/components/ui",
|
||||
"lib": "@/lib",
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"registries": {
|
||||
"@aceternity": "https://ui.aceternity.com/registry/{name}.json",
|
||||
"@magicui": "https://magicui.design/r/{name}.json"
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,6 @@
|
||||
<meta name="description" content="Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking." />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
:root {
|
||||
--font-inter: 'Inter', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
718
package-lock.json
generated
@@ -13,6 +13,8 @@
|
||||
"@headlessui/react": "^2.2.9",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@lobehub/icons": "^1.97.2",
|
||||
"@react-three/drei": "^9.89.2",
|
||||
"@react-three/fiber": "^8.15.12",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@types/node": "^20.19.23",
|
||||
@@ -22,10 +24,12 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"cobe": "^0.6.5",
|
||||
"dotted-map": "^2.2.3",
|
||||
"framer-motion": "^10.18.0",
|
||||
"lucide-react": "^0.544.0",
|
||||
"motion": "^12.23.24",
|
||||
"next": "^14.2.33",
|
||||
"next-themes": "^0.4.6",
|
||||
"popmotion": "^11.0.5",
|
||||
"react": "^18.3.1",
|
||||
"react-countup": "^6.5.3",
|
||||
@@ -35,11 +39,13 @@
|
||||
"react-type-animation": "^3.2.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"tailwindcss": "^4.1.15",
|
||||
"three": "^0.151.0",
|
||||
"typescript": "^5.9.3",
|
||||
"use-debounce": "^10.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.15",
|
||||
"@types/three": "^0.151.0",
|
||||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.1",
|
||||
|
||||
BIN
public/images/agent_icon.png
Normal file
|
After Width: | Height: | Size: 802 KiB |
BIN
public/images/cloud.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/cloud1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/images/cloud_icon.png
Normal file
|
After Width: | Height: | Size: 757 KiB |
BIN
public/images/cloudimg.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/images/kubernetes.png
Normal file
|
After Width: | Height: | Size: 1024 KiB |
BIN
public/images/mchip.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/network_icon.png
Normal file
|
After Width: | Height: | Size: 835 KiB |
@@ -214,7 +214,7 @@
|
||||
</filter>
|
||||
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0 727 -642 0 184 1)">
|
||||
<stop stop-color="#FAFAFA" />
|
||||
<stop stop-color="#FFFFFF" />
|
||||
<stop offset="1" stop-color="#E6E6E6" />
|
||||
</radialGradient>
|
||||
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/videos/cloud.mp4
Normal file
@@ -1,12 +1,13 @@
|
||||
import { useRef } from 'react'
|
||||
import { motion, useInView } from 'framer-motion'
|
||||
|
||||
export function AnimatedSection({ children }: { children: React.ReactNode }) {
|
||||
export function AnimatedSection({ children, id }: { children: React.ReactNode; id?: string }) {
|
||||
const ref = useRef(null)
|
||||
const isInView = useInView(ref, { once: true, margin: '-20% 0px -20% 0px' })
|
||||
|
||||
return (
|
||||
<motion.section
|
||||
id={id}
|
||||
ref={ref}
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
animate={{
|
||||
|
||||
@@ -3,9 +3,9 @@ import clsx from 'clsx'
|
||||
|
||||
const baseStyles = {
|
||||
solid:
|
||||
'inline-flex justify-center rounded-lg py-2 px-3 text-sm font-semibold transition-colors',
|
||||
'inline-flex justify-center rounded-full py-2 px-4 text-sm font-semibold transition-colors',
|
||||
outline:
|
||||
'inline-flex justify-center rounded-lg border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(3)-1px)] text-sm transition-colors',
|
||||
'inline-flex justify-center rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(4)-1px)] text-sm transition-colors',
|
||||
}
|
||||
|
||||
const variantStyles = {
|
||||
|
||||
@@ -1,44 +1,67 @@
|
||||
'use client'
|
||||
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Container } from './Container'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import clsx from 'clsx'
|
||||
import { Button } from './Button'
|
||||
|
||||
export function Header() {
|
||||
|
||||
function NavLinks() {
|
||||
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null)
|
||||
let timeoutRef = useRef<number | null>(null)
|
||||
|
||||
|
||||
return (
|
||||
<header>
|
||||
<nav>
|
||||
<Container className="relative z-50 flex justify-between py-8">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<div className="flex items-center gap-x-5">
|
||||
<Link to="/" aria-label="Home">
|
||||
<img src="/src/images/logomark.svg" alt="Mycelium" className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden lg:flex lg:gap-10">
|
||||
<div className="flex items-center gap-x-5 border-l border-white/10 pl-5">
|
||||
{[
|
||||
['Home', '/'],
|
||||
['Cloud', '/cloud'],
|
||||
['Network', '/network'],
|
||||
['Agents', '/agents'],
|
||||
].map(([label, href], index) => (
|
||||
<Link
|
||||
to="/"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
key={label}
|
||||
to={href}
|
||||
className={clsx(
|
||||
'relative rounded-lg px-3 py-2 text-sm text-black transition-colors delay-150 hover:text-cyan-500 hover:delay-0',
|
||||
)}
|
||||
onMouseEnter={() => {
|
||||
if (timeoutRef.current) {
|
||||
window.clearTimeout(timeoutRef.current)
|
||||
}
|
||||
setHoveredIndex(index)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
timeoutRef.current = window.setTimeout(() => {
|
||||
setHoveredIndex(null)
|
||||
}, 200)
|
||||
}}
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
<Link
|
||||
to="/cloud"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Cloud
|
||||
</Link>
|
||||
<Link
|
||||
to="/network"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="text-base/7 tracking-tight text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
>
|
||||
Agents
|
||||
<AnimatePresence>
|
||||
{hoveredIndex === index && (
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-lg bg-white/10"
|
||||
layoutId="hoverBackground"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1, transition: { duration: 0.15 } }}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.15 },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<span className="relative z-10">{label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center gap-8">
|
||||
<div className="flex items-center gap-6 max-lg:hidden">
|
||||
<Button
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
@@ -54,8 +77,48 @@ export function Header() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</nav>
|
||||
</header>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export function Header() {
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [lastScrollY, setLastScrollY] = useState(0);
|
||||
|
||||
|
||||
const controlHeader = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.scrollY > lastScrollY && window.scrollY > 100) { // Hides when scrolling down past 100px
|
||||
setIsVisible(false);
|
||||
} else { // Shows when scrolling up
|
||||
setIsVisible(true);
|
||||
}
|
||||
setLastScrollY(window.scrollY);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('scroll', controlHeader);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', controlHeader);
|
||||
};
|
||||
}
|
||||
}, [lastScrollY]);
|
||||
|
||||
|
||||
return (
|
||||
<motion.header
|
||||
className="fixed top-4 left-0 right-0 z-50 flex justify-center"
|
||||
initial={{ y: 0, opacity: 1 }}
|
||||
animate={{ y: isVisible ? 0 : -100, opacity: isVisible ? 1 : 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
>
|
||||
<div className="rounded-full bg-white/10 px-5 py-3 shadow-lg ring-1 ring-white/10 backdrop-blur-sm">
|
||||
<NavLinks />
|
||||
</div>
|
||||
</motion.header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Outlet } from 'react-router-dom'
|
||||
import { Header } from './Header'
|
||||
import { FloatingNav } from './ui/floating-navbar'
|
||||
import { Footer } from './Footer'
|
||||
import { Header } from './Header'
|
||||
|
||||
export function Layout() {
|
||||
const navItems = [
|
||||
{ name: 'Home', link: '/' },
|
||||
{ name: 'Cloud', link: '/cloud' },
|
||||
{ name: 'Network', link: '/network' },
|
||||
{ name: 'Agents', link: '/agents' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 antialiased" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||
<div className="bg-white antialiased" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||
<Header />
|
||||
<main>
|
||||
<Outlet />
|
||||
|
||||
22
src/components/ScrollDownArrow.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
|
||||
export function ScrollDownArrow() {
|
||||
const scrollToNextSection = () => {
|
||||
const nextSection = document.querySelector('#next-section') // Assuming the next section has this id
|
||||
if (nextSection) {
|
||||
nextSection.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToNextSection}
|
||||
className="animate-bounce text-gray-500 hover:text-gray-700"
|
||||
aria-label="Scroll to next section"
|
||||
>
|
||||
<ChevronDown size={32} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -3,6 +3,11 @@
|
||||
import React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const fontVariants = {
|
||||
sans: 'font-sans',
|
||||
neuton: 'font-neuton',
|
||||
} as const
|
||||
|
||||
const colorVariants = {
|
||||
primary: 'text-gray-900',
|
||||
secondary: 'text-gray-600',
|
||||
@@ -15,6 +20,7 @@ const colorVariants = {
|
||||
} as const
|
||||
|
||||
type TextOwnProps = {
|
||||
font?: keyof typeof fontVariants
|
||||
color?: keyof typeof colorVariants
|
||||
className?: string
|
||||
}
|
||||
@@ -34,6 +40,7 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
>
|
||||
|
||||
function Text<E extends React.ElementType = DefaultElement>({
|
||||
font = 'sans',
|
||||
as,
|
||||
color = 'primary',
|
||||
className,
|
||||
@@ -43,7 +50,12 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
const Tag = (as || defaultElement) as React.ElementType
|
||||
return (
|
||||
<Tag
|
||||
className={cn(defaultClassName, colorVariants[color], className)}
|
||||
className={cn(
|
||||
defaultClassName,
|
||||
fontVariants[font],
|
||||
colorVariants[color],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
@@ -87,7 +99,7 @@ export const Subtle = createTextComponent(
|
||||
)
|
||||
export const H5 = createTextComponent(
|
||||
'h5',
|
||||
'text-xl lg:text-2xl font-semibold leading-snug tracking-tight'
|
||||
'text-xl lg:text-2xl font-light leading-snug tracking-normal'
|
||||
)
|
||||
export const Eyebrow = createTextComponent(
|
||||
'h2',
|
||||
|
||||
81
src/components/ui/HalfGlobe.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import * as THREE from "three";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import { OrbitControls, useTexture } from "@react-three/drei";
|
||||
import { useRef } from "react";
|
||||
|
||||
function Globe() {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const cloudTexture = useTexture("/images/cloud1.png");
|
||||
|
||||
// Rotate the globe slowly
|
||||
useFrame(() => {
|
||||
if (groupRef.current) {
|
||||
groupRef.current.rotation.y += 0.002;
|
||||
}
|
||||
});
|
||||
|
||||
// Coordinates for markers (half-globe)
|
||||
const points = [
|
||||
[0, 1, 0],
|
||||
[0.7, 0.5, 0.2],
|
||||
[-0.5, 0.4, 0.5],
|
||||
[0.4, 0.3, -0.7],
|
||||
[-0.6, -0.1, 0.3],
|
||||
[0.3, -0.2, 0.8],
|
||||
];
|
||||
|
||||
return (
|
||||
<group ref={groupRef}>
|
||||
{/* Cyan arcs */}
|
||||
{Array.from({ length: 8 }).map((_, i) => {
|
||||
const radius = 2.5;
|
||||
const curve = new THREE.EllipseCurve(
|
||||
0,
|
||||
0,
|
||||
radius,
|
||||
radius / 2,
|
||||
0,
|
||||
Math.PI,
|
||||
false,
|
||||
0
|
||||
);
|
||||
const points = curve.getPoints(100);
|
||||
const geometry = new THREE.BufferGeometry().setFromPoints(
|
||||
points.map((p) => new THREE.Vector3(p.x, Math.sin(p.x / radius) * 0.5, p.y))
|
||||
);
|
||||
return (
|
||||
<line key={i} geometry={geometry}>
|
||||
<lineBasicMaterial color="#00e5ff" linewidth={1} transparent opacity={0.5} />
|
||||
</line>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Cloud markers */}
|
||||
{points.map(([x, y, z], i) => (
|
||||
<mesh key={i} position={[x * 2.5, y * 2.5, z * 2.5]}>
|
||||
<planeGeometry args={[0.3, 0.3]} />
|
||||
<meshBasicMaterial
|
||||
map={cloudTexture}
|
||||
transparent
|
||||
opacity={1}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export function HalfGlobe() {
|
||||
return (
|
||||
<div className="w-full h-[500px] bg-white flex items-center justify-center">
|
||||
<Canvas camera={{ position: [0, 1.5, 4], fov: 45 }}>
|
||||
<ambientLight intensity={0.8} />
|
||||
<Globe />
|
||||
<OrbitControls enableZoom={false} enablePan={false} />
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export function ScrollDown() {
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToNext}
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-white lg:text-3xl animate-blink"
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-black/20 p-2 text-2xl font-medium text-white backdrop-blur-sm lg:text-3xl animate-blink"
|
||||
>
|
||||
<span>scroll</span>
|
||||
<ChevronDoubleDownIcon className="h-6 w-6" />
|
||||
|
||||
@@ -13,7 +13,7 @@ export function ScrollUp() {
|
||||
return (
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 text-2xl font-medium text-[#1c1c49] lg:text-3xl animate-blink"
|
||||
className="fixed bottom-8 right-8 z-50 flex items-center gap-x-2 rounded-full bg-white/20 p-2 text-2xl font-medium text-[#1c1c49] backdrop-blur-sm lg:text-3xl animate-blink"
|
||||
>
|
||||
<span>top</span>
|
||||
<ChevronDoubleUpIcon className="h-6 w-6" />
|
||||
|
||||
64
src/components/ui/aurora-background.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
interface AuroraBackgroundProps extends React.HTMLProps<HTMLDivElement> {
|
||||
children: ReactNode;
|
||||
showRadialGradient?: boolean;
|
||||
}
|
||||
|
||||
export const AuroraBackground = ({
|
||||
className,
|
||||
children,
|
||||
showRadialGradient = true,
|
||||
...props
|
||||
}: AuroraBackgroundProps) => {
|
||||
return (
|
||||
<main>
|
||||
<div
|
||||
className={cn(
|
||||
"transition-bg relative flex h-[110vh] flex-col items-center justify-center bg-cover bg-center",
|
||||
className
|
||||
)}
|
||||
style={{
|
||||
backgroundImage: "url('/images/mchip.webp')",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className="absolute inset-0 overflow-hidden"
|
||||
style={
|
||||
{
|
||||
"--aurora":
|
||||
"repeating-linear-gradient(100deg,#3b82f6_10%,#a5b4fc_15%,#93c5fd_20%,#ddd6fe_25%,#60a5fa_30%)",
|
||||
"--dark-gradient":
|
||||
"repeating-linear-gradient(100deg,#000_0%,#000_7%,transparent_10%,transparent_12%,#000_16%)",
|
||||
"--white-gradient":
|
||||
"repeating-linear-gradient(100deg,#fff_0%,#fff_7%,transparent_10%,transparent_12%,#fff_16%)",
|
||||
|
||||
"--blue-300": "#93c5fd",
|
||||
"--blue-400": "#60a5fa",
|
||||
"--blue-500": "#3b82f6",
|
||||
"--indigo-300": "#a5b4fc",
|
||||
"--violet-200": "#ddd6fe",
|
||||
"--black": "#000",
|
||||
"--white": "#fff",
|
||||
"--transparent": "transparent",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<div
|
||||
// I'm sorry but this is what peak developer performance looks like // trigger warning
|
||||
className={cn(
|
||||
`after:animate-aurora pointer-events-none absolute -inset-[10px] [background-image:var(--white-gradient),var(--aurora)] [background-size:300%,_200%] [background-position:50%_50%,50%_50%] opacity-50 blur-[10px] invert filter will-change-transform [--aurora:repeating-linear-gradient(100deg,var(--blue-500)_10%,var(--indigo-300)_15%,var(--blue-300)_20%,var(--violet-200)_25%,var(--blue-400)_30%)] [--dark-gradient:repeating-linear-gradient(100deg,var(--black)_0%,var(--black)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--black)_16%)] [--white-gradient:repeating-linear-gradient(100deg,var(--white)_0%,var(--white)_7%,var(--transparent)_10%,var(--transparent)_12%,var(--white)_16%)] after:absolute after:inset-0 after:[background-image:var(--white-gradient),var(--aurora)] after:[background-size:200%,_100%] after:[background-attachment:fixed] after:mix-blend-difference after:content-[""] dark:[background-image:var(--dark-gradient),var(--aurora)] dark:invert-0 after:dark:[background-image:var(--dark-gradient),var(--aurora)]`,
|
||||
|
||||
showRadialGradient &&
|
||||
`[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`,
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
106
src/components/ui/evervault-card.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
import { useMotionValue } from "motion/react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useMotionTemplate, motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const EvervaultCard = ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}) => {
|
||||
let mouseX = useMotionValue(0);
|
||||
let mouseY = useMotionValue(0);
|
||||
|
||||
const [randomString, setRandomString] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
let str = generateRandomString(1500);
|
||||
setRandomString(str);
|
||||
}, []);
|
||||
|
||||
function onMouseMove({ currentTarget, clientX, clientY }: any) {
|
||||
let { left, top } = currentTarget.getBoundingClientRect();
|
||||
mouseX.set(clientX - left);
|
||||
mouseY.set(clientY - top);
|
||||
|
||||
const str = generateRandomString(1500);
|
||||
setRandomString(str);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"p-0.5 bg-transparent aspect-square flex items-center justify-center w-full h-full relative",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div
|
||||
onMouseMove={onMouseMove}
|
||||
className="group/card rounded-3xl w-full relative overflow-hidden bg-transparent flex items-center justify-center h-full"
|
||||
>
|
||||
<CardPattern
|
||||
mouseX={mouseX}
|
||||
mouseY={mouseY}
|
||||
randomString={randomString}
|
||||
/>
|
||||
<div className="relative z-10 flex items-center justify-center">
|
||||
<div className="relative p-4">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export function CardPattern({ mouseX, mouseY, randomString }: any) {
|
||||
let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`;
|
||||
let style = { maskImage, WebkitMaskImage: maskImage };
|
||||
|
||||
return (
|
||||
<div className="pointer-events-none">
|
||||
<div className="absolute inset-0 rounded-2xl [mask-image:linear-gradient(white,transparent)] group-hover/card:opacity-50"></div>
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-2xl bg-gradient-to-r from-green-500 to-blue-700 opacity-0 group-hover/card:opacity-100 backdrop-blur-xl transition duration-500"
|
||||
style={style}
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay group-hover/card:opacity-100"
|
||||
style={style}
|
||||
>
|
||||
<p className="absolute inset-x-0 text-xs h-full break-words whitespace-pre-wrap text-white font-mono font-bold transition duration-500">
|
||||
{randomString}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
export const generateRandomString = (length: number) => {
|
||||
let result = "";
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const Icon = ({ className, ...rest }: any) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className={className}
|
||||
{...rest}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
90
src/components/ui/floating-navbar.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
motion,
|
||||
AnimatePresence,
|
||||
useScroll,
|
||||
useMotionValueEvent,
|
||||
} from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
|
||||
export const FloatingNav = ({
|
||||
navItems,
|
||||
className,
|
||||
}: {
|
||||
navItems: {
|
||||
name: string;
|
||||
link: string;
|
||||
icon?: JSX.Element;
|
||||
}[];
|
||||
className?: string;
|
||||
}) => {
|
||||
const { scrollYProgress } = useScroll();
|
||||
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
useMotionValueEvent(scrollYProgress, "change", (current) => {
|
||||
if (typeof current === "number") {
|
||||
const previous = scrollYProgress.getPrevious();
|
||||
// Check if previous is a number to avoid errors
|
||||
if (typeof previous === "number" && current > previous) {
|
||||
setVisible(false); // Scrolling down
|
||||
} else {
|
||||
setVisible(true); // Scrolling up or at the top
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
initial={{
|
||||
opacity: 1,
|
||||
y: -100,
|
||||
}}
|
||||
animate={{
|
||||
y: visible ? 0 : -100,
|
||||
opacity: visible ? 1 : 0,
|
||||
}}
|
||||
transition={{
|
||||
duration: 0.2,
|
||||
}}
|
||||
className={cn(
|
||||
"flex max-w-fit fixed top-10 inset-x-0 mx-auto border border-transparent dark:border-white/[0.2] rounded-xl dark:bg-black bg-gray-700/50 shadow-[0px_2px_3px_-1px_rgba(0,0,0,0.1),0px_1px_0px_0px_rgba(25,28,33,0.02),0px_0px_0px_1px_rgba(25,28,33,0.08)] z-[5000] pr-4 pl-8 py-2 items-center justify-center space-x-12",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{navItems.map((navItem, idx: number) => (
|
||||
<Link
|
||||
key={`link=${idx}`}
|
||||
to={navItem.link}
|
||||
className={cn(
|
||||
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
|
||||
)}
|
||||
>
|
||||
<span className="block sm:hidden">{navItem.icon}</span>
|
||||
<span className="hidden sm:block text-sm">{navItem.name}</span>
|
||||
</Link>
|
||||
))}
|
||||
<div className="flex items-center gap-6">
|
||||
<a
|
||||
href="https://threefold.info/mycelium_network/docs/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"relative dark:text-neutral-50 items-center flex space-x-1 text-neutral-200 dark:hover:text-neutral-300 hover:text-cyan-500"
|
||||
)}
|
||||
>
|
||||
<span className="hidden sm:block text-sm">Docs</span>
|
||||
</a>
|
||||
<Button to="/download" variant="solid" color="cyan">
|
||||
Get Mycelium
|
||||
</Button>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
158
src/components/ui/glowing-stars.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const GlowingStarsBackgroundCard = ({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) => {
|
||||
const [mouseEnter, setMouseEnter] = useState(false);
|
||||
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => {
|
||||
setMouseEnter(true);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setMouseEnter(false);
|
||||
}}
|
||||
className={cn(
|
||||
"bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 max-w-md max-h-[20rem] h-full w-full rounded-xl border border-[#eaeaea] dark:border-neutral-600",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<Illustration mouseEnter={mouseEnter} />
|
||||
</div>
|
||||
<div className="px-2 pb-6">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const GlowingStarsDescription = ({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<p className={cn("text-base text-white max-w-[16rem]", className)}>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const GlowingStarsTitle = ({
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<h2 className={cn("font-bold text-2xl text-[#eaeaea]", className)}>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
};
|
||||
|
||||
export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => {
|
||||
const stars = 108;
|
||||
const columns = 18;
|
||||
|
||||
const [glowingStars, setGlowingStars] = useState<number[]>([]);
|
||||
|
||||
const highlightedStars = useRef<number[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
highlightedStars.current = Array.from({ length: 5 }, () =>
|
||||
Math.floor(Math.random() * stars)
|
||||
);
|
||||
setGlowingStars([...highlightedStars.current]);
|
||||
}, 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="h-48 p-1 w-full"
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
||||
gap: `1px`,
|
||||
}}
|
||||
>
|
||||
{[...Array(stars)].map((_, starIdx) => {
|
||||
const isGlowing = glowingStars.includes(starIdx);
|
||||
const delay = (starIdx % 10) * 0.1;
|
||||
const staticDelay = starIdx * 0.01;
|
||||
return (
|
||||
<div
|
||||
key={`matrix-col-${starIdx}}`}
|
||||
className="relative flex items-center justify-center"
|
||||
>
|
||||
<Star
|
||||
isGlowing={mouseEnter ? true : isGlowing}
|
||||
delay={mouseEnter ? staticDelay : delay}
|
||||
/>
|
||||
{mouseEnter && <Glow delay={staticDelay} />}
|
||||
<AnimatePresence mode="wait">
|
||||
{isGlowing && <Glow delay={delay} />}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => {
|
||||
return (
|
||||
<motion.div
|
||||
key={delay}
|
||||
initial={{
|
||||
scale: 1,
|
||||
}}
|
||||
animate={{
|
||||
scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1,
|
||||
background: isGlowing ? "#fff" : "#666",
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
ease: "easeInOut",
|
||||
delay: delay,
|
||||
}}
|
||||
className={cn("bg-[#666] h-[1px] w-[1px] rounded-full relative z-20")}
|
||||
></motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
const Glow = ({ delay }: { delay: number }) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
}}
|
||||
transition={{
|
||||
duration: 2,
|
||||
ease: "easeInOut",
|
||||
delay: delay,
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
}}
|
||||
className="absolute left-1/2 -translate-x-1/2 z-10 h-[4px] w-[4px] rounded-full bg-blue-500 blur-[1px] shadow-2xl shadow-blue-400"
|
||||
/>
|
||||
);
|
||||
};
|
||||
104
src/components/ui/lamp.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default function LampDemo() {
|
||||
return (
|
||||
<LampContainer>
|
||||
<motion.h1
|
||||
initial={{ opacity: 0.5, y: 100 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="mt-8 bg-gradient-to-br from-gray-300 to-gray-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl"
|
||||
>
|
||||
Build lamps <br /> the right way
|
||||
</motion.h1>
|
||||
</LampContainer>
|
||||
);
|
||||
}
|
||||
|
||||
export const LampContainer = ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex h-screen flex-col items-center justify-start pt-32 overflow-hidden bg-gray-950 w-full z-0",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="relative flex w-full flex-1 scale-y-125 items-center justify-center isolate z-0 ">
|
||||
<motion.div
|
||||
initial={{ opacity: 0.5, width: "15rem" }}
|
||||
whileInView={{ opacity: 1, width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.5,
|
||||
duration: 1.0,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
style={{
|
||||
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
||||
}}
|
||||
className="absolute inset-auto right-1/2 h-56 overflow-visible w-[30rem] bg-gradient-conic from-cyan-500 via-transparent to-transparent text-white [--conic-position:from_70deg_at_center_top]"
|
||||
>
|
||||
<div className="absolute w-[100%] left-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
||||
<div className="absolute w-40 h-[100%] left-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_right,white,transparent)]" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0.5, width: "15rem" }}
|
||||
whileInView={{ opacity: 1, width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
style={{
|
||||
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
||||
}}
|
||||
className="absolute inset-auto left-1/2 h-56 w-[30rem] bg-gradient-conic from-transparent via-transparent to-cyan-500 text-white [--conic-position:from_290deg_at_center_top]"
|
||||
>
|
||||
<div className="absolute w-40 h-[100%] right-0 bg-gray-950 bottom-0 z-20 [mask-image:linear-gradient(to_left,white,transparent)]" />
|
||||
<div className="absolute w-[100%] right-0 bg-gray-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
||||
</motion.div>
|
||||
<div className="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-gray-950 blur-2xl"></div>
|
||||
<div className="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md"></div>
|
||||
<div className="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-cyan-500 opacity-50 blur-3xl"></div>
|
||||
<motion.div
|
||||
initial={{ width: "8rem" }}
|
||||
whileInView={{ width: "16rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="absolute inset-auto z-30 h-36 w-64 -translate-y-[6rem] rounded-full bg-cyan-400 blur-2xl"
|
||||
></motion.div>
|
||||
<motion.div
|
||||
initial={{ width: "15rem" }}
|
||||
whileInView={{ width: "30rem" }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="absolute inset-auto z-50 h-0.5 w-[30rem] -translate-y-[7rem] bg-cyan-400 "
|
||||
></motion.div>
|
||||
|
||||
<div className="absolute inset-auto z-40 h-44 w-full -translate-y-[12.5rem] bg-gray-950 "></div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-50 flex -translate-y-32 flex-col items-center px-5">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
58
src/components/ui/layout-text-flip.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const LayoutTextFlip = ({
|
||||
text = "Build Amazing",
|
||||
words = ["Landing Pages", "Component Blocks", "Page Sections", "3D Shaders"],
|
||||
duration = 1500,
|
||||
}: {
|
||||
text: string;
|
||||
words: string[];
|
||||
duration?: number;
|
||||
}) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCurrentIndex((prevIndex) => (prevIndex + 1) % words.length);
|
||||
}, duration);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.span
|
||||
layoutId="subtext"
|
||||
className="text-2xl font-bold tracking-tight drop-shadow-lg md:text-4xl"
|
||||
>
|
||||
{text}
|
||||
</motion.span>
|
||||
|
||||
<motion.span
|
||||
layout
|
||||
className="relative w-fit overflow-hidden px-2 py-2 font-neuton font-medium italic tracking-tight"
|
||||
>
|
||||
<AnimatePresence mode="popLayout">
|
||||
<motion.span
|
||||
key={currentIndex}
|
||||
initial={{ y: -40, filter: "blur(10px)" }}
|
||||
animate={{
|
||||
y: 0,
|
||||
filter: "blur(0px)",
|
||||
}}
|
||||
exit={{ y: 50, filter: "blur(10px)", opacity: 0 }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
}}
|
||||
className={cn("inline-block whitespace-nowrap")}
|
||||
>
|
||||
{words[currentIndex]}
|
||||
</motion.span>
|
||||
</AnimatePresence>
|
||||
</motion.span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
119
src/components/ui/pointer-highlight.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
"use client";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { motion } from "motion/react";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
|
||||
export function PointerHighlight({
|
||||
children,
|
||||
rectangleClassName,
|
||||
pointerClassName,
|
||||
containerClassName,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
rectangleClassName?: string;
|
||||
pointerClassName?: string;
|
||||
containerClassName?: string;
|
||||
}) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current) {
|
||||
const { width, height } = containerRef.current.getBoundingClientRect();
|
||||
setDimensions({ width, height });
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
setDimensions({ width, height });
|
||||
}
|
||||
});
|
||||
|
||||
if (containerRef.current) {
|
||||
resizeObserver.observe(containerRef.current);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (containerRef.current) {
|
||||
resizeObserver.unobserve(containerRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn("relative w-fit", containerClassName)}
|
||||
ref={containerRef}
|
||||
>
|
||||
{children}
|
||||
{dimensions.width > 0 && dimensions.height > 0 && (
|
||||
<motion.div
|
||||
className="pointer-events-none absolute inset-0 z-0"
|
||||
initial={{ opacity: 0, scale: 0.95, originX: 0, originY: 0 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.5, ease: "easeOut" }}
|
||||
>
|
||||
<motion.div
|
||||
className={cn(
|
||||
"absolute inset-0 border border-neutral-800 dark:border-neutral-200",
|
||||
rectangleClassName,
|
||||
)}
|
||||
initial={{
|
||||
width: 0,
|
||||
height: 0,
|
||||
}}
|
||||
whileInView={{
|
||||
width: dimensions.width,
|
||||
height: dimensions.height,
|
||||
}}
|
||||
transition={{
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
/>
|
||||
<motion.div
|
||||
className="pointer-events-none absolute"
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{
|
||||
opacity: 1,
|
||||
x: dimensions.width + 4,
|
||||
y: dimensions.height + 4,
|
||||
}}
|
||||
style={{
|
||||
rotate: -90,
|
||||
}}
|
||||
transition={{
|
||||
opacity: { duration: 0.1, ease: "easeInOut" },
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<Pointer
|
||||
className={cn("h-5 w-5 text-blue-500", pointerClassName)}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Pointer = ({ ...props }: React.SVGProps<SVGSVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
strokeWidth="1"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
viewBox="0 0 16 16"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path d="M14.082 2.182a.5.5 0 0 1 .103.557L8.528 15.467a.5.5 0 0 1-.917-.007L5.57 10.694.803 8.652a.5.5 0 0 1-.006-.916l12.728-5.657a.5.5 0 0 1 .556.103z"></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
170
src/components/ui/world-map.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import DottedMap from "dotted-map";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
interface MapProps {
|
||||
dots?: Array<{
|
||||
start: { lat: number; lng: number; label?: string };
|
||||
end: { lat: number; lng: number; label?: string };
|
||||
}>;
|
||||
lineColor?: string;
|
||||
}
|
||||
|
||||
export default function WorldMap({
|
||||
dots = [],
|
||||
lineColor = "#06b6d4",
|
||||
}: MapProps) {
|
||||
const svgRef = useRef<SVGSVGElement>(null);
|
||||
const map = new DottedMap({ height: 100, grid: "diagonal" });
|
||||
|
||||
const { theme } = useTheme();
|
||||
|
||||
const svgMap = map.getSVG({
|
||||
radius: 0.22,
|
||||
color: theme === "dark" ? "#FFFFFF40" : "#00000040",
|
||||
shape: "circle",
|
||||
backgroundColor: theme === "dark" ? "black" : "white",
|
||||
});
|
||||
|
||||
const projectPoint = (lat: number, lng: number) => {
|
||||
const x = (lng + 180) * (800 / 360);
|
||||
const y = (90 - lat) * (400 / 180);
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
const createCurvedPath = (
|
||||
start: { x: number; y: number },
|
||||
end: { x: number; y: number }
|
||||
) => {
|
||||
const midX = (start.x + end.x) / 2;
|
||||
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">
|
||||
<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"
|
||||
alt="world map"
|
||||
height="495"
|
||||
width="1056"
|
||||
draggable={false}
|
||||
/>
|
||||
<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);
|
||||
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>
|
||||
);
|
||||
})}
|
||||
|
||||
<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>
|
||||
|
||||
{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>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
BIN
src/images/cloudlayer.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
src/images/kubernetes.png
Normal file
|
After Width: | Height: | Size: 1024 KiB |
@@ -214,7 +214,7 @@
|
||||
</filter>
|
||||
<radialGradient id="b" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0 727 -642 0 184 1)">
|
||||
<stop stop-color="#FAFAFA" />
|
||||
<stop stop-color="#FFFFFF" />
|
||||
<stop offset="1" stop-color="#E6E6E6" />
|
||||
</radialGradient>
|
||||
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse"
|
||||
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,5 +1,8 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Neuton&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap');
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-family: 'Mulish', system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
|
||||
@@ -48,7 +48,7 @@ export function GallerySection() {
|
||||
const prev = () => setActive((i) => wrap(0, galleryItems.length, i - 1))
|
||||
|
||||
return (
|
||||
<div className="bg-[#FAFAFA]">
|
||||
<div className="bg-white">
|
||||
<div className="relative isolate pt-8 pb-0 text-center w-full">
|
||||
<FadeIn transition={{ duration: 0.8, delay: 0.1 }}>
|
||||
<div className="mx-auto max-w-5xl lg:mt-12">
|
||||
|
||||
65
src/pages/home/HomeAgent.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { H2, P } from '@/components/Texts'
|
||||
import { Button } from '@/components/Button'
|
||||
import { LayoutTextFlip } from '@/components/ui/layout-text-flip' // make sure this import path is correct
|
||||
|
||||
export function HomeAgent() {
|
||||
return (
|
||||
<div className="relative isolate overflow-hidden bg-white">
|
||||
<div className="px-6 py-24 sm:py-32 lg:px-8">
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<H2>
|
||||
Deploy your own{" "}
|
||||
<span className="font-neuton text-left text-black font-medium text-7xl italic bg-clip-text bg-gradient-to-r from-blue-400 via-cyan-400 to-violet-400">
|
||||
<LayoutTextFlip
|
||||
text=""
|
||||
words={[
|
||||
"GPT-5",
|
||||
"Claude 3.5",
|
||||
"Gemini 1.5",
|
||||
"Mistral 7B",
|
||||
"Llama 3.1",
|
||||
|
||||
"AI Agents",
|
||||
]}
|
||||
/>
|
||||
</span>
|
||||
</H2>
|
||||
|
||||
<P className="mx-auto mt-6 max-w-xl text-lg/8 text-pretty text-gray-600">
|
||||
Mycelium delivers enterprise-grade AI agents with unmatched customizability and the fastest time to production — all in the agent platform designed for real business use cases.
|
||||
</P>
|
||||
|
||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||
<Button variant="solid" color="cyan" href="/signup">
|
||||
Get started
|
||||
</Button>
|
||||
<a href="/agents" className="text-sm/6 font-semibold text-gray-900 hover:text-gray-600">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
aria-hidden="true"
|
||||
className="absolute top-1/2 left-1/2 -z-10 size-256 -translate-x-1/2 mask-[radial-gradient(closest-side,white,transparent)]"
|
||||
>
|
||||
<circle
|
||||
r={512}
|
||||
cx={512}
|
||||
cy={512}
|
||||
fill="url(#8d958450-c69f-4251-94bc-4e091a323369)"
|
||||
fillOpacity="0.7"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient id="8d958450-c69f-4251-94bc-4e091a323369" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor="#60A5FA" /> {/* blue-400 */}
|
||||
<stop offset="50%" stopColor="#06B6D4" /> {/* cyan-500 */}
|
||||
<stop offset="100%" stopColor="#A78BFA" /> {/* violet-400 */}
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
34
src/pages/home/HomeAurora.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "motion/react";
|
||||
import { H1, H2, H3, H4, H5 } from "@/components/Texts";
|
||||
import { AuroraBackground } from "@/components/ui/aurora-background";
|
||||
import { ScrollDownArrow } from '@/components/ScrollDownArrow';
|
||||
|
||||
export function HomeAurora() {
|
||||
return (
|
||||
<AuroraBackground>
|
||||
<motion.div
|
||||
initial={{ opacity: 0.0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: 0.5,
|
||||
duration: 1,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="relative mb-20 flex flex-col items-center justify-center gap-4 px-4 max-w-5xl"
|
||||
>
|
||||
<div className="text-center text-gray-800">
|
||||
<H1>Decentralized Autonomous <span className="font-neuton text-bold lg:text-8xl italic">Agentic Cloud.</span></H1>
|
||||
</div>
|
||||
<div className="pt-8 text-center font-light text-gray-500 max-w-4xl">
|
||||
<H5>Mycelium Project is a decentralized platform for autonomous AI, powered by distributed compute, sovereign memory, encrypted networking, and stateless GPU orchestration.</H5>
|
||||
</div>
|
||||
<div className="pt-8">
|
||||
<ScrollDownArrow />
|
||||
</div>
|
||||
|
||||
</motion.div>
|
||||
</AuroraBackground>
|
||||
);
|
||||
}
|
||||
66
src/pages/home/HomeCloud.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { CheckCircleIcon } from '@heroicons/react/20/solid'
|
||||
import { H2, P } from '@/components/Texts'
|
||||
|
||||
const benefits = [
|
||||
'Decentralized Infrastructure',
|
||||
'End-to-End Encryption',
|
||||
'Sovereign Data Control',
|
||||
'Scalable Kubernetes Clusters',
|
||||
'Censorship-Resistant',
|
||||
'Peer-to-Peer Networking',
|
||||
]
|
||||
|
||||
export function HomeCloud() {
|
||||
return (
|
||||
<div className="overflow-hidden bg-white py-24">
|
||||
<div className="relative isolate">
|
||||
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
|
||||
<div className="mx-auto flex max-w-2xl flex-col gap-16 bg-white px-6 py-16 shadow-lg ring-1 ring-gray-200/5 sm:rounded-3xl sm:p-8 lg:mx-0 lg:max-w-none lg:flex-row lg:items-center lg:py-20 xl:gap-x-20 xl:px-20">
|
||||
<img
|
||||
alt=""
|
||||
src="/images/kubernetes.png"
|
||||
className="h-96 w-full flex-none rounded-2xl object-cover lg:aspect-square lg:h-auto lg:max-w-sm"
|
||||
/>
|
||||
<div className="w-full flex-auto">
|
||||
<H2 className="">
|
||||
Mycelium <span className="font-neuton font-medium text-7xl italic">Cloud</span>
|
||||
</H2>
|
||||
<P className="mt-6 text-lg/8 text-pretty text-gray-600">
|
||||
A comprehensive platform for deploying and managing Kubernetes clusters on the decentralized Mycelium Grid infrastructure
|
||||
</P>
|
||||
<ul
|
||||
role="list"
|
||||
className="mt-10 grid grid-cols-1 gap-x-8 gap-y-3 text-base/7 text-gray-950 sm:grid-cols-2"
|
||||
>
|
||||
{benefits.map((benefit) => (
|
||||
<li key={benefit} className="flex gap-x-3">
|
||||
<CheckCircleIcon aria-hidden="true" className="h-7 w-5 flex-none text-cyan-500" />
|
||||
{benefit}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="mt-10 flex">
|
||||
<a href="/cloud" className="text-sm/6 font-semibold text-cyan-600 hover:text-cyan-500">
|
||||
Learn more
|
||||
<span aria-hidden="true"> →</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-x-0 -top-16 -z-10 flex transform-gpu justify-center overflow-hidden blur-3xl"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
clipPath:
|
||||
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
|
||||
}}
|
||||
className="aspect-1318/752 w-329.5 flex-none bg-linear-to-r from-[#9fe8fc] to-[#c6c4fa] opacity-50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
77
src/pages/home/HomeFeatures.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { InboxIcon, TrashIcon, UsersIcon } from '@heroicons/react/24/outline'
|
||||
import { H2, P } from '@/components/Texts'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Mycelium Network',
|
||||
description:
|
||||
"A global, end-to-end encrypted overlay that simply doesn't break.",
|
||||
href: '/network',
|
||||
icon: UsersIcon,
|
||||
image: '/images/network_icon.png',
|
||||
},
|
||||
{
|
||||
name: 'Mycelium Cloud',
|
||||
description:
|
||||
'An autonomous, stateless OS that enforces pre-deterministic deployments you define.',
|
||||
href: '/cloud',
|
||||
icon: TrashIcon,
|
||||
image: '/images/cloud_icon.png',
|
||||
},
|
||||
{
|
||||
name: 'Mycelium Agents',
|
||||
description:
|
||||
'Your sovereign agent with private memory and permissioned data access—always under your control.',
|
||||
href: '/agents',
|
||||
icon: InboxIcon,
|
||||
image: '/images/agent_icon.png',
|
||||
},
|
||||
]
|
||||
|
||||
export function HomeFeatures() {
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div className="relative bg-transparent py-24 overflow-hidden">
|
||||
{/* --- Soft background gradients --- */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-x-0 -top-16 -z-10 flex transform-gpu justify-center overflow-hidden blur-3xl"
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
clipPath:
|
||||
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
|
||||
}}
|
||||
className="aspect-1318/752 w-329.5 flex-none bg-linear-to-r from-[#9fd6fc] to-[#c6c4fa] opacity-40"
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl lg:mx-0">
|
||||
<H2 className="">
|
||||
The Building Blocks of <span className="font-neuton font-medium text-7xl italic">Decentralized Future</span>
|
||||
</H2>
|
||||
<P className="mt-6 ">
|
||||
From compute and networking to intelligent automation, these components work together to empower users, developers, and organizations to build freely, without intermediaries.
|
||||
</P>
|
||||
</div>
|
||||
<div className="mx-auto mt-16 max-w-2xl lg:max-w-7xl">
|
||||
<div className="grid grid-cols-1 gap-x-12 gap-y-12 lg:grid-cols-3">
|
||||
{features.map((feature) => (
|
||||
<div key={feature.name} className="relative flex flex-col p-8 rounded-3xl border border-gray-100 bg-white backdrop-blur-lg overflow-hidden shadow-lg hover:shadow-xl hover:border-cyan-500 hover:scale-105 transform transition-all duration-300">
|
||||
<div className="w-30 h-30 bg-white/80 rounded-full flex items-center justify-center">
|
||||
<img src={feature.image} alt="" className="w-25 h-25" />
|
||||
</div>
|
||||
<h3 className="mt-6 text-xl font-semibold text-black">{feature.name}</h3>
|
||||
<p className="mt-4 text-base text-gray-800">{feature.description}</p>
|
||||
<a href={feature.href} className="mt-6 text-base font-semibold text-black">Learn more <span aria-hidden="true"> →</span></a>
|
||||
<div className="absolute -bottom-10 -right-10 h-50 w-50 -z-10" style={{ background: 'radial-gradient(circle, rgba(173, 239, 255, 0.6) 0%, rgba(115, 207, 255, 0.4) 100%)', filter: 'blur(80px)' }}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +1,37 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { TypeAnimation } from 'react-type-animation'
|
||||
import { Container } from '../../components/Container'
|
||||
'use client'
|
||||
|
||||
import { H1, P } from '@/components/Texts'
|
||||
import { FadeIn } from '@/components/FadeIn'
|
||||
import { Button } from '@/components/Button'
|
||||
|
||||
export function HomeHero() {
|
||||
return (
|
||||
<section className="relative bg-white py-20 lg:py-32">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-4xl text-center">
|
||||
<h1 className="text-4xl lg:text-6xl font-medium tracking-tight text-gray-900">
|
||||
<TypeAnimation
|
||||
sequence={[
|
||||
'Decentralized Autonomous Agentic Cloud.',
|
||||
1000,
|
||||
]}
|
||||
wrapper="span"
|
||||
speed={50}
|
||||
repeat={0}
|
||||
/>
|
||||
</h1>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 1, delay: 1 }}
|
||||
className="mt-8"
|
||||
<div
|
||||
className="relative isolate overflow-hidden bg-white"
|
||||
style={{
|
||||
backgroundImage: 'url(/images/cloud.png)',
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<p className="text-lg lg:text-xl text-gray-600">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24 lg:py-32 lg:flex lg:px-8">
|
||||
<div className="mx-auto max-w-2xl shrink-0 lg:mx-0 lg:pt-8">
|
||||
<H1 color="primary" className="mt-10 text-5xl font-semibold tracking-tight text-pretty sm:text-7xl">
|
||||
Decentralized Autonomous Agentic Cloud.
|
||||
</H1>
|
||||
<P color="secondary" className="mt-8 text-lg font-medium text-pretty sm:text-xl/8">
|
||||
Mycelium's advancements in Agentic infrastructure supports private, secure and autonomous Agents that connect, learn and grow with you.
|
||||
</p>
|
||||
</motion.div>
|
||||
</P>
|
||||
<div className="mt-10 flex items-center gap-x-6">
|
||||
<Button variant="solid" color="cyan" href="#">
|
||||
Get started
|
||||
</Button>
|
||||
<a href="#" className="text-sm/6 font-semibold text-gray-50">
|
||||
Learn more <span aria-hidden="true">→</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
35
src/pages/home/HomeHeroDark.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { LampContainer } from "@/components/ui/lamp";
|
||||
|
||||
export function HomeHeroDark() {
|
||||
return (
|
||||
<LampContainer>
|
||||
<motion.h1
|
||||
initial={{ opacity: 0.5, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="bg-gradient-to-br from-slate-300 to-slate-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl max-w-4xl"
|
||||
>
|
||||
Decentralized Autonomous Agentic Cloud.
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
initial={{ opacity: 0.5, y: 60 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{
|
||||
delay: 0.5,
|
||||
duration: 0.8,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
className="mt-8 bg-gradient-to-br from-slate-300 to-slate-500 py-4 bg-clip-text text-center text-lg font-medium tracking-tight text-transparent md:text-xl max-w-3xl"
|
||||
>
|
||||
Mycelium Project is a decentralized platform for autonomous AI, powered by distributed compute, sovereign memory, encrypted networking, and stateless GPU orchestration.
|
||||
</motion.p>
|
||||
</LampContainer>
|
||||
);
|
||||
}
|
||||
69
src/pages/home/HomeHeroLight2.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useEffect } from 'react'
|
||||
import { H1, H5 } from '@/components/Texts'
|
||||
|
||||
export function HomeHeroLight2() {
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const video = videoRef.current;
|
||||
if (video) {
|
||||
video.playbackRate = 0.4;
|
||||
const playPromise = video.play();
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(error => {
|
||||
console.error("Video autoplay was prevented:", error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="relative h-screen overflow-hidden">
|
||||
{/* Background video */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
src="/videos/cloud.mp4"
|
||||
autoPlay
|
||||
loop
|
||||
muted
|
||||
playsInline
|
||||
className="absolute inset-0 h-full w-full object-cover z-[-10]"
|
||||
/>
|
||||
|
||||
{/* Global soft wash + blur (Temporarily commented out for debugging) */}
|
||||
{/* <div className="absolute inset-0 bg-white opacity-30 backdrop-blur-md z-0" /> */}
|
||||
|
||||
{/* Center “halo” for text legibility (Temporarily commented out for debugging) */}
|
||||
{/* <div
|
||||
className="absolute inset-0 z-0"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(ellipse at center, rgba(255,255,255,0.96) 0%, rgba(255,255,255,0.88) 15%, rgba(255,255,255,0.72) 35%, rgba(255,255,255,0.08) 75%)'
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 h-full flex items-center justify-center">
|
||||
<div className="mx-auto max-w-4xl text-center px-6 lg:px-8">
|
||||
|
||||
<H1
|
||||
className="pt-6"
|
||||
style={{ textShadow: '0 2px 8px rgba(0,0,0,0.08)' }}
|
||||
>
|
||||
Decentralized Autonomous Agentic Cloud.
|
||||
</H1>
|
||||
|
||||
<H5
|
||||
className="mt-8"
|
||||
style={{ textShadow: '0 1px 4px rgba(0,0,0,0.06)' }}
|
||||
>
|
||||
Mycelium's advancements in Agentic infrastructure support private, secure, and
|
||||
autonomous Agents that connect, learn, and grow with you.
|
||||
</H5>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
69
src/pages/home/HomeMap.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
import WorldMap from "@/components/ui/world-map";
|
||||
import { motion } from "motion/react";
|
||||
import { H2, P } from "@/components/Texts";
|
||||
|
||||
export function HomeMapSection() {
|
||||
return (
|
||||
<div className=" py-24 dark:bg-black bg-white w-full">
|
||||
<div className="max-w-7xl mx-auto text-center">
|
||||
<H2 className="font-medium text-xl md:text-4xl dark:text-white text-gray-800">
|
||||
Mycelium Network is{" "}
|
||||
<span className="text-black font-neuton text-bold italic">
|
||||
{"Live.".split("").map((word, idx) => (
|
||||
<motion.span
|
||||
key={idx}
|
||||
className="inline-block"
|
||||
initial={{ x: -10, opacity: 0 }}
|
||||
animate={{ x: 0, opacity: 1 }}
|
||||
transition={{ duration: 0.5, delay: idx * 0.04 }}
|
||||
>
|
||||
{word}
|
||||
</motion.span>
|
||||
))}
|
||||
</span>
|
||||
</H2>
|
||||
<P className="text-sm md:text-lg text-neutral-500 max-w-2xl mx-auto py-4">
|
||||
Mycelium Network's advancement technology enables anyone to deploy
|
||||
their own Internet infrastructure, anywhere.
|
||||
</P>
|
||||
</div>
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<WorldMap
|
||||
dots={[
|
||||
{
|
||||
start: {
|
||||
lat: 64.2008,
|
||||
lng: -149.4937,
|
||||
}, // Alaska (Fairbanks)
|
||||
end: {
|
||||
lat: 34.0522,
|
||||
lng: -118.2437,
|
||||
}, // Los Angeles
|
||||
},
|
||||
{
|
||||
start: { lat: 64.2008, lng: -149.4937 }, // Alaska (Fairbanks)
|
||||
end: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília)
|
||||
},
|
||||
{
|
||||
start: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília)
|
||||
end: { lat: 38.7223, lng: -9.1393 }, // Lisbon
|
||||
},
|
||||
{
|
||||
start: { lat: 51.5074, lng: -0.1278 }, // London
|
||||
end: { lat: 28.6139, lng: 77.209 }, // New Delhi
|
||||
},
|
||||
{
|
||||
start: { lat: 28.6139, lng: 77.209 }, // New Delhi
|
||||
end: { lat: 43.1332, lng: 131.9113 }, // Vladivostok
|
||||
},
|
||||
{
|
||||
start: { lat: 28.6139, lng: 77.209 }, // New Delhi
|
||||
end: { lat: -1.2921, lng: 36.8219 }, // Nairobi
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,21 +2,37 @@ import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { HomeHero } from './HomeHero'
|
||||
import { WorldMapSection } from './WorldMapSection'
|
||||
import { StackSection } from './StackSection'
|
||||
import { HomeHeroLight2 } from './HomeHeroLight2'
|
||||
import { HomeHeroDark } from './HomeHeroDark'
|
||||
import { HomeAurora } from './HomeAurora'
|
||||
import { HomeMapSection } from './HomeMap'
|
||||
import { HomeFeatures } from './HomeFeatures'
|
||||
import { HomeCloud } from './HomeCloud'
|
||||
import { HomeAgent } from './HomeAgent'
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div>
|
||||
<AnimatedSection>
|
||||
<HomeHero />
|
||||
<HomeAurora />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection id="next-section">
|
||||
<HomeFeatures />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection >
|
||||
<WorldMapSection />
|
||||
<HomeMapSection />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<StackSection />
|
||||
<HomeCloud />
|
||||
</AnimatedSection>
|
||||
|
||||
<AnimatedSection>
|
||||
<HomeAgent />
|
||||
</AnimatedSection>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ const stackData = [
|
||||
|
||||
export function StackSection() {
|
||||
return (
|
||||
<section className="relative bg-white py-20 lg:py-32">
|
||||
<section className="relative bg-black py-20 lg:py-32">
|
||||
<Container>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-12 items-start">
|
||||
{/* Left Column - Text */}
|
||||
@@ -35,7 +35,7 @@ export function StackSection() {
|
||||
transition={{ duration: 0.5 }}
|
||||
className="lg:col-span-1"
|
||||
>
|
||||
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
|
||||
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white">
|
||||
The Mycelium Stack
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@import 'tailwindcss';
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
--animate-scroll: scroll var(--animation-duration, 40s) var(--animation-direction, forwards) linear infinite;
|
||||
@@ -70,7 +73,10 @@
|
||||
--color-gray-900: oklch(0.205 0 0);
|
||||
--color-gray-950: oklch(0.145 0 0);
|
||||
|
||||
--font-sans: var(--font-inter);
|
||||
--font-sans: 'Mulish', system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
--font-neuton: 'Neuton', serif;
|
||||
|
||||
--font-family-neuton: var(--font-neuton);
|
||||
|
||||
--container-2xl: 40rem;
|
||||
|
||||
@@ -91,10 +97,127 @@
|
||||
|
||||
@theme inline {
|
||||
--animate-marquee: marquee var(--marquee-duration) linear infinite;
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-background: var(--background);
|
||||
|
||||
@keyframes marquee {
|
||||
100% {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
|
||||
--radius-lg: var(--radius);
|
||||
|
||||
--radius-xl: calc(var(--radius) + 4px)
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
|
||||