Compare commits

...
Author SHA1 Message Date
Anton Ovchinnikov 126a29bef9 Update form 2026-06-07 20:24:27 +02:00
Anton Ovchinnikov 20e85cd998 Another stab at landing 2026-06-07 20:18:47 +02:00
Pasha Sviderski e698f6d06e draft hub landing 2026-05-15 17:56:47 +10:00
8 changed files with 2055 additions and 1 deletions
+1 -1
View File
@@ -27,4 +27,4 @@ COPY ./Caddyfile /etc/caddy/Caddyfile
COPY --from=prod /opt/docusaurus/build /usr/share/caddy COPY --from=prod /opt/docusaurus/build /usr/share/caddy
# Copy the landing page assets. # Copy the landing page assets.
COPY landing/images /usr/share/caddy/images COPY landing/images /usr/share/caddy/images
COPY landing/index.html landing/style.css /usr/share/caddy/ COPY landing/index.html landing/hub.html landing/style.css /usr/share/caddy/
+896
View File
@@ -0,0 +1,896 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Uncloud Hub | Hosted dashboard and observability for Uncloud</title>
<meta name="description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster.">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="./style.css" rel="stylesheet">
<link rel="apple-touch-icon" href="./images/apple-touch-icon.png" sizes="180x180"/>
<link rel="icon" type="image/svg+xml" href="./images/logo.svg"/>
<link rel="alternate icon" type="image/png" href="./images/favicon.png" sizes="96x96"/>
<!-- OpenGraph tags for social media -->
<meta property="og:title"
content="Uncloud Hub | Hosted dashboard and observability for Uncloud"/>
<meta property="og:description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster."/>
<meta property="og:image" content="https://uncloud.run/images/logo-wide.png"/>
<meta property="og:image:alt" content="Uncloud Hub - Observability for self-hosted apps"/>
<meta property="og:url" content="https://uncloud.run/hub"/>
<meta property="og:type" content="website"/>
<meta property="og:site_name" content="Uncloud"/>
<!-- Twitter card tags -->
<meta property="twitter:domain" content="uncloud.run">
<meta property="twitter:url" content="https://uncloud.run/hub">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title"
content="Uncloud Hub | Hosted dashboard and observability for Uncloud">
<meta name="twitter:description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster.">
<meta name="twitter:image" content="https://uncloud.run/images/logo-wide.png">
<meta name="twitter:image:alt" content="Uncloud Hub - Observability for self-hosted apps">
<style>
/* Prevent all inline SVGs from appearing full screen. */
svg {
max-width: 24px;
max-height: 24px;
}
</style>
</head>
<body class="font-inter antialiased bg-white text-zinc-900 tracking-tight">
<!-- Page wrapper -->
<div class="flex flex-col min-h-screen overflow-hidden supports-[overflow:clip]:overflow-clip">
<!-- Site header -->
<header class="absolute top-2 md:top-6 w-full z-30">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="flex items-center justify-between h-14">
<!-- Site branding -->
<div class="shrink-0 mr-4">
<a class="flex items-center" href="/">
<img class="w-8 h-8 rounded-lg shadow-sm shadow-zinc-950/20" src="./images/logo.svg" alt="Logo"
width="32" height="32">
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
</a>
</div>
<!-- Navigation links -->
<nav class="flex grow">
<!-- Left side navigation -->
<ul class="flex items-center">
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition inline-flex items-center"
href="/hub">
Hub
<span class="ml-1.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-700 bg-emerald-100 px-1.5 py-0.5 rounded">
New
</span>
</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="/docs">Docs</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="/blog">Blog</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="https://psviderski.substack.com/" target="_blank">Newsletter</a>
</li>
</ul>
<!-- Right side navigation -->
<ul class="flex grow justify-end items-center shrink-0">
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 flex items-center transition"
href="https://discord.gg/eR35KQJhPu">
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>
<span class="ml-1 hidden sm:inline">Discord</span>
</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-1 sm:px-5 py-2 flex items-center transition"
href="https://github.com/psviderski/uncloud">
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
<span class="ml-1 hidden sm:inline">GitHub</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Page content -->
<main class="grow">
<!-- Hero -->
<section
class="relative before:absolute before:inset-0 before:h-80 before:pointer-events-none before:bg-gradient-to-b before:from-zinc-100 before:-z-10">
<div class="pt-32 pb-12 md:pt-40 md:pb-20">
<!-- Section content -->
<div class="px-4 sm:px-6">
<div class="max-w-3xl mx-auto">
<div class="text-center pb-12 md:pb-16">
<!-- Eyebrow -->
<div class="inline-flex items-center text-sm font-medium text-zinc-500 px-4 py-0.5 mb-6 border border-transparent [background:linear-gradient(theme(colors.white),theme(colors.white))_padding-box,linear-gradient(120deg,theme(colors.zinc.300),theme(colors.zinc.100),theme(colors.zinc.300))_border-box] rounded-full">
<span class="relative flex w-1.5 h-1.5 mr-2">
<span class="absolute inline-flex w-full h-full rounded-full bg-violet-500 opacity-75 animate-ping"></span>
<span class="relative inline-flex w-1.5 h-1.5 rounded-full bg-violet-500"></span>
</span>
Coming soon
</div>
<h1 class="font-inter-tight text-4xl md:text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-zinc-500 via-zinc-900 to-zinc-900 pb-4">
See what's happening<br>
<!-- TODO: highlight anything? e.g. "in production" -->
in production
</h1>
<p class="text-lg text-zinc-500 mb-8">
<b>Uncloud Hub</b> is the UI and Grafana stack for your Uncloud clusters.<br>
Get real-time metrics, logs, and alerts out of the box to catch issues early and fix
faster.
</p>
<div class="max-w-xs mx-auto sm:max-w-none sm:inline-flex sm:justify-center space-y-4 sm:space-y-0 sm:space-x-4">
<div>
<a class="btn text-zinc-100 bg-zinc-900 hover:bg-zinc-800 w-full shadow"
href="#feedback">
Help shape Hub
</a>
</div>
<div>
<a class="btn text-zinc-600 bg-white hover:text-zinc-900 w-full shadow flex items-center justify-center"
href="#how-it-works">
How it works
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Hero image -->
<div class="max-w-6xl mx-auto px-4 sm:px-6 flex justify-center pb-12 md:pb-20 relative before:absolute before:-top-12 before:w-96 before:h-96 before:bg-zinc-900 before:opacity-[.15] before:rounded-full before:blur-3xl before:-z-10">
<img class="rounded-lg shadow-2xl" src="./images/hub-hero.webp" width="2560" height="1520"
alt="Uncloud Hub services dashboard, design preview">
</div>
<!-- Trust / stats -->
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<p class="text-center text-base text-zinc-500 mb-8">
Hub is built for Uncloud, the open-source orchestrator that runs your apps across machines.
</p>
<div class="max-w-sm mx-auto grid gap-12 sm:grid-cols-2 md:grid-cols-4 md:-mx-5 md:gap-0 items-start md:max-w-none">
<!-- GitHub stars -->
<a href="https://github.com/psviderski/uncloud" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(5.2)" x-text="counterValue">0</span>K+</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">GitHub stars</p>
</a>
<!-- Downloads -->
<a href="https://github.com/psviderski/uncloud/releases" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(19)" x-text="counterValue">0</span>K+</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Downloads</p>
</a>
<!-- Hacker News front-page -->
<a href="https://news.ycombinator.com/item?id=46144275" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(2)" x-text="counterValue">0</span>x</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Hacker News front page</p>
</a>
<!-- Discord members. -->
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(366)" x-text="counterValue">0</span></h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Fans on Discord</p>
</a>
</div>
<!-- Animate counter functionality: https://github.com/alpinejs/alpine -->
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('counter', (target = 0, duration = 3000) => ({
startTimestamp: null,
step: null,
rawValue: 0,
counterValue: 0,
target: target,
precision: (target % 1 === 0) ? 0 : (target.toString().split('.')[1] || []).length,
animationRequestId: null,
animationCompleted: false,
observer: null,
init() {
// Intersection observer to watch visibility.
this.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
// Check if element is in view.
if (entry.isIntersecting && !this.animationCompleted) {
this.startAnimation()
}
})
})
this.observer.observe(this.$el)
},
startAnimation() {
this.step = (timestamp) => {
if (!this.startTimestamp) this.startTimestamp = timestamp
const progress = Math.min((timestamp - this.startTimestamp) / duration, 1)
const easedProgress = this.easeOut(progress)
this.rawValue = parseFloat((easedProgress * this.target).toFixed(this.precision))
this.counterValue = this.rawValue.toFixed(this.precision)
if (progress < 1) {
this.animationRequestId = window.requestAnimationFrame(this.step)
} else {
this.animationCompleted = true
}
}
this.animationRequestId = window.requestAnimationFrame(this.step);
},
easeOut(t) {
return 1 - Math.pow(1 - t, 5)
},
destroy() {
// Detach the handler to avoid memory and side-effect leakage.
this.animationRequestId && window.cancelAnimationFrame(this.step)
this.observer && this.observer.disconnect()
},
}))
})
</script>
</div>
</div>
</section>
<!-- Feature blocks -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
What you actually see
</h2>
<p class="text-lg text-zinc-500">
Pre-built dashboards and alerts for the failure modes you care about.
</p>
</div>
<div class="grid sm:grid-cols-2 gap-6 lg:gap-8">
<!-- HTTP edge metrics -->
<article
class="flex flex-col border border-transparent [background:linear-gradient(theme(colors.white),theme(colors.zinc.50))_padding-box,linear-gradient(120deg,theme(colors.zinc.300),theme(colors.zinc.100),theme(colors.zinc.300))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<svg class="stroke-zinc-400 shrink-0" xmlns="http://www.w3.org/2000/svg" width="20"
height="20" fill="none" stroke-width="2" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"/>
<path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
</svg>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-900">
HTTP edge metrics from Caddy
</h3>
</div>
<p class="text-sm text-zinc-500 leading-relaxed">
Per-service latency, error rate, status codes. From the proxy your apps already pass
through. No agent in your app. No code changes.
</p>
</article>
<!-- Logs in context -->
<article
class="flex flex-col border border-transparent [background:linear-gradient(theme(colors.white),theme(colors.zinc.50))_padding-box,linear-gradient(120deg,theme(colors.zinc.300),theme(colors.zinc.100),theme(colors.zinc.300))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<svg class="stroke-zinc-400 shrink-0" xmlns="http://www.w3.org/2000/svg" width="20"
height="20" fill="none" stroke-width="2" viewBox="0 0 24 24">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<path d="M14 2v6h6M9 13h6M9 17h6"/>
</svg>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-900">
Logs next to the spike
</h3>
</div>
<p class="text-sm text-zinc-500 leading-relaxed">
When something goes red, the relevant application logs are right there. No hunting for
log files across machines. No new query language to learn.
</p>
</article>
<!-- Slow Postgres queries -->
<article
class="flex flex-col border border-transparent [background:linear-gradient(theme(colors.white),theme(colors.zinc.50))_padding-box,linear-gradient(120deg,theme(colors.zinc.300),theme(colors.zinc.100),theme(colors.zinc.300))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<svg class="stroke-zinc-400 shrink-0" xmlns="http://www.w3.org/2000/svg" width="20"
height="20" fill="none" stroke-width="2" viewBox="0 0 24 24">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M3 5v14a9 3 0 0 0 18 0V5M3 12a9 3 0 0 0 18 0"/>
</svg>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-900">
Slow Postgres queries
</h3>
</div>
<p class="text-sm text-zinc-500 leading-relaxed">
Hub watches every Uncloud-managed Postgres for queries eating your latency budget.
Find them before your users do.
</p>
</article>
<!-- Alerts that mean something -->
<article
class="flex flex-col border border-transparent [background:linear-gradient(theme(colors.white),theme(colors.zinc.50))_padding-box,linear-gradient(120deg,theme(colors.zinc.300),theme(colors.zinc.100),theme(colors.zinc.300))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<svg class="stroke-zinc-400 shrink-0" xmlns="http://www.w3.org/2000/svg" width="20"
height="20" fill="none" stroke-width="2" viewBox="0 0 24 24">
<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9M10 21a2 2 0 0 0 4 0"/>
</svg>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-900">
Alerts that mean something
</h3>
</div>
<p class="text-sm text-zinc-500 leading-relaxed">
Pre-built rules for the failures you actually care about. Service unavailable. Edge
errors climbing. Disk filling. Postgres replication lag.
</p>
</article>
</div>
</div>
</div>
</section>
<!-- How it works -->
<section id="how-it-works"
class="relative bg-zinc-800 text-zinc-400">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
<div class="inline-flex text-sm font-medium text-zinc-400 px-4 py-0.5 border border-transparent [background:linear-gradient(theme(colors.zinc.800),theme(colors.zinc.800))_padding-box,linear-gradient(120deg,theme(colors.zinc.700),theme(colors.zinc.700/0),theme(colors.zinc.700))_border-box] rounded-full mb-4">
How it works
</div>
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-100 mb-4">
Connect, collect, see
</h2>
<p class="text-lg">
Three steps. The same stack you'd build by hand, set up for you.
</p>
</div>
<div class="grid md:grid-cols-3 gap-6 lg:gap-8">
<!-- Step 1 -->
<div class="border border-transparent [background:linear-gradient(theme(colors.zinc.800),theme(colors.zinc.800))_padding-box,linear-gradient(120deg,theme(colors.zinc.700),theme(colors.zinc.700/0),theme(colors.zinc.700))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<div class="w-7 h-7 rounded-full bg-zinc-700 text-zinc-200 flex items-center justify-center font-semibold text-sm">
1
</div>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-100">Connect</h3>
</div>
<p class="text-sm leading-relaxed mb-4">
Got an Uncloud cluster running? Connect it in one command. New to Uncloud? Hub walks
you through setting one up from your own machines.
</p>
<pre class="text-xs bg-zinc-900/60 border border-zinc-700/50 rounded p-3 text-zinc-300 overflow-x-auto"><span
class="text-emerald-500 select-none">$ </span>uc hub connect</pre>
</div>
<!-- Step 2 -->
<div class="border border-transparent [background:linear-gradient(theme(colors.zinc.800),theme(colors.zinc.800))_padding-box,linear-gradient(120deg,theme(colors.zinc.700),theme(colors.zinc.700/0),theme(colors.zinc.700))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<div class="w-7 h-7 rounded-full bg-zinc-700 text-zinc-200 flex items-center justify-center font-semibold text-sm">
2
</div>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-100">Install the agent</h3>
</div>
<p class="text-sm leading-relaxed mb-4">
Hub detects what's running. Caddy as your ingress, Postgres on machine 2, your worker,
your web app. It configures the right collectors for each. No exporters to install by
hand.
</p>
<div class="text-xs bg-zinc-900/60 border border-zinc-700/50 rounded p-3 text-zinc-400 space-y-1">
<div><span class="text-emerald-500"></span> Detected Caddy ingress</div>
<div><span class="text-emerald-500"></span> Detected Postgres 16</div>
<div><span class="text-emerald-500"></span> Configuring exporters</div>
</div>
</div>
<!-- Step 3 -->
<div class="border border-transparent [background:linear-gradient(theme(colors.zinc.800),theme(colors.zinc.800))_padding-box,linear-gradient(120deg,theme(colors.zinc.700),theme(colors.zinc.700/0),theme(colors.zinc.700))_border-box] rounded-lg p-6">
<div class="flex items-center gap-3 mb-3">
<div class="w-7 h-7 rounded-full bg-zinc-700 text-zinc-200 flex items-center justify-center font-semibold text-sm">
3
</div>
<h3 class="font-inter-tight text-lg font-semibold text-zinc-100">See</h3>
</div>
<p class="text-sm leading-relaxed mb-4">
Within minutes, dashboards are live. Failed HTTP requests, slow queries, application
logs, system health. Plus alerts that page when users are affected.
</p>
<div class="text-xs bg-zinc-900/60 border border-zinc-700/50 rounded p-3 text-zinc-400 space-y-1">
<div>4 dashboards live</div>
<div>12 alert rules active</div>
<div>logs streaming</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- See inside the box -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="lg:flex lg:items-start lg:gap-12">
<div class="lg:flex-1 mb-8 lg:mb-0">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
Same Grafana stack you'd have built. Full access.
</h2>
<p class="text-lg text-zinc-500 leading-relaxed">
Hub does the setup and the ongoing maintenance for you. It ships with opinions about
what's worth watching. Underneath, it's still your data and your tools.
</p>
</div>
<div class="lg:flex-1">
<ul class="space-y-4">
<li class="flex gap-3 items-start">
<svg class="w-5 h-5 text-emerald-500 shrink-0 mt-1" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"/>
</svg>
<div>
<div class="font-medium text-zinc-900">Click any chart, see the query</div>
<div class="text-sm text-zinc-500">No black boxes. The PromQL is right there,
ready to copy or edit.
</div>
</div>
</li>
<li class="flex gap-3 items-start">
<svg class="w-5 h-5 text-emerald-500 shrink-0 mt-1" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"/>
</svg>
<div>
<div class="font-medium text-zinc-900">Customise our dashboards or add
your own
</div>
<div class="text-sm text-zinc-500">Full Grafana editor. Your panels live
alongside the pre-built ones.
</div>
</div>
</li>
<li class="flex gap-3 items-start">
<svg class="w-5 h-5 text-emerald-500 shrink-0 mt-1" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"/>
</svg>
<div>
<div class="font-medium text-zinc-900">Hand the data to an AI agent</div>
<div class="text-sm text-zinc-500">Read-only data API. Ask plain-English
questions about your fleet. It's your data.
</div>
</div>
</li>
<li class="flex gap-3 items-start">
<svg class="w-5 h-5 text-emerald-500 shrink-0 mt-1" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 13l4 4L19 7"/>
</svg>
<div>
<div class="font-medium text-zinc-900">Out of the critical path</div>
<div class="text-sm text-zinc-500">If Hub goes down, your apps keep running.
Hub watches. It does not run things.
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Trust block -->
<section class="relative bg-white">
<div class="py-12 md:py-16">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-3xl mx-auto text-center pb-10">
<p class="text-sm font-medium text-zinc-500 uppercase tracking-wide">
Built on the back of Uncloud
</p>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-8 max-w-4xl mx-auto">
<div class="text-center">
<div class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900">5K+</div>
<div class="text-sm text-zinc-500 mt-1">GitHub stars</div>
</div>
<div class="text-center">
<div class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900">19K+</div>
<div class="text-sm text-zinc-500 mt-1">Downloads</div>
</div>
<div class="text-center">
<div class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900">2x</div>
<div class="text-sm text-zinc-500 mt-1">Hacker News front page</div>
</div>
<div class="text-center">
<div class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900">In&nbsp;use</div>
<div class="text-sm text-zinc-500 mt-1">Radboud University</div>
</div>
</div>
</div>
</div>
</section>
<!-- Note from us -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<div class="text-center mb-8">
<h2 class="font-inter-tight text-2xl md:text-3xl font-bold text-zinc-900 mb-2">
A note from us
</h2>
</div>
<div class="space-y-4 text-lg text-zinc-600 leading-relaxed">
<p>
I'm Pasha. I've spent a decade building commercial software and keeping production
infrastructure healthy. Uncloud started because I wanted a simpler way to run my own apps
across cloud VMs and machines at home, without a Kubernetes degree.
</p>
<p>
Hub is the dashboard we needed for that, productised. If the problem above sounds like your
weekend, We'd like to hear about your setup. The design partner round is small. The
feedback we get now is what shapes what ships.
</p>
</div>
<div class="text-center mt-8">
<a class="text-sm font-medium text-zinc-900 hover:underline" href="#feedback">
Tell us about your setup →
</a>
</div>
</div>
</div>
</section>
<!-- Where Hub is going -->
<section class="relative bg-white">
<div class="py-12 md:py-20">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<h2 class="font-inter-tight text-2xl md:text-3xl font-bold text-zinc-900 mb-6 text-center">
Where Hub is going
</h2>
<div class="space-y-4 text-lg text-zinc-600 leading-relaxed">
<p>
Hub starts with observability because that's what we keep hearing from people running things
on their own servers. After that, the direction is broader. Shared cluster access without
juggling SSH keys. Connectivity for machines behind NAT. A clean way to reach your cluster
from your laptop as if you were already inside it.
</p>
<p>
None of this is on the page yet because none of it is shipping yet. Observability lands
first. The rest follows once the wedge is real.
</p>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<div class="text-center pb-10">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-2">
Frequently asked questions
</h2>
</div>
<div class="space-y-3">
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
What's open source and what's paid?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
Uncloud itself stays open source under Apache-2.0. The on-cluster agent that connects
to Hub will be open source too, with a configurable endpoint. The Hub backend is closed
source and paid.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
Does it work without Uncloud?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
No, by design. Hub knows what's running because Uncloud manages it. That auto-discovery
is the difference between Hub and a generic Grafana you set up yourself.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
Can my team log in too?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
Single user for now. Multi-user and team access are planned. If single user is a
blocker for you, please tell us on the form below. That's a strong signal for
prioritising it sooner.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
Can I self-host Hub itself?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
Planned for orgs that need to keep everything on their own infrastructure. Not in the
first release.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
I already run Grafana. What now?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
Keep it if it's working. Hub can sit alongside as a separate dashboard for the
Uncloud-aware views. If you'd rather stop maintaining your own Grafana, Hub replaces it
entirely. Same Grafana underneath either way.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
When does it ship and how much does it cost?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
First version is in design now. Pricing is per cluster, designed to land below the cost
of the machines you're watching. The form below is the way into the design partner
round.
</p>
</details>
<details
class="group bg-white border border-zinc-200 rounded-lg p-5 [&[open]>summary>svg]:rotate-180">
<summary
class="flex items-center justify-between cursor-pointer font-medium text-zinc-900 list-none">
What if Hub is down?
<svg class="w-4 h-4 text-zinc-400 transition-transform" fill="none"
stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 9l-7 7-7-7"/>
</svg>
</summary>
<p class="text-sm text-zinc-600 leading-relaxed mt-3">
Your apps keep running. Hub watches your cluster from outside it and is not in the
critical path of your traffic, your data, or your deploys. If hub.uncloud.run becomes
unavailable, you lose dashboards and alerts for that period. You do not lose your
applications.
</p>
</details>
</div>
</div>
</div>
</section>
<!-- Final CTA -->
<section id="feedback">
<div class="py-12 md:py-20 border-t border-zinc-200">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="relative max-w-3xl mx-auto text-center">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
Help shape
<em class="relative not-italic inline-flex justify-center items-end">
Hub
<svg class="absolute fill-zinc-300 w-[calc(100%+1rem)] -z-10"
style="max-width: none;"
xmlns="http://www.w3.org/2000/svg" width="120" height="10" viewBox="0 0 120 10"
aria-hidden="true" preserveAspectRatio="none">
<path d="M118.273 6.09C79.243 4.558 40.297 5.459 1.305 9.034c-1.507.13-1.742-1.521-.199-1.81C39.81-.228 79.647-1.568 118.443 4.2c1.63.233 1.377 1.943-.17 1.89Z"/>
</svg>
</em>
</h2>
<p class="text-lg text-zinc-500 mb-8">
We're picking 5 to 10 design partners for the first version. If you run things on a few
servers and the Day 2 part is what's grinding you down, We'd like to hear about your setup.
</p>
<!-- Feedback form. Replace href with the real form URL when ready. -->
<div class="flex justify-center mb-6">
<a class="btn text-zinc-100 bg-zinc-900 hover:bg-zinc-800 shadow"
href="#"
data-todo="link-to-real-feedback-form">
Tell us about your setup
</a>
</div>
<div class="text-sm text-zinc-500">
<p>
Or drop your email below and we'll send updates as Hub takes shape.
</p>
</div>
<!-- Email capture fallback. Reuses the Substack embed from the main site. -->
<div class="flex justify-center mt-4">
<iframe src="https://psviderski.substack.com/embed" width="480" height="150"
style="border:1px solid #EEE; background:white;" frameborder="0"
scrolling="no"></iframe>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- Site footer -->
<footer>
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<!-- Top area: Blocks -->
<div class="grid sm:grid-cols-12 gap-8 py-8 md:py-12 border-t border-zinc-200">
<!-- 1st block - Brand -->
<div class="sm:col-span-6 md:col-span-6 lg:col-span-8 max-md:order-1 flex flex-col">
<div class="mb-4">
<!-- Logo -->
<a class="flex items-center" href="index.html">
<img class="w-8 h-8 rounded shadow-sm shadow-zinc-950/20" src="./images/logo.svg"
alt="Logo">
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
</a>
</div>
<div class="grow text-sm text-zinc-500">
Made with ❤️ by
<a class="underline hover:text-zinc-900" href="https://github.com/unlabs-dev">unLabs</a>
in Australia
</div>
<!-- Social links -->
<ul class="flex space-x-4 mt-4 mb-1">
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://discord.gg/eR35KQJhPu" aria-label="Discord">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="20"
height="20">
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://x.com/psviderski" aria-label="X/Twitter">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path d="m7.063 3 3.495 4.475L14.601 3h2.454l-5.359 5.931L18 17h-4.938l-3.866-4.893L4.771 17H2.316l5.735-6.342L2 3h5.063Zm-.74 1.347H4.866l8.875 11.232h1.36L6.323 4.347Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://t.me/spyyy" aria-label="Telegram">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path d="M17.968 3.276a.338.338 0 0 0-.232-.253 1.192 1.192 0 0 0-.63.045S3.087 8.106 2.286 8.664c-.172.121-.23.19-.259.272-.138.4.293.573.293.573l3.613 1.177a.388.388 0 0 0 .183-.011c.822-.519 8.27-5.222 8.7-5.38.068-.02.118 0 .1.049-.172.6-6.606 6.319-6.64 6.354a.138.138 0 0 0-.05.118l-.337 3.528s-.142 1.1.956 0a30.66 30.66 0 0 1 1.9-1.738c1.242.858 2.58 1.806 3.156 2.3a1 1 0 0 0 .732.283.825.825 0 0 0 .7-.622S17.894 5.292 17.98 3.909c.008-.135.021-.217.021-.317a1.177 1.177 0 0 0-.032-.316Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud" aria-label="GitHub">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="20"
height="20">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
</a>
</li>
</ul>
</div>
<!-- 2nd block - Documentation -->
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
<h6 class="text-sm text-zinc-800 font-medium mb-2">Documentation</h6>
<ul class="text-sm space-y-2">
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/docs/#use-cases">Use cases</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/docs/getting-started/deploy-demo-app">Quick start</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/blog">Blog</a>
</li>
</ul>
</div>
<!-- 3rd block - Resources -->
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
<h6 class="text-sm text-zinc-800 font-medium mb-2">Resources</h6>
<ul class="text-sm space-y-2">
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud">GitHub project</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud/releases">Releases</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud/blob/main/LICENSE">License</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://discord.gg/eR35KQJhPu">Community</a>
</li>
</ul>
</div>
</div>
</div>
</footer>
</div>
<script src="./js/vendors/alpinejs.min.js" defer></script>
</body>
</html>
+716
View File
@@ -0,0 +1,716 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Uncloud Hub | Hosted dashboard and observability for Uncloud</title>
<meta name="description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster.">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="./style.css" rel="stylesheet">
<link rel="apple-touch-icon" href="./images/apple-touch-icon.png" sizes="180x180"/>
<link rel="icon" type="image/svg+xml" href="./images/logo.svg"/>
<link rel="alternate icon" type="image/png" href="./images/favicon.png" sizes="96x96"/>
<!-- OpenGraph tags for social media -->
<meta property="og:title"
content="Uncloud Hub | Hosted dashboard and observability for Uncloud"/>
<meta property="og:description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster."/>
<meta property="og:image" content="https://uncloud.run/images/logo-wide.png"/>
<meta property="og:image:alt" content="Uncloud Hub - Observability for self-hosted apps"/>
<meta property="og:url" content="https://uncloud.run/hub"/>
<meta property="og:type" content="website"/>
<meta property="og:site_name" content="Uncloud"/>
<!-- Twitter card tags -->
<meta property="twitter:domain" content="uncloud.run">
<meta property="twitter:url" content="https://uncloud.run/hub">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title"
content="Uncloud Hub | Hosted dashboard and observability for Uncloud">
<meta name="twitter:description"
content="Uncloud Hub is the UI and hosted Grafana stack for your Uncloud clusters. Get metrics, logs, and alerts out of the box to catch issues early and fix faster.">
<meta name="twitter:image" content="https://uncloud.run/images/logo-wide.png">
<meta name="twitter:image:alt" content="Uncloud Hub - Observability for self-hosted apps">
<style>
/* Prevent all inline SVGs from appearing full screen. */
svg {
max-width: 24px;
max-height: 24px;
}
/* Feature cards */
.hub-feature-card {
padding: 1.5rem;
border: 1px solid #e4e4e7;
border-radius: 0.75rem;
background: #fff;
transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
display: flex;
flex-direction: column;
}
.hub-feature-card:hover {
box-shadow: 0 8px 32px -8px rgba(0,0,0,0.12);
border-color: #a1a1aa;
transform: translateY(-2px);
}
.hub-feature-icon {
width: 2.5rem;
height: 2.5rem;
border-radius: 0.5rem;
background: #f5f3ff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
flex-shrink: 0;
}
.hub-feature-grid {
display: grid;
gap: 1.25rem;
max-width: 56rem;
margin: 0 auto;
}
@media (min-width: 768px) {
.hub-feature-grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>
</head>
<body class="font-inter antialiased bg-white text-zinc-900 tracking-tight">
<!-- Page wrapper -->
<div class="flex flex-col min-h-screen overflow-hidden supports-[overflow:clip]:overflow-clip">
<!-- Site header -->
<header class="absolute top-2 md:top-6 w-full z-30">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="flex items-center justify-between h-14">
<!-- Site branding -->
<div class="shrink-0 mr-4">
<a class="flex items-center" href="/">
<img class="w-8 h-8 rounded-lg shadow-sm shadow-zinc-950/20" src="./images/logo.svg" alt="Logo"
width="32" height="32">
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
</a>
</div>
<!-- Navigation links -->
<nav class="flex grow">
<!-- Left side navigation -->
<ul class="flex items-center">
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition inline-flex items-center"
href="/hub">
Hub
<span class="ml-1.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-700 bg-emerald-100 px-1.5 py-0.5 rounded">
New
</span>
</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="/docs">Docs</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="/blog">Blog</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 transition"
href="https://psviderski.substack.com/" target="_blank">Newsletter</a>
</li>
</ul>
<!-- Right side navigation -->
<ul class="flex grow justify-end items-center shrink-0">
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-2 sm:px-5 py-2 flex items-center transition"
href="https://discord.gg/eR35KQJhPu">
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>
<span class="ml-1 hidden sm:inline">Discord</span>
</a>
</li>
<li>
<a class="text-sm font-medium text-zinc-500 hover:text-zinc-900 px-1 sm:px-5 py-2 flex items-center transition"
href="https://github.com/psviderski/uncloud">
<svg class="w-4 h-4 sm:mr-1 fill-current" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
<span class="ml-1 hidden sm:inline">GitHub</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</header>
<!-- Page content -->
<main class="grow">
<!-- Hero -->
<section
class="relative before:absolute before:inset-0 before:h-80 before:pointer-events-none before:bg-gradient-to-b before:from-zinc-100 before:-z-10">
<div class="pt-32 pb-12 md:pt-40 md:pb-20">
<!-- Section content -->
<div class="px-4 sm:px-6">
<div class="max-w-3xl mx-auto">
<div class="text-center pb-12 md:pb-16">
<!-- Eyebrow -->
<div class="inline-flex items-center text-base font-semibold text-violet-700 px-5 py-1.5 mb-6 border border-transparent rounded-full [background:linear-gradient(theme(colors.violet.50),theme(colors.violet.50))_padding-box,linear-gradient(120deg,theme(colors.violet.400),theme(colors.purple.300),theme(colors.violet.400))_border-box]">
<span class="relative flex w-2 h-2 mr-2.5">
<span class="absolute inline-flex w-full h-full rounded-full bg-violet-500 opacity-75 animate-ping"></span>
<span class="relative inline-flex w-2 h-2 rounded-full bg-violet-500"></span>
</span>
Coming soon
</div>
<h1 class="font-inter-tight text-4xl md:text-5xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-zinc-500 via-zinc-900 to-zinc-900 pb-4">
Uncloud <span class="text-violet-600">Hub</span>:<br>
See what's happening in production
</h1>
<p class="text-lg text-zinc-500 mb-8">
<b>Uncloud Hub</b> is the command center for your Uncloud clusters.<br>
Get real-time metrics, logs, and alerts out of the box to catch issues early and fix
faster.
</p>
<div class="max-w-xs mx-auto sm:max-w-none sm:inline-flex sm:justify-center space-y-4 sm:space-y-0 sm:space-x-4">
<div>
<a class="btn text-zinc-100 bg-zinc-900 hover:bg-zinc-800 w-full shadow"
href="#feedback">
Get early access
</a>
</div>
<div>
<a class="btn text-zinc-600 bg-white hover:text-zinc-900 w-full shadow flex items-center justify-center"
href="#how-it-works">
How it works
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Hero image -->
<div class="max-w-6xl mx-auto px-4 sm:px-6 flex justify-center pb-12 md:pb-20 relative before:absolute before:-top-12 before:w-96 before:h-96 before:bg-zinc-900 before:opacity-[.15] before:rounded-full before:blur-3xl before:-z-10">
<img class="rounded-lg shadow-2xl" src="./images/hub-hero.webp" width="2560" height="1520"
alt="Uncloud Hub services dashboard, design preview">
</div>
<!-- Trust / stats -->
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<p class="text-center text-base text-zinc-500 mb-8">
Hub is built for Uncloud, the open-source orchestrator that runs your apps across machines.
</p>
<div class="max-w-sm mx-auto grid gap-12 sm:grid-cols-2 md:grid-cols-4 md:-mx-5 md:gap-0 items-start md:max-w-none">
<!-- GitHub stars -->
<a href="https://github.com/psviderski/uncloud" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(5.2)" x-text="counterValue">0</span>K+</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">GitHub stars</p>
</a>
<!-- Downloads -->
<a href="https://github.com/psviderski/uncloud/releases" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(19)" x-text="counterValue">0</span>K+</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Downloads</p>
</a>
<!-- Hacker News front-page -->
<a href="https://news.ycombinator.com/item?id=46144275" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(2)" x-text="counterValue">0</span>x</h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Hacker News front page</p>
</a>
<!-- Discord members. -->
<a href="https://uncloud.run/discord" target="_blank" rel="noopener"
class="group block relative text-center md:px-5 after:hidden md:after:block after:absolute after:right-0 after:top-1/2 after:-translate-y-1/2 after:w-px after:h-8 after:border-l after:border-zinc-300 after:border-dashed last:after:hidden">
<h4 class="font-inter-tight text-2xl md:text-3xl font-bold tabular-nums mb-2"><span x-data="counter(368)" x-text="counterValue">0</span></h4>
<p class="text-sm text-zinc-500 transition-colors group-hover:text-zinc-800">Fans on Discord</p>
</a>
</div>
<!-- Animate counter functionality: https://github.com/alpinejs/alpine -->
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('counter', (target = 0, duration = 3000) => ({
startTimestamp: null,
step: null,
rawValue: 0,
counterValue: 0,
target: target,
precision: (target % 1 === 0) ? 0 : (target.toString().split('.')[1] || []).length,
animationRequestId: null,
animationCompleted: false,
observer: null,
init() {
// Intersection observer to watch visibility.
this.observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
// Check if element is in view.
if (entry.isIntersecting && !this.animationCompleted) {
this.startAnimation()
}
})
})
this.observer.observe(this.$el)
},
startAnimation() {
this.step = (timestamp) => {
if (!this.startTimestamp) this.startTimestamp = timestamp
const progress = Math.min((timestamp - this.startTimestamp) / duration, 1)
const easedProgress = this.easeOut(progress)
this.rawValue = parseFloat((easedProgress * this.target).toFixed(this.precision))
this.counterValue = this.rawValue.toFixed(this.precision)
if (progress < 1) {
this.animationRequestId = window.requestAnimationFrame(this.step)
} else {
this.animationCompleted = true
}
}
this.animationRequestId = window.requestAnimationFrame(this.step);
},
easeOut(t) {
return 1 - Math.pow(1 - t, 5)
},
destroy() {
// Detach the handler to avoid memory and side-effect leakage.
this.animationRequestId && window.cancelAnimationFrame(this.step)
this.observer && this.observer.disconnect()
},
}))
})
</script>
</div>
</div>
</section>
<!-- Day 2 problem: the long part of running an app. -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<!-- Section heading -->
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
Your app is deployed. Now what?
</h2>
<p class="text-lg text-zinc-500">
Deploying is the easy part. Everything after is where most teams get stuck.
</p>
</div>
<!-- Day-2 checklist: setup done, observability still open. -->
<div class="max-w-3xl mx-auto">
<div class="relative pb-10">
<ul class="space-y-4">
<!-- Setup steps: done. -->
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full bg-emerald-100 border border-emerald-300 flex items-center justify-center shrink-0" aria-hidden="true">
<svg class="w-4 h-4 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
</span>
<span class="text-lg text-zinc-700">Machines provisioned</span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full bg-emerald-100 border border-emerald-300 flex items-center justify-center shrink-0" aria-hidden="true">
<svg class="w-4 h-4 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
</span>
<span class="text-lg text-zinc-700">App deployed — <code class="font-mono text-base text-zinc-900">uc deploy</code></span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full bg-emerald-100 border border-emerald-300 flex items-center justify-center shrink-0" aria-hidden="true">
<svg class="w-4 h-4 text-emerald-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>
</span>
<span class="text-lg text-zinc-700">HTTPS and custom domain</span>
</li>
<!-- Divider -->
<li class="pt-2 pb-1" aria-hidden="true"><hr class="border-dashed border-zinc-200"></li>
<!-- Day-2 operations: still open. -->
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full border-2 border-dashed border-zinc-400 flex items-center justify-center shrink-0 text-sm font-bold text-zinc-500 leading-none" aria-hidden="true">?</span>
<span class="text-lg text-zinc-500">Services healthy right now?</span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full border-2 border-dashed border-zinc-400 flex items-center justify-center shrink-0 text-sm font-bold text-zinc-500 leading-none" aria-hidden="true">?</span>
<span class="text-lg text-zinc-500">Logs you can search when something breaks?</span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full border-2 border-dashed border-zinc-400 flex items-center justify-center shrink-0 text-sm font-bold text-zinc-500 leading-none" aria-hidden="true">?</span>
<span class="text-lg text-zinc-500">Alerts before users notice?</span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full border-2 border-dashed border-zinc-400 flex items-center justify-center shrink-0 text-sm font-bold text-zinc-500 leading-none" aria-hidden="true">?</span>
<span class="text-lg text-zinc-500">Which deploy caused that error spike?</span>
</li>
<li class="flex items-center gap-3">
<span class="w-8 h-8 rounded-full border-2 border-dashed border-zinc-400 flex items-center justify-center shrink-0 text-sm font-bold text-zinc-500 leading-none" aria-hidden="true">?</span>
<span class="text-lg text-zinc-500">Memory or CPU creeping up overnight?</span>
</li>
</ul>
<!-- Arrow pointing into the next section. -->
<span class="absolute left-1/2 -bottom-1 -translate-x-1/2 translate-y-full" aria-hidden="true">
<svg width="10" height="10" viewBox="0 0 10 10" class="block fill-zinc-300">
<path d="M5 10 0 0 H10 Z"/>
</svg>
</span>
</div>
</div>
</div>
</div>
</section>
<!-- Dashboard overview: what Hub shows you. -->
<section class="relative bg-white">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">Your cluster, at a glance</h2>
<p class="text-lg text-zinc-500">Hub provides you with a comprehensive view of your cluster's health and activity.</p>
</div>
<div style="display:grid;grid-template-columns:2fr 4.5fr;gap:4rem;align-items:center;max-width:72rem;margin:0 auto;">
<!-- Left: feature list -->
<div style="display:flex;flex-direction:column;gap:2rem;">
<div style="display:flex;align-items:flex-start;gap:1rem;">
<div style="width:2.75rem;height:2.75rem;border-radius:0.75rem;background:#f3e8ff;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:20px;max-height:20px;">
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>
</svg>
</div>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1rem;color:#18181b;margin:0 0 0.3rem;">Services</p>
<p style="font-size:0.875rem;color:#71717a;line-height:1.6;margin:0;">See what's running, where it's deployed, and whether it's healthy - across every machine.</p>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;">
<div style="width:2.75rem;height:2.75rem;border-radius:0.75rem;background:#f3e8ff;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:20px;max-height:20px;">
<rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/>
</svg>
</div>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1rem;color:#18181b;margin:0 0 0.3rem;">Machines</p>
<p style="font-size:0.875rem;color:#71717a;line-height:1.6;margin:0;">See all nodes, their status, and what's running on each one. No terminal required.</p>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;">
<div style="width:2.75rem;height:2.75rem;border-radius:0.75rem;background:#f3e8ff;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:20px;max-height:20px;">
<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="4"/><line x1="4.93" y1="4.93" x2="9.17" y2="9.17"/><line x1="14.83" y1="14.83" x2="19.07" y2="19.07"/><line x1="14.83" y1="9.17" x2="19.07" y2="4.93"/><line x1="4.93" y1="19.07" x2="9.17" y2="14.83"/>
</svg>
</div>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1rem;color:#18181b;margin:0 0 0.3rem;">Clusters</p>
<p style="font-size:0.875rem;color:#71717a;line-height:1.6;margin:0;">Manage multiple clusters from one place. Switch context without touching a config file.</p>
</div>
</div>
</div>
<!-- Right: dashboard screenshot -->
<div>
<div style="border-radius:1rem;overflow:hidden;border:1px solid #e4e4e7;">
<img src="images/dashboard-machines.png" alt="Hub cluster dashboard" style="width:100%;display:block;">
</div>
<p style="font-size:0.75rem;font-weight:600;color:#ef4444;text-align:center;margin:0.5rem 0 0;letter-spacing:0.05em;">PLACEHOLDER IMAGE</p>
</div>
</div>
</div>
</div>
</section>
<section class="relative bg-white text-zinc-800">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-3xl mx-auto text-center pb-12 md:pb-16">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">
Untangle the observability mess and start monitoring instantly
</h2>
<p class="text-lg text-zinc-500">
Hub provides a hosted Grafana stack that knows about your Uncloud apps.
</p>
</div>
<!-- Feature blocks: 2x2 grid -->
<div class="hub-feature-grid">
<!-- HTTP request metrics -->
<div class="hub-feature-card">
<div class="hub-feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:18px;max-height:18px;">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
</svg>
</div>
<h3 style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1.0625rem;color:#18181b;margin-bottom:0.5rem;letter-spacing:-0.017em;">HTTP request metrics</h3>
<p style="font-size:0.875rem;color:#71717a;line-height:1.65;">Response times, error rates, and traffic patterns from every service. Caddy is already collecting this. Hub just shows it to you.</p>
</div>
<!-- Container logs -->
<div class="hub-feature-card">
<div class="hub-feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:18px;max-height:18px;">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
<line x1="16" y1="13" x2="8" y2="13"/>
<line x1="16" y1="17" x2="8" y2="17"/>
</svg>
</div>
<h3 style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1.0625rem;color:#18181b;margin-bottom:0.5rem;letter-spacing:-0.017em;">Container logs, always searchable</h3>
<p style="font-size:0.875rem;color:#71717a;line-height:1.65;">Filter by service, level, or time range. Jump from an anomaly in your metrics straight to the logs from that exact window.</p>
</div>
<!-- Slow Postgres queries -->
<div class="hub-feature-card">
<div class="hub-feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:18px;max-height:18px;">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
</svg>
</div>
<h3 style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1.0625rem;color:#18181b;margin-bottom:0.5rem;letter-spacing:-0.017em;">Slow Postgres queries</h3>
<p style="font-size:0.875rem;color:#71717a;line-height:1.65;">Find which queries are dragging down response times. Sorted by impact so you know where to look first, not a wall of query logs.</p>
</div>
<!-- Alerts -->
<div class="hub-feature-card">
<div class="hub-feature-icon">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="max-width:18px;max-height:18px;">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
</svg>
</div>
<h3 style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:1.0625rem;color:#18181b;margin-bottom:0.5rem;letter-spacing:-0.017em;">Alerts on what actually affects users</h3>
<p style="font-size:0.875rem;color:#71717a;line-height:1.65;">Get notified when error rates spike or latency climbs past your threshold. Know before a user reports it. Not after.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Roadmap -->
<section class="relative bg-zinc-50">
<div class="py-12 md:py-20">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-2xl mx-auto">
<div class="text-center pb-10 md:pb-14">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-zinc-900 mb-4">What's next for Uncloud Hub</h2>
<p class="text-lg text-zinc-500">Upcoming features to make Hub even more useful</p>
</div>
<ul style="list-style:none;padding:0;margin:0;">
<li style="padding:1.25rem 0;border-bottom:1px solid #e4e4e7;display:flex;align-items:flex-start;gap:1rem;">
<span style="width:0.5rem;height:0.5rem;border-radius:9999px;background:#7c3aed;flex-shrink:0;margin-top:0.45rem;"></span>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:0.9375rem;color:#18181b;margin:0 0 0.25rem;">Team access</p>
<p style="font-size:0.875rem;color:#71717a;margin:0;line-height:1.6;">Share cluster access with your team without handling SSH keys or root credentials.</p>
</div>
</li>
<li style="padding:1.25rem 0;border-bottom:1px solid #e4e4e7;display:flex;align-items:flex-start;gap:1rem;">
<span style="width:0.5rem;height:0.5rem;border-radius:9999px;background:#7c3aed;flex-shrink:0;margin-top:0.45rem;"></span>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:0.9375rem;color:#18181b;margin:0 0 0.25rem;">Push to deploy</p>
<p style="font-size:0.875rem;color:#71717a;margin:0;line-height:1.6;">Deploy applications from your Git repository automatically.</p>
</div>
</li>
<li style="padding:1.25rem 0;border-bottom:1px solid #e4e4e7;display:flex;align-items:flex-start;gap:1rem;">
<span style="width:0.5rem;height:0.5rem;border-radius:9999px;background:#7c3aed;flex-shrink:0;margin-top:0.45rem;"></span>
<div>
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:0.9375rem;color:#18181b;margin:0 0 0.25rem;">Migration assistant</p>
<p style="font-size:0.875rem;color:#71717a;margin:0;line-height:1.6;">Move your data and services from the cloud and onto your own machines automagically.</p>
</div>
</li>
</ul>
<a href="#feedback" style="display:block;margin-top:1.5rem;border:1.5px dashed #c4b5fd;border-radius:0.75rem;padding:1.25rem 1.5rem;background:#faf5ff;text-decoration:none;transition:border-color 0.2s,background 0.2s;"
onmouseover="this.style.borderColor='#7c3aed';this.style.background='#f3e8ff';"
onmouseout="this.style.borderColor='#c4b5fd';this.style.background='#faf5ff';">
<p style="font-family:'Inter Tight',Inter,sans-serif;font-weight:600;font-size:0.9375rem;color:#7c3aed;margin:0 0 0.2rem;">...Add your most-needed feature</p>
<p style="font-size:0.875rem;color:#71717a;margin:0;line-height:1.6;">Something missing from this list? Tell us about your pain points.</p>
</a>
</div>
</div>
</div>
</section>
<!-- Final CTA: early access -->
<section id="feedback" class="relative bg-zinc-900">
<div class="py-16 md:py-24">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="max-w-lg mx-auto">
<div class="text-center pb-10">
<h2 class="font-inter-tight text-3xl md:text-4xl font-bold text-white mb-4">
Get early access
</h2>
<p class="text-lg text-zinc-400">
Leave your email and we'll reach out with updates.
</p>
</div>
<form action="/fill-form" method="POST">
<div class="mb-4">
<label for="hub-email" class="block text-sm font-medium text-zinc-300 mb-1.5">Your email</label>
<input type="email" name="email" id="hub-email" required
placeholder="you@example.com"
style="display:block;width:100%;background:#fff;border:1px solid #3f3f46;border-radius:0.5rem;padding:0.75rem 1rem;font-size:0.875rem;color:#18181b;outline:none;box-sizing:border-box;">
</div>
<div class="mb-6">
<label for="hub-message" class="block text-sm font-medium text-zinc-300 mb-1.5">
What will you use Hub for? What features would you like to see?
<span class="text-zinc-500 font-normal">(optional)</span>
</label>
<textarea name="message" id="hub-message" rows="3"
placeholder="e.g. searchable logs across all my services, or alerts when something goes down..."
style="display:block;width:100%;background:#fff;border:1px solid #3f3f46;border-radius:0.5rem;padding:0.75rem 1rem;font-size:0.875rem;color:#18181b;outline:none;resize:none;box-sizing:border-box;"></textarea>
</div>
<button type="submit"
class="btn text-zinc-900 bg-white hover:bg-zinc-100 w-full shadow font-semibold">
Register interest
</button>
</form>
<p class="text-center text-sm text-zinc-500 mt-6">
Or email <a href="mailto:pasha@uncloud.run"
class="underline text-zinc-400 hover:text-white transition">pasha@uncloud.run</a> directly.
</p>
</div>
</div>
</div>
</section>
</main>
<!-- Site footer -->
<footer>
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<!-- Top area: Blocks -->
<div class="grid sm:grid-cols-12 gap-8 py-8 md:py-12 border-t border-zinc-200">
<!-- 1st block - Brand -->
<div class="sm:col-span-6 md:col-span-6 lg:col-span-8 max-md:order-1 flex flex-col">
<div class="mb-4">
<!-- Logo -->
<a class="flex items-center" href="index.html">
<img class="w-8 h-8 rounded shadow-sm shadow-zinc-950/20" src="./images/logo.svg"
alt="Logo">
<span class="ml-2 font-semibold text-lg text-zinc-900">uncloud</span>
</a>
</div>
<div class="grow text-sm text-zinc-500">
Made with ❤️ by
<a class="underline hover:text-zinc-900" href="https://github.com/unlabs-dev">unLabs</a>
in Australia
</div>
<!-- Social links -->
<ul class="flex space-x-4 mt-4 mb-1">
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://discord.gg/eR35KQJhPu" aria-label="Discord">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="20"
height="20">
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://x.com/psviderski" aria-label="X/Twitter">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path d="m7.063 3 3.495 4.475L14.601 3h2.454l-5.359 5.931L18 17h-4.938l-3.866-4.893L4.771 17H2.316l5.735-6.342L2 3h5.063Zm-.74 1.347H4.866l8.875 11.232h1.36L6.323 4.347Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://t.me/spyyy" aria-label="Telegram">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
<path d="M17.968 3.276a.338.338 0 0 0-.232-.253 1.192 1.192 0 0 0-.63.045S3.087 8.106 2.286 8.664c-.172.121-.23.19-.259.272-.138.4.293.573.293.573l3.613 1.177a.388.388 0 0 0 .183-.011c.822-.519 8.27-5.222 8.7-5.38.068-.02.118 0 .1.049-.172.6-6.606 6.319-6.64 6.354a.138.138 0 0 0-.05.118l-.337 3.528s-.142 1.1.956 0a30.66 30.66 0 0 1 1.9-1.738c1.242.858 2.58 1.806 3.156 2.3a1 1 0 0 0 .732.283.825.825 0 0 0 .7-.622S17.894 5.292 17.98 3.909c.008-.135.021-.217.021-.317a1.177 1.177 0 0 0-.032-.316Z"/>
</svg>
</a>
</li>
<li>
<a class="flex justify-center items-center text-zinc-700 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud" aria-label="GitHub">
<svg class="fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="20"
height="20">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
</a>
</li>
</ul>
</div>
<!-- 2nd block - Documentation -->
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
<h6 class="text-sm text-zinc-800 font-medium mb-2">Documentation</h6>
<ul class="text-sm space-y-2">
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/docs/#use-cases">Use cases</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/docs/getting-started/deploy-demo-app">Quick start</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="/blog">Blog</a>
</li>
</ul>
</div>
<!-- 3rd block - Resources -->
<div class="sm:col-span-6 md:col-span-3 lg:col-span-2">
<h6 class="text-sm text-zinc-800 font-medium mb-2">Resources</h6>
<ul class="text-sm space-y-2">
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud">GitHub project</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud/releases">Releases</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://github.com/psviderski/uncloud/blob/main/LICENSE">License</a>
</li>
<li>
<a class="text-zinc-500 hover:text-zinc-900 transition"
href="https://discord.gg/eR35KQJhPu">Community</a>
</li>
</ul>
</div>
</div>
</div>
</footer>
</div>
<script src="./js/vendors/alpinejs.min.js" defer></script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because one or more lines are too long
+437
View File
@@ -1139,6 +1139,14 @@ input[type="search"]::-webkit-search-results-decoration {
transition: y 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1), width 0.1s ease-out; transition: y 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1), width 0.1s ease-out;
} }
.visible{
visibility: visible;
}
.collapse{
visibility: collapse;
}
.absolute{ .absolute{
position: absolute; position: absolute;
} }
@@ -1147,6 +1155,14 @@ input[type="search"]::-webkit-search-results-decoration {
position: relative; position: relative;
} }
.-bottom-1{
bottom: -0.25rem;
}
.left-1\/2{
left: 50%;
}
.top-2{ .top-2{
top: 0.5rem; top: 0.5rem;
} }
@@ -1172,10 +1188,18 @@ input[type="search"]::-webkit-search-results-decoration {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.mb-3{
margin-bottom: 0.75rem;
}
.mb-4{ .mb-4{
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.mb-6{
margin-bottom: 1.5rem;
}
.mb-8{ .mb-8{
margin-bottom: 2rem; margin-bottom: 2rem;
} }
@@ -1184,6 +1208,10 @@ input[type="search"]::-webkit-search-results-decoration {
margin-left: 0.25rem; margin-left: 0.25rem;
} }
.ml-1\.5{
margin-left: 0.375rem;
}
.ml-2{ .ml-2{
margin-left: 0.5rem; margin-left: 0.5rem;
} }
@@ -1192,6 +1220,10 @@ input[type="search"]::-webkit-search-results-decoration {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
.mr-2\.5{
margin-right: 0.625rem;
}
.mr-3{ .mr-3{
margin-right: 0.75rem; margin-right: 0.75rem;
} }
@@ -1220,6 +1252,10 @@ input[type="search"]::-webkit-search-results-decoration {
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.mt-3{
margin-top: 0.75rem;
}
.mt-4{ .mt-4{
margin-top: 1rem; margin-top: 1rem;
} }
@@ -1232,6 +1268,10 @@ input[type="search"]::-webkit-search-results-decoration {
margin-top: 2rem; margin-top: 2rem;
} }
.mb-1\.5{
margin-bottom: 0.375rem;
}
.block{ .block{
display: block; display: block;
} }
@@ -1256,6 +1296,14 @@ input[type="search"]::-webkit-search-results-decoration {
display: none; display: none;
} }
.h-1{
height: 0.25rem;
}
.h-1\.5{
height: 0.375rem;
}
.h-14{ .h-14{
height: 3.5rem; height: 3.5rem;
} }
@@ -1280,14 +1328,30 @@ input[type="search"]::-webkit-search-results-decoration {
height: 1.25rem; height: 1.25rem;
} }
.h-7{
height: 1.75rem;
}
.h-8{ .h-8{
height: 2rem; height: 2rem;
} }
.h-full{
height: 100%;
}
.min-h-screen{ .min-h-screen{
min-height: 100vh; min-height: 100vh;
} }
.w-1{
width: 0.25rem;
}
.w-1\.5{
width: 0.375rem;
}
.w-2{ .w-2{
width: 0.5rem; width: 0.5rem;
} }
@@ -1304,6 +1368,10 @@ input[type="search"]::-webkit-search-results-decoration {
width: 1.25rem; width: 1.25rem;
} }
.w-7{
width: 1.75rem;
}
.w-8{ .w-8{
width: 2rem; width: 2rem;
} }
@@ -1332,6 +1400,10 @@ input[type="search"]::-webkit-search-results-decoration {
max-width: 28rem; max-width: 28rem;
} }
.max-w-sm{
max-width: 24rem;
}
.max-w-xl{ .max-w-xl{
max-width: 36rem; max-width: 36rem;
} }
@@ -1340,6 +1412,18 @@ input[type="search"]::-webkit-search-results-decoration {
max-width: 20rem; max-width: 20rem;
} }
.max-w-lg{
max-width: 32rem;
}
.max-w-2xl{
max-width: 42rem;
}
.flex-shrink{
flex-shrink: 1;
}
.shrink-0{ .shrink-0{
flex-shrink: 0; flex-shrink: 0;
} }
@@ -1348,11 +1432,44 @@ input[type="search"]::-webkit-search-results-decoration {
flex-grow: 1; flex-grow: 1;
} }
.-translate-x-1\/2{
--tw-translate-x: -50%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.translate-y-full{
--tw-translate-y: 100%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.transform{
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
@keyframes ping{
75%, 100%{
transform: scale(2);
opacity: 0;
}
}
.animate-ping{
animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.cursor-pointer{
cursor: pointer;
}
.select-none{ .select-none{
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
} }
.list-none{
list-style-type: none;
}
.grid-cols-1{ .grid-cols-1{
grid-template-columns: repeat(1, minmax(0, 1fr)); grid-template-columns: repeat(1, minmax(0, 1fr));
} }
@@ -1389,6 +1506,10 @@ input[type="search"]::-webkit-search-results-decoration {
justify-content: space-between; justify-content: space-between;
} }
.gap-12{
gap: 3rem;
}
.gap-2{ .gap-2{
gap: 0.5rem; gap: 0.5rem;
} }
@@ -1417,6 +1538,12 @@ input[type="search"]::-webkit-search-results-decoration {
margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
} }
.space-y-1 > :not([hidden]) ~ :not([hidden]){
--tw-space-y-reverse: 0;
margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
}
.space-y-12 > :not([hidden]) ~ :not([hidden]){ .space-y-12 > :not([hidden]) ~ :not([hidden]){
--tw-space-y-reverse: 0; --tw-space-y-reverse: 0;
margin-top: calc(3rem * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(3rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1495,6 +1622,15 @@ input[type="search"]::-webkit-search-results-decoration {
border-top-width: 1px; border-top-width: 1px;
} }
.border-dashed{
border-style: dashed;
}
.border-emerald-300{
--tw-border-opacity: 1;
border-color: rgb(110 231 183 / var(--tw-border-opacity));
}
.border-gray-200{ .border-gray-200{
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(229 231 235 / var(--tw-border-opacity)); border-color: rgb(229 231 235 / var(--tw-border-opacity));
@@ -1514,10 +1650,25 @@ input[type="search"]::-webkit-search-results-decoration {
border-color: rgb(228 228 231 / var(--tw-border-opacity)); border-color: rgb(228 228 231 / var(--tw-border-opacity));
} }
.border-zinc-300{
--tw-border-opacity: 1;
border-color: rgb(212 212 216 / var(--tw-border-opacity));
}
.border-zinc-700\/50{ .border-zinc-700\/50{
border-color: rgb(63 63 70 / 0.5); border-color: rgb(63 63 70 / 0.5);
} }
.border-zinc-400{
--tw-border-opacity: 1;
border-color: rgb(161 161 170 / var(--tw-border-opacity));
}
.bg-emerald-100{
--tw-bg-opacity: 1;
background-color: rgb(209 250 229 / var(--tw-bg-opacity));
}
.bg-gray-100{ .bg-gray-100{
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity)); background-color: rgb(243 244 246 / var(--tw-bg-opacity));
@@ -1538,6 +1689,11 @@ input[type="search"]::-webkit-search-results-decoration {
background-color: rgb(239 68 68 / var(--tw-bg-opacity)); background-color: rgb(239 68 68 / var(--tw-bg-opacity));
} }
.bg-violet-500{
--tw-bg-opacity: 1;
background-color: rgb(139 92 246 / var(--tw-bg-opacity));
}
.bg-white{ .bg-white{
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity)); background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -1553,6 +1709,11 @@ input[type="search"]::-webkit-search-results-decoration {
background-color: rgb(250 250 250 / var(--tw-bg-opacity)); background-color: rgb(250 250 250 / var(--tw-bg-opacity));
} }
.bg-zinc-700{
--tw-bg-opacity: 1;
background-color: rgb(63 63 70 / var(--tw-bg-opacity));
}
.bg-zinc-800{ .bg-zinc-800{
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(39 39 42 / var(--tw-bg-opacity)); background-color: rgb(39 39 42 / var(--tw-bg-opacity));
@@ -1563,6 +1724,10 @@ input[type="search"]::-webkit-search-results-decoration {
background-color: rgb(24 24 27 / var(--tw-bg-opacity)); background-color: rgb(24 24 27 / var(--tw-bg-opacity));
} }
.bg-zinc-900\/60{
background-color: rgb(24 24 27 / 0.6);
}
.bg-gradient-to-r{ .bg-gradient-to-r{
background-image: linear-gradient(to right, var(--tw-gradient-stops)); background-image: linear-gradient(to right, var(--tw-gradient-stops));
} }
@@ -1603,6 +1768,10 @@ input[type="search"]::-webkit-search-results-decoration {
stroke: #a1a1aa; stroke: #a1a1aa;
} }
.p-3{
padding: 0.75rem;
}
.p-4{ .p-4{
padding: 1rem; padding: 1rem;
} }
@@ -1611,6 +1780,10 @@ input[type="search"]::-webkit-search-results-decoration {
padding: 1.25rem; padding: 1.25rem;
} }
.p-6{
padding: 1.5rem;
}
.px-0{ .px-0{
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
@@ -1626,6 +1799,11 @@ input[type="search"]::-webkit-search-results-decoration {
padding-right: 0.25rem; padding-right: 0.25rem;
} }
.px-1\.5{
padding-left: 0.375rem;
padding-right: 0.375rem;
}
.px-2{ .px-2{
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
@@ -1636,6 +1814,11 @@ input[type="search"]::-webkit-search-results-decoration {
padding-right: 1rem; padding-right: 1rem;
} }
.px-5{
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.px-6{ .px-6{
padding-left: 1.5rem; padding-left: 1.5rem;
padding-right: 1.5rem; padding-right: 1.5rem;
@@ -1651,6 +1834,16 @@ input[type="search"]::-webkit-search-results-decoration {
padding-bottom: 0.125rem; padding-bottom: 0.125rem;
} }
.py-1{
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
.py-1\.5{
padding-top: 0.375rem;
padding-bottom: 0.375rem;
}
.py-12{ .py-12{
padding-top: 3rem; padding-top: 3rem;
padding-bottom: 3rem; padding-bottom: 3rem;
@@ -1671,6 +1864,19 @@ input[type="search"]::-webkit-search-results-decoration {
padding-bottom: 2rem; padding-bottom: 2rem;
} }
.py-16{
padding-top: 4rem;
padding-bottom: 4rem;
}
.pb-1{
padding-bottom: 0.25rem;
}
.pb-10{
padding-bottom: 2.5rem;
}
.pb-12{ .pb-12{
padding-bottom: 3rem; padding-bottom: 3rem;
} }
@@ -1683,6 +1889,10 @@ input[type="search"]::-webkit-search-results-decoration {
padding-left: 1rem; padding-left: 1rem;
} }
.pt-2{
padding-top: 0.5rem;
}
.pt-32{ .pt-32{
padding-top: 8rem; padding-top: 8rem;
} }
@@ -1711,6 +1921,10 @@ input[type="search"]::-webkit-search-results-decoration {
font-family: Inter Tight, sans-serif; font-family: Inter Tight, sans-serif;
} }
.font-mono{
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
.text-2xl{ .text-2xl{
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1.415; line-height: 1.415;
@@ -1729,6 +1943,16 @@ input[type="search"]::-webkit-search-results-decoration {
letter-spacing: -0.017em; letter-spacing: -0.017em;
} }
.text-\[10px\]{
font-size: 10px;
}
.text-base{
font-size: 1rem;
line-height: 1.5;
letter-spacing: -0.017em;
}
.text-lg{ .text-lg{
font-size: 1.125rem; font-size: 1.125rem;
line-height: 1.5; line-height: 1.5;
@@ -1740,6 +1964,11 @@ input[type="search"]::-webkit-search-results-decoration {
line-height: 1.5715; line-height: 1.5715;
} }
.text-xs{
font-size: 0.75rem;
line-height: 1.5;
}
.font-bold{ .font-bold{
font-weight: 700; font-weight: 700;
} }
@@ -1752,14 +1981,39 @@ input[type="search"]::-webkit-search-results-decoration {
font-weight: 600; font-weight: 600;
} }
.font-normal{
font-weight: 400;
}
.uppercase{
text-transform: uppercase;
}
.not-italic{ .not-italic{
font-style: normal; font-style: normal;
} }
.tabular-nums{
--tw-numeric-spacing: tabular-nums;
font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction);
}
.leading-none{
line-height: 1;
}
.leading-relaxed{
line-height: 1.625;
}
.tracking-tight{ .tracking-tight{
letter-spacing: -0.025em; letter-spacing: -0.025em;
} }
.tracking-wide{
letter-spacing: 0.025em;
}
.text-black{ .text-black{
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(0 0 0 / var(--tw-text-opacity)); color: rgb(0 0 0 / var(--tw-text-opacity));
@@ -1770,6 +2024,16 @@ input[type="search"]::-webkit-search-results-decoration {
color: rgb(16 185 129 / var(--tw-text-opacity)); color: rgb(16 185 129 / var(--tw-text-opacity));
} }
.text-emerald-600{
--tw-text-opacity: 1;
color: rgb(5 150 105 / var(--tw-text-opacity));
}
.text-emerald-700{
--tw-text-opacity: 1;
color: rgb(4 120 87 / var(--tw-text-opacity));
}
.text-gray-500{ .text-gray-500{
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity)); color: rgb(107 114 128 / var(--tw-text-opacity));
@@ -1799,6 +2063,16 @@ input[type="search"]::-webkit-search-results-decoration {
color: transparent; color: transparent;
} }
.text-violet-600{
--tw-text-opacity: 1;
color: rgb(124 58 237 / var(--tw-text-opacity));
}
.text-violet-700{
--tw-text-opacity: 1;
color: rgb(109 40 217 / var(--tw-text-opacity));
}
.text-white{ .text-white{
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity)); color: rgb(255 255 255 / var(--tw-text-opacity));
@@ -1814,6 +2088,11 @@ input[type="search"]::-webkit-search-results-decoration {
color: rgb(228 228 231 / var(--tw-text-opacity)); color: rgb(228 228 231 / var(--tw-text-opacity));
} }
.text-zinc-300{
--tw-text-opacity: 1;
color: rgb(212 212 216 / var(--tw-text-opacity));
}
.text-zinc-400{ .text-zinc-400{
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(161 161 170 / var(--tw-text-opacity)); color: rgb(161 161 170 / var(--tw-text-opacity));
@@ -1853,6 +2132,10 @@ input[type="search"]::-webkit-search-results-decoration {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.opacity-75{
opacity: 0.75;
}
.shadow{ .shadow{
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1899,6 +2182,26 @@ input[type="search"]::-webkit-search-results-decoration {
transition-duration: 150ms; transition-duration: 150ms;
} }
.transition-colors{
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.transition-transform{
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.\[background\:linear-gradient\(theme\(colors\.white\)\2c theme\(colors\.white\)\)_padding-box\2c linear-gradient\(120deg\2c theme\(colors\.violet\.400\)\2c theme\(colors\.purple\.300\)\2c theme\(colors\.violet\.400\)\)_border-box\]{
background: linear-gradient(#fff,#fff) padding-box,linear-gradient(120deg,#a78bfa,#d8b4fe,#a78bfa) border-box;
}
.\[background\:linear-gradient\(theme\(colors\.white\)\2c theme\(colors\.white\)\)_padding-box\2c linear-gradient\(120deg\2c theme\(colors\.zinc\.300\)\2c theme\(colors\.zinc\.100\)\2c theme\(colors\.zinc\.300\)\)_border-box\]{
background: linear-gradient(#fff,#fff) padding-box,linear-gradient(120deg,#d4d4d8,#f4f4f5,#d4d4d8) border-box;
}
.\[background\:linear-gradient\(theme\(colors\.white\)\2c theme\(colors\.zinc\.50\)\)_padding-box\2c linear-gradient\(120deg\2c theme\(colors\.zinc\.300\)\2c theme\(colors\.zinc\.100\)\2c theme\(colors\.zinc\.300\)\)_border-box\]{ .\[background\:linear-gradient\(theme\(colors\.white\)\2c theme\(colors\.zinc\.50\)\)_padding-box\2c linear-gradient\(120deg\2c theme\(colors\.zinc\.300\)\2c theme\(colors\.zinc\.100\)\2c theme\(colors\.zinc\.300\)\)_border-box\]{
background: linear-gradient(#fff,#fafafa) padding-box,linear-gradient(120deg,#d4d4d8,#f4f4f5,#d4d4d8) border-box; background: linear-gradient(#fff,#fafafa) padding-box,linear-gradient(120deg,#d4d4d8,#f4f4f5,#d4d4d8) border-box;
} }
@@ -1907,6 +2210,10 @@ input[type="search"]::-webkit-search-results-decoration {
background: linear-gradient(#27272a,#27272a) padding-box,linear-gradient(120deg,#3f3f46,rgb(63 63 70 / 0),#3f3f46) border-box; background: linear-gradient(#27272a,#27272a) padding-box,linear-gradient(120deg,#3f3f46,rgb(63 63 70 / 0),#3f3f46) border-box;
} }
.\[background\:linear-gradient\(theme\(colors\.violet\.50\)\2c theme\(colors\.violet\.50\)\)_padding-box\2c linear-gradient\(120deg\2c theme\(colors\.violet\.400\)\2c theme\(colors\.purple\.300\)\2c theme\(colors\.violet\.400\)\)_border-box\]{
background: linear-gradient(#f5f3ff,#f5f3ff) padding-box,linear-gradient(120deg,#a78bfa,#d8b4fe,#a78bfa) border-box;
}
/* See Alpine.js: https://github.com/alpinejs/alpine#x-cloak */ /* See Alpine.js: https://github.com/alpinejs/alpine#x-cloak */
[x-cloak=""] { [x-cloak=""] {
@@ -2007,6 +2314,21 @@ input[type="search"]::-webkit-search-results-decoration {
top: 0px; top: 0px;
} }
.after\:top-1\/2::after{
content: var(--tw-content);
top: 50%;
}
.after\:hidden::after{
content: var(--tw-content);
display: none;
}
.after\:h-8::after{
content: var(--tw-content);
height: 2rem;
}
.after\:h-full::after{ .after\:h-full::after{
content: var(--tw-content); content: var(--tw-content);
height: 100%; height: 100%;
@@ -2017,6 +2339,33 @@ input[type="search"]::-webkit-search-results-decoration {
width: 24rem; width: 24rem;
} }
.after\:w-px::after{
content: var(--tw-content);
width: 1px;
}
.after\:-translate-y-1\/2::after{
content: var(--tw-content);
--tw-translate-y: -50%;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.after\:border-l::after{
content: var(--tw-content);
border-left-width: 1px;
}
.after\:border-dashed::after{
content: var(--tw-content);
border-style: dashed;
}
.after\:border-zinc-300::after{
content: var(--tw-content);
--tw-border-opacity: 1;
border-color: rgb(212 212 216 / var(--tw-border-opacity));
}
.after\:bg-gradient-to-l::after{ .after\:bg-gradient-to-l::after{
content: var(--tw-content); content: var(--tw-content);
background-image: linear-gradient(to left, var(--tw-gradient-stops)); background-image: linear-gradient(to left, var(--tw-gradient-stops));
@@ -2029,16 +2378,31 @@ input[type="search"]::-webkit-search-results-decoration {
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
} }
.last\:after\:hidden:last-child::after{
content: var(--tw-content);
display: none;
}
.hover\:bg-zinc-800:hover{ .hover\:bg-zinc-800:hover{
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(39 39 42 / var(--tw-bg-opacity)); background-color: rgb(39 39 42 / var(--tw-bg-opacity));
} }
.hover\:bg-zinc-100:hover{
--tw-bg-opacity: 1;
background-color: rgb(244 244 245 / var(--tw-bg-opacity));
}
.hover\:text-zinc-900:hover{ .hover\:text-zinc-900:hover{
--tw-text-opacity: 1; --tw-text-opacity: 1;
color: rgb(24 24 27 / var(--tw-text-opacity)); color: rgb(24 24 27 / var(--tw-text-opacity));
} }
.hover\:text-white:hover{
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.hover\:underline:hover{ .hover\:underline:hover{
text-decoration-line: underline; text-decoration-line: underline;
} }
@@ -2122,6 +2486,11 @@ input[type="search"]::-webkit-search-results-decoration {
--tw-ring-offset-width: 2px; --tw-ring-offset-width: 2px;
} }
.group:hover .group-hover\:text-zinc-800{
--tw-text-opacity: 1;
color: rgb(39 39 42 / var(--tw-text-opacity));
}
@supports (overflow:clip){ @supports (overflow:clip){
.supports-\[overflow\:clip\]\:overflow-clip{ .supports-\[overflow\:clip\]\:overflow-clip{
overflow: clip; overflow: clip;
@@ -2222,6 +2591,11 @@ input[type="search"]::-webkit-search-results-decoration {
grid-column: span 6 / span 6; grid-column: span 6 / span 6;
} }
.md\:-mx-5{
margin-left: -1.25rem;
margin-right: -1.25rem;
}
.md\:mb-0{ .md\:mb-0{
margin-bottom: 0px; margin-bottom: 0px;
} }
@@ -2230,6 +2604,10 @@ input[type="search"]::-webkit-search-results-decoration {
max-width: 56rem; max-width: 56rem;
} }
.md\:max-w-none{
max-width: none;
}
.md\:grid-cols-2{ .md\:grid-cols-2{
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
@@ -2238,16 +2616,39 @@ input[type="search"]::-webkit-search-results-decoration {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
.md\:grid-cols-4{
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.md\:gap-0{
gap: 0px;
}
.md\:px-5{
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.md\:py-12{ .md\:py-12{
padding-top: 3rem; padding-top: 3rem;
padding-bottom: 3rem; padding-bottom: 3rem;
} }
.md\:py-16{
padding-top: 4rem;
padding-bottom: 4rem;
}
.md\:py-20{ .md\:py-20{
padding-top: 5rem; padding-top: 5rem;
padding-bottom: 5rem; padding-bottom: 5rem;
} }
.md\:py-24{
padding-top: 6rem;
padding-bottom: 6rem;
}
.md\:pb-16{ .md\:pb-16{
padding-bottom: 4rem; padding-bottom: 4rem;
} }
@@ -2260,6 +2661,16 @@ input[type="search"]::-webkit-search-results-decoration {
padding-top: 10rem; padding-top: 10rem;
} }
.md\:pb-14{
padding-bottom: 3.5rem;
}
.md\:text-3xl{
font-size: 2rem;
line-height: 1.3125;
letter-spacing: -0.017em;
}
.md\:text-4xl{ .md\:text-4xl{
font-size: 2.5rem; font-size: 2.5rem;
line-height: 1.25; line-height: 1.25;
@@ -2271,6 +2682,11 @@ input[type="search"]::-webkit-search-results-decoration {
line-height: 1.2; line-height: 1.2;
letter-spacing: -0.017em; letter-spacing: -0.017em;
} }
.md\:after\:block::after{
content: var(--tw-content);
display: block;
}
} }
@media (min-width: 1024px){ @media (min-width: 1024px){
@@ -2282,6 +2698,10 @@ input[type="search"]::-webkit-search-results-decoration {
grid-column: span 8 / span 8; grid-column: span 8 / span 8;
} }
.lg\:mb-0{
margin-bottom: 0px;
}
.lg\:mt-24{ .lg\:mt-24{
margin-top: 6rem; margin-top: 6rem;
} }
@@ -2306,10 +2726,22 @@ input[type="search"]::-webkit-search-results-decoration {
max-width: none; max-width: none;
} }
.lg\:flex-1{
flex: 1 1 0%;
}
.lg\:grid-cols-3{ .lg\:grid-cols-3{
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
.lg\:items-start{
align-items: flex-start;
}
.lg\:gap-12{
gap: 3rem;
}
.lg\:gap-16{ .lg\:gap-16{
gap: 4rem; gap: 4rem;
} }
@@ -2338,3 +2770,8 @@ input[type="search"]::-webkit-search-results-decoration {
margin-left: calc(6rem * calc(1 - var(--tw-space-x-reverse))); margin-left: calc(6rem * calc(1 - var(--tw-space-x-reverse)));
} }
} }
.\[\&\[open\]\>summary\>svg\]\:rotate-180[open]>summary>svg{
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}