improvement
10
package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"framer-motion": "^12.23.23",
|
"framer-motion": "^12.23.23",
|
||||||
|
"lucide-react": "^0.545.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router-dom": "^7.9.4"
|
"react-router-dom": "^7.9.4"
|
||||||
@@ -3111,6 +3112,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.545.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.545.0.tgz",
|
||||||
|
"integrity": "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge2": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"framer-motion": "^12.23.23",
|
"framer-motion": "^12.23.23",
|
||||||
|
"lucide-react": "^0.545.0",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
"react-router-dom": "^7.9.4"
|
"react-router-dom": "^7.9.4"
|
||||||
|
BIN
public/images/ceo-kristof.png
Normal file
After Width: | Height: | Size: 491 KiB |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24" >
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 2.25A.75.75 0 0 1 9 3v.75h2.25V3a.75.75 0 0 1 1.5 0v.75H15V3a.75.75 0 0 1 1.5 0v.75h.75a3 3 0 0 1 3 3v.75H21A.75.75 0 0 1 21 9h-.75v2.25H21a.75.75 0 0 1 0 1.5h-.75V15H21a.75.75 0 0 1 0 1.5h-.75v.75a3 3 0 0 1-3 3h-.75V21a.75.75 0 0 1-1.5 0v-.75h-2.25V21a.75.75 0 0 1-1.5 0v-.75H9V21a.75.75 0 0 1-1.5 0v-.75h-.75a3 3 0 0 1-3-3v-.75H3A.75.75 0 0 1 3 15h.75v-2.25H3a.75.75 0 0 1 0-1.5h.75V9H3a.75.75 0 0 1 0-1.5h.75v-.75a3 3 0 0 1 3-3h.75V3a.75.75 0 0 1 .75-.75ZM6 6.75A.75.75 0 0 1 6.75 6h10.5a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75H6.75a.75.75 0 0 1-.75-.75V6.75Z" stroke="#FFFFFF" fill="none" stroke-width="1.5px"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 2.25A.75.75 0 0 1 9 3v.75h2.25V3a.75.75 0 0 1 1.5 0v.75H15V3a.75.75 0 0 1 1.5 0v.75h.75a3 3 0 0 1 3 3v.75H21A.75.75 0 0 1 21 9h-.75v2.25H21a.75.75 0 0 1 0 1.5h-.75V15H21a.75.75 0 0 1 0 1.5h-.75v.75a3 3 0 0 1-3 3h-.75V21a.75.75 0 0 1-1.5 0v-.75h-2.25V21a.75.75 0 0 1-1.5 0v-.75H9V21a.75.75 0 0 1-1.5 0v-.75h-.75a3 3 0 0 1-3-3v-.75H3A.75.75 0 0 1 3 15h.75v-2.25H3a.75.75 0 0 1 0-1.5h.75V9H3a.75.75 0 0 1 0-1.5h.75v-.75a3 3 0 0 1 3-3h.75V3a.75.75 0 0 1 .75-.75ZM6 6.75A.75.75 0 0 1 6.75 6h10.5a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75H6.75a.75.75 0 0 1-.75-.75V6.75Z" stroke="#4350d7" fill="none" stroke-width="1.5px"></path>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 957 B After Width: | Height: | Size: 928 B |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24" >
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.622 1.602a.75.75 0 0 1 .756 0l2.25 1.313a.75.75 0 0 1-.756 1.295L12 3.118 10.128 4.21a.75.75 0 1 1-.756-1.295l2.25-1.313ZM5.898 5.81a.75.75 0 0 1-.27 1.025l-1.14.665 1.14.665a.75.75 0 1 1-.756 1.295L3.75 8.806v.944a.75.75 0 0 1-1.5 0V7.5a.75.75 0 0 1 .372-.648l2.25-1.312a.75.75 0 0 1 1.026.27Zm12.204 0a.75.75 0 0 1 1.026-.27l2.25 1.312a.75.75 0 0 1 .372.648v2.25a.75.75 0 0 1-1.5 0v-.944l-1.122.654a.75.75 0 1 1-.756-1.295l1.14-.665-1.14-.665a.75.75 0 0 1-.27-1.025Zm-9 5.25a.75.75 0 0 1 1.026-.27L12 11.882l1.872-1.092a.75.75 0 1 1 .756 1.295l-1.878 1.096V15a.75.75 0 0 1-1.5 0v-1.82l-1.878-1.095a.75.75 0 0 1-.27-1.025ZM3 13.5a.75.75 0 0 1 .75.75v1.82l1.878 1.095a.75.75 0 1 1-.756 1.295l-2.25-1.312a.75.75 0 0 1-.372-.648v-2.25A.75.75 0 0 1 3 13.5Zm18 0a.75.75 0 0 1 .75.75v2.25a.75.75 0 0 1-.372.648l-2.25 1.312a.75.75 0 1 1-.756-1.295l1.878-1.096V14.25a.75.75 0 0 1 .75-.75Zm-9 5.25a.75.75 0 0 1 .75.75v.944l1.122-.654a.75.75 0 1 1 .756 1.295l-2.25 1.313a.75.75 0 0 1-.756 0l-2.25-1.313a.75.75 0 1 1 .756-1.295l1.122.654V19.5a.75.75 0 0 1 .75-.75Z" stroke="#FFFFFF" fill="none" stroke-width="1.5px"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M11.622 1.602a.75.75 0 0 1 .756 0l2.25 1.313a.75.75 0 0 1-.756 1.295L12 3.118 10.128 4.21a.75.75 0 1 1-.756-1.295l2.25-1.313ZM5.898 5.81a.75.75 0 0 1-.27 1.025l-1.14.665 1.14.665a.75.75 0 1 1-.756 1.295L3.75 8.806v.944a.75.75 0 0 1-1.5 0V7.5a.75.75 0 0 1 .372-.648l2.25-1.312a.75.75 0 0 1 1.026.27Zm12.204 0a.75.75 0 0 1 1.026-.27l2.25 1.312a.75.75 0 0 1 .372.648v2.25a.75.75 0 0 1-1.5 0v-.944l-1.122.654a.75.75 0 1 1-.756-1.295l1.14-.665-1.14-.665a.75.75 0 0 1-.27-1.025Zm-9 5.25a.75.75 0 0 1 1.026-.27L12 11.882l1.872-1.092a.75.75 0 1 1 .756 1.295l-1.878 1.096V15a.75.75 0 0 1-1.5 0v-1.82l-1.878-1.095a.75.75 0 0 1-.27-1.025ZM3 13.5a.75.75 0 0 1 .75.75v1.82l1.878 1.095a.75.75 0 1 1-.756 1.295l-2.25-1.312a.75.75 0 0 1-.372-.648v-2.25A.75.75 0 0 1 3 13.5Zm18 0a.75.75 0 0 1 .75.75v2.25a.75.75 0 0 1-.372.648l-2.25 1.312a.75.75 0 1 1-.756-1.295l1.878-1.096V14.25a.75.75 0 0 1 .75-.75Zm-9 5.25a.75.75 0 0 1 .75.75v.944l1.122-.654a.75.75 0 1 1 .756 1.295l-2.25 1.313a.75.75 0 0 1-.756 0l-2.25-1.313a.75.75 0 1 1 .756-1.295l1.122.654V19.5a.75.75 0 0 1 .75-.75Z" stroke="#4350d7" fill="none" stroke-width="1.5px"></path>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24" >
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 3.75a6 6 0 0 0-5.98 6.496A5.25 5.25 0 0 0 6.75 20.25H18a4.5 4.5 0 0 0 2.206-8.423 3.75 3.75 0 0 0-4.133-4.303A6.001 6.001 0 0 0 10.5 3.75Zm2.03 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v4.94a.75.75 0 0 0 1.5 0v-4.94l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" stroke="#FFFFFF" fill="none" stroke-width="1.5px"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 3.75a6 6 0 0 0-5.98 6.496A5.25 5.25 0 0 0 6.75 20.25H18a4.5 4.5 0 0 0 2.206-8.423 3.75 3.75 0 0 0-4.133-4.303A6.001 6.001 0 0 0 10.5 3.75Zm2.03 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v4.94a.75.75 0 0 0 1.5 0v-4.94l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" stroke="#4350d7" fill="none" stroke-width="1.5px"></path>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 636 B |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24" >
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 1.5a5.25 5.25 0 0 0-5.25 5.25v3a3 3 0 0 0-3 3v6.75a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3v-6.75a3 3 0 0 0-3-3v-3c0-2.9-2.35-5.25-5.25-5.25Zm3.75 8.25v-3a3.75 3.75 0 1 0-7.5 0v3h7.5Z" stroke="#FFFFFF" fill="none" stroke-width="1.5px"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 1.5a5.25 5.25 0 0 0-5.25 5.25v3a3 3 0 0 0-3 3v6.75a3 3 0 0 0 3 3h10.5a3 3 0 0 0 3-3v-6.75a3 3 0 0 0-3-3v-3c0-2.9-2.35-5.25-5.25-5.25Zm3.75 8.25v-3a3.75 3.75 0 1 0-7.5 0v3h7.5Z" stroke="#4350d7" fill="none" stroke-width="1.5px"></path>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 563 B After Width: | Height: | Size: 534 B |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24" >
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-brand-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" width="24" height="24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 2.25a.75.75 0 0 1 .75.75v.54l1.838-.46a9.75 9.75 0 0 1 6.725.738l.108.054A8.25 8.25 0 0 0 18 4.524l3.11-.732a.75.75 0 0 1 .917.81 47.784 47.784 0 0 0 .005 10.337.75.75 0 0 1-.574.812l-3.114.733a9.75 9.75 0 0 1-6.594-.77l-.108-.054a8.25 8.25 0 0 0-5.69-.625l-2.202.55V21a.75.75 0 0 1-1.5 0V3A.75.75 0 0 1 3 2.25Z" stroke="#FFFFFF" fill="none" stroke-width="1.5px"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M3 2.25a.75.75 0 0 1 .75.75v.54l1.838-.46a9.75 9.75 0 0 1 6.725.738l.108.054A8.25 8.25 0 0 0 18 4.524l3.11-.732a.75.75 0 0 1 .917.81 47.784 47.784 0 0 0 .005 10.337.75.75 0 0 1-.574.812l-3.114.733a9.75 9.75 0 0 1-6.594-.77l-.108-.054a8.25 8.25 0 0 0-5.69-.625l-2.202.55V21a.75.75 0 0 1-1.5 0V3A.75.75 0 0 1 3 2.25Z" stroke="#4350d7" fill="none" stroke-width="1.5px"></path>
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 698 B After Width: | Height: | Size: 669 B |
@@ -13,9 +13,10 @@ type FooterColumn = {
|
|||||||
|
|
||||||
const footerColumns: FooterColumn[] = [
|
const footerColumns: FooterColumn[] = [
|
||||||
{
|
{
|
||||||
title: 'Affiliate Projects',
|
title: 'GeoMind',
|
||||||
links: [
|
links: [
|
||||||
{ label: 'Project Mycelium', href: 'https://project.mycelium.tf', target: '_blank' },
|
{ label: 'Technology', href: '/technology' },
|
||||||
|
{ label: 'Use Cases', href: '/usecases' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -25,13 +26,6 @@ const footerColumns: FooterColumn[] = [
|
|||||||
{ label: 'Support', href: 'mailto:support@threefold.tech', target: '_blank' },
|
{ label: 'Support', href: 'mailto:support@threefold.tech', target: '_blank' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'GeoMind',
|
|
||||||
links: [
|
|
||||||
{ label: 'Technology', href: '/technology' },
|
|
||||||
{ label: 'Use Cases', href: '/usecases' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const Footer = () => {
|
export const Footer = () => {
|
||||||
@@ -59,7 +53,7 @@ export const Footer = () => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid flex-1 grid-cols-1 gap-8 text-sm sm:grid-cols-2 lg:grid-cols-3">
|
<div className="grid flex-1 grid-cols-1 gap-8 text-sm sm:grid-cols-2 lg:grid-cols-2 lg:ml-auto lg:max-w-md">
|
||||||
{footerColumns.map((column) => (
|
{footerColumns.map((column) => (
|
||||||
<div key={column.title}>
|
<div key={column.title}>
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-slate-400">
|
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-slate-400">
|
||||||
|
@@ -37,15 +37,12 @@ export const Header = () => {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="mx-auto flex max-w-6xl items-center justify-between px-6 py-4 lg:px-8">
|
<div className="mx-auto flex max-w-6xl items-center justify-between px-6 py-4 lg:px-8">
|
||||||
<Link to="/" className="flex items-center gap-2">
|
<Link to="/" className="flex items-center">
|
||||||
<img
|
<img
|
||||||
src="/images/geomind_logo.png"
|
src="/images/geomind_logo.png"
|
||||||
alt="Geomind logo"
|
alt="Geomind logo"
|
||||||
className="h-10 w-10 rounded-full object-contain shadow-subtle"
|
className="h-12 w-auto object-contain"
|
||||||
/>
|
/>
|
||||||
<span className="text-base font-semibold tracking-wider text-ink">
|
|
||||||
GEOMIND
|
|
||||||
</span>
|
|
||||||
</Link>
|
</Link>
|
||||||
<nav className="hidden items-center gap-8 md:flex">
|
<nav className="hidden items-center gap-8 md:flex">
|
||||||
{navItems.map(({ label, to }) => (
|
{navItems.map(({ label, to }) => (
|
||||||
@@ -64,7 +61,7 @@ export const Header = () => {
|
|||||||
))}
|
))}
|
||||||
<a
|
<a
|
||||||
href="mailto:support@threefold.tech"
|
href="mailto:support@threefold.tech"
|
||||||
className="rounded-full border border-brand-200 bg-white px-4 py-2 text-sm font-semibold text-brand-700 shadow-subtle transition-all duration-300 hover:-translate-y-0.5 hover:bg-brand-600 hover:text-white"
|
className="rounded-full border border-brand-200 bg-white px-4 py-2 text-sm font-semibold text-brand-700 shadow-subtle transition-all duration-300 hover:-translate-y-0.5 hover:border-brand-700 hover:bg-brand-700 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2"
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
</a>
|
</a>
|
||||||
@@ -123,7 +120,7 @@ export const Header = () => {
|
|||||||
))}
|
))}
|
||||||
<a
|
<a
|
||||||
href="mailto:support@threefold.tech"
|
href="mailto:support@threefold.tech"
|
||||||
className="rounded-full border border-brand-200 bg-brand-600 px-4 py-2 text-center text-sm font-semibold text-white shadow-subtle"
|
className="rounded-full border border-brand-200 bg-brand-600 px-4 py-2 text-center text-sm font-semibold text-white shadow-subtle transition-colors duration-300 hover:bg-brand-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2"
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
</a>
|
</a>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { Footer } from './Footer';
|
import { Footer } from './Footer';
|
||||||
|
import { ScrollToTop } from './ScrollToTop';
|
||||||
|
|
||||||
type LayoutProps = {
|
type LayoutProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -8,9 +9,15 @@ type LayoutProps = {
|
|||||||
|
|
||||||
export const Layout = ({ children }: LayoutProps) => {
|
export const Layout = ({ children }: LayoutProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-b from-white via-mist to-white">
|
<div className="relative min-h-screen overflow-hidden bg-gradient-to-br from-slate-50 via-white to-brand-50/50">
|
||||||
|
<ScrollToTop />
|
||||||
|
{/* Decorative gradient blurs */}
|
||||||
|
<div className="pointer-events-none fixed -top-32 left-6 h-80 w-80 rounded-full bg-brand-200/40 blur-3xl lg:left-24" />
|
||||||
|
<div className="pointer-events-none fixed top-1/3 right-10 h-96 w-96 rounded-full bg-indigo-200/30 blur-3xl lg:right-24" />
|
||||||
|
<div className="pointer-events-none fixed bottom-1/4 left-1/4 h-72 w-72 rounded-full bg-brand-200/30 blur-3xl" />
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
<main className="mx-auto max-w-6xl px-6 pb-24 pt-28 lg:px-8 lg:pt-32">{children}</main>
|
<main className="relative z-10 mx-auto max-w-6xl px-6 pb-24 pt-28 lg:px-8 lg:pt-32">{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
16
src/components/layout/ScrollToTop.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
export const ScrollToTop = () => {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const animationFrame = window.requestAnimationFrame(() => {
|
||||||
|
window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => window.cancelAnimationFrame(animationFrame);
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
@@ -16,11 +16,11 @@ const styles: Record<
|
|||||||
string
|
string
|
||||||
> = {
|
> = {
|
||||||
solid:
|
solid:
|
||||||
'bg-brand-600 text-white shadow-subtle hover:bg-brand-500 hover:-translate-y-0.5',
|
'bg-brand-600 text-white shadow-subtle hover:bg-brand-500 hover:text-white hover:shadow-lg',
|
||||||
outline:
|
outline:
|
||||||
'border border-brand-200 bg-white text-brand-700 hover:border-brand-400 hover:-translate-y-0.5',
|
'border border-brand-200 bg-white text-brand-700 hover:border-brand-400 hover:bg-white/95 hover:text-brand-700 hover:shadow-md',
|
||||||
ghost:
|
ghost:
|
||||||
'bg-transparent text-brand-600 hover:text-brand-500',
|
'bg-transparent text-brand-600 hover:bg-brand-50/80 hover:text-brand-700',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PrimaryButton = ({
|
export const PrimaryButton = ({
|
||||||
@@ -32,7 +32,7 @@ export const PrimaryButton = ({
|
|||||||
target,
|
target,
|
||||||
}: PrimaryButtonProps) => {
|
}: PrimaryButtonProps) => {
|
||||||
const baseClasses =
|
const baseClasses =
|
||||||
'inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold transition-all duration-300 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2';
|
'inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold transition-all duration-300 hover:-translate-y-0.5 active:translate-y-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2';
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
return (
|
return (
|
||||||
|
@@ -1,44 +1,59 @@
|
|||||||
import { motion } from 'framer-motion';
|
|
||||||
|
|
||||||
export const MissionVision = () => {
|
export const MissionVision = () => {
|
||||||
return (
|
return (
|
||||||
<section className="py-16 lg:py-24">
|
<section className="relative py-20 lg:py-28">
|
||||||
<div className="relative overflow-hidden rounded-3xl border border-slate-100 bg-white/80 p-8 shadow-subtle backdrop-blur lg:p-16">
|
<div className="relative mx-auto max-w-6xl px-6 lg:px-12">
|
||||||
<div className="pointer-events-none absolute -top-36 right-10 h-72 w-72 rounded-full bg-brand-100 opacity-50 blur-3xl" />
|
<div className="flex flex-col gap-12 rounded-[32px] bg-gradient-to-br from-white via-white to-brand-50/40 p-8 shadow-xl ring-1 ring-brand-100/40 sm:p-12 lg:grid lg:grid-cols-[minmax(0,0.9fr)_1fr] lg:items-center">
|
||||||
<motion.div
|
<div className="order-1 lg:order-none">
|
||||||
initial={{ opacity: 0, y: 24 }}
|
<figure className="relative mx-auto max-w-sm overflow-hidden rounded-[28px] bg-gradient-to-br from-emerald-100/50 via-white to-brand-100/40 p-6 shadow-lg ring-1 ring-brand-200/40">
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
<div className="absolute inset-0 rounded-[24px] bg-[radial-gradient(circle_at_20%_20%,rgba(56,189,248,0.22),transparent_55%),radial-gradient(circle_at_80%_40%,rgba(59,130,246,0.18),transparent_60%)]" />
|
||||||
viewport={{ once: true, amount: 0.35 }}
|
<img
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
src="/images/ceo-kristof.png"
|
||||||
className="relative z-10 space-y-12 lg:grid lg:grid-cols-2 lg:gap-16 lg:space-y-0"
|
alt="Kristof De Spiegeleer, Founder and CEO of GeoMind"
|
||||||
|
className="relative z-10 mx-auto w-full max-w-xs object-cover"
|
||||||
|
/>
|
||||||
|
<figcaption className="relative z-10 mt-6 text-center text-sm font-medium text-slate-500">
|
||||||
|
Kristof De Spiegeleer — Founder & CEO
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex flex-col gap-8 rounded-3xl bg-white/90 p-8 shadow-inner ring-1 ring-brand-100/50 backdrop-blur">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="h-px flex-1 bg-gradient-to-r from-brand-500/50 via-brand-300/50 to-transparent" />
|
||||||
|
<span className="text-xs font-semibold uppercase tracking-[0.35em] text-brand-600">
|
||||||
|
Technology with Purpose
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="float-left mr-3 -mt-3 text-6xl font-serif text-brand-500/25 leading-none"
|
||||||
>
|
>
|
||||||
<div>
|
“
|
||||||
<h2 className="text-xs font-semibold uppercase tracking-[0.35em] text-brand-600">
|
</span>
|
||||||
Our Mission
|
<div className="space-y-6 text-base leading-8 text-slate-600 sm:text-lg sm:leading-8">
|
||||||
</h2>
|
<p className="italic text-brand-700">
|
||||||
<h3 className="mt-4 text-3xl font-semibold text-ink sm:text-4xl">
|
When we first started, our goal was simple, to build the foundation for the world’s digital future.
|
||||||
Building Inclusive Digital Infrastructure as a Fundamental Human Right
|
Over time, we realized that technology isn’t just about performance or scale, it’s about purpose.
|
||||||
</h3>
|
It’s about people, communities, and the planet we share.
|
||||||
<p className="mt-6 text-base text-slate-600 sm:text-lg">
|
</p>
|
||||||
GeoMind is committed to placing people and the planet at the forefront of our mission.
|
<p>
|
||||||
We believe that access to computing resources is a basic human right. By integrating
|
Today, we’re creating the next generation of datacenters, designed not only for the AI era but for a
|
||||||
sustainable practices and innovative technologies, we aim to create an inclusive digital
|
sustainable, inclusive future. Our mission is clear: to make digital decentralized infrastructure a
|
||||||
infrastructure that empowers communities and fosters global equity.
|
universal right, accessible and responsible in equal measure.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
We’ve spent decades pioneering technologies that power the internet. Now, we’re redefining what
|
||||||
|
datacenters stand for, combining efficiency, sovereignty, and sustainability to serve both humanity and
|
||||||
|
innovation.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-between text-right">
|
|
||||||
<h2 className="text-xs font-semibold uppercase tracking-[0.35em] text-brand-600">
|
|
||||||
Our Journey
|
|
||||||
</h2>
|
|
||||||
<p className="mt-4 text-base text-slate-600 sm:text-lg">
|
|
||||||
We began by building the backbone of modern digital infrastructure. Over the years, we've
|
|
||||||
pioneered cloud computing, advanced storage systems, and scalable, secure datacenter
|
|
||||||
technologies. We're now creating the next generation of datacenters, a new standard built
|
|
||||||
for the demands of the AI era, offering efficiency, sovereignty, and profitability that
|
|
||||||
traditional datacenters simply can't deliver.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
<div className="border-t border-slate-200 pt-6 text-sm">
|
||||||
|
<p className="font-semibold text-brand-700">Kristof De Spiegeleer</p>
|
||||||
|
<p className="text-slate-500">Founder & CEO, GeoMind</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
@@ -3,22 +3,27 @@ import { PrimaryButton } from '../../../components/ui/PrimaryButton';
|
|||||||
|
|
||||||
export const FinalCallToAction = () => {
|
export const FinalCallToAction = () => {
|
||||||
return (
|
return (
|
||||||
<section className="pt-16 lg:pt-24">
|
<section className="py-20 lg:py-32">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scale: 0.98 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, scale: 1 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.3 }}
|
viewport={{ once: true, amount: 0.3 }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.7, ease: 'easeOut' }}
|
||||||
className="rounded-3xl border border-brand-100 bg-white/80 p-10 text-center shadow-subtle backdrop-blur lg:p-16"
|
className="mx-auto max-w-4xl text-center"
|
||||||
>
|
>
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.45em] text-brand-500">
|
<h2 className="text-3xl font-semibold text-ink sm:text-4xl lg:text-5xl">
|
||||||
GeoMind
|
{' '}
|
||||||
</p>
|
<span className="bg-gradient-to-r from-brand-500 via-brand-600 to-brand-700 bg-clip-text text-transparent">
|
||||||
<h2 className="mt-4 text-3xl font-semibold text-ink sm:text-4xl">
|
The Datacenter Standard
|
||||||
The datacenter standard for the next era of cloud and AI.
|
</span>{' '}
|
||||||
|
<br />
|
||||||
|
for the next era of Cloud and AI.
|
||||||
</h2>
|
</h2>
|
||||||
<div className="mt-8 flex justify-center">
|
<p className="mt-6 text-lg leading-relaxed text-ink/70 sm:text-xl">
|
||||||
<PrimaryButton to="/technology">Discover Technology</PrimaryButton>
|
For years we've been pioneering infrastructure technologies that power the world's most demanding cloud workloads. Learn more about our team and expertise behind our mission.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex justify-center">
|
||||||
|
<PrimaryButton to="/about">About Us</PrimaryButton>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
|
@@ -16,21 +16,13 @@ export const HomeHero = () => {
|
|||||||
</video>
|
</video>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#0f172a]/70 via-[#1e1b4b]/60 to-[#312e81]/80" />
|
<div className="absolute inset-0 bg-gradient-to-br from-[#0f172a]/70 via-[#1e1b4b]/60 to-[#312e81]/80" />
|
||||||
<div className="relative z-10 px-6 py-20 sm:px-10 lg:px-16">
|
<div className="relative z-10 px-6 py-20 sm:px-10 lg:px-16">
|
||||||
<motion.p
|
|
||||||
initial={{ opacity: 0, y: 10 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ delay: 0.1, duration: 0.6 }}
|
|
||||||
className="text-xs font-semibold uppercase tracking-[0.4em] text-white/70"
|
|
||||||
>
|
|
||||||
The Planet's Sovereign Agentic Cloud
|
|
||||||
</motion.p>
|
|
||||||
<motion.h1
|
<motion.h1
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.2, duration: 0.6 }}
|
transition={{ delay: 0.2, duration: 0.6 }}
|
||||||
className="mt-6 text-4xl font-semibold leading-tight sm:text-5xl lg:text-6xl"
|
className="text-3xl font-semibold leading-tight sm:text-4xl lg:text-5xl"
|
||||||
>
|
>
|
||||||
GeoMind
|
The planet's sovereign agentic cloud
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
@@ -52,7 +44,7 @@ export const HomeHero = () => {
|
|||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
to="/usecases"
|
to="/usecases"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="border border-white/40 text-white hover:bg-white/15"
|
className="border border-white/40 text-white hover:bg-white hover:text-brand-600"
|
||||||
>
|
>
|
||||||
Use Cases
|
Use Cases
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
|
@@ -1,23 +1,110 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
import { PrimaryButton } from '../../../components/ui/PrimaryButton';
|
import { PrimaryButton } from '../../../components/ui/PrimaryButton';
|
||||||
|
|
||||||
export const ImpactBanner = () => {
|
export const ImpactBanner = () => {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
// Set canvas size
|
||||||
|
const resizeCanvas = () => {
|
||||||
|
canvas.width = canvas.offsetWidth;
|
||||||
|
canvas.height = canvas.offsetHeight;
|
||||||
|
};
|
||||||
|
resizeCanvas();
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
|
||||||
|
// Grid configuration
|
||||||
|
const gridSpacing = 40;
|
||||||
|
const dotRadius = 2;
|
||||||
|
const glowRadius = 8;
|
||||||
|
const dots: Array<{
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
baseX: number;
|
||||||
|
baseY: number;
|
||||||
|
phase: number;
|
||||||
|
speed: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
// Create grid of dots
|
||||||
|
for (let x = 0; x < canvas.width; x += gridSpacing) {
|
||||||
|
for (let y = 0; y < canvas.height; y += gridSpacing) {
|
||||||
|
dots.push({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
baseX: x,
|
||||||
|
baseY: y,
|
||||||
|
phase: Math.random() * Math.PI * 2,
|
||||||
|
speed: 0.5 + Math.random() * 0.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let animationFrameId: number;
|
||||||
|
let time = 0;
|
||||||
|
|
||||||
|
const animate = () => {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
time += 0.01;
|
||||||
|
|
||||||
|
dots.forEach((dot) => {
|
||||||
|
// Subtle floating animation
|
||||||
|
const offsetX = Math.sin(time * dot.speed + dot.phase) * 3;
|
||||||
|
const offsetY = Math.cos(time * dot.speed * 0.8 + dot.phase) * 3;
|
||||||
|
dot.x = dot.baseX + offsetX;
|
||||||
|
dot.y = dot.baseY + offsetY;
|
||||||
|
|
||||||
|
// Pulsing opacity
|
||||||
|
const opacity = 0.15 + Math.sin(time * dot.speed + dot.phase) * 0.1;
|
||||||
|
|
||||||
|
// Draw glow
|
||||||
|
const gradient = ctx.createRadialGradient(dot.x, dot.y, 0, dot.x, dot.y, glowRadius);
|
||||||
|
gradient.addColorStop(0, `rgba(99, 102, 241, ${opacity * 0.3})`);
|
||||||
|
gradient.addColorStop(1, 'rgba(99, 102, 241, 0)');
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.fillRect(dot.x - glowRadius, dot.y - glowRadius, glowRadius * 2, glowRadius * 2);
|
||||||
|
|
||||||
|
// Draw dot
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(dot.x, dot.y, dotRadius, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = `rgba(99, 102, 241, ${opacity + 0.2})`;
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
animate();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', resizeCanvas);
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative overflow-hidden rounded-3xl bg-white shadow-subtle">
|
<section className="relative overflow-hidden py-16 text-center lg:py-20">
|
||||||
<div className="absolute inset-0 -z-10">
|
{/* Animated background canvas */}
|
||||||
<img
|
<canvas
|
||||||
src="/images/mesh2.gif"
|
ref={canvasRef}
|
||||||
alt=""
|
className="absolute inset-0 h-full w-full"
|
||||||
className="h-full w-full object-cover opacity-50"
|
style={{ opacity: 0.6 }}
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-white via-white/80 to-white/40 backdrop-blur" />
|
|
||||||
</div>
|
{/* Content */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 28 }}
|
initial={{ opacity: 0, y: 28 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.3 }}
|
viewport={{ once: true, amount: 0.3 }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.6, ease: 'easeOut' }}
|
||||||
className="px-6 py-12 text-center sm:px-10 lg:px-16 lg:py-16"
|
className="relative z-10 px-6 sm:px-10"
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl font-semibold text-ink sm:text-4xl">Designed for Real-World Impact</h2>
|
<h2 className="text-3xl font-semibold text-ink sm:text-4xl">Designed for Real-World Impact</h2>
|
||||||
<p className="mx-auto mt-5 max-w-3xl text-base text-slate-600 sm:text-lg">
|
<p className="mx-auto mt-5 max-w-3xl text-base text-slate-600 sm:text-lg">
|
||||||
|
@@ -29,30 +29,24 @@ const pillars = [
|
|||||||
|
|
||||||
export const WhyGeomind = () => {
|
export const WhyGeomind = () => {
|
||||||
return (
|
return (
|
||||||
<section className="relative overflow-hidden rounded-3xl">
|
<section className="relative px-6 py-16 sm:px-10 lg:px-16 lg:py-24">
|
||||||
<img
|
<div className="pointer-events-none absolute -left-32 top-0 h-56 w-56 rounded-full bg-brand-100 opacity-60 blur-3xl lg:-left-24" />
|
||||||
src="/images/hometech.jpg"
|
<div className="pointer-events-none absolute -right-24 bottom-0 h-64 w-64 rounded-full bg-brand-200 opacity-50 blur-3xl" />
|
||||||
alt=""
|
|
||||||
className="absolute inset-0 h-full w-full object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#0f172a]/80 via-[#111827]/85 to-[#020617]/80" />
|
|
||||||
<div className="relative z-10 px-6 py-16 text-white sm:px-10 lg:px-16 lg:py-24">
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 24 }}
|
initial={{ opacity: 0, y: 24 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.3 }}
|
viewport={{ once: true, amount: 0.3 }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.6, ease: 'easeOut' }}
|
||||||
className="max-w-3xl"
|
className="relative z-10 max-w-3xl"
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl font-semibold sm:text-4xl">Why GeoMind</h2>
|
<h2 className="text-3xl font-semibold text-ink sm:text-4xl">Why GeoMind</h2>
|
||||||
<p className="mt-5 text-base text-white/70 sm:text-lg">
|
<p className="mt-5 text-base text-slate-600 sm:text-lg">
|
||||||
Traditional datacenters are increasingly limited by geopolitics, inefficiency, and
|
Traditional datacenters are increasingly limited by geopolitics, inefficiency, and energy
|
||||||
energy waste. GeoMind eliminates those constraints with a resilient, autonomous
|
waste. GeoMind eliminates those constraints with a resilient, autonomous infrastructure that
|
||||||
infrastructure that delivers planetary scalability and sovereign performance anywhere
|
delivers planetary scalability and sovereign performance anywhere in the world.
|
||||||
in the world.
|
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className="mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
<div className="relative z-10 mt-12 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{pillars.map((pillar, index) => (
|
{pillars.map((pillar, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={pillar.title}
|
key={pillar.title}
|
||||||
@@ -60,10 +54,10 @@ export const WhyGeomind = () => {
|
|||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.25 }}
|
viewport={{ once: true, amount: 0.25 }}
|
||||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||||
className="rounded-3xl bg-white/10 p-6 backdrop-blur-lg"
|
className="rounded-3xl border border-slate-100 bg-white/90 p-6 shadow-subtle"
|
||||||
>
|
>
|
||||||
<h3 className="text-lg font-semibold text-white">{pillar.title}</h3>
|
<h3 className="text-lg font-semibold text-ink">{pillar.title}</h3>
|
||||||
<ul className="mt-4 space-y-3 text-sm text-white/70">
|
<ul className="mt-4 space-y-3 text-sm text-slate-600">
|
||||||
{pillar.points.map((point) => (
|
{pillar.points.map((point) => (
|
||||||
<li key={point} className="flex gap-3">
|
<li key={point} className="flex gap-3">
|
||||||
<span className="mt-1 inline-block h-1.5 w-1.5 flex-shrink-0 rounded-full bg-brand-300" />
|
<span className="mt-1 inline-block h-1.5 w-1.5 flex-shrink-0 rounded-full bg-brand-300" />
|
||||||
@@ -74,7 +68,6 @@ export const WhyGeomind = () => {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,30 +1,31 @@
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
|
import { ShieldCheck, Globe, Leaf, LineChart, Layers } from 'lucide-react';
|
||||||
|
|
||||||
const benefits = [
|
const benefits = [
|
||||||
{
|
{
|
||||||
title: 'Ultra-Secure',
|
title: 'Ultra-Secure',
|
||||||
description: 'Protect sensitive workloads with end-to-end security.',
|
description: 'Protect sensitive workloads with end-to-end security.',
|
||||||
icon: '/images/performance.svg',
|
icon: ShieldCheck,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Scales Globally',
|
title: 'Scales Globally',
|
||||||
description: 'Supports workloads anywhere, from local nodes to planetary scale.',
|
description: 'Supports workloads anywhere, from local nodes to planetary scale.',
|
||||||
icon: '/images/security.svg',
|
icon: Globe,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Energy Efficient',
|
title: 'Energy Efficient',
|
||||||
description: 'Up to 10x less energy for specific workloads.',
|
description: 'Up to 10x less energy for specific workloads.',
|
||||||
icon: '/images/sovereignty.svg',
|
icon: Leaf,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Profitable',
|
title: 'Profitable',
|
||||||
description: 'Monetize idle capacity; achieve ROI up to 3x higher.',
|
description: 'Monetize idle capacity; achieve ROI up to 3x higher.',
|
||||||
icon: '/images/computing.svg',
|
icon: LineChart,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Flexible',
|
title: 'Flexible',
|
||||||
description: 'Compatible with existing infrastructure and hyperscaler requirements.',
|
description: 'Compatible with existing infrastructure and hyperscaler requirements.',
|
||||||
icon: '/images/decentralized.svg',
|
icon: Layers,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ export const TechnologicalBenefits = () => {
|
|||||||
className="flex h-full flex-col rounded-3xl border border-slate-100 bg-white/80 p-6 shadow-subtle backdrop-blur"
|
className="flex h-full flex-col rounded-3xl border border-slate-100 bg-white/80 p-6 shadow-subtle backdrop-blur"
|
||||||
>
|
>
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-50">
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-50">
|
||||||
<img src={benefit.icon} alt="" className="h-7 w-7" />
|
<benefit.icon className="h-7 w-7 text-brand-600" />
|
||||||
</div>
|
</div>
|
||||||
<h3 className="mt-6 text-lg font-semibold text-ink">{benefit.title}</h3>
|
<h3 className="mt-6 text-lg font-semibold text-ink">{benefit.title}</h3>
|
||||||
<p className="mt-3 text-sm leading-6 text-slate-600">{benefit.description}</p>
|
<p className="mt-3 text-sm leading-6 text-slate-600">{benefit.description}</p>
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
|
|
||||||
type Bullet = {
|
type Bullet = {
|
||||||
@@ -8,28 +8,52 @@ type Bullet = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Tab = {
|
type Tab = {
|
||||||
id: 'compute' | 'data' | 'network';
|
id: 'zero-os' | 'compute' | 'data' | 'network';
|
||||||
label: string;
|
label: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
bullets: Bullet[];
|
bullets: Bullet[];
|
||||||
|
cardText: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tabs: Tab[] = [
|
const tabs: Tab[] = [
|
||||||
|
{
|
||||||
|
id: 'zero-os',
|
||||||
|
label: 'Zero-OS',
|
||||||
|
title: 'Zero-OS',
|
||||||
|
description:
|
||||||
|
'Minimal, self-healing bare-metal OS that powers autonomous nodes across the ThreeFold Grid.',
|
||||||
|
cardText: 'Stateless architecture ensures every boot is secure and unmodified, eliminating configuration drift and security vulnerabilities.',
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
heading: 'Immutable by Design',
|
||||||
|
body: 'Boots statelessly from a signed image each time, removing drift and shrinking the attack surface.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Autonomous Operations',
|
||||||
|
body: 'Digital twin controllers redeploy workloads to healthy nodes and roll out updates with zero downtime.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Zero-Trust Networking',
|
||||||
|
body: 'Integrated WireGuard overlay and cryptographic identities secure every peer-to-peer connection.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: 'Unified Resource Fabric',
|
||||||
|
body: 'Exposes compute, storage, and network primitives through a single API for edge, datacenter, or hybrid deployments.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'compute',
|
id: 'compute',
|
||||||
label: 'Compute',
|
label: 'Compute',
|
||||||
title: 'Compute',
|
title: 'Compute',
|
||||||
description: 'A self-healing compute fabric designed for resilience, decentralization, and scale.',
|
description: 'A self-healing compute fabric designed for resilience, decentralization, and scale.',
|
||||||
|
cardText: 'Autonomous workload orchestration across distributed nodes ensures maximum uptime and performance.',
|
||||||
bullets: [
|
bullets: [
|
||||||
{
|
{
|
||||||
heading: 'Autonomous Workload Management',
|
heading: 'Autonomous Workload Management',
|
||||||
body: 'Workloads automatically migrate to healthy nodes to ensure fault tolerance and high availability.',
|
body: 'Workloads automatically migrate to healthy nodes to ensure fault tolerance and high availability.',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
heading: 'P2P Compute Marketplace',
|
|
||||||
body: 'Individuals and enterprises can monetize spare compute and GPU resources through a decentralized network.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
heading: 'AI & Web3 Ready',
|
heading: 'AI & Web3 Ready',
|
||||||
body: 'Run LLMs, autonomous agents, blockchain nodes, and immersive metaverse apps at the edge.',
|
body: 'Run LLMs, autonomous agents, blockchain nodes, and immersive metaverse apps at the edge.',
|
||||||
@@ -54,6 +78,7 @@ const tabs: Tab[] = [
|
|||||||
label: 'Data',
|
label: 'Data',
|
||||||
title: 'Data',
|
title: 'Data',
|
||||||
description: 'Private, distributed, and AI-native storage with user sovereignty at its core.',
|
description: 'Private, distributed, and AI-native storage with user sovereignty at its core.',
|
||||||
|
cardText: 'Quantum-safe encryption and distributed redundancy protect your data while maintaining lightning-fast access.',
|
||||||
bullets: [
|
bullets: [
|
||||||
{
|
{
|
||||||
heading: 'Privacy-First',
|
heading: 'Privacy-First',
|
||||||
@@ -75,6 +100,7 @@ const tabs: Tab[] = [
|
|||||||
title: 'Network',
|
title: 'Network',
|
||||||
description:
|
description:
|
||||||
'A secure, peer-to-peer internet backbone, self-sustaining, censorship-resistant, and optimized for performance.',
|
'A secure, peer-to-peer internet backbone, self-sustaining, censorship-resistant, and optimized for performance.',
|
||||||
|
cardText: 'Mesh topology with intelligent routing creates a resilient network that adapts to changing conditions.',
|
||||||
bullets: [
|
bullets: [
|
||||||
{
|
{
|
||||||
heading: 'End-to-End Encryption',
|
heading: 'End-to-End Encryption',
|
||||||
@@ -96,10 +122,42 @@ const tabs: Tab[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Floating particle component
|
||||||
|
const FloatingParticle = ({ delay, index }: { delay: number; index: number }) => {
|
||||||
|
const startX = (index % 3) * 40 + 10;
|
||||||
|
const startY = Math.floor(index / 3) * 60 + 20;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="absolute h-3 w-3 rounded-full bg-blue-400"
|
||||||
|
style={{ left: `${startX}%`, top: `${startY}%` }}
|
||||||
|
initial={{ opacity: 0, scale: 0 }}
|
||||||
|
animate={{
|
||||||
|
y: [-10, -30, -10],
|
||||||
|
x: [0, Math.sin(index) * 15, 0],
|
||||||
|
opacity: [0, 0.7, 0],
|
||||||
|
scale: [0, 1.2, 0],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 3,
|
||||||
|
delay,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const TechnologyArchitecture = () => {
|
export const TechnologyArchitecture = () => {
|
||||||
const [activeTab, setActiveTab] = useState<Tab['id']>('compute');
|
const [activeTab, setActiveTab] = useState<Tab['id']>('zero-os');
|
||||||
|
const [particles, setParticles] = useState<number[]>([]);
|
||||||
const current = tabs.find((tab) => tab.id === activeTab) ?? tabs[0];
|
const current = tabs.find((tab) => tab.id === activeTab) ?? tabs[0];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Generate random particles on tab change
|
||||||
|
setParticles(Array.from({ length: 6 }, (_, i) => i));
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-16 lg:py-24">
|
<section className="py-16 lg:py-24">
|
||||||
<div className="mx-auto max-w-4xl text-center">
|
<div className="mx-auto max-w-4xl text-center">
|
||||||
@@ -110,17 +168,17 @@ export const TechnologyArchitecture = () => {
|
|||||||
planetary scale.
|
planetary scale.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-12 rounded-3xl border border-slate-200 bg-white/80 p-6 shadow-subtle backdrop-blur lg:p-10">
|
<div className="mt-12 lg:mt-16">
|
||||||
<div className="flex flex-wrap gap-3 border-b border-slate-200 pb-4">
|
<div className="flex flex-wrap gap-3 pb-4">
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<button
|
<button
|
||||||
key={tab.id}
|
key={tab.id}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setActiveTab(tab.id)}
|
onClick={() => setActiveTab(tab.id)}
|
||||||
className={`rounded-full px-4 py-2 text-sm font-semibold transition-all duration-300 ${
|
className={`rounded-full px-4 py-2 text-sm font-semibold transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-300 focus-visible:ring-offset-2 ${
|
||||||
activeTab === tab.id
|
activeTab === tab.id
|
||||||
? 'bg-brand-600 text-white shadow-subtle'
|
? 'bg-brand-600 text-white shadow-subtle hover:bg-brand-700'
|
||||||
: 'bg-white text-slate-500 hover:text-brand-600'
|
: 'border border-slate-200 bg-white text-slate-500 hover:border-brand-300 hover:bg-brand-50 hover:text-brand-700 hover:shadow-md'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{tab.label}
|
{tab.label}
|
||||||
@@ -159,15 +217,131 @@ export const TechnologyArchitecture = () => {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden h-full rounded-3xl border border-dashed border-brand-100 bg-brand-50/50 p-6 lg:flex lg:flex-col lg:items-start lg:justify-between">
|
<div className="hidden h-full rounded-3xl bg-brand-50/50 p-6 lg:flex lg:flex-col lg:items-start lg:justify-between">
|
||||||
<p className="text-sm font-semibold uppercase tracking-[0.35em] text-brand-500">
|
<motion.p
|
||||||
|
key={`label-${current.id}`}
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="text-sm font-semibold uppercase tracking-[0.35em] text-brand-500"
|
||||||
|
>
|
||||||
{current.label}
|
{current.label}
|
||||||
</p>
|
</motion.p>
|
||||||
<p className="mt-6 text-sm text-brand-700">
|
<motion.p
|
||||||
Intelligent agents orchestrate infrastructure layers to guarantee sovereignty and
|
key={`text-${current.id}`}
|
||||||
resilience while maintaining the elegance of a single control plane.
|
initial={{ opacity: 0 }}
|
||||||
</p>
|
animate={{ opacity: 1 }}
|
||||||
<div className="mt-6 h-40 w-full rounded-2xl bg-gradient-to-br from-brand-100 via-white to-brand-200 opacity-80" />
|
transition={{ duration: 0.5, delay: 0.2 }}
|
||||||
|
className="mt-6 text-sm text-brand-700"
|
||||||
|
>
|
||||||
|
{current.cardText}
|
||||||
|
</motion.p>
|
||||||
|
<div className="relative mt-6 h-40 w-full overflow-hidden rounded-2xl bg-gradient-to-br from-blue-50 via-indigo-50 to-purple-50">
|
||||||
|
{/* Animated gradient overlay */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute inset-0 bg-gradient-to-br from-blue-200/40 via-indigo-200/40 to-purple-200/40"
|
||||||
|
animate={{
|
||||||
|
opacity: [0.3, 0.6, 0.3],
|
||||||
|
scale: [1, 1.05, 1],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 4,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Floating particles */}
|
||||||
|
{particles.map((i) => (
|
||||||
|
<FloatingParticle key={`${current.id}-${i}`} delay={i * 0.4} index={i} />
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Animated circles */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute left-1/4 top-1/4 h-20 w-20 rounded-full bg-blue-300/30"
|
||||||
|
animate={{
|
||||||
|
scale: [1, 1.5, 1],
|
||||||
|
opacity: [0.3, 0.5, 0.3],
|
||||||
|
x: [0, 20, 0],
|
||||||
|
y: [0, -10, 0],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 5,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="absolute right-1/4 bottom-1/4 h-16 w-16 rounded-full bg-indigo-300/30"
|
||||||
|
animate={{
|
||||||
|
scale: [1, 1.3, 1],
|
||||||
|
opacity: [0.3, 0.6, 0.3],
|
||||||
|
x: [0, -15, 0],
|
||||||
|
y: [0, 10, 0],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 4,
|
||||||
|
delay: 0.5,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Central glowing orb */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute left-1/2 top-1/2 h-24 w-24 -translate-x-1/2 -translate-y-1/2 rounded-full bg-gradient-to-br from-blue-400/40 to-purple-400/40 blur-2xl"
|
||||||
|
animate={{
|
||||||
|
scale: [1, 1.4, 1],
|
||||||
|
opacity: [0.4, 0.7, 0.4],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 3,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'easeInOut',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Animated lines */}
|
||||||
|
<svg className="absolute inset-0 h-full w-full opacity-30">
|
||||||
|
<motion.line
|
||||||
|
x1="10%"
|
||||||
|
y1="20%"
|
||||||
|
x2="90%"
|
||||||
|
y2="80%"
|
||||||
|
stroke="rgb(99, 102, 241)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeDasharray="5,5"
|
||||||
|
animate={{
|
||||||
|
strokeDashoffset: [0, -20],
|
||||||
|
opacity: [0.3, 0.6, 0.3],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 2,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'linear',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<motion.line
|
||||||
|
x1="90%"
|
||||||
|
y1="20%"
|
||||||
|
x2="10%"
|
||||||
|
y2="80%"
|
||||||
|
stroke="rgb(147, 51, 234)"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeDasharray="5,5"
|
||||||
|
animate={{
|
||||||
|
strokeDashoffset: [0, 20],
|
||||||
|
opacity: [0.3, 0.6, 0.3],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 2.5,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: 'linear',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
@@ -17,20 +17,16 @@ const stackItems = [
|
|||||||
|
|
||||||
export const TechnologyStack = () => {
|
export const TechnologyStack = () => {
|
||||||
return (
|
return (
|
||||||
<section className="relative overflow-hidden rounded-3xl text-white">
|
<section className="py-16 lg:py-24">
|
||||||
<img
|
<div className="mx-auto max-w-6xl px-6 sm:px-10 lg:px-16">
|
||||||
src="/images/hometech.jpg"
|
|
||||||
alt=""
|
|
||||||
className="absolute inset-0 h-full w-full object-cover"
|
|
||||||
/>
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-br from-[#0f172a]/85 via-[#312e81]/70 to-[#020617]/85" />
|
|
||||||
<div className="relative z-10 px-6 py-16 sm:px-10 lg:px-16 lg:py-24">
|
|
||||||
<div className="mx-auto max-w-3xl text-center">
|
<div className="mx-auto max-w-3xl text-center">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-white/60">
|
<p className="text-xs font-semibold uppercase tracking-[0.35em] text-slate-500">
|
||||||
Technology Stack
|
Technology Stack
|
||||||
</p>
|
</p>
|
||||||
<h2 className="mt-4 text-3xl font-semibold sm:text-4xl">An Infrastructure Built for the AI Era</h2>
|
<h2 className="mt-4 text-3xl font-semibold text-ink sm:text-4xl">
|
||||||
<p className="mt-5 text-base text-white/70 sm:text-lg">
|
An Infrastructure Built for the AI Era
|
||||||
|
</h2>
|
||||||
|
<p className="mt-5 text-base text-slate-600 sm:text-lg">
|
||||||
Our unique technology stack delivers unmatched security, scalability, and flexibility,
|
Our unique technology stack delivers unmatched security, scalability, and flexibility,
|
||||||
preparing you for the AI workforce of the future.
|
preparing you for the AI workforce of the future.
|
||||||
</p>
|
</p>
|
||||||
@@ -43,14 +39,15 @@ export const TechnologyStack = () => {
|
|||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.3 }}
|
viewport={{ once: true, amount: 0.3 }}
|
||||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||||
className="overflow-hidden rounded-3xl bg-white/10 backdrop-blur"
|
className="overflow-hidden rounded-3xl"
|
||||||
>
|
>
|
||||||
<div className="relative h-56 overflow-hidden">
|
<div className="h-56 overflow-hidden">
|
||||||
<img src={item.image} alt={item.title} className="h-full w-full object-cover" />
|
<img src={item.image} alt={item.title} className="h-full w-full object-cover" />
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-[#020617]/70 via-transparent to-transparent" />
|
|
||||||
<h3 className="absolute bottom-6 left-6 text-xl font-semibold">{item.title}</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="p-6 text-sm leading-6 text-white/75">{item.description}</p>
|
<div className="p-6">
|
||||||
|
<h3 className="text-xl font-semibold text-ink">{item.title}</h3>
|
||||||
|
<p className="mt-3 text-sm leading-6 text-slate-600">{item.description}</p>
|
||||||
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -42,7 +42,7 @@ const useCases = [
|
|||||||
export const UseCasesGrid = () => {
|
export const UseCasesGrid = () => {
|
||||||
return (
|
return (
|
||||||
<section className="py-16 lg:py-24">
|
<section className="py-16 lg:py-24">
|
||||||
<div className="relative overflow-hidden rounded-3xl border border-slate-100 bg-white/80 p-8 shadow-subtle backdrop-blur lg:p-16">
|
<div className="relative p-8 sm:rounded-3xl lg:p-16">
|
||||||
<div className="pointer-events-none absolute -bottom-20 -left-32 h-72 w-72 rounded-full bg-brand-100 opacity-50 blur-3xl" />
|
<div className="pointer-events-none absolute -bottom-20 -left-32 h-72 w-72 rounded-full bg-brand-100 opacity-50 blur-3xl" />
|
||||||
<div className="pointer-events-none absolute -top-24 right-12 h-48 w-48 rounded-full bg-brand-200 opacity-40 blur-3xl" />
|
<div className="pointer-events-none absolute -top-24 right-12 h-48 w-48 rounded-full bg-brand-200 opacity-40 blur-3xl" />
|
||||||
<motion.div
|
<motion.div
|
||||||
|
@@ -4,4 +4,7 @@ import react from '@vitejs/plugin-react'
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
server: {
|
||||||
|
allowedHosts: ['coucha-royal-nontannic.ngrok-free.dev'],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|