feat: interactive site redesign with dot matrix effects and component polish
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
const navItems = [
|
||||
{ label: 'Dashboard', icon: 'grid', active: true },
|
||||
{ label: 'Nodes', icon: 'server', active: false },
|
||||
@@ -60,7 +61,7 @@ const tasks = [
|
||||
{ chip: { label: 'RUNNING', cls: 'border-warning/30 bg-warning/10 text-warning' }, text: 'start VM 100', node: 'pve1', time: '12s ago' },
|
||||
{ chip: { label: 'OK', cls: 'border-success/25 bg-success/10 text-success' }, text: 'backup VM 101 → local-lvm', node: 'pve2', time: '3m ago' },
|
||||
{ chip: { label: 'OK', cls: 'border-success/25 bg-success/10 text-success' }, text: 'migrate VM 102', node: 'pve1 → pve3', time: '18m ago' },
|
||||
{ chip: { label: 'DONE', cls: 'border-white/[0.06] bg-muted text-muted-foreground' }, text: 'update apt', node: 'pve3', time: '1h ago' },
|
||||
{ chip: { label: 'DONE', cls: 'border-dotted border-white/10 bg-muted text-muted-foreground' }, text: 'update apt', node: 'pve3', time: '1h ago' },
|
||||
]
|
||||
const gauges = [
|
||||
{ label: 'CPU', icon: 'cpu', percent: '30', width: '30', detail: '7.3 / 24 cores' },
|
||||
@@ -97,14 +98,14 @@ const quickActions = [
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<div class="overflow-hidden rounded-xl border border-white/[0.08] bg-card shadow-card">
|
||||
<div class="relative flex h-9 items-center border-b border-white/[0.06] bg-background">
|
||||
<div class="overflow-hidden rounded-xl border border-dotted border-white/10 bg-card shadow-card">
|
||||
<div class="relative flex h-9 items-center border-b border-dotted border-white/10 bg-background">
|
||||
<div class="absolute left-4 flex items-center gap-1.5" aria-hidden="true">
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-muted-foreground/40"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-muted-foreground/40"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-muted-foreground/40"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-muted-foreground/40"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-muted-foreground/40"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-muted-foreground/40"></span>
|
||||
</div>
|
||||
<p class="w-full text-center font-mono text-[10px] tabular-nums text-muted-foreground">Clustri — Ops Console</p>
|
||||
<div class="w-full flex justify-center"><DotMatrixText text="CLUSTRI" size="xs" dotClass="text-muted-foreground" /></div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 border-b border-warning/25 bg-warning/10 px-4 py-1.5">
|
||||
@@ -114,14 +115,13 @@ const quickActions = [
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<aside class="hidden w-52 shrink-0 flex-col border-r border-white/[0.06] bg-card sm:flex">
|
||||
<div class="flex h-14 shrink-0 items-center gap-2.5 border-b border-white/[0.06] px-4">
|
||||
<aside class="hidden w-52 shrink-0 flex-col border-r border-dotted border-white/10 bg-card sm:flex">
|
||||
<div class="flex h-14 shrink-0 items-center gap-2.5 border-b border-dotted border-white/10 px-4">
|
||||
<div class="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-primary/15 text-primary">
|
||||
<svg class="h-4 w-4" aria-hidden="true"><use href="#i-hex" /></svg>
|
||||
</div>
|
||||
<div class="min-w-0 leading-tight">
|
||||
<p class="truncate text-sm font-semibold tracking-tight text-foreground">Clustri</p>
|
||||
<p class="font-mono text-[9px] font-medium uppercase tracking-[0.14em] text-muted-foreground">Ops Console</p>
|
||||
<DotMatrixText text="CLUSTRI" size="xs" dotClass="text-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-h-0 flex-1 p-2">
|
||||
@@ -146,7 +146,7 @@ const quickActions = [
|
||||
</div>
|
||||
<p class="ml-7 font-mono text-[10px] text-muted-foreground">Offline</p>
|
||||
</div>
|
||||
<p class="px-3 pb-1 pt-3 font-mono text-[10px] font-medium uppercase tracking-[0.14em] text-muted-foreground">Cluster nodes</p>
|
||||
<div class="px-3 pb-1 pt-3"><DotMatrixText text="CLUSTER NODES" size="xs" dotClass="text-muted-foreground" /></div>
|
||||
<div class="space-y-0.5">
|
||||
{clusterNodes.map((n) => (
|
||||
<div class="flex w-full items-center gap-2.5 rounded-md px-3 py-1.5">
|
||||
@@ -158,11 +158,11 @@ const quickActions = [
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1 border-t border-white/[0.06] p-2">
|
||||
<div class="space-y-1 border-t border-dotted border-white/10 p-2">
|
||||
<button type="button" class="flex w-full items-center gap-2.5 rounded-md px-3 py-1.5 text-[12px] font-medium text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground">
|
||||
<svg class="h-4 w-4 shrink-0" aria-hidden="true"><use href="#i-gear" /></svg><span>Settings</span>
|
||||
</button>
|
||||
<button type="button" class="flex w-full items-center gap-2 rounded-md border border-white/[0.06] bg-background px-3 py-1.5 text-[12px] font-medium text-muted-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground">
|
||||
<button type="button" class="flex w-full items-center gap-2 rounded-md border border-dotted border-white/10 bg-background px-3 py-1.5 text-[12px] font-medium text-muted-foreground shadow-sm transition-colors hover:bg-accent hover:text-accent-foreground">
|
||||
<svg class="h-3.5 w-3.5 shrink-0" aria-hidden="true"><use href="#i-plus" /></svg><span>Add Connection</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -171,7 +171,7 @@ const quickActions = [
|
||||
<main class="min-w-0 flex-1 bg-background p-4">
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
<h2 class="text-base font-semibold tracking-tight">Overview</h2>
|
||||
<DotMatrixText text="OVERVIEW" size="sm" dotClass="text-foreground" />
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span class="font-mono text-[10px] tabular-nums text-muted-foreground">home-lab · 192.168.1.10:8006</span>
|
||||
<span class="font-mono text-[10px] tabular-nums text-muted-foreground">3 nodes · 2 online</span>
|
||||
@@ -181,7 +181,7 @@ const quickActions = [
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
{nodeCards.map((node) => (
|
||||
<article class="rounded-md border border-white/[0.06] bg-card p-3">
|
||||
<article class="rounded-md border border-dotted border-white/10 bg-card p-3">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class={`h-1.5 w-1.5 shrink-0 rounded-full ${node.dot}`} aria-hidden="true"></span>
|
||||
@@ -216,8 +216,8 @@ const quickActions = [
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-5">
|
||||
<section class="rounded-md border border-white/[0.06] bg-card sm:col-span-3">
|
||||
<header class="flex items-center gap-2 border-b border-white/[0.06] px-3 py-2">
|
||||
<section class="rounded-md border border-dotted border-white/10 bg-card sm:col-span-3">
|
||||
<header class="flex items-center gap-2 border-b border-dotted border-white/10 px-3 py-2">
|
||||
<svg class="h-3.5 w-3.5 shrink-0 text-muted-foreground" aria-hidden="true"><use href="#i-activity" /></svg>
|
||||
<h3 class="font-mono text-[10px] font-medium uppercase tracking-[0.14em] text-muted-foreground">Task activity</h3>
|
||||
<span class="ml-auto h-1.5 w-1.5 animate-pulse rounded-full bg-success" aria-hidden="true"></span>
|
||||
@@ -237,7 +237,7 @@ const quickActions = [
|
||||
|
||||
<div class="space-y-3 sm:col-span-2">
|
||||
{gauges.map((gauge) => (
|
||||
<section class="rounded-md border border-white/[0.06] bg-card p-3">
|
||||
<section class="rounded-md border border-dotted border-white/10 bg-card p-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<svg class="h-3.5 w-3.5 text-muted-foreground" aria-hidden="true"><use href={`#i-${gauge.icon}`} /></svg>
|
||||
<span class="font-mono text-[9px] font-medium uppercase tracking-[0.14em] text-muted-foreground">{gauge.label}</span>
|
||||
@@ -256,7 +256,7 @@ const quickActions = [
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
{quickActions.map((action) => (
|
||||
<button type="button" class="flex items-center gap-1.5 rounded border border-white/[0.06] px-3 py-1.5 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground">
|
||||
<button type="button" class="flex items-center gap-1.5 rounded border border-dotted border-white/10 px-3 py-1.5 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-accent/60 hover:text-foreground">
|
||||
<svg class="h-3 w-3" aria-hidden="true"><use href={`#i-${action.icon}`} /></svg><span>{action.label}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -1,45 +1,48 @@
|
||||
<section id="architecture" aria-labelledby="arch-title" class="scroll-mt-24 border-t border-white/[0.06] py-20 sm:py-28">
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<section id="architecture" aria-labelledby="arch-title" class="scroll-mt-24 border-t border-dotted border-white/10 py-20 sm:py-28">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="max-w-2xl" data-reveal>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">Architecture</p>
|
||||
<DotMatrixText text="ARCHITECTURE" size="xs" dotClass="text-primary" />
|
||||
<h2 id="arch-title" class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl">One proxy between you and the API.</h2>
|
||||
<p class="mt-4 text-[15px] leading-relaxed text-muted-foreground">The frontend calls Rust commands over Tauri IPC. The backend proxies them to the Proxmox API over HTTPS.</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 grid gap-3 lg:grid-cols-[1fr_auto_1fr_auto_1fr] lg:items-stretch" data-stagger>
|
||||
<div class="rounded-xl border border-white/[0.06] bg-card p-5" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-foreground">React frontend</p><span class="font-mono text-[10px] text-muted-foreground">01</span></div>
|
||||
<div class="rounded-xl border border-dotted border-white/10 bg-card p-5 dot-grid" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-foreground">React frontend</p><DotMatrixText text="01" size="xs" dotClass="text-muted-foreground" /></div>
|
||||
<div class="mt-2 space-y-1 font-mono text-[11px] text-muted-foreground"><p>TanStack Query · Zustand</p><p>Geist · JetBrains Mono</p><p>React 19</p></div>
|
||||
</div>
|
||||
<div class="hidden flex-col items-center justify-center gap-1 lg:flex flow-arrow" aria-hidden="true"><span class="text-sm text-primary">→</span><span class="font-mono text-[9px] text-muted-foreground">tauri invoke</span></div>
|
||||
<div class="flex items-center justify-center gap-1.5 py-1 lg:hidden flow-arrow" aria-hidden="true"><span class="text-sm text-primary">↓</span><span class="font-mono text-[9px] text-muted-foreground">tauri invoke</span></div>
|
||||
|
||||
<div class="rounded-xl border border-primary/25 bg-primary/[0.07] p-5" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-primary">Rust backend</p><span class="font-mono text-[10px] text-muted-foreground">02</span></div>
|
||||
<div class="rounded-xl border border-dotted border-primary/30 bg-primary/[0.07] p-5 dot-grid" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-primary">Rust backend</p><DotMatrixText text="02" size="xs" dotClass="text-primary" /></div>
|
||||
<div class="mt-2 space-y-1 font-mono text-[11px] text-primary/75"><p>ConnectionManager</p><p>endpoint rotation · retry</p><p>TOFU cert pinning</p><p>OS keyring</p></div>
|
||||
</div>
|
||||
<div class="hidden flex-col items-center justify-center gap-1 lg:flex flow-arrow" aria-hidden="true"><span class="text-sm text-primary">→</span><span class="font-mono text-[9px] text-muted-foreground">HTTPS</span></div>
|
||||
<div class="flex items-center justify-center gap-1.5 py-1 lg:hidden flow-arrow" aria-hidden="true"><span class="text-sm text-primary">↓</span><span class="font-mono text-[9px] text-muted-foreground">HTTPS</span></div>
|
||||
|
||||
<div class="rounded-xl border border-white/[0.06] bg-card p-5" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-foreground">Proxmox API</p><span class="font-mono text-[10px] text-muted-foreground">03</span></div>
|
||||
<div class="rounded-xl border border-dotted border-white/10 bg-card p-5 dot-grid" data-stagger-child>
|
||||
<div class="flex items-start justify-between gap-4"><p class="text-sm font-semibold text-foreground">Proxmox API</p><DotMatrixText text="03" size="xs" dotClass="text-muted-foreground" /></div>
|
||||
<div class="mt-2 space-y-1 font-mono text-[11px] text-muted-foreground"><p>/api2/json</p><p>nodes · cluster · vms</p><p>self-signed TLS</p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid gap-6 lg:grid-cols-2">
|
||||
<div class="overflow-hidden rounded-xl border border-white/[0.06] bg-card" data-reveal>
|
||||
<div class="border-b border-white/[0.06] bg-background px-4 py-2.5 font-mono text-[10px] text-muted-foreground">POST /nodes/pve/qemu/100/status/start</div>
|
||||
<div class="overflow-hidden rounded-xl border border-dotted border-white/10 bg-card" data-reveal>
|
||||
<div class="border-b border-dotted border-white/10 bg-background px-4 py-2.5 font-mono text-[10px] text-muted-foreground">POST /nodes/pve/qemu/100/status/start</div>
|
||||
<div class="px-4 py-4">
|
||||
<p class="truncate font-mono text-[13px] tabular-nums text-foreground">https://192.168.1.10:8006/api2/json/nodes/pve/qemu/100/status/start</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<span class="rounded border border-white/[0.08] bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">PVEAPIToken</span>
|
||||
<span class="rounded border border-white/[0.08] bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">PVEAuthCookie + CSRFPreventionToken</span>
|
||||
<span class="rounded border border-dotted border-white/10 bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">PVEAPIToken</span>
|
||||
<span class="rounded border border-dotted border-white/10 bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">PVEAuthCookie + CSRFPreventionToken</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-2" data-reveal data-reveal-delay="100">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground">Failover model</p>
|
||||
<DotMatrixText text="FAILOVER MODEL" size="xs" dotClass="text-muted-foreground" />
|
||||
<p class="mt-3 text-sm leading-relaxed text-muted-foreground">Ordered, deduped endpoints: primary → fallbacks → discovered nodes. Connect / timeout / DNS / transport failure → retry next endpoint.</p>
|
||||
<div class="mt-4 flex gap-4 font-mono text-[10px]">
|
||||
<span class="inline-flex items-center gap-1.5"><span class="h-1.5 w-1.5 rounded-full bg-success"></span> connected</span>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<div id="cmdk-overlay" class="cmdk-overlay fixed inset-0 z-[100] hidden items-start justify-center px-4 pt-[20vh]" role="dialog" aria-modal="true" aria-label="Command palette">
|
||||
<div id="cmdk-panel" class="cmdk-panel w-full max-w-[520px] overflow-hidden rounded-xl border border-white/[0.08] bg-card">
|
||||
<div class="flex items-center gap-3 border-b border-white/[0.06] px-4">
|
||||
<div id="cmdk-panel" class="cmdk-panel w-full max-w-[520px] overflow-hidden rounded-xl border border-dotted border-white/10 bg-card dot-grid">
|
||||
<div class="flex items-center gap-3 border-b border-dotted border-white/10 px-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="h-4 w-4 shrink-0 text-muted-foreground" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
|
||||
<input id="cmdk-input" type="text" placeholder="Type a command…" autocomplete="off" spellcheck="false" class="h-12 w-full bg-transparent font-mono text-sm text-foreground placeholder:text-muted-foreground/60 focus:outline-none" />
|
||||
<kbd class="hidden rounded border border-white/[0.08] bg-white/[0.04] px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground sm:inline">ESC</kbd>
|
||||
<kbd class="hidden rounded border border-dotted border-white/10 bg-white/[0.04] px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground sm:inline">ESC</kbd>
|
||||
</div>
|
||||
<div id="cmdk-list" class="max-h-[320px] overflow-auto p-2"></div>
|
||||
<div class="border-t border-white/[0.06] px-3 py-2 font-mono text-[10px] text-muted-foreground/60">↑↓ to navigate · ↵ to select · esc to close</div>
|
||||
<div class="border-t border-dotted border-white/10 px-3 py-2 flex items-center justify-between">
|
||||
<DotMatrixText text="UP/DN NAVIGATE / ENTER SELECT / ESC CLOSE" size="xs" dotClass="text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
import { GLYPH_COLS, GLYPH_ROWS, getGlyph } from '../lib/dotMatrixGlyphs'
|
||||
|
||||
interface Props {
|
||||
text: string
|
||||
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
class?: string
|
||||
dotClass?: string
|
||||
}
|
||||
|
||||
const { text, size = 'sm', class: className, dotClass } = Astro.props as Props
|
||||
|
||||
const sizeMap = {
|
||||
xs: { dot: 1.8, gap: 1.3, charGap: 5 },
|
||||
sm: { dot: 2.2, gap: 1.5, charGap: 6 },
|
||||
md: { dot: 2.6, gap: 1.8, charGap: 7 },
|
||||
lg: { dot: 3.0, gap: 2.0, charGap: 9 },
|
||||
xl: { dot: 3.8, gap: 2.4, charGap: 12 },
|
||||
} as const
|
||||
|
||||
const { dot, gap, charGap } = sizeMap[size]
|
||||
const chars = Array.from(text.toUpperCase())
|
||||
---
|
||||
|
||||
<span aria-label={text} role="text" class:list={['inline-flex items-center leading-none', dotClass, className]} style={`gap:${charGap}px`}>
|
||||
{chars.map((ch) => {
|
||||
if (ch === ' ') return <span aria-hidden="true" style={`width:${charGap * 1.8}px`} />
|
||||
const glyph = getGlyph(ch)
|
||||
if (!glyph) return <span class="font-mono text-[0.7em] leading-none" aria-hidden="true">{ch}</span>
|
||||
const w = GLYPH_COLS * dot + (GLYPH_COLS - 1) * gap
|
||||
const h = GLYPH_ROWS * dot + (GLYPH_ROWS - 1) * gap
|
||||
const r = dot / 2
|
||||
return (
|
||||
<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} aria-hidden="true" class="shrink-0" shape-rendering="geometricPrecision">
|
||||
{glyph.map((row, y) =>
|
||||
Array.from(row).map((cell, x) => {
|
||||
if (cell === ' ') return null
|
||||
const cx = x * (dot + gap) + r
|
||||
const cy = y * (dot + gap) + r
|
||||
return <circle cx={cx} cy={cy} r={r} fill="currentColor" />
|
||||
}),
|
||||
)}
|
||||
</svg>
|
||||
)
|
||||
})}
|
||||
</span>
|
||||
@@ -1,26 +1,29 @@
|
||||
<section id="download" class="scroll-mt-24 border-t border-white/[0.06] py-20 sm:py-28">
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<section id="download" class="scroll-mt-24 border-t border-dotted border-white/10 py-20 sm:py-28">
|
||||
<div class="mx-auto max-w-3xl px-6 text-center">
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary" data-reveal>Releases</p>
|
||||
<div data-reveal><DotMatrixText text="RELEASES" size="xs" dotClass="text-primary" /></div>
|
||||
<h2 class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl" data-reveal data-reveal-delay="80">Coming soon.</h2>
|
||||
<p class="mx-auto mt-4 max-w-[46ch] text-[15px] leading-relaxed text-muted-foreground" data-reveal data-reveal-delay="140">
|
||||
Clustri is in active development. Packages for Linux, macOS, and Windows are on the way.
|
||||
</p>
|
||||
|
||||
<div class="relative mt-10 overflow-hidden rounded-xl border border-white/[0.07] bg-card p-6 sm:p-8 shadow-card" data-reveal data-reveal-delay="200">
|
||||
<div class="pointer-events-none absolute inset-0 grid-bg opacity-[0.12]" aria-hidden="true"></div>
|
||||
<div class="relative mt-10 overflow-hidden rounded-xl border border-dotted border-white/10 bg-card p-6 sm:p-8 shadow-card dot-grid" data-reveal data-reveal-delay="200">
|
||||
<div class="pointer-events-none absolute inset-0 dot-grid opacity-[0.10]" aria-hidden="true"></div>
|
||||
<div class="pointer-events-none absolute inset-x-0 top-0 h-px bg-primary/30" aria-hidden="true"></div>
|
||||
|
||||
<div class="relative">
|
||||
<div class="flex flex-wrap items-center justify-center gap-3">
|
||||
<a href="#" class="rounded-[10px] bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground transition hover:bg-primary/90 active:scale-[0.98]">Get Clustri</a>
|
||||
<a href="#" class="rounded-[10px] border border-white/[0.08] bg-white/[0.03] px-6 py-3 text-sm font-semibold text-muted-foreground transition hover:border-white/15 hover:text-foreground active:scale-[0.98]">Build from source</a>
|
||||
<a href="#" class="rounded-[10px] border border-dotted border-white/10 bg-white/[0.03] px-6 py-3 text-sm font-semibold text-muted-foreground transition hover:border-white/15 hover:text-foreground active:scale-[0.98]">Build from source</a>
|
||||
</div>
|
||||
<div class="mt-6 flex flex-wrap items-center justify-center gap-2">
|
||||
<span class="rounded-md border border-white/[0.06] bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Linux</span>
|
||||
<span class="rounded-md border border-white/[0.06] bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">macOS</span>
|
||||
<span class="rounded-md border border-white/[0.06] bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Windows</span>
|
||||
<span class="rounded-md border border-dotted border-white/10 bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Linux</span>
|
||||
<span class="rounded-md border border-dotted border-white/10 bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">macOS</span>
|
||||
<span class="rounded-md border border-dotted border-white/10 bg-background px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Windows</span>
|
||||
</div>
|
||||
<p class="mt-4 font-mono text-[10px] text-muted-foreground/60">cargo test · 64 tests · MIT · v0.1.0</p>
|
||||
<div class="mt-4 flex justify-center"><DotMatrixText text="CARGO TEST / 64 TESTS / MIT / V0.1.0" size="xs" dotClass="text-muted-foreground" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<section id="failover-playground" aria-labelledby="fp-title" class="relative overflow-hidden border-y border-white/[0.06] bg-card/40">
|
||||
<div class="pointer-events-none absolute inset-0 grid-bg opacity-[0.18]" aria-hidden="true"></div>
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<section id="failover-playground" aria-labelledby="fp-title" class="relative overflow-hidden border-y border-dotted border-white/10 bg-card/40">
|
||||
<div class="pointer-events-none absolute inset-0 dot-grid opacity-[0.12]" aria-hidden="true"></div>
|
||||
|
||||
<div class="relative mx-auto max-w-7xl px-6 py-20 lg:px-8 lg:py-28">
|
||||
<div class="grid gap-10 lg:grid-cols-12 lg:items-start">
|
||||
<!-- Copy + controls -->
|
||||
<div class="lg:col-span-5">
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary" data-reveal>Failover — try it</p>
|
||||
<div data-reveal><DotMatrixText text="FAILOVER / TRY IT" size="xs" dotClass="text-primary" /></div>
|
||||
<h2 id="fp-title" class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl" data-reveal data-reveal-delay="80">
|
||||
One node dies.<br /><span class="text-muted-foreground">You don't notice.</span>
|
||||
</h2>
|
||||
@@ -14,9 +16,9 @@
|
||||
</p>
|
||||
|
||||
<div class="mt-8 flex flex-wrap gap-2" data-reveal data-reveal-delay="180">
|
||||
<button id="fp-kill-pve1" type="button" class="rounded-md border border-white/[0.08] bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve1</button>
|
||||
<button id="fp-kill-pve2" type="button" class="rounded-md border border-white/[0.08] bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve2</button>
|
||||
<button id="fp-kill-pve3" type="button" class="rounded-md border border-white/[0.08] bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve3</button>
|
||||
<button id="fp-kill-pve1" type="button" class="rounded-md border border-dotted border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve1</button>
|
||||
<button id="fp-kill-pve2" type="button" class="rounded-md border border-dotted border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve2</button>
|
||||
<button id="fp-kill-pve3" type="button" class="rounded-md border border-dotted border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-[11px] font-medium text-foreground transition hover:border-white/15 hover:bg-white/[0.07]">Kill pve3</button>
|
||||
<button id="fp-heal" type="button" class="rounded-md bg-primary px-3 py-2 font-mono text-[11px] font-semibold text-primary-foreground transition hover:bg-primary/90">Heal all</button>
|
||||
</div>
|
||||
|
||||
@@ -27,19 +29,19 @@
|
||||
<span class="ml-1 text-muted-foreground">— tap a node or use the buttons</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 rounded-md border border-white/[0.06] bg-background px-3 py-2.5 font-mono text-[11px] leading-relaxed text-muted-foreground" data-reveal data-reveal-delay="260">
|
||||
<div class="mt-6 rounded-md border border-dotted border-white/10 bg-background px-3 py-2.5 font-mono text-[11px] leading-relaxed text-muted-foreground" data-reveal data-reveal-delay="260">
|
||||
<span class="text-foreground">How it works:</span> deduped order <span class="text-foreground">primary → fallbacks → discovered</span>. On connect / timeout / DNS / transport failure, retry the next endpoint. Status reflects which endpoint actually answered.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Diagram -->
|
||||
<div class="lg:col-span-7" data-reveal data-reveal-delay="120">
|
||||
<div class="relative overflow-hidden rounded-xl border border-white/[0.07] bg-card shadow-card">
|
||||
<div class="relative overflow-hidden rounded-xl border border-dotted border-white/10 bg-card shadow-card">
|
||||
<!-- top rule -->
|
||||
<div id="fp-top-rule" class="h-px w-full bg-primary/30 transition-colors" aria-hidden="true"></div>
|
||||
|
||||
<!-- header -->
|
||||
<div class="flex items-center justify-between gap-3 border-b border-white/[0.06] px-4 py-3">
|
||||
<div class="flex items-center justify-between gap-3 border-b border-dotted border-white/10 px-4 py-3">
|
||||
<p class="font-mono text-[10px] tracking-[0.06em] text-muted-foreground">request → endpoints[0] → retry on failure</p>
|
||||
<span id="fp-status-chip" class="rounded border border-success/25 bg-success/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-success">Connected</span>
|
||||
</div>
|
||||
@@ -47,7 +49,7 @@
|
||||
<!-- body: rail + nodes -->
|
||||
<div class="relative flex gap-0">
|
||||
<!-- packet rail -->
|
||||
<div class="relative w-[36px] shrink-0 border-r border-white/[0.04] bg-background/40">
|
||||
<div class="relative w-[36px] shrink-0 border-r border-dotted border-white/10 bg-background/40">
|
||||
<div class="absolute left-1/2 top-3 bottom-3 w-px -translate-x-1/2 bg-white/[0.08]" aria-hidden="true"></div>
|
||||
<!-- dots injected by JS -->
|
||||
<div id="fp-rail" class="absolute inset-0 overflow-hidden" aria-hidden="true"></div>
|
||||
@@ -56,7 +58,7 @@
|
||||
|
||||
<!-- node list -->
|
||||
<div class="min-w-0 flex-1">
|
||||
<button id="fp-node-0" type="button" class="flex w-full items-center justify-between gap-3 border-b border-white/[0.06] px-4 py-3.5 text-left transition hover:bg-white/[0.02]">
|
||||
<button id="fp-node-0" type="button" class="flex w-full items-center justify-between gap-3 border-b border-dotted border-white/10 px-4 py-3.5 text-left transition hover:bg-white/[0.02]">
|
||||
<span class="min-w-0">
|
||||
<span class="flex items-center gap-2">
|
||||
<span id="fp-dot-0" class="h-1.5 w-1.5 shrink-0 rounded-full bg-success shadow-[0_0_6px] shadow-success/50"></span>
|
||||
@@ -66,7 +68,7 @@
|
||||
</span>
|
||||
<span class="shrink-0 rounded border border-primary/30 bg-primary/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-primary">Primary</span>
|
||||
</button>
|
||||
<button id="fp-node-1" type="button" class="flex w-full items-center justify-between gap-3 border-b border-white/[0.06] px-4 py-3.5 text-left transition hover:bg-white/[0.02]">
|
||||
<button id="fp-node-1" type="button" class="flex w-full items-center justify-between gap-3 border-b border-dotted border-white/10 px-4 py-3.5 text-left transition hover:bg-white/[0.02]">
|
||||
<span class="min-w-0">
|
||||
<span class="flex items-center gap-2">
|
||||
<span id="fp-dot-1" class="h-1.5 w-1.5 shrink-0 rounded-full bg-success"></span>
|
||||
@@ -90,7 +92,7 @@
|
||||
</div>
|
||||
|
||||
<!-- status bar -->
|
||||
<div class="border-t border-white/[0.06] bg-background px-4 py-3">
|
||||
<div class="border-t border-dotted border-white/10 bg-background px-4 py-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<span id="fp-status-text" class="font-mono text-[11px] tabular-nums text-muted-foreground">status: connected — pve1 answered</span>
|
||||
<span id="fp-latency" class="font-mono text-[10px] tabular-nums text-muted-foreground">~18 ms</span>
|
||||
@@ -214,16 +216,20 @@
|
||||
document.getElementById('fp-heal')?.addEventListener('click', healAll)
|
||||
nodes.forEach((n, i) => n?.addEventListener('click', () => kill(i)))
|
||||
|
||||
const DOT_COUNT = 3
|
||||
const CYCLE_SPEED = 0.012
|
||||
|
||||
function createDots() {
|
||||
if (!rail) return
|
||||
rail.innerHTML = ''
|
||||
dotEls = []
|
||||
for (let k = 0; k < 2; k++) {
|
||||
for (let k = 0; k < DOT_COUNT; k++) {
|
||||
const d = document.createElement('div')
|
||||
d.className = 'absolute left-1/2 h-2 w-2 -translate-x-1/2 rounded-full bg-primary shadow-[0_0_8px] shadow-primary/60'
|
||||
d.style.top = '8px'
|
||||
d.className = 'absolute h-2 w-2 rounded-full bg-primary shadow-[0_0_8px] shadow-primary/60'
|
||||
d.style.left = '50%'
|
||||
d.style.top = '0px'
|
||||
// @ts-ignore
|
||||
d._offset = k * 0.5
|
||||
d._phase = k / DOT_COUNT
|
||||
rail.appendChild(d)
|
||||
dotEls.push(d)
|
||||
}
|
||||
@@ -232,33 +238,32 @@
|
||||
function restartDots() {
|
||||
if (prefersReduced) return
|
||||
cancelAnimationFrame(raf)
|
||||
t = 0
|
||||
createDots()
|
||||
animate()
|
||||
}
|
||||
|
||||
function targetYs() {
|
||||
function getTargetY() {
|
||||
const ai = activeIndex()
|
||||
if (ai === -1) return [0.88]
|
||||
// map active index to rail progress (0..1): pve1 ~0.26, pve2 ~0.55, pve3 ~0.84
|
||||
if (ai === -1) return 0.88
|
||||
const map = [0.28, 0.56, 0.86]
|
||||
return [map[ai]]
|
||||
return map[ai]
|
||||
}
|
||||
|
||||
function animate() {
|
||||
if (prefersReduced || !rail) return
|
||||
t += 0.018
|
||||
t += CYCLE_SPEED
|
||||
const h = rail.clientHeight || 160
|
||||
const targets = targetYs()
|
||||
const target = getTargetY()
|
||||
const ai = activeIndex()
|
||||
|
||||
dotEls.forEach((el) => {
|
||||
const phase = (t + el._offset) % 1
|
||||
// ease: move from 0 to target, then fade and reset
|
||||
const ty = phase * (targets[0] * h)
|
||||
const visible = phase < 0.92
|
||||
// @ts-ignore
|
||||
const phase = (t + el._phase) % 1
|
||||
const ty = phase * target * h
|
||||
const visible = phase < 0.88
|
||||
el.style.transform = `translate(-50%, ${ty}px)`
|
||||
el.style.opacity = visible ? '1' : '0'
|
||||
// if failed, make dot red and slower
|
||||
const ai = activeIndex()
|
||||
|
||||
if (ai === -1) {
|
||||
el.style.background = 'var(--color-destructive)'
|
||||
// @ts-ignore
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<!-- Security -->
|
||||
<section id="security" aria-labelledby="sec-title" class="scroll-mt-24 py-20 sm:py-28">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="grid gap-10 lg:grid-cols-12 lg:items-start">
|
||||
<div class="lg:col-span-6" data-reveal>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">Security</p>
|
||||
<DotMatrixText text="SECURITY" size="xs" dotClass="text-primary" />
|
||||
<h2 id="sec-title" class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl">
|
||||
Your keys stay<br /><span class="text-muted-foreground">in your pocket.</span>
|
||||
</h2>
|
||||
@@ -18,14 +21,14 @@
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-6" data-reveal data-reveal-delay="120">
|
||||
<div class="rounded-xl border border-white/[0.07] bg-card p-5 shadow-card">
|
||||
<div class="rounded-xl border border-dotted border-white/10 bg-card p-5 shadow-card">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="h-2 w-2 rounded-full bg-success shadow-[0_0_6px] shadow-success/60" aria-hidden="true"></span>
|
||||
<span class="font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground">SHA-256 fingerprint — pinned</span>
|
||||
<span class="ml-auto rounded border border-success/20 bg-success/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-success">Pinned</span>
|
||||
</div>
|
||||
<p class="mt-4 overflow-x-auto whitespace-nowrap font-mono text-[13px] tracking-[0.12em] text-foreground">A3:0F:71:C9:52:B8:4D:E0:11:2A:7F:9C:63:D4:18:0B</p>
|
||||
<div class="mt-4 flex flex-wrap gap-2 border-t border-white/[0.06] pt-4 font-mono text-[10px]">
|
||||
<div class="mt-4 flex flex-wrap gap-2 border-t border-dotted border-white/10 pt-4 font-mono text-[10px]">
|
||||
<span class="rounded bg-white/[0.04] px-2 py-1 text-muted-foreground">OS keyring</span>
|
||||
<span class="rounded bg-white/[0.04] px-2 py-1 text-muted-foreground">rustls · sha2 · x509-cert</span>
|
||||
<span class="rounded bg-white/[0.04] px-2 py-1 text-muted-foreground">no telemetry</span>
|
||||
@@ -34,15 +37,15 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4 grid grid-cols-3 gap-3">
|
||||
<div class="rounded-lg border border-white/[0.06] bg-background p-3 text-center">
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-background p-3 text-center">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground">Tokens</p>
|
||||
<p class="mt-1 font-mono text-[11px] font-medium text-foreground">Keyring only</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-white/[0.06] bg-background p-3 text-center">
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-background p-3 text-center">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground">Certs</p>
|
||||
<p class="mt-1 font-mono text-[11px] font-medium text-foreground">TOFU SHA-256</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-white/[0.06] bg-background p-3 text-center">
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-background p-3 text-center">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground">Transport</p>
|
||||
<p class="mt-1 font-mono text-[11px] font-medium text-foreground">rustls</p>
|
||||
</div>
|
||||
@@ -53,11 +56,11 @@
|
||||
</section>
|
||||
|
||||
<!-- Bold statement — full bleed -->
|
||||
<section class="relative overflow-hidden border-y border-white/[0.06] bg-[#08080a]">
|
||||
<section class="relative overflow-hidden border-y border-dotted border-white/10 bg-[#08080a]">
|
||||
<div class="pointer-events-none absolute inset-0 grid-bg opacity-[0.22]" aria-hidden="true"></div>
|
||||
<div class="relative mx-auto max-w-7xl px-6 py-20 sm:py-28 lg:px-8">
|
||||
<div class="max-w-4xl" data-reveal>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">The idea</p>
|
||||
<DotMatrixText text="THE IDEA" size="xs" dotClass="text-primary" />
|
||||
<p class="mt-6 text-3xl font-semibold leading-[0.95] tracking-[-0.04em] text-foreground sm:text-5xl lg:text-[3.4rem]">
|
||||
Proxmox is powerful.<br />
|
||||
<span class="text-muted-foreground">The web UI is not the point of failure.</span>
|
||||
@@ -75,27 +78,27 @@
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="grid gap-10 lg:grid-cols-12">
|
||||
<div class="lg:col-span-5" data-reveal>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">Console</p>
|
||||
<DotMatrixText text="CONSOLE" size="xs" dotClass="text-primary" />
|
||||
<h2 id="console-title" class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl">Type like you're there.</h2>
|
||||
<p class="mt-4 max-w-[44ch] text-[15px] leading-relaxed text-muted-foreground">
|
||||
noVNC for VMs, xterm.js for containers. Fullscreen, keyboard passthrough, and Ctrl+Alt+Del where you expect it.
|
||||
</p>
|
||||
<div class="mt-6 inline-flex gap-2">
|
||||
<span class="rounded border border-white/[0.06] bg-card px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Fullscreen</span>
|
||||
<span class="rounded border border-white/[0.06] bg-card px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Ctrl+Alt+Del</span>
|
||||
<span class="rounded border border-dotted border-white/10 bg-card px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Fullscreen</span>
|
||||
<span class="rounded border border-dotted border-white/10 bg-card px-2.5 py-1 font-mono text-[11px] text-muted-foreground">Ctrl+Alt+Del</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-7" data-reveal data-reveal-delay="120">
|
||||
<div class="overflow-hidden rounded-xl border border-white/[0.07] bg-card shadow-card">
|
||||
<div class="flex items-center justify-between border-b border-white/[0.06] bg-background px-4 py-2.5">
|
||||
<div class="overflow-hidden rounded-xl border border-dotted border-white/10 bg-card shadow-card">
|
||||
<div class="flex items-center justify-between border-b border-dotted border-white/10 bg-background px-4 py-2.5">
|
||||
<div class="flex items-center gap-1.5" aria-hidden="true">
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
</div>
|
||||
<p class="font-mono text-[10px] text-muted-foreground">root@pve1 · xterm</p>
|
||||
<kbd class="rounded border border-white/[0.08] bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground">Ctrl+Alt+Del</kbd>
|
||||
<kbd class="rounded border border-dotted border-white/10 bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground">Ctrl+Alt+Del</kbd>
|
||||
</div>
|
||||
<div class="bg-[#0a0a0a] px-4 py-4 font-mono text-[12px] leading-relaxed" data-terminal>
|
||||
<div data-line><span class="text-primary">root@pve1:~#</span> <span class="text-foreground">pct enter 101</span></div>
|
||||
@@ -110,11 +113,11 @@
|
||||
</section>
|
||||
|
||||
<!-- Power user -->
|
||||
<section id="power-user" aria-labelledby="power-title" class="scroll-mt-24 border-t border-white/[0.06] py-20 sm:py-28">
|
||||
<section id="power-user" aria-labelledby="power-title" class="scroll-mt-24 border-t border-dotted border-white/10 py-20 sm:py-28">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="flex flex-wrap items-end justify-between gap-6" data-reveal>
|
||||
<div>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">Power user</p>
|
||||
<DotMatrixText text="POWER USER" size="xs" dotClass="text-primary" />
|
||||
<h2 id="power-title" class="mt-4 text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl">Built for the people who keep the lights on.</h2>
|
||||
</div>
|
||||
<p class="max-w-[44ch] text-[15px] leading-relaxed text-muted-foreground">
|
||||
@@ -122,16 +125,16 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 grid gap-0 border-y border-white/[0.06] sm:grid-cols-2" data-stagger>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-white/[0.06] px-1 py-4 sm:border-r sm:px-4" data-stagger-child>
|
||||
<kbd class="rounded border border-white/[0.08] bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">Cmd / Ctrl + K</kbd>
|
||||
<div class="mt-10 grid gap-0 border-y border-dotted border-white/10 sm:grid-cols-2" data-stagger>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-dotted border-white/10 px-1 py-4 sm:border-r sm:px-4" data-stagger-child>
|
||||
<kbd class="rounded border border-dotted border-white/10 bg-muted px-2 py-1 font-mono text-[10px] text-muted-foreground">Cmd / Ctrl + K</kbd>
|
||||
<span class="text-sm text-muted-foreground">Command palette</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-white/[0.06] px-1 py-4 sm:px-4" data-stagger-child>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-dotted border-white/10 px-1 py-4 sm:px-4" data-stagger-child>
|
||||
<span class="font-mono text-[11px] font-medium text-primary">WebSocket</span>
|
||||
<span class="text-sm text-muted-foreground">Live task & event stream</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-white/[0.06] px-1 py-4 sm:border-r sm:px-4 sm:border-b-0" data-stagger-child>
|
||||
<div class="flex items-center justify-between gap-4 border-b border-dotted border-white/10 px-1 py-4 sm:border-r sm:px-4 sm:border-b-0" data-stagger-child>
|
||||
<span class="font-mono text-[11px] font-medium text-primary">Tray menu</span>
|
||||
<span class="text-sm text-muted-foreground">Show / hide · connections · quit</span>
|
||||
</div>
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
const year = new Date().getFullYear()
|
||||
---
|
||||
|
||||
<footer class="border-t border-white/[0.06] bg-[#08080a]">
|
||||
<footer class="border-t border-dotted border-white/10 bg-[#08080a] dot-grid">
|
||||
<div class="mx-auto max-w-7xl px-6 py-10 lg:px-8">
|
||||
<div class="flex flex-col gap-8 md:flex-row md:items-start md:justify-between">
|
||||
<div>
|
||||
<div class="flex items-center gap-2.5">
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-[8px] bg-primary text-primary-foreground">
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-[8px] border border-dotted border-white/10 bg-primary text-primary-foreground">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-3.5 w-3.5" aria-hidden="true"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" /></svg>
|
||||
</span>
|
||||
<span class="text-sm font-semibold tracking-tight text-foreground">Clustri</span>
|
||||
<span class="hidden font-mono text-[9px] uppercase tracking-[0.14em] text-muted-foreground sm:inline">Ops Console</span>
|
||||
<DotMatrixText text="CLUSTRI" size="xs" dotClass="text-foreground" />
|
||||
<span class="hidden sm:inline"><DotMatrixText text="OPS CONSOLE" size="xs" dotClass="text-muted-foreground opacity-70" /></span>
|
||||
</div>
|
||||
<p class="mt-3 font-mono text-[11px] text-muted-foreground">v0.1.0 · MIT · Not affiliated with Proxmox.</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-8 sm:flex-row sm:gap-16">
|
||||
<nav aria-label="Navigate" class="space-y-2">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Navigate</p>
|
||||
<a href="#failover-playground" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Failover demo</a>
|
||||
<a href="#security" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Security</a>
|
||||
<a href="#tour" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Tour</a>
|
||||
<a href="#console" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Console</a>
|
||||
<a href="#architecture" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Architecture</a>
|
||||
<a href="#download" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Download</a>
|
||||
<DotMatrixText text="NAVIGATE" size="xs" dotClass="text-muted-foreground" />
|
||||
<div class="mt-2 space-y-1">
|
||||
<a href="#failover-playground" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Failover demo</a>
|
||||
<a href="#security" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Security</a>
|
||||
<a href="#tour" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Tour</a>
|
||||
<a href="#console" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Console</a>
|
||||
<a href="#architecture" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Architecture</a>
|
||||
<a href="#download" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Download</a>
|
||||
</div>
|
||||
</nav>
|
||||
<nav aria-label="Source" class="space-y-2">
|
||||
<p class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Source</p>
|
||||
<a href="#" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">GitHub</a>
|
||||
<a href="#" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Build from source</a>
|
||||
<DotMatrixText text="SOURCE" size="xs" dotClass="text-muted-foreground" />
|
||||
<div class="mt-2 space-y-1">
|
||||
<a href="#" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">GitHub</a>
|
||||
<a href="#" class="block font-mono text-[12px] text-muted-foreground transition-colors hover:text-foreground">Build from source</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 flex flex-col gap-2 border-t border-white/[0.06] pt-6 font-mono text-[11px] text-muted-foreground/60 md:flex-row md:items-center md:justify-between">
|
||||
<div class="mt-10 flex flex-col gap-2 border-t border-dotted border-white/10 pt-6 font-mono text-[11px] text-muted-foreground/60 md:flex-row md:items-center md:justify-between">
|
||||
<p>© {year} Clustri. MIT license.</p>
|
||||
<p class="flex items-center gap-2">Press <kbd class="rounded border border-white/[0.08] bg-white/[0.04] px-1.5 py-0.5 text-[10px]">⌘K</kbd> anywhere</p>
|
||||
<p class="flex items-center gap-2">Press <kbd class="rounded border border-dotted border-white/15 bg-white/[0.04] px-1.5 py-0.5 text-[10px]">⌘K</kbd> anywhere</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,38 +1,21 @@
|
||||
---
|
||||
import AppWindowMockup from './AppWindowMockup.astro'
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
|
||||
<section class="relative overflow-hidden border-b border-white/[0.06]">
|
||||
<!-- Subtle grid -->
|
||||
<div class="pointer-events-none absolute inset-0 grid-bg opacity-[0.35] [mask-image:radial-gradient(ellipse_80%_60%_at_50%_0%,black_40%,transparent_85%)]" aria-hidden="true"></div>
|
||||
<section class="relative overflow-hidden border-b border-dotted border-white/10">
|
||||
<div class="pointer-events-none absolute inset-0 dot-grid opacity-[0.18] [mask-image:radial-gradient(ellipse_80%_60%_at_50%_0%,black_40%,transparent_85%)]" aria-hidden="true"></div>
|
||||
|
||||
<!-- Faint topology lines top-right -->
|
||||
<svg class="pointer-events-none absolute right-0 top-0 hidden h-[520px] w-[700px] opacity-[0.07] lg:block" viewBox="0 0 700 520" fill="none" aria-hidden="true">
|
||||
<circle cx="520" cy="120" r="3" fill="white" /><circle cx="620" cy="200" r="3" fill="white" /><circle cx="480" cy="260" r="3" fill="white" />
|
||||
<path d="M520 120 L620 200 M520 120 L480 260 M620 200 L480 260" stroke="white" stroke-width="1" stroke-dasharray="6 6" />
|
||||
<circle cx="520" cy="120" r="18" stroke="white" stroke-width="0.7" opacity="0.5" />
|
||||
<circle cx="620" cy="200" r="18" stroke="white" stroke-width="0.7" opacity="0.5" />
|
||||
<circle cx="480" cy="260" r="18" stroke="white" stroke-width="0.7" opacity="0.5" />
|
||||
<path d="M520 120 L620 200 M520 120 L480 260 M620 200 L480 260" stroke="white" stroke-width="1" stroke-dasharray="2 6" />
|
||||
<circle cx="520" cy="120" r="18" stroke="white" stroke-width="0.7" opacity="0.5" stroke-dasharray="1 4" />
|
||||
<circle cx="620" cy="200" r="18" stroke="white" stroke-width="0.7" opacity="0.5" stroke-dasharray="1 4" />
|
||||
<circle cx="480" cy="260" r="18" stroke="white" stroke-width="0.7" opacity="0.5" stroke-dasharray="1 4" />
|
||||
</svg>
|
||||
|
||||
<div class="relative mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<!-- Copy -->
|
||||
<div class="pt-28 sm:pt-36 lg:pt-40">
|
||||
<div class="flex flex-wrap items-center gap-3 font-mono text-[11px]">
|
||||
<span class="inline-flex items-center gap-1.5 rounded-full border border-success/20 bg-success/10 px-2.5 py-1 text-success">
|
||||
<span class="h-1.5 w-1.5 rounded-full bg-success shadow-[0_0_6px] shadow-success/60"></span>
|
||||
Live
|
||||
</span>
|
||||
<span class="text-muted-foreground">v0.1.0</span>
|
||||
<span class="text-white/20">·</span>
|
||||
<span class="text-muted-foreground">Linux · macOS · Windows</span>
|
||||
<span class="hidden items-center gap-1.5 text-muted-foreground/60 sm:inline-flex">
|
||||
<span class="text-white/10">—</span>
|
||||
<button data-cmdk-trigger class="rounded border border-white/10 px-1.5 py-0.5 text-[10px] hover:border-white/15 hover:text-foreground">⌘K</button>
|
||||
<span>to jump</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 class="mt-6 max-w-[14ch] text-[2.6rem] font-semibold leading-[0.92] tracking-[-0.04em] text-foreground sm:text-[3.5rem] lg:text-[4.6rem]">
|
||||
Your cluster,<br />
|
||||
<span class="text-muted-foreground">not your problem.</span>
|
||||
@@ -44,31 +27,22 @@ import AppWindowMockup from './AppWindowMockup.astro'
|
||||
|
||||
<div class="mt-8 flex flex-wrap items-center gap-3">
|
||||
<a href="#download" class="rounded-[10px] bg-primary px-6 py-3 text-sm font-semibold text-primary-foreground transition hover:bg-primary/90 active:scale-[0.98]">Get Clustri</a>
|
||||
<a href="#failover-playground" class="rounded-[10px] border border-white/[0.08] bg-white/[0.04] px-6 py-3 text-sm font-semibold text-foreground transition hover:border-white/15 hover:bg-white/[0.06] active:scale-[0.98]">Try the failover demo ↓</a>
|
||||
<a href="#failover-playground" class="rounded-[10px] border border-dotted border-white/15 bg-white/[0.04] px-6 py-3 text-sm font-semibold text-foreground transition hover:border-white/20 hover:bg-white/[0.06] active:scale-[0.98]">Try the failover demo ↓</a>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex flex-wrap gap-x-4 gap-y-1 font-mono text-[10px] uppercase tracking-[0.08em] text-muted-foreground/70">
|
||||
<span>Rust · Tauri 2</span><span class="text-white/10">/</span><span>React 19 · Tailwind v4</span><span class="text-white/10">/</span><span>MIT licensed</span>
|
||||
<div class="mt-6 flex flex-wrap gap-x-4 gap-y-1 font-mono text-[11px] uppercase tracking-[0.08em] text-muted-foreground">
|
||||
<DotMatrixText text="RUST" size="xs" dotClass="text-foreground" /><span class="text-muted-foreground">· Tauri 2</span><span class="text-white/10">/</span><DotMatrixText text="REACT" size="xs" dotClass="text-foreground" /><span class="text-muted-foreground">· Tailwind v4</span><span class="text-white/10">/</span><DotMatrixText text="MIT" size="xs" dotClass="text-foreground" /><span class="text-muted-foreground">licensed</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mockup -->
|
||||
<div class="mt-12 sm:mt-16">
|
||||
<div class="relative overflow-hidden rounded-xl border border-white/[0.08] bg-card shadow-card" data-parallax="0.06">
|
||||
<!-- thin orange top rule -->
|
||||
<div class="h-px w-full bg-primary/40" aria-hidden="true"></div>
|
||||
<div class="relative overflow-hidden rounded-xl border border-dotted border-white/15 bg-card shadow-card dot-grid" data-parallax="0.06">
|
||||
<div class="h-px w-full bg-primary/40" style="border-top: 1px dotted oklch(0.72 0.16 52 / 0.4)" aria-hidden="true"></div>
|
||||
<div class="p-2 sm:p-3">
|
||||
<AppWindowMockup />
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-3 text-center font-mono text-[10px] text-muted-foreground/60">Live demo — values tick, tasks stream. The real app looks like this.</p>
|
||||
<p class="mt-3 text-center font-mono text-[10px] text-muted-foreground">Live demo — values tick, tasks stream. The real app looks like this.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
@keyframes hero-in {
|
||||
from { opacity: 0; transform: translateY(14px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
|
||||
<header class="nav-header fixed top-0 left-0 right-0 z-50 px-4 pt-4">
|
||||
<nav
|
||||
class="nav-pill mx-auto flex h-[46px] max-w-6xl items-center justify-between gap-6 rounded-[12px] border border-white/[0.07] bg-[#0f0f14]/70 px-4 backdrop-blur-xl transition-all duration-400"
|
||||
class="nav-pill mx-auto flex h-[46px] max-w-6xl items-center justify-between rounded-[12px] border border-dotted border-white/15 bg-[#0f0f14]/70 px-5 backdrop-blur-xl transition-all duration-400 dot-grid"
|
||||
aria-label="Primary"
|
||||
>
|
||||
<a href="#" class="flex items-center gap-2.5">
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-[8px] bg-primary text-primary-foreground">
|
||||
<a href="#" class="group flex shrink-0 items-center gap-2.5">
|
||||
<span class="flex h-7 w-7 items-center justify-center rounded-[8px] border border-dotted border-white/10 bg-primary text-primary-foreground transition-colors group-hover:bg-primary/90">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-3.5 w-3.5" aria-hidden="true">
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="text-sm font-semibold tracking-tight text-foreground">Clustri</span>
|
||||
<span class="hidden font-mono text-[9px] uppercase tracking-[0.14em] text-muted-foreground sm:inline">Ops Console</span>
|
||||
<DotMatrixText text="CLUSTRI" size="xs" dotClass="text-foreground group-hover:text-primary transition-colors" />
|
||||
</a>
|
||||
|
||||
<div class="hidden items-center gap-5 md:flex">
|
||||
<a href="#failover-playground" class="font-mono text-[11px] uppercase tracking-[0.08em] text-muted-foreground transition-colors hover:text-foreground">Failover</a>
|
||||
<a href="#security" class="font-mono text-[11px] uppercase tracking-[0.08em] text-muted-foreground transition-colors hover:text-foreground">Security</a>
|
||||
<a href="#tour" class="font-mono text-[11px] uppercase tracking-[0.08em] text-muted-foreground transition-colors hover:text-foreground">Tour</a>
|
||||
<a href="#architecture" class="font-mono text-[11px] uppercase tracking-[0.08em] text-muted-foreground transition-colors hover:text-foreground">Architecture</a>
|
||||
<div class="hidden items-center gap-6 md:flex">
|
||||
<span class="h-3 w-px bg-white/10" aria-hidden="true"></span>
|
||||
<a href="#failover-playground" class="group font-mono text-[11px] tracking-wide text-muted-foreground transition-colors hover:text-foreground">FAILOVER</a>
|
||||
<a href="#security" class="group font-mono text-[11px] tracking-wide text-muted-foreground transition-colors hover:text-foreground">SECURITY</a>
|
||||
<a href="#tour" class="group font-mono text-[11px] tracking-wide text-muted-foreground transition-colors hover:text-foreground">TOUR</a>
|
||||
<a href="#architecture" class="group font-mono text-[11px] tracking-wide text-muted-foreground transition-colors hover:text-foreground">ARCH</a>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button data-cmdk-trigger class="hidden sm:flex items-center gap-1.5 rounded-md border border-white/[0.06] bg-white/[0.04] px-2.5 py-1.5 font-mono text-[11px] text-muted-foreground transition-colors hover:border-white/10 hover:text-foreground" aria-label="Open command palette">
|
||||
<div class="flex shrink-0 items-center gap-3">
|
||||
<button data-cmdk-trigger class="hidden sm:flex items-center gap-1.5 rounded-md border border-dotted border-white/10 bg-white/[0.04] px-2.5 py-1.5 font-mono text-[11px] text-muted-foreground transition-colors hover:border-white/15 hover:text-foreground" aria-label="Open command palette">
|
||||
<span>⌘K</span>
|
||||
</button>
|
||||
<a href="#download" class="rounded-[10px] bg-primary px-4 py-2 text-[13px] font-semibold text-primary-foreground transition-all hover:bg-primary/90 active:scale-[0.98]">Get Clustri</a>
|
||||
<a href="#download" class="rounded-[10px] border border-dotted border-primary/30 bg-primary px-4 py-2 text-[13px] font-semibold text-primary-foreground transition-all hover:bg-primary/90 active:scale-[0.98]">Get Clustri</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.nav-scrolled .nav-pill {
|
||||
border-color: rgba(255, 255, 255, 0.08);
|
||||
background-color: oklch(0.11 0.008 275 / 0.85);
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
background-color: oklch(0.145 0.012 250 / 0.85);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<section id="tour" aria-labelledby="tour-title" class="scroll-mt-24 py-20 sm:py-28">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="flex flex-wrap items-end justify-between gap-6">
|
||||
<div data-reveal>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.18em] text-primary">Tour</p>
|
||||
<DotMatrixText text="TOUR" size="xs" dotClass="text-primary" />
|
||||
<h2 id="tour-title" class="mt-4 max-w-[14ch] text-3xl font-semibold tracking-[-0.03em] text-foreground sm:text-4xl">
|
||||
Feels like the Proxmox UI,<br /><span class="text-muted-foreground">without the browser.</span>
|
||||
</h2>
|
||||
@@ -12,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="flex gap-1 rounded-lg border border-white/[0.06] bg-card p-1" role="tablist" aria-label="Screenshot tour" data-reveal data-reveal-delay="100">
|
||||
<div class="flex gap-1 rounded-lg border border-dotted border-white/10 bg-card p-1" role="tablist" aria-label="Screenshot tour" data-reveal data-reveal-delay="100">
|
||||
<button role="tab" aria-selected="true" data-tour-tab="vms" class="rounded-md bg-primary px-3.5 py-1.5 font-mono text-[11px] font-semibold text-primary-foreground">VMs</button>
|
||||
<button role="tab" aria-selected="false" data-tour-tab="console" class="rounded-md px-3.5 py-1.5 font-mono text-[11px] font-medium text-muted-foreground hover:text-foreground">Console</button>
|
||||
<button role="tab" aria-selected="false" data-tour-tab="backups" class="rounded-md px-3.5 py-1.5 font-mono text-[11px] font-medium text-muted-foreground hover:text-foreground">Backups</button>
|
||||
@@ -20,12 +23,12 @@
|
||||
</div>
|
||||
|
||||
<!-- Frame -->
|
||||
<div class="mt-10 overflow-hidden rounded-xl border border-white/[0.07] bg-card shadow-card" data-reveal data-reveal-delay="160">
|
||||
<div class="flex h-9 items-center justify-between border-b border-white/[0.06] bg-background px-4">
|
||||
<div class="mt-10 overflow-hidden rounded-xl border border-dotted border-white/10 bg-card shadow-card" data-reveal data-reveal-delay="160">
|
||||
<div class="flex h-9 items-center justify-between border-b border-dotted border-white/10 bg-background px-4">
|
||||
<div class="flex items-center gap-1.5" aria-hidden="true">
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-white/[0.06] bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
<span class="h-2.5 w-2.5 rounded-full border border-dotted border-white/10 bg-white/10"></span>
|
||||
</div>
|
||||
<p id="tour-frame-label" class="font-mono text-[10px] tabular-nums text-muted-foreground">Virtual Machines — 4 VMs across the cluster</p>
|
||||
<span class="hidden font-mono text-[10px] text-success sm:inline">● live</span>
|
||||
@@ -36,16 +39,16 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4 grid gap-3 sm:grid-cols-3">
|
||||
<div class="rounded-lg border border-white/[0.06] bg-card p-4" data-reveal>
|
||||
<p class="font-mono text-[11px] font-medium uppercase tracking-[0.08em] text-foreground">VMs & Containers</p>
|
||||
<p class="mt-1.5 text-[13px] leading-relaxed text-muted-foreground">Filter by node, type, status. Start / stop / migrate / console in one click. QEMU and LXC.</p>
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-card p-4 dot-grid" data-reveal>
|
||||
<DotMatrixText text="VMS + CONTAINERS" size="xs" dotClass="text-foreground" />
|
||||
<p class="mt-2 text-[13px] leading-relaxed text-muted-foreground">Filter by node, type, status. Start / stop / migrate / console in one click. QEMU and LXC.</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-white/[0.06] bg-card p-4" data-reveal data-reveal-delay="80">
|
||||
<p class="font-mono text-[11px] font-medium uppercase tracking-[0.08em] text-foreground">Console</p>
|
||||
<p class="mt-1.5 text-[13px] leading-relaxed text-muted-foreground">noVNC for VMs, xterm.js for CTs. Fullscreen, resize, Ctrl+Alt+Del passthrough.</p>
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-card p-4 dot-grid" data-reveal data-reveal-delay="80">
|
||||
<DotMatrixText text="CONSOLE" size="xs" dotClass="text-foreground" />
|
||||
<p class="mt-2 text-[13px] leading-relaxed text-muted-foreground">noVNC for VMs, xterm.js for CTs. Fullscreen, resize, Ctrl+Alt+Del passthrough.</p>
|
||||
</div>
|
||||
<div class="rounded-lg border border-white/[0.06] bg-card p-4" data-reveal data-reveal-delay="160">
|
||||
<p class="font-mono text-[11px] font-medium uppercase tracking-[0.08em] text-foreground">Backups</p>
|
||||
<div class="rounded-lg border border-dotted border-white/10 bg-card p-4 dot-grid" data-reveal data-reveal-delay="160">
|
||||
<DotMatrixText text="BACKUPS" size="xs" dotClass="text-foreground" />
|
||||
<p class="mt-1.5 text-[13px] leading-relaxed text-muted-foreground">Jobs, groups, snapshots. Verify, prune (dry-run), GC, archive download via OS dialog.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
<section aria-label="Facts" class="border-y border-white/[0.06] bg-[#08080a]">
|
||||
---
|
||||
import DotMatrixText from './DotMatrixText.astro'
|
||||
---
|
||||
<section aria-label="Facts" class="border-y border-dotted border-white/10 bg-[#08080a] dot-grid">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div class="grid grid-cols-2 gap-0 py-8 sm:grid-cols-4" data-stagger>
|
||||
<div class="flex flex-col items-center gap-1 border-r border-white/[0.06] px-2 py-6" data-stagger-child>
|
||||
<div class="flex flex-col items-center gap-2 border-r border-dotted border-white/10 px-2 py-6" data-stagger-child>
|
||||
<span class="font-mono text-3xl font-semibold tracking-[-0.03em] tabular-nums text-foreground sm:text-4xl" data-countup="0">0</span>
|
||||
<span class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Telemetry</span>
|
||||
<span class="font-mono text-[10px] text-muted-foreground/60">ships with the app</span>
|
||||
<DotMatrixText text="TELEMETRY" size="xs" dotClass="text-muted-foreground" />
|
||||
<span class="font-mono text-[11px] text-muted-foreground">ships with the app</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1 border-r border-white/[0.06] px-2 py-6 sm:border-r" data-stagger-child>
|
||||
<div class="flex flex-col items-center gap-2 border-r border-dotted border-white/10 px-2 py-6 sm:border-r" data-stagger-child>
|
||||
<span class="font-mono text-3xl font-semibold tracking-[-0.03em] tabular-nums text-foreground sm:text-4xl" data-countup="0">0</span>
|
||||
<span class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Cloud dependency</span>
|
||||
<span class="font-mono text-[10px] text-muted-foreground/60">talks to your nodes</span>
|
||||
<DotMatrixText text="CLOUD" size="xs" dotClass="text-muted-foreground" />
|
||||
<span class="font-mono text-[11px] text-muted-foreground">talks to your nodes</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1 border-r border-white/[0.06] px-2 py-6 max-sm:border-r-0" data-stagger-child>
|
||||
<div class="flex flex-col items-center gap-2 border-r border-dotted border-white/10 px-2 py-6 max-sm:border-r-0" data-stagger-child>
|
||||
<span class="font-mono text-3xl font-semibold tracking-[-0.03em] tabular-nums text-primary sm:text-4xl" data-countup="64">0</span>
|
||||
<span class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Rust tests</span>
|
||||
<span class="font-mono text-[10px] text-muted-foreground/60">httpmock · rcgen · tempfile</span>
|
||||
<DotMatrixText text="RUST TESTS" size="xs" dotClass="text-muted-foreground" />
|
||||
<span class="font-mono text-[11px] text-muted-foreground">httpmock · rcgen · tempfile</span>
|
||||
</div>
|
||||
<div class="flex flex-col items-center gap-1 px-2 py-6" data-stagger-child>
|
||||
<div class="flex flex-col items-center gap-2 px-2 py-6" data-stagger-child>
|
||||
<span class="font-mono text-3xl font-semibold tracking-[-0.03em] tabular-nums text-foreground sm:text-4xl" data-countup="3">0</span>
|
||||
<span class="font-mono text-[10px] uppercase tracking-[0.14em] text-muted-foreground">Platforms</span>
|
||||
<span class="font-mono text-[10px] text-muted-foreground/60">Linux · macOS · Windows</span>
|
||||
<DotMatrixText text="PLATFORMS" size="xs" dotClass="text-muted-foreground" />
|
||||
<span class="font-mono text-[11px] text-muted-foreground">Linux · macOS · Windows</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- mono ticker -->
|
||||
<div class="overflow-hidden border-t border-white/[0.06] py-3" aria-hidden="true">
|
||||
<div class="flex gap-6 whitespace-nowrap font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground/40">
|
||||
<span>start</span><span class="text-white/10">·</span><span>stop</span><span class="text-white/10">·</span><span>shutdown</span><span class="text-white/10">·</span><span>reboot</span><span class="text-white/10">·</span><span>suspend</span><span class="text-white/10">·</span><span>resume</span><span class="text-white/10">·</span><span>migrate</span><span class="text-white/10">·</span><span>snapshot</span><span class="text-white/10">·</span><span>backup</span><span class="text-white/10">·</span><span>restore</span><span class="text-white/10">·</span><span>prune</span><span class="text-white/10">·</span><span>GC</span><span class="text-white/10">·</span><span>noVNC</span><span class="text-white/10">·</span><span>xterm.js</span>
|
||||
<div class="overflow-hidden border-t border-dotted border-white/10 py-3" aria-hidden="true">
|
||||
<div class="flex gap-6 whitespace-nowrap font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground/50">
|
||||
<DotMatrixText text="START / STOP / SHUTDOWN / REBOOT / SUSPEND / RESUME / MIGRATE / SNAPSHOT / BACKUP / RESTORE / PRUNE / GC / NOVNC / XTERM.JS" size="xs" dotClass="text-muted-foreground/50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user