forgedock 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -3
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +2205 -541
- package/bin/github-app-manifest.json.license +2 -0
- package/bin/hooks/session-start.mjs +4 -5
- package/bin/registry.mjs +20 -0
- package/bin/tui.mjs +172 -96
- package/commands/adopt.md +433 -0
- package/commands/analytics.md +2 -0
- package/commands/audit-agents.md +2 -0
- package/commands/audit.md +2 -0
- package/commands/autopilot.md +77 -0
- package/commands/changelog.md +354 -0
- package/commands/cleanup.md +2 -0
- package/commands/compat-audit.md +330 -0
- package/commands/deploy-info.md +6 -5
- package/commands/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- package/commands/diagnose.md +483 -0
- package/commands/explain.md +349 -0
- package/commands/forge-stats.md +2 -0
- package/commands/forgedock-init.md +2 -0
- package/commands/geo-audit.md +2 -0
- package/commands/incident-response.md +2 -0
- package/commands/issue.md +9 -3
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +239 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +39 -2
- package/commands/quality-gate.md +67 -0
- package/commands/replay.md +263 -0
- package/commands/resume.md +264 -0
- package/commands/review-pr-agents.md +5 -1
- package/commands/review-pr-staging.md +160 -2
- package/commands/review-pr.md +85 -3
- package/commands/rollback.md +2 -0
- package/commands/scope.md +280 -0
- package/commands/security-audit.md +2 -0
- package/commands/status.md +262 -0
- package/commands/sync-ecosystem.md +2 -0
- package/commands/test-gate.md +929 -0
- package/commands/validate.md +2 -0
- package/commands/work-on/build/architect.md +128 -18
- package/commands/work-on/build/context.md +130 -17
- package/commands/work-on/build/implement.md +7 -3
- package/commands/work-on/build/validate.md +2 -0
- package/commands/work-on/build.md +32 -7
- package/commands/work-on/close.md +85 -0
- package/commands/work-on/decompose.md +2 -0
- package/commands/work-on/investigate.md +30 -3
- package/commands/work-on/review.md +13 -1
- package/commands/work-on-monolithic.md +2 -0
- package/commands/work-on.md +676 -38
- package/docs/BRAND.md +51 -0
- package/docs/CI.md +177 -0
- package/docs/CONFIG.md +94 -0
- package/docs/FORGE-PROTOCOL.md +324 -0
- package/docs/articles/b-vs-c-baseline.md +210 -0
- package/docs/articles/full-abc-benchmark.md +205 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/articles/selective-spec-loading-token-savings.md +140 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/design/abc-benchmark.md +195 -0
- package/docs/design/design-architect-rationale.md +123 -0
- package/docs/design/design-memory.md +61 -0
- package/docs/design/design-spec-schema.md +248 -0
- package/docs/design/design-system-lint.md +150 -0
- package/docs/design/divergent-generation.md +106 -0
- package/docs/design/effects-appropriateness.md +133 -0
- package/docs/design/fixtures/briefs/README.md +47 -0
- package/docs/design/fixtures/briefs/cadence.md +39 -0
- package/docs/design/fixtures/briefs/plume.md +39 -0
- package/docs/design/fixtures/briefs/slipstream.md +39 -0
- package/docs/design/fixtures/briefs/tender.md +39 -0
- package/docs/design/fixtures/briefs/voltage.md +39 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
- package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
- package/docs/design/foundations/brutalist.css +294 -0
- package/docs/design/foundations/editorial.css +300 -0
- package/docs/design/foundations/minimal-luxury.css +295 -0
- package/docs/design/foundations/technical.css +293 -0
- package/docs/design/foundations/warm-photo.css +299 -0
- package/docs/design/reference-corpus.md +1726 -0
- package/docs/design/render-critique-loop.md +57 -0
- package/docs/site/getting-started.md +21 -0
- package/docs/spec-graph-schema.md +397 -0
- package/package.json +3 -3
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/classify-lane.sh +138 -0
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +169 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/scripts/validate-spec-graph.sh +423 -0
- package/templates/devdocs/README.md +72 -0
- package/templates/devdocs/agent/using-forgedock.md +3 -0
- package/templates/devdocs/agent/using-github.md +3 -0
- package/templates/devdocs/index.yaml +162 -0
- package/templates/devdocs/project/architecture.md +3 -0
- package/templates/devdocs/project/conventions.md +3 -0
- package/templates/devdocs/project/custom-instructions.md +3 -0
- package/templates/devdocs/project/glossary.md +3 -0
- package/templates/devdocs/project/stack.md +3 -0
- package/templates/devdocs/templates/commit-convention.md +3 -0
- package/templates/devdocs/templates/issue.md +3 -0
- package/templates/devdocs/templates/pull-request.md +3 -0
- package/templates/devdocs/templates/review-finding.md +3 -0
- package/templates/workflows/forgedock-review.yml +96 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
ARCHETYPE: technical-dense
|
|
3
|
+
RATIONALE: ML engineers and data scientists live in terminals and dashboards. They distrust
|
|
4
|
+
marketing fluff and trust code. technical-dense signals "built by people who code" — the
|
|
5
|
+
density itself communicates capability. Dark-first matches the IDE context they're already in.
|
|
6
|
+
SIGNATURE MOVE: The hero IS a live terminal session — a scrolling fake shell that shows
|
|
7
|
+
a Voltage function being decorated, invoked, and returning GPU metrics. The product demo
|
|
8
|
+
IS the headline.
|
|
9
|
+
EFFECTS PLAN:
|
|
10
|
+
- Dark background (#0a0e14) with electric-blue (#3b82f6) and phosphor-green (#22c55e) accents
|
|
11
|
+
- Monospace (JetBrains Mono) for all code, Inter for prose
|
|
12
|
+
- Animated terminal cursor blink on hero shell
|
|
13
|
+
- Stat counters: "0→ N containers" with counting animation on scroll
|
|
14
|
+
- Grid-density layout: feature cards packed tight, not spaced out luxuriously
|
|
15
|
+
- Horizontal rule separators as thick #3b82f6 bars (engineering aesthetic)
|
|
16
|
+
- Code blocks with syntax highlighting done in pure CSS spans
|
|
17
|
+
- Subtle scanline overlay on terminal for CRT depth
|
|
18
|
+
NEGATIVES CHECKED:
|
|
19
|
+
N1 - No lorem ipsum
|
|
20
|
+
N2 - No generic hero ("Welcome to the future" avoided — headline is specific)
|
|
21
|
+
N3 - No stock photo placeholders
|
|
22
|
+
N4 - No mismatched archetype signals (no warm rounded cards in a dark dense grid)
|
|
23
|
+
N5 - No centered-everything layout (left-aligned content, grid structures)
|
|
24
|
+
N6 - Color palette constrained: #0a0e14 bg, #111827 surface, #3b82f6 blue, #22c55e green, #f8fafc text
|
|
25
|
+
N7 - No more than 2 typefaces (JetBrains Mono + Inter)
|
|
26
|
+
N8 - CTA is specific ("Run your first function") not generic ("Get started")
|
|
27
|
+
N9 - No wall-of-text paragraphs — prose broken into short bursts
|
|
28
|
+
N10 - Hero communicates VALUE not category (shows what it does, not just what it is)
|
|
29
|
+
N11 - Spacing is intentional density, not cramped accident
|
|
30
|
+
N12 - Mobile-considered (stack at sm, readable mono at 13px min)
|
|
31
|
+
-->
|
|
32
|
+
<!DOCTYPE html>
|
|
33
|
+
<html lang="en" class="scroll-smooth">
|
|
34
|
+
<head>
|
|
35
|
+
<meta charset="UTF-8" />
|
|
36
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
37
|
+
<title>Voltage — Serverless GPU Compute for AI/ML</title>
|
|
38
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
39
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
40
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
41
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
|
|
42
|
+
<script>
|
|
43
|
+
tailwind.config = {
|
|
44
|
+
theme: {
|
|
45
|
+
extend: {
|
|
46
|
+
fontFamily: {
|
|
47
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
48
|
+
mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
|
|
49
|
+
},
|
|
50
|
+
colors: {
|
|
51
|
+
volt: {
|
|
52
|
+
bg: '#0a0e14',
|
|
53
|
+
surface: '#111827',
|
|
54
|
+
border: '#1f2937',
|
|
55
|
+
blue: '#3b82f6',
|
|
56
|
+
'blue-dim':'#1d4ed8',
|
|
57
|
+
green: '#22c55e',
|
|
58
|
+
'green-dim':'#15803d',
|
|
59
|
+
amber: '#f59e0b',
|
|
60
|
+
text: '#f8fafc',
|
|
61
|
+
muted: '#94a3b8',
|
|
62
|
+
subtle: '#475569',
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
animation: {
|
|
66
|
+
'blink': 'blink 1s step-end infinite',
|
|
67
|
+
'slide-up': 'slideUp 0.4s ease-out forwards',
|
|
68
|
+
'fade-in': 'fadeIn 0.6s ease-out forwards',
|
|
69
|
+
},
|
|
70
|
+
keyframes: {
|
|
71
|
+
blink: { '0%,100%': { opacity: '1' }, '50%': { opacity: '0' } },
|
|
72
|
+
slideUp: { from: { opacity: '0', transform: 'translateY(12px)' }, to: { opacity: '1', transform: 'translateY(0)' } },
|
|
73
|
+
fadeIn: { from: { opacity: '0' }, to: { opacity: '1' } },
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
79
|
+
<style>
|
|
80
|
+
body { background: #0a0e14; color: #f8fafc; font-family: 'Inter', sans-serif; }
|
|
81
|
+
|
|
82
|
+
/* Scanline effect on terminal */
|
|
83
|
+
.scanlines::before {
|
|
84
|
+
content: '';
|
|
85
|
+
position: absolute;
|
|
86
|
+
inset: 0;
|
|
87
|
+
background: repeating-linear-gradient(
|
|
88
|
+
to bottom,
|
|
89
|
+
transparent 0px,
|
|
90
|
+
transparent 2px,
|
|
91
|
+
rgba(0,0,0,0.15) 2px,
|
|
92
|
+
rgba(0,0,0,0.15) 4px
|
|
93
|
+
);
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
z-index: 10;
|
|
96
|
+
border-radius: inherit;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.terminal-line { opacity: 0; }
|
|
100
|
+
.terminal-line.visible { animation: slideUp 0.3s ease-out forwards; }
|
|
101
|
+
|
|
102
|
+
/* Syntax colors */
|
|
103
|
+
.kw { color: #c084fc; } /* keyword */
|
|
104
|
+
.dec { color: #3b82f6; } /* decorator */
|
|
105
|
+
.fn { color: #22c55e; } /* function name */
|
|
106
|
+
.str { color: #f59e0b; } /* string */
|
|
107
|
+
.num { color: #fb923c; } /* number */
|
|
108
|
+
.cm { color: #475569; } /* comment */
|
|
109
|
+
.cls { color: #67e8f9; } /* class */
|
|
110
|
+
.pun { color: #94a3b8; } /* punctuation */
|
|
111
|
+
|
|
112
|
+
/* Thick blue rule */
|
|
113
|
+
.rule-blue { border: none; height: 2px; background: #3b82f6; opacity: 0.4; }
|
|
114
|
+
|
|
115
|
+
.stat-num { font-variant-numeric: tabular-nums; }
|
|
116
|
+
|
|
117
|
+
/* Green pulse on the live indicator */
|
|
118
|
+
@keyframes livePulse {
|
|
119
|
+
0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.6); }
|
|
120
|
+
50% { box-shadow: 0 0 0 5px rgba(34,197,94,0); }
|
|
121
|
+
}
|
|
122
|
+
.live-dot { animation: livePulse 2s ease-in-out infinite; }
|
|
123
|
+
</style>
|
|
124
|
+
</head>
|
|
125
|
+
<body class="antialiased">
|
|
126
|
+
|
|
127
|
+
<!-- NAV -->
|
|
128
|
+
<nav class="fixed top-0 left-0 right-0 z-50 border-b border-volt-border bg-volt-bg/90 backdrop-blur-md">
|
|
129
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 flex items-center justify-between h-14">
|
|
130
|
+
<div class="flex items-center gap-6">
|
|
131
|
+
<a href="#" class="font-mono font-bold text-lg tracking-tight text-volt-text flex items-center gap-2">
|
|
132
|
+
<span class="text-volt-blue">⚡</span>voltage
|
|
133
|
+
</a>
|
|
134
|
+
<div class="hidden md:flex items-center gap-5 text-sm text-volt-muted font-mono">
|
|
135
|
+
<a href="#" class="hover:text-volt-text transition-colors">docs</a>
|
|
136
|
+
<a href="#" class="hover:text-volt-text transition-colors">pricing</a>
|
|
137
|
+
<a href="#" class="hover:text-volt-text transition-colors">examples</a>
|
|
138
|
+
<a href="#" class="hover:text-volt-text transition-colors">changelog</a>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="flex items-center gap-3">
|
|
142
|
+
<a href="#" class="text-sm font-mono text-volt-muted hover:text-volt-text transition-colors hidden sm:block">sign in</a>
|
|
143
|
+
<a href="#" class="text-sm font-mono font-medium bg-volt-blue text-white px-4 py-1.5 rounded hover:bg-volt-blue-dim transition-colors">
|
|
144
|
+
get started →
|
|
145
|
+
</a>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</nav>
|
|
149
|
+
|
|
150
|
+
<!-- HERO -->
|
|
151
|
+
<section class="pt-28 pb-16 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
152
|
+
<div class="grid lg:grid-cols-2 gap-12 items-start">
|
|
153
|
+
|
|
154
|
+
<!-- LEFT: headline + stats -->
|
|
155
|
+
<div>
|
|
156
|
+
<!-- Status badge -->
|
|
157
|
+
<div class="flex items-center gap-2 mb-6">
|
|
158
|
+
<span class="w-2 h-2 rounded-full bg-volt-green live-dot inline-block"></span>
|
|
159
|
+
<span class="font-mono text-xs text-volt-green uppercase tracking-widest">Production · 99.97% uptime</span>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<h1 class="text-4xl sm:text-5xl font-bold text-volt-text leading-tight mb-4">
|
|
163
|
+
Serverless compute<br/>
|
|
164
|
+
<span class="text-volt-blue">for serious ML work.</span>
|
|
165
|
+
</h1>
|
|
166
|
+
|
|
167
|
+
<p class="text-volt-muted text-base sm:text-lg leading-relaxed mb-8 max-w-md">
|
|
168
|
+
Decorate a Python function. Attach a GPU. It runs in the cloud —
|
|
169
|
+
scaling from zero to thousands of containers and back to zero.
|
|
170
|
+
No Docker. No YAML. No cluster ops.
|
|
171
|
+
</p>
|
|
172
|
+
|
|
173
|
+
<div class="flex flex-wrap gap-3 mb-10">
|
|
174
|
+
<a href="#" class="font-mono text-sm font-medium bg-volt-blue text-white px-5 py-2.5 rounded hover:bg-volt-blue-dim transition-colors">
|
|
175
|
+
$ pip install voltage
|
|
176
|
+
</a>
|
|
177
|
+
<a href="#" class="font-mono text-sm font-medium border border-volt-border text-volt-muted px-5 py-2.5 rounded hover:border-volt-blue hover:text-volt-text transition-colors">
|
|
178
|
+
read the docs →
|
|
179
|
+
</a>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<!-- Stats row -->
|
|
183
|
+
<div class="grid grid-cols-3 gap-4 border border-volt-border rounded-lg p-4 bg-volt-surface">
|
|
184
|
+
<div>
|
|
185
|
+
<div class="font-mono text-2xl font-bold text-volt-text stat-num" id="stat-containers">0</div>
|
|
186
|
+
<div class="text-xs text-volt-muted mt-0.5">containers/job max</div>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="border-l border-volt-border pl-4">
|
|
189
|
+
<div class="font-mono text-2xl font-bold text-volt-green stat-num"><200ms</div>
|
|
190
|
+
<div class="text-xs text-volt-muted mt-0.5">cold start</div>
|
|
191
|
+
</div>
|
|
192
|
+
<div class="border-l border-volt-border pl-4">
|
|
193
|
+
<div class="font-mono text-2xl font-bold text-volt-amber stat-num">$0</div>
|
|
194
|
+
<div class="text-xs text-volt-muted mt-0.5">when idle</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<!-- RIGHT: Terminal -->
|
|
200
|
+
<div class="relative">
|
|
201
|
+
<!-- Terminal chrome -->
|
|
202
|
+
<div class="rounded-lg overflow-hidden border border-volt-border bg-[#0d1117] shadow-2xl shadow-black/60 relative scanlines">
|
|
203
|
+
<!-- Title bar -->
|
|
204
|
+
<div class="flex items-center gap-2 px-4 py-3 border-b border-volt-border bg-[#161b22]">
|
|
205
|
+
<span class="w-3 h-3 rounded-full bg-[#ff5f56]"></span>
|
|
206
|
+
<span class="w-3 h-3 rounded-full bg-[#ffbd2e]"></span>
|
|
207
|
+
<span class="w-3 h-3 rounded-full bg-[#27c93f]"></span>
|
|
208
|
+
<span class="flex-1 text-center font-mono text-xs text-volt-subtle">train.py — voltage</span>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<!-- Code content -->
|
|
212
|
+
<div class="p-5 font-mono text-sm leading-relaxed overflow-x-auto" id="terminal-code">
|
|
213
|
+
<div class="cm"># pip install voltage</div>
|
|
214
|
+
<div class="mt-3">
|
|
215
|
+
<span class="kw">import</span> <span class="cls">voltage</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="mt-3">
|
|
218
|
+
<span class="dec">@voltage.function</span><span class="pun">(</span>
|
|
219
|
+
</div>
|
|
220
|
+
<div class="pl-4">
|
|
221
|
+
<span class="pun">gpu=</span><span class="str">"A100"</span><span class="pun">,</span>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="pl-4">
|
|
224
|
+
<span class="pun">memory=</span><span class="str">"80GB"</span><span class="pun">,</span>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="pl-4">
|
|
227
|
+
<span class="pun">concurrency=</span><span class="num">512</span><span class="pun">,</span>
|
|
228
|
+
</div>
|
|
229
|
+
<div><span class="pun">)</span></div>
|
|
230
|
+
<div class="mt-1">
|
|
231
|
+
<span class="kw">def</span> <span class="fn">train_model</span><span class="pun">(</span><span class="cls">dataset_path</span><span class="pun">:</span> <span class="cls">str</span><span class="pun">):</span>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="pl-4 text-volt-muted">
|
|
234
|
+
<span class="cm"># Your existing code, unchanged.</span>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="pl-4">
|
|
237
|
+
<span class="cm"># Voltage handles the rest.</span>
|
|
238
|
+
</div>
|
|
239
|
+
<div class="pl-4 mt-1">
|
|
240
|
+
<span class="kw">return</span> <span class="fn">run_training_loop</span><span class="pun">(</span><span class="cls">dataset_path</span><span class="pun">)</span>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div class="mt-5 border-t border-volt-border pt-4">
|
|
244
|
+
<div class="text-volt-subtle text-xs mb-2">— invoke from anywhere —</div>
|
|
245
|
+
<div>
|
|
246
|
+
<span class="text-volt-subtle">$ </span>
|
|
247
|
+
<span class="kw">python</span> <span class="pun">-c</span> <span class="str">"from train import train_model; train_model.remote('/data/imagenet')"</span>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div class="mt-4 space-y-1 text-xs">
|
|
252
|
+
<div>
|
|
253
|
+
<span class="text-volt-green">✓</span>
|
|
254
|
+
<span class="text-volt-muted"> container provisioned </span>
|
|
255
|
+
<span class="text-volt-subtle">(138ms)</span>
|
|
256
|
+
</div>
|
|
257
|
+
<div>
|
|
258
|
+
<span class="text-volt-green">✓</span>
|
|
259
|
+
<span class="text-volt-muted"> A100 attached </span>
|
|
260
|
+
<span class="text-volt-subtle">(GPU util: 0% → 94%)</span>
|
|
261
|
+
</div>
|
|
262
|
+
<div>
|
|
263
|
+
<span class="text-volt-green">✓</span>
|
|
264
|
+
<span class="text-volt-muted"> streaming logs → </span>
|
|
265
|
+
<span class="text-volt-blue">voltage logs --follow</span>
|
|
266
|
+
</div>
|
|
267
|
+
<div class="flex items-center gap-1 mt-2">
|
|
268
|
+
<span class="text-volt-green font-bold">→</span>
|
|
269
|
+
<span class="text-volt-text">epoch 1/10 · loss: 2.847 · acc: 0.312</span>
|
|
270
|
+
<span class="animate-blink text-volt-green font-bold ml-1">█</span>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
<!-- GPU pill overlay -->
|
|
277
|
+
<div class="absolute -bottom-3 -right-3 bg-volt-surface border border-volt-green/40 rounded-md px-3 py-1.5 font-mono text-xs">
|
|
278
|
+
<span class="text-volt-green">●</span>
|
|
279
|
+
<span class="text-volt-muted ml-1.5">A100 · 94% util · $0.0023/min</span>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
</section>
|
|
284
|
+
|
|
285
|
+
<hr class="rule-blue max-w-7xl mx-auto" />
|
|
286
|
+
|
|
287
|
+
<!-- FEATURE GRID -->
|
|
288
|
+
<section class="py-16 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
289
|
+
<div class="flex items-center gap-3 mb-10">
|
|
290
|
+
<span class="font-mono text-xs text-volt-subtle uppercase tracking-widest">capabilities</span>
|
|
291
|
+
<div class="flex-1 border-t border-volt-border"></div>
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
295
|
+
|
|
296
|
+
<!-- Card 1 -->
|
|
297
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
298
|
+
<div class="font-mono text-volt-blue text-xs mb-3 uppercase tracking-widest">compute</div>
|
|
299
|
+
<h3 class="font-semibold text-volt-text mb-2">On-demand GPUs</h3>
|
|
300
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
301
|
+
A100s, H100s, L40s. Pick in a decorator argument. Billed per second while your
|
|
302
|
+
function runs — not per hour, not per reservation.
|
|
303
|
+
</p>
|
|
304
|
+
<div class="font-mono text-xs bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
305
|
+
<span class="dec">@voltage.function</span><span class="pun">(gpu=</span><span class="str">"H100"</span><span class="pun">)</span>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<!-- Card 2 -->
|
|
310
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
311
|
+
<div class="font-mono text-volt-blue text-xs mb-3 uppercase tracking-widest">scale</div>
|
|
312
|
+
<h3 class="font-semibold text-volt-text mb-2">Zero to thousands</h3>
|
|
313
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
314
|
+
Submit 5,000 tasks. Voltage spins up containers in parallel, fans out work,
|
|
315
|
+
collects results. No queue configuration. No autoscaling policy YAML.
|
|
316
|
+
</p>
|
|
317
|
+
<div class="font-mono text-xs bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
318
|
+
<span class="fn">results</span> <span class="pun">= </span><span class="fn">process.map</span><span class="pun">(dataset, concurrency=</span><span class="num">5000</span><span class="pun">)</span>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<!-- Card 3 -->
|
|
323
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
324
|
+
<div class="font-mono text-volt-green text-xs mb-3 uppercase tracking-widest">inference</div>
|
|
325
|
+
<h3 class="font-semibold text-volt-text mb-2">Inference endpoints</h3>
|
|
326
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
327
|
+
One decorator converts any function into a persistent HTTP endpoint.
|
|
328
|
+
Auto-scales to traffic, scales to zero at idle. No Kubernetes.
|
|
329
|
+
</p>
|
|
330
|
+
<div class="font-mono text-xs bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
331
|
+
<span class="dec">@voltage.endpoint</span><span class="pun">(min_replicas=</span><span class="num">0</span><span class="pun">)</span>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<!-- Card 4 -->
|
|
336
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
337
|
+
<div class="font-mono text-volt-amber text-xs mb-3 uppercase tracking-widest">scheduling</div>
|
|
338
|
+
<h3 class="font-semibold text-volt-text mb-2">Cron + job scheduling</h3>
|
|
339
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
340
|
+
Schedule retraining pipelines, data processing jobs, or nightly evals.
|
|
341
|
+
Trigger from CI, webhooks, or a cron expression — all in Python.
|
|
342
|
+
</p>
|
|
343
|
+
<div class="font-mono text-xs bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
344
|
+
<span class="dec">@voltage.cron</span><span class="pun">(schedule=</span><span class="str">"0 2 * * *"</span><span class="pun">)</span>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<!-- Card 5 -->
|
|
349
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
350
|
+
<div class="font-mono text-volt-blue text-xs mb-3 uppercase tracking-widest">cold start</div>
|
|
351
|
+
<h3 class="font-semibold text-volt-text mb-2">Sub-200ms cold starts</h3>
|
|
352
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
353
|
+
Image layers are cached at the network edge. Container memory snapshots
|
|
354
|
+
cut cold start to under 200ms — even for PyTorch environments.
|
|
355
|
+
</p>
|
|
356
|
+
<div class="font-mono text-xs text-volt-green bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
357
|
+
container ready in 138ms ✓
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<!-- Card 6 -->
|
|
362
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface hover:border-volt-blue/50 transition-colors group">
|
|
363
|
+
<div class="font-mono text-volt-green text-xs mb-3 uppercase tracking-widest">storage</div>
|
|
364
|
+
<h3 class="font-semibold text-volt-text mb-2">Attached volumes</h3>
|
|
365
|
+
<p class="text-volt-muted text-sm leading-relaxed mb-4">
|
|
366
|
+
Mount persistent volumes, S3 buckets, or distributed file systems.
|
|
367
|
+
Model weights, datasets, checkpoints — available inside your function
|
|
368
|
+
as a regular path.
|
|
369
|
+
</p>
|
|
370
|
+
<div class="font-mono text-xs bg-[#0a0e14] rounded p-2 border border-volt-border">
|
|
371
|
+
<span class="pun">volume=</span><span class="fn">voltage.Volume</span><span class="pun">(</span><span class="str">"/data"</span><span class="pun">)</span>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
</div>
|
|
376
|
+
</section>
|
|
377
|
+
|
|
378
|
+
<hr class="rule-blue max-w-7xl mx-auto" />
|
|
379
|
+
|
|
380
|
+
<!-- COMPARISON / POSITIONING -->
|
|
381
|
+
<section class="py-16 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
382
|
+
<div class="flex items-center gap-3 mb-10">
|
|
383
|
+
<span class="font-mono text-xs text-volt-subtle uppercase tracking-widest">vs. the alternatives</span>
|
|
384
|
+
<div class="flex-1 border-t border-volt-border"></div>
|
|
385
|
+
</div>
|
|
386
|
+
|
|
387
|
+
<div class="grid sm:grid-cols-3 gap-6">
|
|
388
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface">
|
|
389
|
+
<div class="font-mono text-xs text-volt-subtle mb-3">BEFORE: Cloud VMs</div>
|
|
390
|
+
<ul class="space-y-2 text-sm text-volt-muted">
|
|
391
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Provision instance manually</li>
|
|
392
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Pay 24/7 even when idle</li>
|
|
393
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> SSH, tmux, pray</li>
|
|
394
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Can't parallelize easily</li>
|
|
395
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> No reproducible environments</li>
|
|
396
|
+
</ul>
|
|
397
|
+
</div>
|
|
398
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface">
|
|
399
|
+
<div class="font-mono text-xs text-volt-subtle mb-3">BEFORE: Kubernetes</div>
|
|
400
|
+
<ul class="space-y-2 text-sm text-volt-muted">
|
|
401
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Write YAML manifests</li>
|
|
402
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Manage node pools</li>
|
|
403
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Helm charts, operators</li>
|
|
404
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> GPU device plugins</li>
|
|
405
|
+
<li class="flex gap-2"><span class="text-red-400 mt-0.5">✗</span> Months of cluster ops work</li>
|
|
406
|
+
</ul>
|
|
407
|
+
</div>
|
|
408
|
+
<div class="border border-volt-blue/40 rounded-lg p-5 bg-volt-blue/5">
|
|
409
|
+
<div class="font-mono text-xs text-volt-blue mb-3">WITH VOLTAGE</div>
|
|
410
|
+
<ul class="space-y-2 text-sm text-volt-text">
|
|
411
|
+
<li class="flex gap-2"><span class="text-volt-green mt-0.5">✓</span> Decorator on a Python function</li>
|
|
412
|
+
<li class="flex gap-2"><span class="text-volt-green mt-0.5">✓</span> Pay only while it runs</li>
|
|
413
|
+
<li class="flex gap-2"><span class="text-volt-green mt-0.5">✓</span> GPU attached in <200ms</li>
|
|
414
|
+
<li class="flex gap-2"><span class="text-volt-green mt-0.5">✓</span> .map() for parallel jobs</li>
|
|
415
|
+
<li class="flex gap-2"><span class="text-volt-green mt-0.5">✓</span> Reproducible, versioned envs</li>
|
|
416
|
+
</ul>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</section>
|
|
420
|
+
|
|
421
|
+
<hr class="rule-blue max-w-7xl mx-auto" />
|
|
422
|
+
|
|
423
|
+
<!-- PRICING SIGNAL -->
|
|
424
|
+
<section class="py-16 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
425
|
+
<div class="flex items-center gap-3 mb-10">
|
|
426
|
+
<span class="font-mono text-xs text-volt-subtle uppercase tracking-widest">pricing model</span>
|
|
427
|
+
<div class="flex-1 border-t border-volt-border"></div>
|
|
428
|
+
</div>
|
|
429
|
+
|
|
430
|
+
<div class="grid sm:grid-cols-3 gap-4 mb-8">
|
|
431
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface text-center">
|
|
432
|
+
<div class="font-mono text-3xl font-bold text-volt-text mb-1">$0</div>
|
|
433
|
+
<div class="font-mono text-xs text-volt-muted">when containers are idle</div>
|
|
434
|
+
</div>
|
|
435
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface text-center">
|
|
436
|
+
<div class="font-mono text-3xl font-bold text-volt-blue mb-1">per-second</div>
|
|
437
|
+
<div class="font-mono text-xs text-volt-muted">billing granularity</div>
|
|
438
|
+
</div>
|
|
439
|
+
<div class="border border-volt-border rounded-lg p-5 bg-volt-surface text-center">
|
|
440
|
+
<div class="font-mono text-3xl font-bold text-volt-green mb-1">no minimum</div>
|
|
441
|
+
<div class="font-mono text-xs text-volt-muted">commit or reservation</div>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
|
|
445
|
+
<div class="font-mono text-sm text-volt-muted text-center">
|
|
446
|
+
A100 · <span class="text-volt-text">$2.49/hr</span> |
|
|
447
|
+
H100 · <span class="text-volt-text">$3.89/hr</span> |
|
|
448
|
+
L40S · <span class="text-volt-text">$1.79/hr</span> |
|
|
449
|
+
CPU · <span class="text-volt-text">$0.018/hr per vCPU</span>
|
|
450
|
+
</div>
|
|
451
|
+
</section>
|
|
452
|
+
|
|
453
|
+
<!-- CTA -->
|
|
454
|
+
<section class="py-20 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
455
|
+
<div class="border border-volt-blue/30 rounded-xl bg-volt-surface p-10 text-center">
|
|
456
|
+
<div class="font-mono text-xs text-volt-blue uppercase tracking-widest mb-4">get started in 60 seconds</div>
|
|
457
|
+
<h2 class="text-3xl font-bold text-volt-text mb-3">Run your first function.</h2>
|
|
458
|
+
<p class="text-volt-muted mb-8 max-w-md mx-auto text-sm leading-relaxed">
|
|
459
|
+
Install the SDK, add a decorator, call <code class="text-volt-green bg-[#0a0e14] px-1.5 py-0.5 rounded text-xs">.remote()</code>.
|
|
460
|
+
Your GPU is running in the cloud before you finish the sentence.
|
|
461
|
+
</p>
|
|
462
|
+
|
|
463
|
+
<div class="font-mono text-sm bg-[#0a0e14] border border-volt-border rounded-lg p-4 max-w-sm mx-auto mb-6 text-left">
|
|
464
|
+
<div class="text-volt-subtle mb-1">$ install</div>
|
|
465
|
+
<div class="text-volt-green">pip install voltage</div>
|
|
466
|
+
<div class="text-volt-subtle mt-3 mb-1">$ run</div>
|
|
467
|
+
<div class="text-volt-text">python train.py<span class="animate-blink text-volt-green">▌</span></div>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
<div class="flex flex-wrap justify-center gap-3">
|
|
471
|
+
<a href="#" class="font-mono text-sm font-medium bg-volt-blue text-white px-6 py-3 rounded hover:bg-volt-blue-dim transition-colors">
|
|
472
|
+
Run your first function →
|
|
473
|
+
</a>
|
|
474
|
+
<a href="#" class="font-mono text-sm border border-volt-border text-volt-muted px-6 py-3 rounded hover:border-volt-blue hover:text-volt-text transition-colors">
|
|
475
|
+
view examples on GitHub
|
|
476
|
+
</a>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
</section>
|
|
480
|
+
|
|
481
|
+
<!-- FOOTER -->
|
|
482
|
+
<footer class="border-t border-volt-border py-10 px-4 sm:px-6 max-w-7xl mx-auto">
|
|
483
|
+
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
|
|
484
|
+
<div class="font-mono font-bold text-volt-text flex items-center gap-2">
|
|
485
|
+
<span class="text-volt-blue">⚡</span>voltage
|
|
486
|
+
</div>
|
|
487
|
+
<div class="flex flex-wrap gap-5 font-mono text-xs text-volt-subtle">
|
|
488
|
+
<a href="#" class="hover:text-volt-muted transition-colors">docs</a>
|
|
489
|
+
<a href="#" class="hover:text-volt-muted transition-colors">status</a>
|
|
490
|
+
<a href="#" class="hover:text-volt-muted transition-colors">pricing</a>
|
|
491
|
+
<a href="#" class="hover:text-volt-muted transition-colors">github</a>
|
|
492
|
+
<a href="#" class="hover:text-volt-muted transition-colors">discord</a>
|
|
493
|
+
<a href="#" class="hover:text-volt-muted transition-colors">privacy</a>
|
|
494
|
+
</div>
|
|
495
|
+
<div class="font-mono text-xs text-volt-subtle">© 2024 Voltage Inc.</div>
|
|
496
|
+
</div>
|
|
497
|
+
</footer>
|
|
498
|
+
|
|
499
|
+
<script>
|
|
500
|
+
// Animate stat counter
|
|
501
|
+
function animateCounter(el, target, suffix = '', duration = 1200) {
|
|
502
|
+
let start = 0;
|
|
503
|
+
const step = target / (duration / 16);
|
|
504
|
+
const interval = setInterval(() => {
|
|
505
|
+
start = Math.min(start + step, target);
|
|
506
|
+
el.textContent = Math.floor(start).toLocaleString() + suffix;
|
|
507
|
+
if (start >= target) clearInterval(interval);
|
|
508
|
+
}, 16);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const observer = new IntersectionObserver((entries) => {
|
|
512
|
+
entries.forEach(e => {
|
|
513
|
+
if (e.isIntersecting) {
|
|
514
|
+
const el = document.getElementById('stat-containers');
|
|
515
|
+
if (el) animateCounter(el, 10000, '+');
|
|
516
|
+
observer.disconnect();
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
}, { threshold: 0.5 });
|
|
520
|
+
|
|
521
|
+
const statEl = document.getElementById('stat-containers');
|
|
522
|
+
if (statEl) observer.observe(statEl);
|
|
523
|
+
</script>
|
|
524
|
+
</body>
|
|
525
|
+
</html>
|