Compare commits
93 Commits
a59e09e8ed
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d1e2f4c7d | |||
| 0f2f6df299 | |||
| c56f67ae2e | |||
| 39b748cdac | |||
| 6d186c2304 | |||
| 681a7606d5 | |||
| 0e8de7e7fe | |||
| 3919b72b0c | |||
| f796ec1218 | |||
| 5af349ad4a | |||
| 6485e1e3ce | |||
| e7b33b75c9 | |||
| 2e22ed9683 | |||
| 15e81cb5cd | |||
| 0e6ecedf85 | |||
| 75dd7dfbc5 | |||
| d8ce04252a | |||
| c14c0e92d4 | |||
| 9730129506 | |||
| 8b4e0defb9 | |||
| 15cc1f70e3 | |||
| ae3e78f75a | |||
| 7ee6da68fe | |||
| 6e2ea7c87c | |||
| 5f0c749f94 | |||
| b3836062a3 | |||
| b1c59a9b5a | |||
| db92f1271e | |||
| 6dc318704a | |||
| 6304f8fc1d | |||
| d7769a5ecd | |||
| 005d8c35d4 | |||
| a73608ce6c | |||
| ef7dc12bc2 | |||
| 2d856a5858 | |||
| 8fac6f8edd | |||
| 560ec7dcd0 | |||
| aaf02d0a7c | |||
| b767bdbcb4 | |||
| c25f6aaba6 | |||
| 2bd3026bac | |||
| 88d6a90f60 | |||
| 8d9f02a846 | |||
| e16456bb94 | |||
| 388654fcbd | |||
| 865252274c | |||
| 8b892c9432 | |||
| 7f9023c631 | |||
| f3456eb470 | |||
| 46272e939d | |||
| 0daabe56f5 | |||
| 85afc05c35 | |||
| 866e1df042 | |||
| 28ea2ab49d | |||
| 1f11ca5319 | |||
| 3e99e85e48 | |||
| b39694ea24 | |||
| 363b822d8b | |||
| 00802c7064 | |||
| e45ca7c86f | |||
| 664897ee19 | |||
| 91d249f097 | |||
| 4ba88257eb | |||
| 4c4a0c5dd1 | |||
| 8817272932 | |||
| 6a882371f0 | |||
| 4e8e714f37 | |||
| 01c3c226a9 | |||
| c7371ec21b | |||
| c15b110afe | |||
| 3564b5cb0f | |||
| 51ef8dffb5 | |||
| 9d8f1a6919 | |||
| 0f93199aa4 | |||
| 57fa97cc70 | |||
| fa7c524b18 | |||
| f1c388cbab | |||
| ea3ee4d455 | |||
| 24f6da37ed | |||
| c861f15492 | |||
| bfe3c1e4bd | |||
| 26ae2f156a | |||
| 33c940c604 | |||
| 0837a8313c | |||
| b311cb22a4 | |||
| b987f1a072 | |||
| dd4eba2215 | |||
| ea1ef853f1 | |||
| 5b05dababb | |||
| ab5ac43793 | |||
| fb9250c365 | |||
| 04d08a4265 | |||
| 07f76639d2 |
@@ -3,7 +3,7 @@
|
||||
All in one project mycelium website
|
||||
|
||||
|
||||
- **Repository:** [https://git.ourworld.tf/ourworld_web/www_project_mycelium_com/](https://git.ourworld.tf/ourworld_web/www_project_mycelium_com/)
|
||||
- **Repository:** [https://git.ourworld.tf/ourworld_web/www_projectmycelium_com/](https://git.ourworld.tf/ourworld_web/www_projectmycelium_com/)
|
||||
|
||||
|
||||
- **Main Branch (Production):** [https://www.projectmycelium.com/](https://www.projectmycelium.com)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<meta name="description" content="Project Mycelium's technology enables anyone to deploy their own Internet infrastructure, anywhere." />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
public/images/ChatGPT Image Oct 31, 2025, 01_46_54 AM.png
Normal file
|
After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 992 KiB |
|
Before Width: | Height: | Size: 802 KiB |
BIN
public/images/agenthero1.webp
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/agents.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/agents.webp
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/agentshero2.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
public/images/bento-01-integrations.png
Normal file
|
After Width: | Height: | Size: 431 KiB |
BIN
public/images/bento-agent.jpg
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
public/images/bento-cloud.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
public/images/bento-compute.png
Normal file
|
After Width: | Height: | Size: 895 KiB |
BIN
public/images/bento-gpu.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
public/images/bento-network.png
Normal file
|
After Width: | Height: | Size: 151 KiB |
BIN
public/images/bento-network.webp
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/images/bento-storage.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
public/images/bg.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/images/bg2.jpg
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 723 KiB |
BIN
public/images/cloud/billing.jpg
Normal file
|
After Width: | Height: | Size: 878 KiB |
BIN
public/images/cloud/billing.png
Normal file
|
After Width: | Height: | Size: 778 KiB |
BIN
public/images/cloud/kubeconfig.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/cloud/kubeconfig.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/cloud/reserve.jpg
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/images/cloud/reserve.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 757 KiB |
BIN
public/images/cloudhero.webp
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
public/images/cloudhero2.webp
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/images/cloudhero3.webp
Normal file
|
After Width: | Height: | Size: 938 KiB |
BIN
public/images/cloudhero4.webp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/cloudhosting.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/cloudhosting.webp
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/computehero.webp
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/computehero11.webp
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
BIN
public/images/gpuhero2.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/hero11.webp
Normal file
|
After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
BIN
public/images/iot-gdb6f0c390_1280-1024x706.webp
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
public/images/k82.png
Normal file
|
After Width: | Height: | Size: 910 KiB |
BIN
public/images/kubernetes.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/images/linux.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
public/images/logo/1_eVqphQ2aNKxqHPMPxjRzAA (1).png
Normal file
|
After Width: | Height: | Size: 286 KiB |
BIN
public/images/logo/1_eVqphQ2aNKxqHPMPxjRzAA.png
Normal file
|
After Width: | Height: | Size: 286 KiB |
BIN
public/images/logo/cryptpad.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/images/logo/gitea.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/images/logo/images.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
public/images/logo/lifekit.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/images/logo/matrix.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/images/logo/nextcloud.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
public/images/logo/stalwart.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 835 KiB |
BIN
public/images/pages/agent.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
public/images/pages/agent.webp
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
public/images/pages/cloud.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/images/pages/cloud.webp
Normal file
|
After Width: | Height: | Size: 232 KiB |
BIN
public/images/pages/compute.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
public/images/pages/compute.webp
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
public/images/pages/gpu.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
public/images/pages/gpu.webp
Normal file
|
After Width: | Height: | Size: 268 KiB |
BIN
public/images/pages/gpuhero2.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/pages/network.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
public/images/pages/network.webp
Normal file
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 6.1 MiB |
BIN
public/images/pages/storage.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
public/images/pages/storage.webp
Normal file
|
After Width: | Height: | Size: 271 KiB |
BIN
public/images/pages/storage1.webp
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
public/images/storage.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/images/storage2.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
public/images/testpic.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/images/vm.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
public/videos/mhero.mp4
Normal file
@@ -1,4 +1,5 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
type AnimatedSectionProps = {
|
||||
children: React.ReactNode
|
||||
@@ -6,9 +7,11 @@ type AnimatedSectionProps = {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function AnimatedSection({ children, id, className }: AnimatedSectionProps) {
|
||||
export const AnimatedSection = forwardRef<HTMLElement, AnimatedSectionProps>(
|
||||
({ children, id, className }, ref) => {
|
||||
return (
|
||||
<motion.section
|
||||
ref={ref}
|
||||
id={id}
|
||||
className={className}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
@@ -19,4 +22,5 @@ export function AnimatedSection({ children, id, className }: AnimatedSectionProp
|
||||
{children}
|
||||
</motion.section>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@@ -3,9 +3,9 @@ import clsx from 'clsx'
|
||||
|
||||
const baseStyles = {
|
||||
solid:
|
||||
'inline-flex justify-center rounded-full py-2 px-4 text-sm font-semibold transition-colors',
|
||||
'inline-flex justify-center rounded-full py-2 px-5 text-base font-semibold transition-colors',
|
||||
outline:
|
||||
'inline-flex justify-center rounded-full border py-[calc(--spacing(2)-1px)] px-[calc(--spacing(4)-1px)] text-sm 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',
|
||||
}
|
||||
|
||||
const variantStyles = {
|
||||
@@ -17,7 +17,7 @@ const variantStyles = {
|
||||
green: 'bg-green-500 text-white hover:bg-green-600',
|
||||
},
|
||||
outline: {
|
||||
cyan: 'border-cyan-500 text-cyan-500 hover:bg-cyan-50',
|
||||
cyan: 'border-cyan-500 text-cyan-500',
|
||||
gray: 'border-gray-300 text-gray-700 hover:border-cyan-500 active:border-cyan-500',
|
||||
white: 'border-gray-300 text-white hover:border-cyan-500 active:border-cyan-500',
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Container } from './Container'
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="border-t border-gray-200">
|
||||
<footer className="">
|
||||
<Container>
|
||||
<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>
|
||||
|
||||
@@ -1,15 +1,37 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import { Dropdown } from './ui/Dropdown'
|
||||
import { ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||
import { Container } from './Container'
|
||||
import { Button } from './Button'
|
||||
import pmyceliumLogo from '../images/logos/logo_1.png'
|
||||
import { Dialog } from '@headlessui/react'
|
||||
import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline'
|
||||
|
||||
const cloudNavItems = [
|
||||
{ name: 'Cloud', href: '/cloud' },
|
||||
{ name: 'Compute', href: '/compute' },
|
||||
{ name: 'Storage', href: '/storage' },
|
||||
{ name: 'GPU', href: '/gpu' },
|
||||
]
|
||||
|
||||
export function Header() {
|
||||
const location = useLocation()
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||
|
||||
const getCurrentPageName = () => {
|
||||
const currentPath = location.pathname;
|
||||
if (currentPath.startsWith('/compute')) return 'Compute';
|
||||
if (currentPath.startsWith('/storage')) return 'Storage';
|
||||
if (currentPath.startsWith('/gpu')) return 'GPU';
|
||||
if (currentPath.startsWith('/cloud')) return 'Cloud';
|
||||
return 'Cloud';
|
||||
};
|
||||
|
||||
return (
|
||||
<header>
|
||||
<nav>
|
||||
<Container className="relative z-50 flex justify-between py-4">
|
||||
<header className="bg-white">
|
||||
<nav className="border-b border-gray-200">
|
||||
<Container className="flex bg-transparent justify-between py-4">
|
||||
<div className="relative z-10 flex items-center gap-16">
|
||||
<Link to="/" aria-label="Home">
|
||||
<img src={pmyceliumLogo} alt="Mycelium" className="h-8 w-auto" />
|
||||
@@ -18,16 +40,11 @@ export function Header() {
|
||||
<Dropdown
|
||||
buttonContent={
|
||||
<>
|
||||
Cloud
|
||||
{getCurrentPageName()}
|
||||
<ChevronDownIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</>
|
||||
}
|
||||
items={[
|
||||
{ name: 'Cloud', href: '/cloud' },
|
||||
{ name: 'Compute', href: '/compute' },
|
||||
{ name: 'Storage', href: '/storage' },
|
||||
{ name: 'GPU', href: '/gpu' },
|
||||
]}
|
||||
items={cloudNavItems}
|
||||
/>
|
||||
<Link
|
||||
to="/network"
|
||||
@@ -58,9 +75,84 @@ export function Header() {
|
||||
Get Mycelium Connector
|
||||
</Button>
|
||||
</div>
|
||||
<div className="lg:hidden">
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
onClick={() => setMobileMenuOpen(true)}
|
||||
>
|
||||
<span className="sr-only">Open main menu</span>
|
||||
<Bars3Icon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</nav>
|
||||
<Dialog as="div" className="lg:hidden" open={mobileMenuOpen} onClose={setMobileMenuOpen}>
|
||||
<div className="fixed inset-0 z-10" />
|
||||
<Dialog.Panel className="fixed inset-y-0 right-0 z-10 w-full overflow-y-auto bg-white px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-900/10">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link to="#" className="-m-1.5 p-1.5">
|
||||
<span className="sr-only">Mycelium</span>
|
||||
<img
|
||||
className="h-8 w-auto"
|
||||
src={pmyceliumLogo}
|
||||
alt=""
|
||||
/>
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
className="-m-2.5 rounded-md p-2.5 text-gray-700 hover:text-cyan-500 transition-colors"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<span className="sr-only">Close menu</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-6 flow-root">
|
||||
<div className="-my-6 divide-y divide-gray-500/10">
|
||||
<div className="space-y-2 py-6">
|
||||
{cloudNavItems.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.href}
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
to="/network"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
>
|
||||
Network
|
||||
</Link>
|
||||
<Link
|
||||
to="/agents"
|
||||
className="-mx-3 block rounded-lg px-3 py-2 text-base font-semibold leading-7 text-gray-900 hover:bg-gray-50"
|
||||
>
|
||||
Agents
|
||||
</Link>
|
||||
</div>
|
||||
<div className="py-6">
|
||||
<Button
|
||||
to="https://myceliumcloud.tf"
|
||||
variant="outline"
|
||||
as="a"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full"
|
||||
>
|
||||
Start Deployment
|
||||
</Button>
|
||||
<Button to="/download" variant="solid" color="cyan" className="mt-4 w-full">
|
||||
Get Mycelium Connector
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ import { Header } from './Header'
|
||||
export function Layout() {
|
||||
|
||||
return (
|
||||
<div className="bg-white antialiased" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||
<div className="bg-[#fdfdfd] antialiased relative" style={{ fontFamily: 'var(--font-inter)' }}>
|
||||
<div className="relative z-10">
|
||||
<Header />
|
||||
<main className="">
|
||||
<main>
|
||||
<Outlet />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const colorVariants = {
|
||||
secondary: 'text-gray-600',
|
||||
light: 'text-gray-50',
|
||||
accent: 'text-cyan-500',
|
||||
cyan: 'text-cyan-50',
|
||||
white: 'text-white',
|
||||
dark: 'text-gray-950',
|
||||
tertiary: 'text-gray-700',
|
||||
@@ -33,7 +34,8 @@ type PolymorphicProps<E extends React.ElementType, P> = P & {
|
||||
|
||||
const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
defaultElement: DefaultElement,
|
||||
defaultClassName: string
|
||||
defaultClassName: string,
|
||||
defaultProps: Omit<TextOwnProps, 'className' | 'children'> = {}
|
||||
) => {
|
||||
type Props<E extends React.ElementType = DefaultElement> = PolymorphicProps<
|
||||
E,
|
||||
@@ -41,9 +43,9 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
>
|
||||
|
||||
function Text<E extends React.ElementType = DefaultElement>({
|
||||
font = 'sans',
|
||||
font = defaultProps.font || 'sans',
|
||||
as,
|
||||
color = 'primary',
|
||||
color = defaultProps.color || 'primary',
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
@@ -72,7 +74,7 @@ const createTextComponent = <DefaultElement extends React.ElementType>(
|
||||
// Exports based on your tailwind.css and the example
|
||||
export const H1 = createTextComponent(
|
||||
'h1',
|
||||
'text-6xl lg:text-7xl font-medium leading-tight tracking-tight'
|
||||
'text-5xl lg:text-7xl font-medium leading-tight tracking-tight'
|
||||
)
|
||||
export const H2 = createTextComponent(
|
||||
'h2',
|
||||
@@ -88,7 +90,7 @@ export const H4 = createTextComponent(
|
||||
)
|
||||
export const P = createTextComponent(
|
||||
'p',
|
||||
'text-base lg:text-lg leading-relaxed'
|
||||
'text-base lg:text-xl leading-relaxed'
|
||||
)
|
||||
export const Small = createTextComponent(
|
||||
'small',
|
||||
@@ -100,11 +102,12 @@ export const Subtle = createTextComponent(
|
||||
)
|
||||
export const H5 = createTextComponent(
|
||||
'h5',
|
||||
'text-xl lg:text-2xl font-light leading-snug tracking-normal'
|
||||
'text-xl lg:text-2xl font-light lg:leading-snug leading-[0.85] tracking-normal'
|
||||
)
|
||||
export const Eyebrow = createTextComponent(
|
||||
'h2',
|
||||
'text-base/7 font-semibold tracking-wide'
|
||||
'text-base/7 font-semibold tracking-[0.18em] uppercase',
|
||||
{ color: 'accent' }
|
||||
)
|
||||
export const SectionHeader = createTextComponent(
|
||||
'p',
|
||||
@@ -128,7 +131,7 @@ export const FeatureTitle = createTextComponent(
|
||||
)
|
||||
export const FeatureDescription = createTextComponent(
|
||||
'p',
|
||||
'text-sm leading-normal tracking-normal'
|
||||
'lg:text-base text-sm leading-normal tracking-normal'
|
||||
)
|
||||
export const MobileFeatureTitle = createTextComponent(
|
||||
'h3',
|
||||
@@ -160,4 +163,4 @@ export const DownloadCardDescription = createTextComponent(
|
||||
)
|
||||
|
||||
export const CT = createTextComponent('span', 'text-lg lg:text-xl font-semibold')
|
||||
export const CP = createTextComponent('p', 'text-sm lg:text-sm tracking-wide leading-[1.525] font-light')
|
||||
export const CP = createTextComponent('p', 'text-sm lg:text-base tracking-wide leading-tight font-light')
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function FeaturesSectionDemo() {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 relative z-10 py-10 max-w-7xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 relative z-10 py-8 max-w-7xl mx-auto">
|
||||
{features.map((feature, index) => (
|
||||
<Feature key={feature.title} {...feature} index={index} />
|
||||
))}
|
||||
@@ -81,7 +81,7 @@ const Feature = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col lg:border-r py-10 relative group/feature dark:border-neutral-800",
|
||||
"flex flex-col lg:border-r py-8 relative group/feature dark:border-neutral-800",
|
||||
(index === 0 || index === 4) && "lg:border-l dark:border-neutral-800",
|
||||
index < 4 && "lg:border-b dark:border-neutral-800"
|
||||
)}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function FeaturesSectionDemo() {
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="relative z-20 py-10 lg:py-40 max-w-7xl mx-auto">
|
||||
<div className="relative z-20 py-8 lg:py-40 max-w-7xl mx-auto">
|
||||
<div className="px-8">
|
||||
<h4 className="text-3xl lg:text-5xl lg:leading-tight max-w-5xl mx-auto text-center tracking-tight font-medium text-black dark:text-white">
|
||||
Packed with thousands of features
|
||||
|
||||
@@ -26,7 +26,7 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
|
||||
<path
|
||||
fill={`url(#cube-gradient-${index})`}
|
||||
d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z"
|
||||
stroke="rgba(59, 130, 246, 0.25)"
|
||||
stroke="rgba(107, 114, 128, 0.3)"
|
||||
strokeWidth="0.5"
|
||||
/>
|
||||
<defs>
|
||||
@@ -79,8 +79,8 @@ export function CubeLight({
|
||||
<div
|
||||
className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${
|
||||
isActive
|
||||
? "bg-blue-400/40 opacity-70"
|
||||
: "bg-blue-200/20 opacity-40"
|
||||
? "bg-cyan-400/20 opacity-30"
|
||||
: "bg-cyan-200/20 opacity-40"
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -90,8 +90,8 @@ export function CubeLight({
|
||||
className="w-48 sm:w-64 lg:w-80 h-auto relative"
|
||||
style={{
|
||||
filter: isActive
|
||||
? "drop-shadow(0 0 25px rgba(59, 130, 246, 0.4)) brightness(1.1)"
|
||||
: "drop-shadow(0 0 10px rgba(59, 130, 246, 0.15)) brightness(1)",
|
||||
? "drop-shadow(0 0 15px rgba(34, 211, 238, 0.25)) brightness(1.05)"
|
||||
: "drop-shadow(0 0 10px rgba(34, 211, 238, 0.15)) brightness(1)",
|
||||
transition: "all 0.4s ease",
|
||||
}}
|
||||
/>
|
||||
@@ -99,10 +99,10 @@ export function CubeLight({
|
||||
{/* Title overlay */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<h3
|
||||
className="text-blue-900 text-sm lg:text-base font-medium text-center px-4"
|
||||
className="text-cyan-900 text-sm lg:text-base font-medium text-center px-4"
|
||||
style={{
|
||||
textShadow:
|
||||
"0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(59, 130, 246, 0.5)",
|
||||
"0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(34, 211, 238, 0.5)",
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
@@ -131,7 +131,7 @@ export function CubeLight({
|
||||
y1="1"
|
||||
x2="120"
|
||||
y2="1"
|
||||
stroke="rgba(59, 130, 246, 0.6)"
|
||||
stroke="rgba(34, 211, 238, 0.6)"
|
||||
strokeWidth="1"
|
||||
opacity="0.8"
|
||||
/>
|
||||
|
||||
@@ -35,8 +35,8 @@ const stackData = [
|
||||
];
|
||||
|
||||
export function StackedCubesLight() {
|
||||
const [active, setActive] = useState<string | null>("agent");
|
||||
const [selectedForMobile, setSelectedForMobile] = useState<string | null>("agent");
|
||||
const [active, setActive] = useState<string | null>("network");
|
||||
const [selectedForMobile, setSelectedForMobile] = useState<string | null>("network");
|
||||
|
||||
const handleCubeClick = (id: string) => {
|
||||
setSelectedForMobile((prev) => (prev === id ? null : id));
|
||||
@@ -49,10 +49,10 @@ export function StackedCubesLight() {
|
||||
return (
|
||||
<div className="flex flex-col items-center relative">
|
||||
{/* ✨ Ambient cyan-white gradient background */}
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-white via-cyan-50/40 to-white blur-3xl opacity-70 pointer-events-none" />
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-white via-cyan-50/30 to-white blur-3xl opacity-70 pointer-events-none" />
|
||||
<div
|
||||
className="relative w-full flex items-center justify-center lg:justify-center min-h-[450px] lg:min-h-[400px]"
|
||||
onMouseLeave={() => setActive("agent")}
|
||||
onMouseLeave={() => setActive("network")}
|
||||
>
|
||||
<motion.div
|
||||
className="relative lg:pl-0 pl-6 h-[300px] lg:h-[400px] w-64 sm:w-80 lg:w-96"
|
||||
@@ -77,8 +77,8 @@ export function StackedCubesLight() {
|
||||
<div
|
||||
className={`absolute inset-0 blur-2xl rounded-3xl transition-all duration-500 ${
|
||||
active === layer.id
|
||||
? "bg-cyan-300/40 opacity-70"
|
||||
: "bg-cyan-200/20 opacity-40"
|
||||
? "bg-cyan-300/20 opacity-20"
|
||||
: "bg-cyan-200/20 opacity-20"
|
||||
}`}
|
||||
/>
|
||||
<CubeLight
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { motion } from "motion/react";
|
||||
import { DarkCard } from "./cards";
|
||||
|
||||
interface CarouselProps {
|
||||
items: JSX.Element[];
|
||||
@@ -61,7 +62,7 @@ export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
<div
|
||||
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20"
|
||||
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-8 [scrollbar-width:none] md:py-20"
|
||||
ref={carouselRef}
|
||||
onScroll={checkScrollability}
|
||||
>
|
||||
@@ -73,7 +74,7 @@ export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-row justify-start gap-4 pl-4",
|
||||
"flex flex-row justify-start gap-6 pl-4",
|
||||
"mx-auto max-w-7xl", // remove max-w-4xl if you want the carousel to span the full width of its container
|
||||
)}
|
||||
>
|
||||
@@ -93,7 +94,7 @@ export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
|
||||
},
|
||||
}}
|
||||
key={"card" + index}
|
||||
className="rounded-3xl last:pr-[5%] md:last:pr-[33%]"
|
||||
className="rounded-3xl last:pr-[5%] md:last:pr-[33%] "
|
||||
>
|
||||
{item}
|
||||
</motion.div>
|
||||
@@ -127,42 +128,44 @@ export const Card = ({
|
||||
card: Card;
|
||||
layout?: boolean;
|
||||
}) => {
|
||||
|
||||
return (
|
||||
<Link to={card.link}>
|
||||
<DarkCard className="p-0 rounded-3xl">
|
||||
<motion.div
|
||||
layoutId={layout ? `card-${card.title}` : undefined}
|
||||
className="relative z-10 flex h-60 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl md:h-120 md:w-96 hover:scale-105 transition-transform duration-200"
|
||||
style={{
|
||||
backgroundImage: `url(${card.bg})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
className="relative z-10 flex h-104 w-80 flex-col justify-between overflow-hidden rounded-3xl p-8 md:h-120 md:w-96"
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent" />
|
||||
<div className="relative z-40 p-8 w-full">
|
||||
<div className="flex h-2/5 flex-col justify-center py-4 px-4">
|
||||
<motion.p
|
||||
layoutId={layout ? `category-${card.category}` : undefined}
|
||||
className="text-left font-sans text-sm font-medium text-white md:text-base"
|
||||
className="text-left font-sans font-semibold text-cyan-500 uppercase tracking-wider text-xs md:text-sm"
|
||||
>
|
||||
{card.category}
|
||||
</motion.p>
|
||||
<motion.p
|
||||
layoutId={layout ? `title-${card.title}` : undefined}
|
||||
className="mt-2 max-w-xs text-left font-sans text-xl font-semibold [text-wrap:balance] text-white md:text-3xl"
|
||||
className="mt-1 max-w-xs text-left font-sans text-xl font-semibold text-white [text-wrap:balance] lg:text-2xl"
|
||||
>
|
||||
{card.title}
|
||||
</motion.p>
|
||||
<div className="flex flex-row justify-between items-center w-full mt-4">
|
||||
<motion.p className="max-w-xs text-left font-sans text-sm text-neutral-300">
|
||||
<div className="mt-2 flex w-full flex-row items-center justify-between md:mt-4">
|
||||
<motion.p className="max-w-xs text-left font-sans lg:text-xl text-lg text-neutral-300 lg:leading-normal leading-tight">
|
||||
{card.description}
|
||||
</motion.p>
|
||||
<div className="h-8 w-8 bg-[#212121] rounded-full flex items-center justify-center text-[#858585] shrink-0 hover:bg-[#262626] hover:text-white active:bg-[#262626] active:text-white transition-colors duration-200">
|
||||
<IconChevronRight className="h-6 w-6" />
|
||||
<div className="flex h-6 w-6 ml-2 shrink-0 items-center justify-center rounded-full bg-[#212121] text-[#858585] transition-colors duration-200 hover:bg-[#262626] hover:text-white md:h-8 md:w-8">
|
||||
<IconChevronRight className="h-4 w-4 md:h-6 md:w-6 " />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex h-3/5 w-full items-end justify-end bg-transparent">
|
||||
<img
|
||||
src={card.src}
|
||||
alt={card.title}
|
||||
className="h-full w-full origin-bottom-right scale-75 object-contain mb-10"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</DarkCard>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
19
src/components/ui/cards.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const DarkCard = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"border border-white/10 bg-white/3 p-8 backdrop-blur-sm transition hover:-translate-y-1 hover:border-cyan-300/50 rounded-xl hover:bg-white/6 hover:scale-105 hover:shadow-lg hover:shadow-cyan-500/15",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DarkCard.displayName = "DarkCard";
|
||||
|
||||
export { DarkCard };
|
||||
@@ -33,7 +33,7 @@ export const LayoutTextFlip = ({
|
||||
|
||||
<motion.span
|
||||
layout
|
||||
className="relative w-fit overflow-hidden px-2 py-2 font-medium italic tracking-tight"
|
||||
className="relative w-fit overflow-hidden px-2 py-2 font-medium tracking-tight"
|
||||
>
|
||||
<AnimatePresence mode="popLayout">
|
||||
<motion.span
|
||||
|
||||