1/**
2 * Orbit — SaaS / developer-platform launch page, laid out as an orbital system.
3 *
4 * Cosmic Technical Dark: deep navy, electric cyan, warm white. No images, no
5 * web fonts, no network — the star field is a canvas, the planets are CSS
6 * spheres, every diagram is inline SVG.
7 *
8 * Motion
9 * - Hero: a pinned planetary system. Five orbits each hold a product feature;
10 * scrolling flies the camera inward (log-interpolated zoom, a streaming
11 * star field, outer rings falling off-screen) until the core fills the view.
12 * - Anatomy: a sticky ring diagram follows the feature you are reading.
13 * - Telemetry: a terminal types a deploy, counters + sparklines draw in.
14 * - Integrations: two counter-rotating rings of tools; scroll adds spin.
15 * - Pricing: plans as concentric rings whose arcs fill to their quota.
16 * - Changelog: a pinned spiral draws inward through time as you scroll.
17 *
18 * Reduced motion: no pinning, no rotation, no typing — a complete static
19 * composition (system at rest, spiral fully drawn, all changelog entries).
20 * SSR-safe: no window/document access during render.
21 */
22import { useEffect, useRef, useState, type CSSProperties, type FormEvent, type ReactNode } from "react";
23
24/* ------------------------------------------------------------------ */
25/* Data */
26/* ------------------------------------------------------------------ */
27const sv = (o: Record<string, string | number>) => o as CSSProperties;
28const clamp = (v: number, a = 0, b = 1) => Math.min(b, Math.max(a, v));
29const ease = (t: number) => t * t * (3 - 2 * t);
30
31type GlyphKey = "bolt" | "wave" | "eye" | "shield" | "layers";
32const GLYPHS: Record<GlyphKey, string> = {
33 bolt: "M13 2 4 14h6l-1 8 9-12h-6z",
34 wave: "M2 12c2-7 4-7 6 0s4 7 6 0 4-7 6 0 1 3 2 0",
35 eye: "M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12zM12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z",
36 shield: "M12 2 4 5v6c0 5 3.5 9 8 11 4.5-2 8-6 8-11V5z",
37 layers: "M12 3 2 8l10 5 10-5zM2 13l10 5 10-5",
38};
39function Glyph({ k, size = 24 }: { k: GlyphKey; size?: number }) {
40 return (
41 <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
42 <path d={GLYPHS[k]} />
43 </svg>
44 );
45}
46
47/** Orbits listed outer → inner; the camera visits them in that order. */
48const ORBITS: {
49 name: string; short: string; r: number; speed: number; phase: number; size: number;
50 glyph: GlyphKey; tint: string; tint2: string; line: string; stat: string; body: string;
51 bullets: string[]; code: string;
52}[] = [
53 { name: "Edge Functions", short: "EDGE", r: 430, speed: 0.05, phase: 0.55, size: 50, glyph: "bolt", tint: "#7DF0FF", tint2: "#0B6C86", line: "Run code in 312 cities.", stat: "4.7 ms cold start",
54 body: "Deploy TypeScript to every point of presence with one command. Requests land on the nearest node and start in under five milliseconds.",
55 bullets: ["312 points of presence, 14 core regions", "Streaming responses and WebSockets", "Region pinning for data residency"],
56 code: "export default {\n async fetch(req, env) {\n const geo = req.cf.city;\n return Response.json({ hello: geo });\n },\n};" },
57 { name: "Realtime Sync", short: "SYNC", r: 330, speed: 0.08, phase: 2.35, size: 46, glyph: "wave", tint: "#BFE6FF", tint2: "#3556C8", line: "Every client, same state.", stat: "38 ms p99 fan-out",
58 body: "Presence, live cursors and conflict-free documents over a single socket. Subscribe to a query and the diff arrives before the user blinks.",
59 bullets: ["CRDT documents with offline merge", "Presence for 100k concurrent viewers", "Typed channels, generated from your schema"],
60 code: "const room = orbit.room(\"doc:42\");\nroom.on(\"presence\", (peers) => draw(peers));\nroom.doc.text(\"body\").insert(0, \"Hello\");" },
61 { name: "Observability", short: "TRACE", r: 240, speed: 0.12, phase: 4.1, size: 52, glyph: "eye", tint: "#FBF3E4", tint2: "#9C7B4A", line: "See every request, end to end.", stat: "1 trace, 0 config",
62 body: "Traces, logs and metrics are correlated by default. Click a slow span and jump straight to the log line, the deploy and the commit that caused it.",
63 bullets: ["Automatic distributed tracing", "30-day retention on every plan above Probe", "Alerts that open with the failing trace attached"],
64 code: "$ orbit trace --slow 400ms\n▸ POST /checkout 612ms\n ├ auth.verify 8ms\n ├ db.query 481ms ← orders_by_user\n └ queue.publish 12ms" },
65 { name: "Auth & Access", short: "AUTH", r: 160, speed: 0.19, phase: 1.2, size: 44, glyph: "shield", tint: "#7DF0FF", tint2: "#1F3E9E", line: "Passkeys to policy, one file.", stat: "SOC 2 Type II",
66 body: "Passkeys, SSO and row-level policy declared next to your code. Roles are evaluated at the edge, so a denied request never reaches your database.",
67 bullets: ["Passkeys, magic links, OIDC and SAML", "SCIM provisioning for Station and above", "Policies tested in CI with orbit policy test"],
68 code: "policy(\"orders\", {\n read: (u, row) => row.owner === u.id,\n write: (u) => u.role === \"admin\",\n});" },
69 { name: "Queues & Cron", short: "QUEUE", r: 90, speed: 0.31, phase: 5.2, size: 40, glyph: "layers", tint: "#38E1FF", tint2: "#0A4F9E", line: "Jobs that run exactly once.", stat: "0 lost messages",
70 body: "Durable queues with exactly-once delivery, retries with jitter and cron triggers that survive deploys. The innermost orbit, closest to your data.",
71 bullets: ["Exactly-once delivery with idempotency keys", "Dead-letter inspector and one-click replay", "Cron with timezone and DST awareness"],
72 code: "queue(\"emails\").process(async (job) => {\n await send(job.data);\n}, { retries: 5, backoff: \"jitter\" });" },
73];
74
75/** Camera zoom keyframes. Hold windows sit at the chapter centres. */
76const CH_C = (c: number) => 0.17 + c * 0.14;
77const Z_KEYS: [number, number][] = (() => {
78 const zs = [1, 1.3, 1.79, 2.69, 4.78, 9.5];
79 const k: [number, number][] = [[0, 1]];
80 zs.forEach((z, c) => { k.push([CH_C(c) - 0.035, z]); k.push([CH_C(c) + 0.035, z]); });
81 k.push([1, 11]);
82 return k;
83})();
84function zAt(p: number) {
85 const k = Z_KEYS;
86 if (p <= k[0][0]) return k[0][1];
87 for (let i = 1; i < k.length; i++) {
88 if (p <= k[i][0]) {
89 const [p0, z0] = k[i - 1]; const [p1, z1] = k[i];
90 const u = ease((p - p0) / (p1 - p0 || 1));
91 return Math.exp(Math.log(z0) + (Math.log(z1) - Math.log(z0)) * u);
92 }
93 }
94 return k[k.length - 1][1];
95}
96const CHAPTERS = [
97 ...ORBITS.map((o, i) => ({ k: `ORBIT 0${i + 1} / 05`, t: o.name, b: o.line + " " + o.stat + ".", g: o.glyph })),
98 { k: "CORE", t: "One deploy command", b: "Everything above resolves to a single line: orbit deploy. Welcome to the centre of the system.", g: "bolt" as GlyphKey },
99];
100
101const INTEGRATIONS: { n: string; d: string; c: string; ring: 0 | 1 }[] = [
102 { n: "Marrow", d: "Postgres-compatible serverless database. Branch it per pull request.", c: "#38E1FF", ring: 0 },
103 { n: "Kestrel", d: "Streaming event bus. Two-way sync with Orbit queues.", c: "#BFE6FF", ring: 0 },
104 { n: "Tessera", d: "Enterprise SSO directory. SAML and SCIM, ready in minutes.", c: "#FBF3E4", ring: 0 },
105 { n: "Pylon", d: "Global CDN. Purge from a deploy hook.", c: "#7DF0FF", ring: 0 },
106 { n: "Quill", d: "Transactional email with typed templates.", c: "#9FB4FF", ring: 0 },
107 { n: "Harbor", d: "Container registry. Pull images straight into edge workers.", c: "#38E1FF", ring: 0 },
108 { n: "Sable", d: "Secrets manager. Rotate keys without a redeploy.", c: "#FBF3E4", ring: 0 },
109 { n: "Ledger", d: "Payments and invoicing with webhooks signed at the edge.", c: "#BFE6FF", ring: 0 },
110 { n: "Northstar", d: "Product analytics streamed from request logs.", c: "#7DF0FF", ring: 1 },
111 { n: "Relay", d: "Team chat alerts with the failing trace attached.", c: "#9FB4FF", ring: 1 },
112 { n: "Cinder", d: "CI runner. Preview environments per commit.", c: "#FBF3E4", ring: 1 },
113 { n: "Fathom", d: "Log search with a SQL-like query language.", c: "#38E1FF", ring: 1 },
114 { n: "Lattice", d: "Feature flags evaluated at the edge in microseconds.", c: "#BFE6FF", ring: 1 },
115 { n: "Vellum", d: "Docs hosting with versioned API references.", c: "#7DF0FF", ring: 1 },
116];
117
118const PLANS = [
119 { n: "Probe", sub: "For side projects and first light.", m: 0, a: 0, q: 22, feats: ["100k requests / month", "3 regions, community support", "1 project, 7-day logs"] },
120 { n: "Satellite", sub: "For small teams shipping weekly.", m: 39, a: 31, q: 48, feats: ["5M requests / month", "14 regions, email support", "10 projects, 14-day traces"] },
121 { n: "Station", sub: "For products that cannot go down.", m: 199, a: 159, q: 76, feats: ["100M requests / month", "312 PoPs, SSO and audit log", "99.95% SLA, unlimited projects"] },
122 { n: "Deep Space", sub: "For regulated, planet-scale workloads.", m: -1, a: -1, q: 100, feats: ["Dedicated cells, private links", "99.99% SLA, named engineer", "SOC 2, HIPAA, regional residency"] },
123];
124
125const CHANGELOG = [
126 { v: "4.2", d: "Oct 2026", t: "Perihelion", b: "Region pinning for edge functions and cold starts below five milliseconds on the new V9 isolate pool.", f: 0.04 },
127 { v: "4.1", d: "Aug 2026", t: "Passkey-first auth", b: "Passkeys become the default sign-in. SCIM provisioning ships for Station and above.", f: 0.17 },
128 { v: "4.0", d: "Jun 2026", t: "Event horizon", b: "A rewritten scheduler. Queues reach general availability with exactly-once delivery.", f: 0.3 },
129 { v: "3.8", d: "Apr 2026", t: "Trace to log", b: "Spans link to log lines and deploys. Thirty-day retention becomes the default.", f: 0.43 },
130 { v: "3.5", d: "Jan 2026", t: "Presence v2", b: "Rebuilt presence channel scales to 100k viewers. CRDT documents enter beta.", f: 0.55 },
131 { v: "3.0", d: "Sep 2025", t: "Marrow bindings", b: "Multi-region Postgres bindings with automatic read replicas near your users.", f: 0.67 },
132 { v: "2.0", d: "Mar 2025", t: "Edge runtime", b: "The edge runtime goes public in fourteen regions. The first thousand teams arrive in a week.", f: 0.8 },
133 { v: "0.1", d: "Nov 2023", t: "First light", b: "A command line tool, a single region and a very long list of ideas.", f: 0.93 },
134];
135
136/* Archimedean spiral drawn newest (outside) → oldest (centre) */
137const SPIRAL = (() => {
138 const N = 600, turns = 2.7, th0 = turns * Math.PI * 2, r0 = 22, r1 = 352;
139 const xs: number[] = [], ys: number[] = [], cum: number[] = [0];
140 for (let i = 0; i <= N; i++) {
141 const th = th0 * (1 - i / N);
142 const r = r0 + (r1 - r0) * (th / th0);
143 const a = th - 0.9;
144 xs.push(r * Math.cos(a)); ys.push(r * Math.sin(a));
145 if (i) cum.push(cum[i - 1] + Math.hypot(xs[i] - xs[i - 1], ys[i] - ys[i - 1]));
146 }
147 const total = cum[N];
148 const at = (f: number) => {
149 const target = clamp(f) * total;
150 let lo = 0, hi = N;
151 while (lo < hi - 1) { const m = (lo + hi) >> 1; if (cum[m] <= target) lo = m; else hi = m; }
152 return { x: xs[lo], y: ys[lo] };
153 };
154 const d = xs.map((x, i) => `${i ? "L" : "M"}${x.toFixed(1)} ${ys[i].toFixed(1)}`).join("");
155 return { d, at };
156})();
157
158/* ------------------------------------------------------------------ */
159/* Small hooks / pieces */
160/* ------------------------------------------------------------------ */
161function useInView<T extends Element>(opts?: { once?: boolean; margin?: string }) {
162 const ref = useRef<T>(null);
163 const [on, setOn] = useState(false);
164 useEffect(() => {
165 const el = ref.current; if (!el) return;
166 if (typeof IntersectionObserver === "undefined") { setOn(true); return; }
167 const io = new IntersectionObserver(([e]) => {
168 if (e.isIntersecting) { setOn(true); if (opts?.once !== false) io.disconnect(); } else if (opts?.once === false) setOn(false);
169 }, { rootMargin: opts?.margin ?? "0px 0px -12% 0px", threshold: 0.01 });
170 io.observe(el);
171 return () => io.disconnect();
172 }, []); // eslint-disable-line react-hooks/exhaustive-deps
173 return [ref, on] as const;
174}
175
176const prefersReduced = () => typeof window !== "undefined" && !!window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
177
178function Count({ to, dec = 0, pre = "", suf = "" }: { to: number; dec?: number; pre?: string; suf?: string }) {
179 const [ref, on] = useInView<HTMLSpanElement>();
180 const [v, setV] = useState(0);
181 useEffect(() => {
182 if (!on) return;
183 if (prefersReduced()) { setV(to); return; }
184 let raf = 0; const t0 = performance.now();
185 const f = (n: number) => { const u = clamp((n - t0) / 1600); setV(to * (1 - Math.pow(1 - u, 4))); if (u < 1) raf = requestAnimationFrame(f); };
186 raf = requestAnimationFrame(f);
187 return () => cancelAnimationFrame(raf);
188 }, [on, to]);
189 return <span ref={ref}>{pre}{(on ? v : 0).toFixed(dec)}{suf}</span>;
190}
191
192function Spark({ pts, on }: { pts: number[]; on: boolean }) {
193 const w = 160, h = 44;
194 const d = pts.map((p, i) => `${i ? "L" : "M"}${(i / (pts.length - 1)) * w} ${h - 4 - p * (h - 8)}`).join("");
195 return (
196 <svg className={"orb-spark" + (on ? " on" : "")} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" aria-hidden="true">
197 <path d={`${d}L${w} ${h}L0 ${h}Z`} className="orb-spark-fill" />
198 <path d={d} pathLength={1} className="orb-spark-line" />
199 </svg>
200 );
201}
202
203function Eyebrow({ children }: { children: ReactNode }) {
204 return <div className="orb-eyebrow"><i />{children}</div>;
205}
206
207const NOOP = (e: { preventDefault: () => void }) => e.preventDefault();
208
209/* ------------------------------------------------------------------ */
210/* Page */
211/* ------------------------------------------------------------------ */
212const NAV = [
213 ["orb-top", "Launch"], ["orb-anatomy", "Anatomy"], ["orb-telemetry", "Telemetry"], ["orb-integrations", "Integrations"],
214 ["orb-pricing", "Pricing"], ["orb-changelog", "Changelog"], ["orb-touchdown", "Touchdown"],
215] as const;
216
217export function Page() {
218 const rootRef = useRef<HTMLDivElement>(null);
219 const heroRef = useRef<HTMLElement | null>(null);
220 const starRef = useRef<HTMLCanvasElement>(null);
221 const sysRef = useRef<HTMLDivElement>(null);
222 const svgRef = useRef<SVGSVGElement>(null);
223 const ringRefs = useRef<(SVGEllipseElement | null)[]>([]);
224 const dashRefs = useRef<(SVGEllipseElement | null)[]>([]);
225 const plRefs = useRef<(HTMLDivElement | null)[]>([]);
226 const coreRef = useRef<HTMLDivElement>(null);
227 const copyRef = useRef<HTMLDivElement>(null);
228 const hudRef = useRef<HTMLSpanElement>(null);
229 const barRef = useRef<HTMLSpanElement>(null);
230 const hintRef = useRef<HTMLDivElement>(null);
231 const intRef = useRef<HTMLElement | null>(null);
232 const intBoxRef = useRef<HTMLDivElement>(null);
233 const nodeRefs = useRef<(HTMLButtonElement | null)[]>([]);
234 const chgRef = useRef<HTMLElement | null>(null);
235 const cometRef = useRef<SVGGElement>(null);
236 const spiralRef = useRef<SVGPathElement>(null);
237 const secRefs = useRef<(HTMLElement | null)[]>([]);
238
239 const [ch, setCh] = useState(0);
240 const [feat, setFeat] = useState(0);
241 const [sel, setSel] = useState(-1);
242 const [plan, setPlan] = useState(1);
243 const [annual, setAnnual] = useState(true);
244 const [shown, setShown] = useState(31);
245 const [reached, setReached] = useState(0);
246 const [rail, setRail] = useState(0);
247 const [navSolid, setNavSolid] = useState(false);
248 const [done, setDone] = useState(false);
249 const [email, setEmail] = useState("");
250
251 const [pricingRef, pricingOn] = useInView<HTMLDivElement>();
252 const [termRef, termOn] = useInView<HTMLDivElement>();
253 const [teleRef, teleOn] = useInView<HTMLDivElement>();
254 const [typed, setTyped] = useState(0);
255
256 const TERM = [
257 { c: "cmd", t: "$ orbit init acme-api --runtime edge" },
258 { c: "ok", t: "✓ linked project acme-api (fra, iad, sin)" },
259 { c: "cmd", t: "$ orbit deploy" },
260 { c: "dim", t: "▸ bundling 38 modules ............ 412 ms" },
261 { c: "dim", t: "▸ shipping to 312 points of presence" },
262 { c: "ok", t: "✓ live at https://acme-api.orbit.run" },
263 { c: "dim", t: " p50 11 ms · p99 38 ms · 0 errors" },
264 ];
265 const TERM_LEN = TERM.reduce((a, l) => a + l.t.length + 1, 0);
266
267 /* terminal typing */
268 useEffect(() => {
269 if (!termOn) return;
270 if (prefersReduced()) { setTyped(TERM_LEN); return; }
271 let n = 0;
272 const id = window.setInterval(() => { n += 2; setTyped(n); if (n >= TERM_LEN) window.clearInterval(id); }, 24);
273 return () => window.clearInterval(id);
274 }, [termOn, TERM_LEN]);
275
276 /* pricing number tween */
277 useEffect(() => {
278 const P = PLANS[plan]; const target = P.m < 0 ? 0 : annual ? P.a : P.m;
279 if (prefersReduced()) { setShown(target); return; }
280 let raf = 0; const from = shown; const t0 = performance.now();
281 const f = (n: number) => { const u = clamp((n - t0) / 520); setShown(Math.round(from + (target - from) * (1 - Math.pow(1 - u, 3)))); if (u < 1) raf = requestAnimationFrame(f); };
282 raf = requestAnimationFrame(f);
283 return () => cancelAnimationFrame(raf);
284 }, [plan, annual]); // eslint-disable-line react-hooks/exhaustive-deps
285
286 /* feature scrollspy */
287 useEffect(() => {
288 const root = rootRef.current; if (!root) return;
289 const els = Array.from(root.querySelectorAll<HTMLElement>("[data-feat]"));
290 if (typeof IntersectionObserver === "undefined") return;
291 const io = new IntersectionObserver((es) => {
292 es.forEach((e) => { if (e.isIntersecting) setFeat(Number((e.target as HTMLElement).dataset.feat)); });
293 }, { rootMargin: "-45% 0px -45% 0px" });
294 els.forEach((e) => io.observe(e));
295 return () => io.disconnect();
296 }, []);
297
298 /* generic reveals */
299 useEffect(() => {
300 const root = rootRef.current; if (!root) return;
301 root.classList.add("orb-js");
302 const els = Array.from(root.querySelectorAll<HTMLElement>("[data-rv]"));
303 if (typeof IntersectionObserver === "undefined") { els.forEach((e) => e.classList.add("in")); return; }
304 const io = new IntersectionObserver((es) => es.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }), { rootMargin: "0px 0px -10% 0px" });
305 els.forEach((e) => io.observe(e));
306 return () => io.disconnect();
307 }, []);
308
309 /* master scroll / frame loop */
310 useEffect(() => {
311 const root = rootRef.current, hero = heroRef.current, sys = sysRef.current, svg = svgRef.current, cvs = starRef.current;
312 if (!root || !hero || !sys || !svg || !cvs) return;
313 const rm = prefersReduced();
314 const ctx = cvs.getContext("2d");
315 let vw = 0, vh = 0, S = 0, TILT = 0.34, cy = 0, dpr = 1, intW = 600;
316 const ROT = (-14 * Math.PI) / 180, cosA = Math.cos(ROT), sinA = Math.sin(ROT);
317 const stars = Array.from({ length: 300 }, (_, i) => {
318 const a = (i * 2.399963) % (Math.PI * 2), rr = 0.05 + ((i * 7919) % 1000) / 1000 * 1.1;
319 return { x: Math.cos(a) * rr, y: Math.sin(a) * rr, d: ((i * 104729) % 1000) / 1000, s: 0.5 + ((i * 31) % 10) / 9, w: (i % 5) * 1.3 };
320 });
321 let chCur = -1, featRail = -1, solid = false, reachedCur = -1;
322
323 const resize = () => {
324 vw = window.innerWidth; vh = window.innerHeight; dpr = Math.min(window.devicePixelRatio || 1, 2);
325 S = Math.min(vw * 0.98, 1500);
326 TILT = vw < 700 ? 0.52 : 0.34;
327 cy = vw < 700 ? vh * 0.63 : vh * 0.58;
328 cvs.width = Math.round(vw * dpr); cvs.height = Math.round(vh * dpr);
329 sys.style.top = cy + "px";
330 svg.style.width = S + "px"; svg.style.height = S + "px";
331 ORBITS.forEach((o, i) => {
332 [ringRefs.current[i], dashRefs.current[i]].forEach((el) => el && el.setAttribute("ry", String(o.r * TILT)));
333 });
334 if (intBoxRef.current) intW = intBoxRef.current.clientWidth;
335 };
336
337 const drawStars = (p: number, t: number) => {
338 if (!ctx) return;
339 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
340 ctx.clearRect(0, 0, vw, vh);
341 const cx = vw / 2, spread = Math.max(vw, vh) * 0.5;
342 for (const s of stars) {
343 let zz = (s.d - p * 2.4 - t * 0.004) % 1; if (zz < 0) zz += 1;
344 zz = zz * 0.95 + 0.05;
345 const x = cx + (s.x * spread) / (zz * 1.6), y = cy + (s.y * spread) / (zz * 1.6);
346 if (x < -4 || x > vw + 4 || y < -4 || y > vh + 4) continue;
347 const k = 1 - zz, tw = 0.75 + 0.25 * Math.sin(t * 1.4 + s.w);
348 ctx.globalAlpha = clamp(0.25 + k * 0.9) * tw;
349 ctx.fillStyle = s.d > 0.8 ? "#FBF3E4" : s.d > 0.4 ? "#BFE6FF" : "#7DF0FF";
350 const sz = s.s * (0.6 + k * 1.6);
351 ctx.fillRect(x, y, sz, sz);
352 }
353 ctx.globalAlpha = 1;
354 };
355
356 const renderHero = (p: number, t: number) => {
357 const z = zAt(p), s = S / 1000;
358 svg.style.transform = `translate(-50%,-50%) scale(${z})`;
359 const cc = clamp(Math.round((p - 0.17) / 0.14), 0, 5);
360 if (cc !== chCur) { chCur = cc; setCh(cc); }
361 ORBITS.forEach((o, i) => {
362 const rx = o.r * s * z;
363 const fade = clamp(1 - (rx - vw * 0.62) / (vw * 0.4));
364 const ring = ringRefs.current[i], dash = dashRefs.current[i];
365 if (ring) ring.style.opacity = String(fade * (i === cc ? 1 : 0.42));
366 if (dash) dash.style.opacity = String(fade * (i === cc ? 0.9 : 0.25));
367 const el = plRefs.current[i]; if (!el) return;
368 const th = o.phase + t * o.speed;
369 const ux = o.r * Math.cos(th), uy = o.r * TILT * Math.sin(th);
370 const x = (ux * cosA - uy * sinA) * s * z, y = (ux * sinA + uy * cosA) * s * z;
371 const depth = Math.sin(th);
372 const ps = Math.min(1.55, Math.pow(z, 0.32)) * (0.62 + 0.38 * (depth + 1) / 2) * (vw < 700 ? 0.8 : 1);
373 const edge = clamp(1 - (Math.abs(x) - vw * 0.6) / (vw * 0.15));
374 el.style.transform = `translate3d(${x.toFixed(1)}px,${y.toFixed(1)}px,0) translate(-50%,-50%) scale(${ps.toFixed(3)})`;
375 el.style.opacity = String(edge * (depth < 0 ? 0.62 : 1) * (i === cc || p < 0.12 ? 1 : 0.75));
376 el.style.zIndex = depth > 0 ? "6" : "2";
377 });
378 const core = coreRef.current;
379 if (core) {
380 const cs = 1 + (z - 1) * 1.05;
381 core.style.transform = `translate(-50%,-50%) scale(${cs.toFixed(3)})`;
382 core.style.opacity = String(1 - clamp((p - 0.955) / 0.045));
383 }
384 if (copyRef.current) {
385 copyRef.current.style.opacity = String(1 - clamp(p / 0.08));
386 copyRef.current.style.transform = `translate3d(0,${(-p * 380).toFixed(1)}px,0)`;
387 }
388 if (hintRef.current) hintRef.current.style.opacity = String(1 - clamp(p / 0.03));
389 if (hudRef.current) hudRef.current.textContent = z.toFixed(2) + "×";
390 if (barRef.current) barRef.current.style.transform = `scaleY(${p.toFixed(3)})`;
391 drawStars(p, t);
392 };
393
394 const renderInt = (t: number, sy: number) => {
395 const box = intBoxRef.current; if (!box) return;
396 const outer = INTEGRATIONS.filter((n) => n.ring === 0).length, inner = INTEGRATIONS.length - outer;
397 let io = 0, ii = 0;
398 INTEGRATIONS.forEach((n, i) => {
399 const el = nodeRefs.current[i]; if (!el) return;
400 const isOuter = n.ring === 0;
401 const idx = isOuter ? io++ : ii++;
402 const cnt = isOuter ? outer : inner;
403 const R = intW * (isOuter ? 0.42 : 0.25);
404 const dir = isOuter ? 1 : -1;
405 const a = (idx / cnt) * Math.PI * 2 + dir * (t * 0.045 + sy * 0.00045) + (isOuter ? 0 : 0.5);
406 el.style.transform = `translate(-50%,-50%) translate3d(${(Math.cos(a) * R).toFixed(1)}px,${(Math.sin(a) * R).toFixed(1)}px,0)`;
407 });
408 };
409
410 const renderSpiral = (p: number) => {
411 const draw = clamp(p * 1.12 - 0.02);
412 if (spiralRef.current) spiralRef.current.style.strokeDashoffset = String(1 - draw);
413 const head = SPIRAL.at(draw);
414 if (cometRef.current) cometRef.current.setAttribute("transform", `translate(${head.x.toFixed(1)} ${head.y.toFixed(1)})`);
415 let r = 0; CHANGELOG.forEach((c, i) => { if (draw >= c.f) r = i; });
416 if (r !== reachedCur) { reachedCur = r; setReached(r); }
417 };
418
419 const rect = (el: HTMLElement) => el.getBoundingClientRect();
420 const frame = (now: number) => {
421 const t = (now - t0) / 1000;
422 const sy = window.scrollY;
423 const hr = rect(hero);
424 if (hr.bottom > -50 && hr.top < vh) renderHero(clamp(-hr.top / (hr.height - vh)), t);
425 const ir = intRef.current && rect(intRef.current);
426 if (ir && ir.bottom > -100 && ir.top < vh + 100) renderInt(t, sy);
427 const cr = chgRef.current && rect(chgRef.current);
428 if (cr && cr.bottom > -50 && cr.top < vh) renderSpiral(clamp(-cr.top / (cr.height - vh)));
429 const sol = sy > vh * 0.4; if (sol !== solid) { solid = sol; setNavSolid(sol); }
430 let act = 0; secRefs.current.forEach((el, i) => { if (el && rect(el).top <= vh * 0.5) act = i; });
431 if (act !== featRail) { featRail = act; setRail(act); }
432 raf = requestAnimationFrame(frame);
433 };
434
435 let raf = 0; const t0 = performance.now();
436 resize();
437 if (rm) {
438 renderHero(0, 3); renderInt(2, 0); renderSpiral(1);
439 const onR = () => { resize(); renderHero(0, 3); renderInt(2, 0); };
440 window.addEventListener("resize", onR);
441 return () => window.removeEventListener("resize", onR);
442 }
443 raf = requestAnimationFrame(frame);
444 window.addEventListener("resize", resize);
445 return () => { cancelAnimationFrame(raf); window.removeEventListener("resize", resize); };
446 }, []);
447
448 const go = (id: string) => (e: { preventDefault: () => void }) => {
449 e.preventDefault();
450 const el = document.getElementById(id); if (!el) return;
451 el.scrollIntoView({ behavior: prefersReduced() ? "auto" : "smooth", block: "start" });
452 };
453
454 const P = PLANS[plan];
455 const price = P.m < 0 ? "Custom" : "$" + shown;
456 const activeInt = sel >= 0 ? INTEGRATIONS[sel] : null;
457 const termText = (() => {
458 let left = typed; return TERM.map((l) => { const n = clamp(left, 0, l.t.length); left -= l.t.length + 1; return { c: l.c, t: l.t.slice(0, n) }; });
459 })();
460 const typingDone = typed >= TERM_LEN;
461
462 return (
463 <div ref={rootRef} className="orb" id="orb-top">
464 <style>{CSS}</style>
465
466 {/* ───────────── nav ───────────── */}
467 <header className={"orb-nav" + (navSolid ? " solid" : "")}>
468 <a href="#orb-top" className="orb-brand" onClick={go("orb-top")} aria-label="Orbit home">
469 <svg width="26" height="26" viewBox="0 0 26 26" aria-hidden="true">
470 <ellipse cx="13" cy="13" rx="11.5" ry="4.8" transform="rotate(-24 13 13)" fill="none" stroke="#38E1FF" strokeWidth="1.4" />
471 <circle cx="13" cy="13" r="3.6" fill="#FBF3E4" />
472 <circle cx="22" cy="8.4" r="1.9" fill="#38E1FF" />
473 </svg>
474 <span>Orbit</span>
475 </a>
476 <nav className="orb-links" aria-label="Sections">
477 {NAV.slice(1, 6).map(([id, l]) => <a key={id} href={"#" + id} onClick={go(id)}>{l}</a>)}
478 </nav>
479 <div className="orb-nav-cta">
480 <a href="#" onClick={NOOP} className="orb-link-quiet">Sign in</a>
481 <a href="#orb-touchdown" onClick={go("orb-touchdown")} className="orb-btn sm">Start building</a>
482 </div>
483 </header>
484
485 <nav className="orb-rail" aria-label="Depth">
486 {NAV.map(([id, l], i) => (
487 <a key={id} href={"#" + id} onClick={go(id)} className={i === rail ? "on" : ""} aria-label={l}><span>{l}</span><i /></a>
488 ))}
489 </nav>
490
491 {/* ───────────── hero: the system ───────────── */}
492 <section className="orb-hero" ref={(el) => { heroRef.current = el; secRefs.current[0] = el; }} aria-label="Orbit, the orbital platform">
493 <div className="orb-stick">
494 <div className="orb-nebula" aria-hidden="true" />
495 <canvas ref={starRef} className="orb-stars" aria-hidden="true" />
496 <div className="orb-sys" ref={sysRef} aria-hidden="true">
497 <svg aria-hidden="true" focusable="false" ref={svgRef} className="orb-rings" viewBox="-500 -500 1000 1000">
498 <g transform="rotate(-14)" fill="none">
499 {ORBITS.map((o, i) => (
500 <g key={o.name}>
501 <ellipse ref={(el) => { ringRefs.current[i] = el; }} rx={o.r} ry={o.r * 0.34} stroke="#38E1FF" strokeWidth="1.2" vectorEffect="non-scaling-stroke" />
502 <ellipse ref={(el) => { dashRefs.current[i] = el; }} rx={o.r + 7} ry={(o.r + 7) * 0.34} stroke="#BFE6FF" strokeWidth="1" strokeDasharray="2 9" vectorEffect="non-scaling-stroke" />
503 </g>
504 ))}
505 </g>
506 </svg>
507 <div className="orb-core" ref={coreRef}><i /><b /></div>
508 {ORBITS.map((o, i) => (
509 <div key={o.name} className="orb-pl" ref={(el) => { plRefs.current[i] = el; }}>
510 <div className="orb-pl-body" style={sv({ "--t1": o.tint, "--t2": o.tint2, width: o.size, height: o.size })}>
511 {i === 2 && <em className="orb-pl-ring" />}
512 <Glyph k={o.glyph} size={o.size * 0.46} />
513 </div>
514 <span className="orb-pl-tag" style={{ marginTop: o.size / 2 + 6 }}>{o.short}</span>
515 </div>
516 ))}
517 </div>
518
519 <div className="orb-copy" ref={copyRef}>
520 <div className="orb-pill"><i /> Orbit 4.2 is live · sub-5 ms cold starts</div>
521 <h1>Ship at <span>orbital</span> velocity.</h1>
522 <p>The developer platform where edge compute, realtime, auth and queues share one orbit. Deploy once, land everywhere.</p>
523 <div className="orb-cta">
524 <a href="#orb-touchdown" onClick={go("orb-touchdown")} className="orb-btn">Start building free</a>
525 <a href="#orb-anatomy" onClick={go("orb-anatomy")} className="orb-btn ghost">Explore the system</a>
526 </div>
527 </div>
528
529 <div className="orb-hint" ref={hintRef}><span /> Scroll to fly inward</div>
530
531 <div className="orb-hud">
532 <div className="orb-hud-cap">
533 {CHAPTERS.map((c, i) => (
534 <div key={c.t} className={"orb-hud-card" + (i === ch ? " on" : "")} aria-hidden={i !== ch}>
535 <div className="k">{c.k}</div>
536 <h2><Glyph k={c.g} size={20} />{c.t}</h2>
537 <p>{c.b}</p>
538 </div>
539 ))}
540 </div>
541 <div className="orb-hud-meter" aria-hidden="true">
542 <span className="lab">ZOOM</span>
543 <span className="val" ref={hudRef}>1.00×</span>
544 <span className="trk"><span ref={barRef} /></span>
545 </div>
546 </div>
547 </div>
548 </section>
549
550 {/* ───────────── anatomy ───────────── */}
551 <section className="orb-sec orb-anat" id="orb-anatomy" ref={(el) => { secRefs.current[1] = el; }}>
552 <div className="orb-wrap orb-anat-grid">
553 <div className="orb-anat-side">
554 <Eyebrow>Anatomy of the system</Eyebrow>
555 <h2 className="orb-h2">Five orbits. <em>One gravity.</em></h2>
556 <p className="orb-lede">Each capability sits in its own orbit but pulls on the same core: one config, one deploy, one bill. Scroll the system.</p>
557 <svg className="orb-anat-svg" viewBox="-200 -200 400 400" role="img" aria-label={`Orbit diagram, ${ORBITS[feat].name} highlighted`}>
558 <defs>
559 <radialGradient id="orbg-core"><stop offset="0" stopColor="#FBF3E4" /><stop offset=".4" stopColor="#7DF0FF" /><stop offset="1" stopColor="#38E1FF" stopOpacity="0" /></radialGradient>
560 </defs>
561 <circle r="34" fill="url(#orbg-core)" opacity=".9" />
562 {[...ORBITS].reverse().map((o, k) => {
563 const i = ORBITS.length - 1 - k, r = 48 + k * 33, on = i === feat;
564 return (
565 <g key={o.name} className={"orb-anat-ring" + (on ? " on" : "")} style={{ animationDuration: 26 - k * 3 + "s" }}>
566 <circle r={r} fill="none" stroke={on ? "#38E1FF" : "#7aa5d8"} strokeOpacity={on ? 1 : 0.28} strokeWidth={on ? 1.6 : 1} strokeDasharray={on ? "none" : "3 6"} />
567 <circle cx={r} cy="0" r={on ? 7 : 4} fill={on ? "#FBF3E4" : "#8CA2C6"} />
568 </g>
569 );
570 })}
571 </svg>
572 </div>
573 <div className="orb-anat-list">
574 {ORBITS.map((o, i) => (
575 <article key={o.name} data-feat={i} data-rv className={"orb-feat" + (i === feat ? " on" : "")}>
576 <div className="orb-feat-k"><Glyph k={o.glyph} size={18} /> ORBIT 0{i + 1} · {o.stat}</div>
577 <h3>{o.name}</h3>
578 <p>{o.body}</p>
579 <ul>{o.bullets.map((b) => <li key={b}>{b}</li>)}</ul>
580 <pre className="orb-code" aria-label={o.name + " example"}><code>{o.code}</code></pre>
581 </article>
582 ))}
583 </div>
584 </div>
585 </section>
586
587 {/* ───────────── telemetry ───────────── */}
588 <section className="orb-sec orb-tele" id="orb-telemetry" ref={(el) => { secRefs.current[2] = el; }}>
589 <div className="orb-wrap">
590 <div className="orb-tele-head" data-rv>
591 <Eyebrow>Launch sequence</Eyebrow>
592 <h2 className="orb-h2">T-minus <em>one command.</em></h2>
593 </div>
594 <div className="orb-tele-grid">
595 <div className="orb-term" ref={termRef} data-rv>
596 <div className="orb-term-bar"><i /><i /><i /><span>~/acme-api — orbit</span></div>
597 <div className="orb-term-body" aria-label="Terminal session deploying to Orbit">
598 {termText.map((l, i) => (
599 <div key={i} className={"ln " + l.c}>{l.t || " "}{!typingDone && l.t.length > 0 && l.t.length < TERM[i].t.length && <b className="orb-caret" />}</div>
600 ))}
601 {typingDone && <div className="ln cmd">$ <b className="orb-caret" /></div>}
602 </div>
603 </div>
604 <div className="orb-stats" ref={teleRef}>
605 {[
606 { l: "Global p99 latency", to: 38, suf: " ms", dec: 0, pts: [0.7, 0.62, 0.66, 0.5, 0.44, 0.48, 0.36, 0.3, 0.28, 0.22] },
607 { l: "Points of presence", to: 312, suf: "", dec: 0, pts: [0.1, 0.14, 0.2, 0.3, 0.38, 0.5, 0.62, 0.74, 0.86, 0.95] },
608 { l: "Uptime, trailing 12 months", to: 99.995, suf: "%", dec: 3, pts: [0.8, 0.84, 0.82, 0.88, 0.9, 0.9, 0.94, 0.95, 0.96, 0.97] },
609 { l: "Requests served per day", to: 2.1, suf: "B", dec: 1, pts: [0.15, 0.2, 0.28, 0.3, 0.42, 0.52, 0.6, 0.7, 0.83, 0.92] },
610 ].map((s, i) => (
611 <div key={s.l} className="orb-stat" data-rv style={sv({ "--d": i * 0.08 + "s" })}>
612 <div className="orb-stat-l">{s.l}</div>
613 <div className="orb-stat-v"><Count to={s.to} dec={s.dec} suf={s.suf} /></div>
614 <Spark pts={s.pts} on={teleOn} />
615 </div>
616 ))}
617 </div>
618 </div>
619 </div>
620 </section>
621
622 {/* ───────────── integrations ring ───────────── */}
623 <section className="orb-sec orb-int" id="orb-integrations" ref={(el) => { intRef.current = el; secRefs.current[3] = el; }}>
624 <div className="orb-wrap orb-center">
625 <div data-rv>
626 <Eyebrow>Integrations</Eyebrow>
627 <h2 className="orb-h2">Plays well with <em>everything in range.</em></h2>
628 </div>
629 </div>
630 <div className="orb-ring-box" ref={intBoxRef}>
631 <svg className="orb-ring-svg" viewBox="0 0 100 100" aria-hidden="true">
632 <circle cx="50" cy="50" r="42" fill="none" stroke="#38E1FF" strokeOpacity=".28" strokeWidth="1" vectorEffect="non-scaling-stroke" />
633 <circle cx="50" cy="50" r="25" fill="none" stroke="#BFE6FF" strokeOpacity=".22" strokeWidth="1" strokeDasharray="1 4" vectorEffect="non-scaling-stroke" />
634 <circle cx="50" cy="50" r="11" fill="none" stroke="#38E1FF" strokeOpacity=".18" strokeWidth="1" vectorEffect="non-scaling-stroke" />
635 </svg>
636 <div className="orb-ring-mid" aria-live="polite">
637 {activeInt ? (
638 <><div className="orb-ring-n" style={{ color: activeInt.c }}>{activeInt.n}</div><p>{activeInt.d}</p></>
639 ) : (
640 <><div className="orb-ring-n">{INTEGRATIONS.length}+ native</div><p>Hover or focus a node. Hundreds more through webhooks and the open connector spec.</p></>
641 )}
642 </div>
643 {INTEGRATIONS.map((n, i) => (
644 <button key={n.n} type="button" className={"orb-node" + (n.ring ? " in2" : "") + (i === sel ? " on" : "")}
645 ref={(el) => { nodeRefs.current[i] = el; }}
646 style={sv({ "--c": n.c })}
647 onMouseEnter={() => setSel(i)} onFocus={() => setSel(i)} onMouseLeave={() => setSel(-1)} onBlur={() => setSel(-1)}
648 aria-label={n.n + ": " + n.d}>
649 <span>{n.n.slice(0, 1)}</span><em>{n.n}</em>
650 </button>
651 ))}
652 </div>
653 </section>
654
655 {/* ───────────── pricing rings ───────────── */}
656 <section className="orb-sec orb-price" id="orb-pricing" ref={(el) => { secRefs.current[4] = el; }}>
657 <div className="orb-wrap">
658 <div className="orb-tele-head" data-rv>
659 <Eyebrow>Pricing</Eyebrow>
660 <h2 className="orb-h2">Pick your <em>altitude.</em></h2>
661 </div>
662 <div className="orb-price-grid">
663 <div className={"orb-price-fig" + (pricingOn ? " on" : "")} ref={pricingRef}>
664 <svg viewBox="0 0 520 520" role="img" aria-label={`Pricing rings, ${P.n} selected`}>
665 <defs>
666 <filter id="orbg-glow" x="-30%" y="-30%" width="160%" height="160%"><feGaussianBlur stdDeviation="5" result="b" /><feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge></filter>
667 </defs>
668 {PLANS.map((pl, i) => {
669 const r = 72 + i * 46, on = i === plan;
670 return (
671 <g key={pl.n} className={"orb-pr" + (on ? " on" : "")} onClick={() => setPlan(i)}>
672 <circle cx="260" cy="260" r={r} className="orb-pr-track" fill="none" strokeWidth="22" />
673 <circle cx="260" cy="260" r={r} className="orb-pr-arc" fill="none" strokeWidth="22" strokeLinecap="butt" pathLength={100}
674 transform="rotate(-90 260 260)" style={sv({ "--q": pl.q })} filter={on ? "url(#orbg-glow)" : undefined} />
675 <circle cx="260" cy="260" r={r} fill="none" stroke="transparent" strokeWidth="40" style={{ cursor: "pointer" }} />
676 <text x="260" y={260 - r + 4} textAnchor="middle" className="orb-pr-lab">{pl.n.toUpperCase()}</text>
677 </g>
678 );
679 })}
680 <text x="260" y="256" textAnchor="middle" className="orb-pr-price">{price}</text>
681 <text x="260" y="278" textAnchor="middle" className="orb-pr-per">{P.m < 0 ? "talk to us" : P.m === 0 ? "forever" : "per month" + (annual ? ", billed yearly" : "")}</text>
682 </svg>
683 </div>
684 <div className="orb-price-info" data-rv>
685 <div className="orb-seg" role="group" aria-label="Billing period">
686 <button type="button" aria-pressed={!annual} className={!annual ? "on" : ""} onClick={() => setAnnual(false)}>Monthly</button>
687 <button type="button" aria-pressed={annual} className={annual ? "on" : ""} onClick={() => setAnnual(true)}>Yearly <small>−20%</small></button>
688 </div>
689 <div className="orb-plans" role="tablist" aria-label="Plans">
690 {PLANS.map((pl, i) => (
691 <button key={pl.n} type="button" role="tab" aria-selected={i === plan} className={i === plan ? "on" : ""} onClick={() => setPlan(i)}>{pl.n}</button>
692 ))}
693 </div>
694 <div className="orb-plan-card" key={plan}>
695 <h3>{P.n}</h3>
696 <p className="sub">{P.sub}</p>
697 <ul>{P.feats.map((f) => <li key={f}>{f}</li>)}</ul>
698 <a href="#orb-touchdown" onClick={go("orb-touchdown")} className="orb-btn">{P.m < 0 ? "Talk to an engineer" : P.m === 0 ? "Launch for free" : "Start " + P.n}</a>
699 </div>
700 </div>
701 </div>
702 </div>
703 </section>
704
705 {/* ───────────── changelog spiral ───────────── */}
706 <section className="orb-chg" id="orb-changelog" ref={(el) => { chgRef.current = el; secRefs.current[5] = el; }}>
707 <div className="orb-chg-stick">
708 <div className="orb-chg-fig">
709 <svg viewBox="-400 -400 800 800" role="img" aria-label="Spiral timeline of Orbit releases">
710 <defs>
711 <filter id="orbg-glow2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b" /><feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge></filter>
712 </defs>
713 <path d={SPIRAL.d} fill="none" stroke="#7aa5d8" strokeOpacity=".16" strokeWidth="1.5" strokeDasharray="2 7" />
714 <path ref={spiralRef} d={SPIRAL.d} pathLength={1} fill="none" stroke="#38E1FF" strokeWidth="2.4" strokeLinecap="round" className="orb-spiral" filter="url(#orbg-glow2)" />
715 {CHANGELOG.map((c, i) => {
716 const pt = SPIRAL.at(c.f), on = i <= reached, cur = i === reached;
717 return (
718 <g key={c.v} transform={`translate(${pt.x.toFixed(1)} ${pt.y.toFixed(1)})`} className={"orb-spn" + (on ? " on" : "") + (cur ? " cur" : "")}>
719 <circle r={cur ? 9 : 5.5} />
720 <text y={-15} textAnchor="middle">v{c.v}</text>
721 </g>
722 );
723 })}
724 <g ref={cometRef} className="orb-comet"><circle r="14" fill="#38E1FF" opacity=".25" /><circle r="5" fill="#FBF3E4" /></g>
725 </svg>
726 </div>
727 <div className="orb-chg-info">
728 <Eyebrow>Changelog</Eyebrow>
729 <h2 className="orb-h2">Fall back <em>through time.</em></h2>
730 <div className="orb-chg-cards">
731 {CHANGELOG.map((c, i) => (
732 <div key={c.v} className={"orb-chg-card" + (i === reached ? " on" : "")}>
733 <div className="meta"><b>v{c.v}</b><span>{c.d}</span></div>
734 <h3>{c.t}</h3>
735 <p>{c.b}</p>
736 </div>
737 ))}
738 </div>
739 <div className="orb-chg-count" aria-hidden="true">{String(reached + 1).padStart(2, "0")} / {String(CHANGELOG.length).padStart(2, "0")}</div>
740 </div>
741 </div>
742 </section>
743
744 {/* ───────────── touchdown ───────────── */}
745 <section className="orb-sec orb-land" id="orb-touchdown" ref={(el) => { secRefs.current[6] = el; }}>
746 <div className="orb-land-sun" aria-hidden="true"><i /><i /><i /></div>
747 <div className="orb-wrap orb-center">
748 <div data-rv>
749 <Eyebrow>Touchdown</Eyebrow>
750 <h2 className="orb-h3big">Land your next <em>release.</em></h2>
751 <p className="orb-lede">Free forever for side projects. No card, no countdown. The first deploy takes about ninety seconds.</p>
752 </div>
753 <form className="orb-form" data-rv onSubmit={(e: FormEvent) => { e.preventDefault(); if (email.includes("@")) setDone(true); }}>
754 {done ? (
755 <div className="orb-done" role="status"><span /> You are on the manifest. Check {email} for your launch key.</div>
756 ) : (
757 <>
758 <label className="orb-sr" htmlFor="orb-email">Work email</label>
759 <input id="orb-email" type="email" required placeholder="you@company.dev" value={email} onChange={(e) => setEmail(e.target.value)} />
760 <button type="submit" className="orb-btn">Get my launch key</button>
761 </>
762 )}
763 </form>
764 </div>
765 <footer className="orb-foot">
766 <div className="orb-wrap orb-foot-grid">
767 <div>
768 <div className="orb-brand static"><svg width="22" height="22" viewBox="0 0 26 26" aria-hidden="true"><ellipse cx="13" cy="13" rx="11.5" ry="4.8" transform="rotate(-24 13 13)" fill="none" stroke="#38E1FF" strokeWidth="1.4" /><circle cx="13" cy="13" r="3.6" fill="#FBF3E4" /></svg><span>Orbit</span></div>
769 <p className="orb-foot-note">Kepler Works, Inc. Built for teams who would rather ship than configure.</p>
770 </div>
771 {[
772 ["Platform", ["Edge Functions", "Realtime Sync", "Observability", "Auth & Access", "Queues & Cron"]],
773 ["Company", ["About", "Careers", "Customers", "Press kit"]],
774 ["Resources", ["Documentation", "API reference", "Status", "Security"]],
775 ].map(([h, ls]) => (
776 <div key={h as string}>
777 <h4>{h as string}</h4>
778 <ul>{(ls as string[]).map((l) => <li key={l}><a href="#" onClick={NOOP}>{l}</a></li>)}</ul>
779 </div>
780 ))}
781 </div>
782 <div className="orb-wrap orb-legal"><span>© 2026 Kepler Works, Inc.</span><span>Privacy · Terms · DPA</span></div>
783 </footer>
784 </section>
785 </div>
786 );
787}
788
789export default Page;
790
791/* ------------------------------------------------------------------ */
792/* Styles */
793/* ------------------------------------------------------------------ */
794const CSS = `
795.orb{--orb-bg:#040A18;--orb-bg2:#071230;--orb-panel:#0A1A3A;--orb-line:rgba(125,190,255,.16);--orb-cyan:#38E1FF;--orb-cyan2:#7DF0FF;--orb-ice:#BFE6FF;--orb-warm:#FBF3E4;--orb-mute:#8CA2C6;--orb-indigo:#5B6BFF;
796--orb-sans:"Space Grotesk","Inter",ui-sans-serif,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
797--orb-mono:"JetBrains Mono","SF Mono",ui-monospace,Menlo,Consolas,"Liberation Mono",monospace;
798--orb-out:cubic-bezier(.22,1,.36,1);
799position:relative;box-sizing:border-box;width:100%;background:var(--orb-bg);color:var(--orb-warm);font-family:var(--orb-sans);font-size:16px;line-height:1.55;font-weight:400;text-align:left;overflow-x:clip;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
800.orb *,.orb *::before,.orb *::after{box-sizing:border-box}
801:where(.orb) :where(h1,h2,h3,h4,p,ul,li,pre,code,figure,form){margin:0;padding:0;font:inherit;letter-spacing:inherit}
802:where(.orb) :where(ul){list-style:none}
803:where(.orb) :where(a){color:inherit;text-decoration:none}
804:where(.orb) :where(button,input){font:inherit;color:inherit;margin:0;border:0;background:none;padding:0;appearance:none;-webkit-appearance:none;border-radius:0}
805:where(.orb) :where(button){cursor:pointer}
806.orb :focus-visible{outline:2px solid var(--orb-cyan);outline-offset:3px;border-radius:6px}
807:where(.orb) :where(svg){display:block;max-width:100%}
808.orb .orb-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
809
810.orb-wrap{width:100%;max-width:1240px;margin:0 auto;padding:0 40px}
811.orb-center{text-align:center}
812.orb-sec{position:relative;padding:150px 0}
813.orb-eyebrow{display:inline-flex;align-items:center;gap:10px;font-family:var(--orb-mono);font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:var(--orb-cyan);margin-bottom:22px}
814.orb-eyebrow i{width:26px;height:1px;background:var(--orb-cyan);box-shadow:0 0 10px var(--orb-cyan)}
815.orb-h2{font-size:clamp(2.2rem,5vw,4.2rem);line-height:1.04;letter-spacing:-.035em;font-weight:600;text-wrap:balance}
816.orb-h2 em,.orb-h3big em{font-style:normal;background:linear-gradient(100deg,var(--orb-cyan2),var(--orb-cyan) 45%,#8da2ff);-webkit-background-clip:text;background-clip:text;color:transparent}
817.orb-h3big{font-size:clamp(2.8rem,8vw,7rem);line-height:.98;letter-spacing:-.045em;font-weight:600;text-wrap:balance}
818.orb-lede{max-width:34em;margin-top:22px;font-size:1.15rem;line-height:1.6;color:var(--orb-mute)}
819.orb-center .orb-lede{margin-left:auto;margin-right:auto}
820
821.orb-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:52px;padding:0 28px;border-radius:999px;background:var(--orb-cyan);color:#021018;font-weight:600;font-size:15px;letter-spacing:.005em;box-shadow:0 0 0 1px rgba(255,255,255,.2) inset,0 10px 40px -8px rgba(56,225,255,.6);transition:transform .35s var(--orb-out),box-shadow .35s,background .2s}
822.orb-btn:hover{transform:translateY(-2px);background:var(--orb-cyan2);box-shadow:0 0 0 1px rgba(255,255,255,.3) inset,0 16px 50px -8px rgba(56,225,255,.8)}
823.orb-btn.ghost{background:rgba(125,190,255,.06);color:var(--orb-warm);box-shadow:0 0 0 1px rgba(160,210,255,.28) inset}
824.orb-btn.ghost:hover{background:rgba(125,190,255,.14)}
825.orb-btn.sm{height:38px;padding:0 18px;font-size:14px}
826
827/* nav */
828.orb-nav{position:fixed;z-index:50;top:0;left:0;right:0;display:flex;align-items:center;justify-content:space-between;gap:20px;padding:16px 40px;transition:background .4s,backdrop-filter .4s,border-color .4s;border-bottom:1px solid transparent}
829.orb-nav.solid{background:rgba(4,10,24,.72);backdrop-filter:blur(16px) saturate(1.4);-webkit-backdrop-filter:blur(16px) saturate(1.4);border-color:var(--orb-line)}
830.orb-brand{display:inline-flex;align-items:center;gap:10px;font-weight:600;font-size:20px;letter-spacing:-.02em}
831.orb-links{display:flex;gap:30px;font-size:14px;color:var(--orb-ice)}
832.orb-links a{opacity:.72;transition:opacity .2s}.orb-links a:hover{opacity:1}
833.orb-nav-cta{display:flex;align-items:center;gap:20px}
834.orb-link-quiet{font-size:14px;color:var(--orb-ice);opacity:.8}
835.orb-rail{position:fixed;z-index:40;right:22px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:14px;align-items:flex-end}
836.orb-rail a{display:flex;align-items:center;gap:12px;font-family:var(--orb-mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--orb-mute)}
837.orb-rail span{opacity:0;transform:translateX(6px);transition:opacity .3s,transform .3s var(--orb-out)}
838.orb-rail a:hover span,.orb-rail a.on span,.orb-rail a:focus-visible span{opacity:1;transform:none}
839.orb-rail i{width:7px;height:7px;border-radius:50%;border:1px solid var(--orb-mute);transition:all .3s}
840.orb-rail a.on{color:var(--orb-cyan)}.orb-rail a.on i{background:var(--orb-cyan);border-color:var(--orb-cyan);box-shadow:0 0 12px var(--orb-cyan);transform:scale(1.35)}
841
842/* hero */
843.orb-hero{position:relative;height:640vh}
844.orb-stick{position:sticky;top:0;height:100vh;overflow:hidden;background:radial-gradient(120% 90% at 50% 62%,#0B1D4A 0%,#06112B 42%,#030815 100%)}
845.orb-nebula{position:absolute;inset:0;background:radial-gradient(40% 30% at 18% 24%,rgba(91,107,255,.22),transparent 70%),radial-gradient(34% 28% at 84% 70%,rgba(56,225,255,.16),transparent 70%),radial-gradient(26% 20% at 70% 14%,rgba(251,243,228,.06),transparent 70%)}
846.orb-stars{position:absolute;inset:0;width:100%;height:100%}
847.orb-sys{position:absolute;left:50%;top:58%;width:0;height:0}
848.orb .orb-rings{position:absolute;left:0;top:0;transform:translate(-50%,-50%);overflow:visible;max-width:none}
849.orb-core{position:absolute;left:0;top:0;width:84px;height:84px;border-radius:50%;z-index:4;transform:translate(-50%,-50%)}
850.orb-core i{position:absolute;inset:0;border-radius:50%;background:radial-gradient(circle at 42% 38%,#fff 0%,#FBF3E4 22%,#8FF3FF 52%,#1CB7DD 82%,#0A6E9A 100%);box-shadow:0 0 30px 6px rgba(56,225,255,.45)}
851.orb-core b{position:absolute;inset:-110%;border-radius:50%;background:radial-gradient(circle,rgba(125,240,255,.4),rgba(56,225,255,.12) 38%,transparent 66%);animation:orbPulse 5s ease-in-out infinite}
852@keyframes orbPulse{50%{transform:scale(1.16);opacity:.7}}
853.orb-pl{position:absolute;left:0;top:0;width:0;height:0;will-change:transform,opacity}
854.orb-pl-body{position:absolute;left:0;top:0;transform:translate(-50%,-50%);border-radius:50%;display:grid;place-items:center;color:#021018;
855background:radial-gradient(circle at 34% 28%,#fff 0,var(--t1) 28%,var(--t2) 100%);box-shadow:inset -8px -10px 18px rgba(2,8,24,.55),inset 5px 5px 10px rgba(255,255,255,.35),0 0 30px -4px var(--t1)}
856.orb-pl-ring{position:absolute;left:50%;top:50%;width:190%;height:52%;border-radius:50%;border:2px solid rgba(251,243,228,.7);transform:translate(-50%,-50%) rotate(-22deg);box-shadow:0 0 14px rgba(251,243,228,.35)}
857.orb-pl-tag{position:absolute;left:0;top:0;transform:translateX(-50%);white-space:nowrap;font-family:var(--orb-mono);font-size:10px;letter-spacing:.2em;color:var(--orb-ice);text-shadow:0 0 8px #040A18,0 0 3px #040A18}
858.orb-copy{position:absolute;left:0;right:0;top:calc(var(--orb-navh,0px) + 12vh);padding:0 24px;text-align:center;will-change:transform,opacity;pointer-events:none;z-index:10}
859.orb-copy>*{pointer-events:auto}
860.orb-pill{display:inline-flex;align-items:center;gap:10px;padding:7px 16px;border-radius:999px;border:1px solid rgba(160,210,255,.25);background:rgba(10,26,58,.55);backdrop-filter:blur(8px);font-size:13px;color:var(--orb-ice)}
861.orb-pill i{width:7px;height:7px;border-radius:50%;background:var(--orb-cyan);box-shadow:0 0 10px var(--orb-cyan);animation:orbBlink 2s infinite}
862@keyframes orbBlink{50%{opacity:.35}}
863.orb-copy h1{margin:22px auto 0;max-width:11em;font-size:clamp(2.7rem,8.4vw,7.6rem);line-height:.95;letter-spacing:-.05em;font-weight:600;text-wrap:balance;text-shadow:0 4px 60px rgba(4,10,24,.8)}
864.orb-copy h1 span{background:linear-gradient(100deg,#fff,var(--orb-cyan2) 40%,var(--orb-cyan));-webkit-background-clip:text;background-clip:text;color:transparent}
865.orb-copy p{max-width:36em;margin:22px auto 0;font-size:clamp(1rem,1.5vw,1.25rem);color:var(--orb-ice);opacity:.85}
866.orb-cta{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-top:32px}
867.orb-hint{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);display:flex;align-items:center;gap:12px;font-family:var(--orb-mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--orb-mute);white-space:nowrap}
868.orb-hint span{width:1px;height:28px;background:linear-gradient(var(--orb-cyan),transparent);animation:orbDrop 2s infinite}
869@keyframes orbDrop{0%{transform:scaleY(0);transform-origin:top}50%{transform:scaleY(1);transform-origin:top}51%{transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}
870.orb-hud{position:absolute;left:0;right:0;bottom:0;padding:0 40px 38px;display:flex;justify-content:space-between;align-items:flex-end;gap:24px;pointer-events:none}
871.orb-hud-cap{position:relative;width:min(430px,100%);height:150px}
872.orb-hud-card{position:absolute;left:0;bottom:0;width:100%;padding:20px 22px;border-radius:18px;border:1px solid rgba(160,210,255,.2);background:rgba(6,16,40,.66);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);opacity:0;transform:translateY(14px);transition:opacity .5s var(--orb-out),transform .6s var(--orb-out)}
873.orb-hud-card.on{opacity:1;transform:none}
874.orb-hud-card .k{font-family:var(--orb-mono);font-size:11px;letter-spacing:.2em;color:var(--orb-cyan)}
875.orb-hud-card h2{display:flex;align-items:center;gap:10px;margin-top:8px;font-size:1.45rem;font-weight:600;letter-spacing:-.02em}
876.orb-hud-card h2 svg{color:var(--orb-cyan)}
877.orb-hud-card p{margin-top:6px;font-size:.92rem;line-height:1.5;color:var(--orb-mute)}
878.orb-hud-meter{display:grid;grid-template-columns:auto auto;grid-template-rows:auto 1fr;gap:4px 14px;align-items:end;font-family:var(--orb-mono);font-size:11px;letter-spacing:.14em;color:var(--orb-mute);height:96px}
879.orb-hud-meter .val{font-size:22px;color:var(--orb-warm);letter-spacing:0;text-align:right;font-variant-numeric:tabular-nums}
880.orb-hud-meter .trk{grid-row:1/3;grid-column:3;width:2px;height:100%;background:rgba(160,210,255,.2);position:relative;overflow:hidden}
881.orb-hud-meter .trk span{position:absolute;inset:0;background:var(--orb-cyan);box-shadow:0 0 10px var(--orb-cyan);transform:scaleY(0);transform-origin:top}
882
883/* reveals */
884.orb.orb-js [data-rv]{opacity:0;transform:translate3d(0,34px,0);transition:opacity .9s var(--orb-out) var(--d,0s),transform .9s var(--orb-out) var(--d,0s)}
885.orb.orb-js [data-rv].in{opacity:1;transform:none}
886
887/* anatomy */
888.orb-anat{background:linear-gradient(180deg,#030815,var(--orb-bg) 18%)}
889.orb-anat-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,6fr);gap:70px;align-items:start}
890.orb-anat-side{position:sticky;top:12vh}
891.orb-anat-svg{width:min(430px,100%);margin-top:36px}
892.orb-anat-ring{transform-box:view-box;transform-origin:0 0;animation:orbSpin linear infinite}
893.orb-anat-ring:nth-of-type(even){animation-direction:reverse}
894@keyframes orbSpin{to{transform:rotate(360deg)}}
895.orb-anat-ring circle{transition:stroke .5s,stroke-opacity .5s,r .5s}
896.orb-anat-list{display:flex;flex-direction:column;gap:18vh;padding:10vh 0 6vh}
897.orb-feat{padding:36px;border-radius:24px;border:1px solid var(--orb-line);background:linear-gradient(160deg,rgba(14,32,72,.55),rgba(7,18,48,.35));transition:border-color .5s,box-shadow .5s,background .5s}
898.orb-feat.on{border-color:rgba(56,225,255,.5);box-shadow:0 0 0 1px rgba(56,225,255,.12),0 30px 80px -30px rgba(56,225,255,.35)}
899.orb-feat-k{display:flex;align-items:center;gap:10px;font-family:var(--orb-mono);font-size:11px;letter-spacing:.16em;color:var(--orb-cyan)}
900.orb-feat h3{margin-top:14px;font-size:clamp(1.8rem,3vw,2.6rem);line-height:1.05;letter-spacing:-.03em;font-weight:600}
901.orb-feat p{margin-top:14px;color:var(--orb-mute);font-size:1.05rem}
902.orb-feat ul{margin-top:20px;display:grid;gap:9px}
903.orb-feat li{position:relative;padding-left:22px;color:var(--orb-ice);font-size:.98rem}
904.orb-feat li::before{content:"";position:absolute;left:0;top:.62em;width:9px;height:9px;border-radius:50%;border:1.5px solid var(--orb-cyan)}
905.orb-code{margin-top:24px;padding:18px 20px;border-radius:14px;background:#030a1b;border:1px solid var(--orb-line);font-family:var(--orb-mono);font-size:12.5px;line-height:1.7;color:var(--orb-ice);overflow-x:auto;white-space:pre}
906.orb-code code{font:inherit}
907
908/* telemetry */
909.orb-tele{background:radial-gradient(70% 60% at 80% 20%,rgba(56,225,255,.07),transparent 70%),var(--orb-bg)}
910.orb-tele-head{margin-bottom:56px}
911.orb-tele-grid{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:28px}
912.orb-term{border-radius:20px;overflow:hidden;border:1px solid var(--orb-line);background:#030a1b;box-shadow:0 40px 100px -40px rgba(56,225,255,.3)}
913.orb-term-bar{display:flex;align-items:center;gap:8px;padding:14px 18px;border-bottom:1px solid var(--orb-line);font-family:var(--orb-mono);font-size:12px;color:var(--orb-mute)}
914.orb-term-bar i{width:11px;height:11px;border-radius:50%;background:#1b2c52}
915.orb-term-bar span{margin-left:10px}
916.orb-term-body{padding:24px 24px 30px;font-family:var(--orb-mono);font-size:clamp(11.5px,1.3vw,14px);line-height:2;min-height:320px;overflow-x:auto}
917.orb-term-body .ln{white-space:pre}
918.orb-term-body .cmd{color:var(--orb-warm)}.orb-term-body .ok{color:var(--orb-cyan)}.orb-term-body .dim{color:var(--orb-mute)}
919.orb-caret{display:inline-block;width:.6em;height:1.1em;vertical-align:-.18em;margin-left:2px;background:var(--orb-cyan);animation:orbBlink 1s steps(2) infinite}
920.orb-stats{display:grid;grid-template-columns:1fr 1fr;gap:16px}
921.orb-stat{padding:22px 22px 14px;border-radius:20px;border:1px solid var(--orb-line);background:linear-gradient(160deg,rgba(14,32,72,.5),rgba(7,18,48,.3));display:flex;flex-direction:column;overflow:hidden}
922.orb-stat-l{font-size:12.5px;color:var(--orb-mute);min-height:2.6em}
923.orb-stat-v{margin-top:6px;font-size:clamp(1.9rem,3vw,2.7rem);font-weight:600;letter-spacing:-.04em;font-variant-numeric:tabular-nums;color:var(--orb-warm)}
924.orb-spark{width:calc(100% + 44px);margin:12px -22px 0;height:44px}
925.orb-spark-line{fill:none;stroke:var(--orb-cyan);stroke-width:2;vector-effect:non-scaling-stroke;stroke-dasharray:1;stroke-dashoffset:1;transition:stroke-dashoffset 1.8s var(--orb-out) .3s}
926.orb-spark-fill{fill:rgba(56,225,255,.1);opacity:0;transition:opacity 1.2s 1s}
927.orb-spark.on .orb-spark-line{stroke-dashoffset:0}.orb-spark.on .orb-spark-fill{opacity:1}
928
929/* integrations */
930.orb-int{padding-bottom:120px;background:radial-gradient(60% 50% at 50% 70%,rgba(91,107,255,.14),transparent 70%),var(--orb-bg);overflow:hidden}
931.orb-ring-box{position:relative;width:min(760px,calc(100vw - 20px));aspect-ratio:1;margin:30px auto 0}
932.orb-ring-svg{position:absolute;inset:0;width:100%;height:100%}
933.orb-ring-mid{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:30%;text-align:center}
934.orb-ring-n{font-size:clamp(1rem,2.2vw,1.6rem);font-weight:600;letter-spacing:-.02em;color:var(--orb-warm)}
935.orb-ring-mid p{margin-top:6px;font-size:clamp(.66rem,1.15vw,.86rem);line-height:1.4;color:var(--orb-mute)}
936.orb-node{position:absolute;left:50%;top:50%;width:62px;height:62px;border-radius:18px;display:grid;place-items:center;border:1px solid rgba(160,210,255,.4);background:linear-gradient(150deg,#16346f,#0a1c48);color:var(--c);box-shadow:0 10px 30px -10px rgba(0,0,0,.7);will-change:transform;transition:border-color .3s,box-shadow .3s,background .3s}
937.orb-node.in2{width:48px;height:48px;border-radius:14px}
938.orb-node span{font-size:22px;font-weight:700;letter-spacing:-.04em}
939.orb-node.in2 span{font-size:17px}
940.orb-node em{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);font-style:normal;font-family:var(--orb-mono);font-size:10px;letter-spacing:.08em;color:var(--orb-ice);opacity:.7;white-space:nowrap}
941.orb-node:hover,.orb-node.on{border-color:var(--c);box-shadow:0 0 30px -4px var(--c);background:linear-gradient(150deg,#12307a,#0a1a44);z-index:3}
942
943/* pricing */
944.orb-price{background:linear-gradient(180deg,var(--orb-bg),#050d22 50%,var(--orb-bg))}
945.orb-price-grid{display:grid;grid-template-columns:minmax(0,6fr) minmax(0,5fr);gap:60px;align-items:center}
946.orb-price-fig svg{width:min(560px,100%);margin:0 auto;overflow:visible}
947.orb-pr-track{stroke:rgba(125,190,255,.07)}
948.orb-pr-arc{stroke:#6f93c9;stroke-opacity:.32;stroke-dasharray:0 100;transition:stroke-dasharray 1.6s var(--orb-out),stroke .4s,stroke-opacity .4s}
949.orb-price-fig.on .orb-pr-arc{stroke-dasharray:var(--q) 100}
950.orb-pr.on .orb-pr-arc{stroke:var(--orb-cyan);stroke-opacity:1}
951.orb-pr{cursor:pointer}
952.orb-pr:hover .orb-pr-arc{stroke-opacity:.8}
953.orb-pr-lab{font-family:var(--orb-mono);font-size:9.5px;letter-spacing:.2em;fill:#021018;font-weight:700;pointer-events:none}
954.orb-pr:not(.on) .orb-pr-lab{fill:var(--orb-ice)}
955.orb-pr-price{font-size:40px;font-weight:600;letter-spacing:-.04em;fill:var(--orb-warm)}
956.orb-pr-per{font-family:var(--orb-mono);font-size:10px;letter-spacing:.1em;fill:var(--orb-mute)}
957.orb-seg{display:inline-flex;padding:4px;border-radius:999px;border:1px solid var(--orb-line);background:rgba(10,26,58,.5)}
958.orb-seg button{height:36px;padding:0 18px;border-radius:999px;font-size:14px;color:var(--orb-mute);transition:background .3s,color .3s}
959.orb-seg button.on{background:var(--orb-cyan);color:#021018;font-weight:600}
960.orb-seg small{font-family:var(--orb-mono);font-size:11px;margin-left:4px}
961.orb-plans{display:flex;gap:8px;flex-wrap:wrap;margin:20px 0}
962.orb-plans button{height:38px;padding:0 16px;border-radius:12px;border:1px solid var(--orb-line);font-size:14px;color:var(--orb-ice);transition:all .3s}
963.orb-plans button.on{border-color:var(--orb-cyan);color:var(--orb-cyan);background:rgba(56,225,255,.08)}
964.orb-plan-card{padding:32px;border-radius:24px;border:1px solid rgba(56,225,255,.35);background:linear-gradient(160deg,rgba(14,32,72,.65),rgba(7,18,48,.4));animation:orbIn .6s var(--orb-out)}
965@keyframes orbIn{from{opacity:0;transform:translateY(12px)}}
966.orb-plan-card h3{font-size:2rem;font-weight:600;letter-spacing:-.03em}
967.orb-plan-card .sub{margin-top:6px;color:var(--orb-mute)}
968.orb-plan-card ul{margin:22px 0 28px;display:grid;gap:11px}
969.orb-plan-card li{position:relative;padding-left:26px;color:var(--orb-ice)}
970.orb-plan-card li::before{content:"";position:absolute;left:3px;top:.45em;width:6px;height:11px;border:solid var(--orb-cyan);border-width:0 2px 2px 0;transform:rotate(40deg)}
971
972/* changelog */
973.orb-chg{position:relative;height:480vh;background:radial-gradient(60% 50% at 30% 50%,rgba(56,225,255,.07),transparent 70%),var(--orb-bg)}
974.orb-chg-stick{position:sticky;top:0;height:100vh;max-width:1240px;margin:0 auto;padding:80px 40px 40px;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.85fr);gap:50px;align-items:center}
975.orb-chg-fig svg{width:min(78vh,100%);margin:0 auto;overflow:visible}
976.orb-spiral{stroke-dasharray:1;stroke-dashoffset:1}
977.orb-spn circle{fill:var(--orb-bg);stroke:#4d6ea6;stroke-width:1.5;transition:all .5s}
978.orb-spn text{font-family:var(--orb-mono);font-size:15px;fill:#6c85b0;paint-order:stroke;stroke:#040A18;stroke-width:5px;transition:fill .5s}
979.orb-spn.on circle{stroke:var(--orb-cyan);fill:var(--orb-cyan)}.orb-spn.on text{fill:var(--orb-ice)}
980.orb-spn.cur circle{fill:var(--orb-warm);stroke:var(--orb-cyan);stroke-width:3}.orb-spn.cur text{fill:var(--orb-warm);font-weight:700}
981.orb-chg-cards{position:relative;display:grid;margin-top:34px}
982.orb-chg-card{grid-area:1/1;padding:28px;border-radius:22px;border:1px solid rgba(56,225,255,.35);background:linear-gradient(160deg,rgba(14,32,72,.65),rgba(7,18,48,.4));opacity:0;transform:translateY(16px);transition:opacity .5s var(--orb-out),transform .6s var(--orb-out);pointer-events:none}
983.orb-chg-card.on{opacity:1;transform:none;pointer-events:auto}
984.orb-chg-card .meta{display:flex;align-items:center;gap:14px;font-family:var(--orb-mono);font-size:12px;color:var(--orb-mute)}
985.orb-chg-card .meta b{padding:4px 10px;border-radius:8px;background:rgba(56,225,255,.12);color:var(--orb-cyan);font-weight:600}
986.orb-chg-card h3{margin-top:14px;font-size:1.9rem;font-weight:600;letter-spacing:-.03em}
987.orb-chg-card p{margin-top:10px;color:var(--orb-mute)}
988.orb-chg-count{margin-top:18px;font-family:var(--orb-mono);font-size:12px;letter-spacing:.2em;color:var(--orb-mute)}
989
990/* touchdown */
991.orb-land{padding:170px 0 0;background:var(--orb-bg);overflow:hidden}
992.orb-land-sun{position:absolute;left:50%;top:-18%;width:min(1100px,150vw);aspect-ratio:1;transform:translateX(-50%);pointer-events:none}
993.orb-land-sun i{position:absolute;inset:0;border-radius:50%;border:1px solid rgba(56,225,255,.2)}
994.orb-land-sun i:nth-child(1){background:radial-gradient(circle,rgba(56,225,255,.28),rgba(56,225,255,.04) 55%,transparent 70%)}
995.orb-land-sun i:nth-child(2){inset:14%;border-style:dashed;animation:orbSpin 80s linear infinite}
996.orb-land-sun i:nth-child(3){inset:28%;animation:orbSpin 55s linear infinite reverse;border-color:rgba(251,243,228,.18)}
997.orb-land .orb-wrap{position:relative}
998.orb-form{display:flex;gap:12px;justify-content:center;margin:42px auto 0;max-width:520px;flex-wrap:wrap}
999.orb-form input{flex:1 1 240px;height:52px;padding:0 22px;border-radius:999px;border:1px solid rgba(160,210,255,.3);background:rgba(10,26,58,.6);backdrop-filter:blur(8px);font-size:15px;color:var(--orb-warm)}
1000.orb-form input::placeholder{color:#6f86ad}
1001.orb-done{display:flex;align-items:center;gap:12px;padding:16px 24px;border-radius:999px;border:1px solid rgba(56,225,255,.45);background:rgba(56,225,255,.08);color:var(--orb-ice)}
1002.orb-done span{width:10px;height:10px;border-radius:50%;background:var(--orb-cyan);box-shadow:0 0 12px var(--orb-cyan);flex:none}
1003.orb-foot{position:relative;margin-top:150px;border-top:1px solid var(--orb-line);background:rgba(3,8,21,.7);padding:60px 0 30px}
1004.orb-foot-grid{display:grid;grid-template-columns:1.6fr 1fr 1fr 1fr;gap:40px}
1005.orb-brand.static{font-size:18px}
1006.orb-foot-note{margin-top:14px;max-width:22em;color:var(--orb-mute);font-size:14px}
1007.orb-foot h4{font-family:var(--orb-mono);font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--orb-cyan);margin-bottom:16px}
1008.orb-foot li{margin:9px 0}.orb-foot li a{color:var(--orb-ice);opacity:.75;font-size:14px;transition:opacity .2s}.orb-foot li a:hover{opacity:1}
1009.orb-legal{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-top:50px;padding-top:24px;border-top:1px solid var(--orb-line);font-size:13px;color:var(--orb-mute)}
1010
1011/* responsive */
1012@media (max-width:1100px){.orb-rail{display:none}}
1013@media (max-width:900px){
1014 .orb-wrap{padding:0 20px}.orb-nav{padding:12px 20px}.orb-links,.orb-link-quiet{display:none}
1015 .orb-sec{padding:100px 0}
1016 .orb-anat-grid,.orb-tele-grid,.orb-price-grid{grid-template-columns:1fr;gap:40px}
1017 .orb-anat-side{position:sticky;top:62px;z-index:3;background:var(--orb-bg);padding:12px 0;display:flex;align-items:center;justify-content:space-between;gap:12px;border-bottom:1px solid var(--orb-line)}
1018 .orb-anat-side .orb-lede,.orb-anat-side .orb-eyebrow{display:none}
1019 .orb-anat-svg{width:76px;margin:0;flex:none}
1020 .orb-anat-side .orb-h2{font-size:1.7rem}
1021 .orb-cta .ghost{display:none}.orb-copy p{font-size:.95rem;margin-top:16px}.orb-cta{margin-top:22px}
1022 .orb-anat-list{gap:30px;padding:0}
1023 .orb-feat{padding:24px}
1024 .orb-hud{padding:0 16px 22px;flex-direction:column;align-items:stretch;gap:10px}
1025 .orb-hud-cap{width:100%;height:136px}
1026 .orb-hud-meter{display:none}
1027 .orb-hint{display:none}
1028 .orb-copy{top:11vh}
1029 .orb-chg-stick{grid-template-columns:1fr;padding:70px 16px 16px;gap:8px;align-content:start}
1030 .orb-chg-fig svg{width:min(42vh,100%)}
1031 .orb-chg-info .orb-h2{font-size:1.7rem}.orb-chg-info .orb-eyebrow{margin-bottom:10px}
1032 .orb-chg-cards{margin-top:14px}.orb-chg-card{padding:18px}.orb-chg-card h3{font-size:1.35rem}.orb-chg-count{margin-top:10px}
1033 .orb-foot-grid{grid-template-columns:1fr 1fr}
1034 .orb-stats{grid-template-columns:1fr 1fr}
1035 .orb-node{width:46px;height:46px;border-radius:13px}.orb-node.in2{width:38px;height:38px}.orb-node span{font-size:17px}.orb-node.in2 span{font-size:14px}.orb-node em{display:none}
1036 .orb-ring-mid{width:34%}
1037}
1038@media (max-width:520px){.orb-stats{grid-template-columns:1fr}.orb-stat-l{min-height:0}.orb-foot-grid{grid-template-columns:1fr}}
1039
1040@media (prefers-reduced-motion:reduce){
1041 .orb *{animation:none!important}
1042 .orb.orb-js [data-rv]{opacity:1;transform:none;transition:none}
1043 .orb-hero{height:100vh}.orb-hint,.orb-hud,.orb-rail{display:none}
1044 .orb-chg{height:auto;padding:100px 0}
1045 .orb-chg-stick{position:static;height:auto}
1046 .orb-chg-cards{gap:14px}.orb-chg-card{grid-area:auto;opacity:1;transform:none;pointer-events:auto}
1047 .orb-chg-count{display:none}
1048 .orb-pr-arc,.orb-spark-line,.orb-spark-fill{transition:none}
1049 .orb-anat-side{position:static}
1050}
1051`;