diff --git a/.gitignore b/.gitignore
index b336acd..545b248 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,8 @@ htmlcov/
# Local config / secrets
.env
*.local
+
+# Benchmark artifacts
+benchmark_results.txt
+benchmarks/benchmark_results.txt
+benchmarks/*.swp
diff --git a/benchmarks/benchmark_quantization.py b/benchmarks/benchmark_quantization.py
new file mode 100644
index 0000000..f3340ff
--- /dev/null
+++ b/benchmarks/benchmark_quantization.py
@@ -0,0 +1,197 @@
+"""Benchmark ScalarQuantizer vs ProductQuantizer."""
+
+import time
+
+import numpy as np
+
+from dynavec.quantization import ProductQuantizer, ScalarQuantizer
+
+
+def generate_dataset(n_vectors=10_000, dim=32, seed=0):
+ """Generate a reproducible clustered float32 dataset."""
+ rng = np.random.default_rng(seed)
+
+ centers = rng.normal(size=(8, dim)).astype(np.float32)
+ assign = rng.integers(0, 8, size=n_vectors)
+
+ x = centers[assign] + 0.05 * rng.normal(
+ size=(n_vectors, dim)
+ ).astype(np.float32)
+
+ return x.astype(np.float32)
+
+
+def benchmark_encode(quantizer, vectors, iterations=5):
+ """Measure average encode time."""
+ # Warm-up
+ quantizer.encode(vectors)
+
+ times = []
+
+ for _ in range(iterations):
+ start = time.perf_counter()
+ quantizer.encode(vectors)
+ times.append(time.perf_counter() - start)
+
+ return np.mean(times)
+
+
+def benchmark_decode(quantizer, codes, iterations=5):
+ """Measure average decode time."""
+ # Warm-up
+ quantizer.decode(codes)
+
+ times = []
+
+ for _ in range(iterations):
+ start = time.perf_counter()
+ quantizer.decode(codes)
+ times.append(time.perf_counter() - start)
+
+ return np.mean(times)
+
+
+def benchmark():
+ dimensions = 32
+ dataset_sizes = [1_000, 10_000]
+
+ print("=" * 80)
+ print("Dynavec Quantization Benchmark")
+ print("ScalarQuantizer INT8 vs ProductQuantizer")
+ print("=" * 80)
+
+ print()
+
+ for n_vectors in dataset_sizes:
+ print(f"Dataset: {n_vectors:,} vectors × {dimensions} dimensions")
+ print("-" * 80)
+
+ vectors = generate_dataset(
+ n_vectors=n_vectors,
+ dim=dimensions,
+ )
+
+ raw_bytes_per_vector = dimensions * 4
+
+ # ---------------------------------------------------------
+ # Scalar Quantizer
+ # ---------------------------------------------------------
+ scalar = ScalarQuantizer()
+
+ start = time.perf_counter()
+ scalar.fit(vectors)
+ scalar_fit_time = time.perf_counter() - start
+
+ scalar_codes = scalar.encode(vectors)
+
+ scalar_encode_time = benchmark_encode(
+ scalar,
+ vectors,
+ )
+
+ scalar_decode_time = benchmark_decode(
+ scalar,
+ scalar_codes,
+ )
+
+ scalar_reconstruction_error = scalar.reconstruction_error(
+ vectors
+ )
+
+ scalar_bytes_per_vector = scalar.code_size_bytes
+ scalar_total_bytes = scalar_codes.nbytes
+ scalar_compression = (
+ raw_bytes_per_vector / scalar_bytes_per_vector
+ )
+
+ # ---------------------------------------------------------
+ # Product Quantizer
+ # ---------------------------------------------------------
+ pq = ProductQuantizer(
+ m=8,
+ nbits=8,
+ iters=25,
+ seed=0,
+ )
+
+ start = time.perf_counter()
+ pq.fit(vectors)
+ pq_fit_time = time.perf_counter() - start
+
+ pq_codes = pq.encode(vectors)
+
+ pq_encode_time = benchmark_encode(
+ pq,
+ vectors,
+ )
+
+ pq_decode_time = benchmark_decode(
+ pq,
+ pq_codes,
+ )
+
+ pq_reconstruction_error = pq.reconstruction_error(
+ vectors
+ )
+
+ pq_bytes_per_vector = pq.code_size_bytes
+ pq_total_bytes = pq_codes.nbytes
+ pq_compression = (
+ raw_bytes_per_vector / pq_bytes_per_vector
+ )
+
+ # ---------------------------------------------------------
+ # Results
+ # ---------------------------------------------------------
+ print()
+ print("ScalarQuantizer (INT8)")
+ print(f" Fit time: {scalar_fit_time:.4f} sec")
+ print(f" Encode time: {scalar_encode_time:.4f} sec")
+ print(f" Decode time: {scalar_decode_time:.4f} sec")
+ print(
+ f" Reconstruction error: "
+ f"{scalar_reconstruction_error:.8f}"
+ )
+ print(f" Bytes/vector: {scalar_bytes_per_vector}")
+ print(f" Total encoded size: {scalar_total_bytes / 1024:.2f} KB")
+ print(f" Compression: {scalar_compression:.2f}x")
+
+ print()
+ print("ProductQuantizer (PQ)")
+ print(f" Fit time: {pq_fit_time:.4f} sec")
+ print(f" Encode time: {pq_encode_time:.4f} sec")
+ print(f" Decode time: {pq_decode_time:.4f} sec")
+ print(
+ f" Reconstruction error: "
+ f"{pq_reconstruction_error:.8f}"
+ )
+ print(f" Bytes/vector: {pq_bytes_per_vector}")
+ print(f" Total encoded size: {pq_total_bytes / 1024:.2f} KB")
+ print(f" Compression: {pq_compression:.2f}x")
+
+ print()
+ print("Comparison")
+ print(
+ f" Scalar/PQ compression: "
+ f"{scalar_compression / pq_compression:.2f}x"
+ )
+
+ if scalar_encode_time > 0:
+ print(
+ f" PQ encode / Scalar encode: "
+ f"{pq_encode_time / scalar_encode_time:.2f}x"
+ )
+
+ if scalar_decode_time > 0:
+ print(
+ f" PQ decode / Scalar decode: "
+ f"{pq_decode_time / scalar_decode_time:.2f}x"
+ )
+
+ print()
+ print("=" * 80)
+ print()
+
+
+if __name__ == "__main__":
+ benchmark()
diff --git a/dashboard/app/globals.css b/dashboard/app/globals.css
index 3f1a43f..9c599b8 100644
--- a/dashboard/app/globals.css
+++ b/dashboard/app/globals.css
@@ -3,14 +3,77 @@
@tailwind components;
@tailwind utilities;
-:root { color-scheme: light; }
+/* --- Light: the landing-site palette (styles.css) --- */
+:root {
+ color-scheme: light;
+
+ --bg: #fbfaf8; /* warm paper */
+ --surface: #ffffff;
+ --ink: #14110f; /* warm near-black */
+ --muted: #6f6862;
+ --faint: #a99f97;
+ --line: #ece6df;
+ --accent: #e8623b; /* coral */
+ --accent-ink: #b8472a; /* darker coral for text/hover on light */
+ --accent-soft: #fdeee8; /* light coral wash */
+ --ok: #2f7d5b;
+ --err: #b8472a;
+
+ --track: #f0ece6; /* meter/track fill */
+ --thead: #faf6f1; /* table header wash */
+ --shadow: rgba(20, 17, 15, .07);
+ --scroll-thumb: #e0d9d1;
+ --tip-bg: #ffffff; /* recharts tooltip */
+ --tip-line: #ece6df;
+
+ /* op badges */
+ --op-search-bg: #eef3ff; --op-search-fg: #3b5bdb; --op-search-line: #dbe3ff;
+ --op-graph-bg: #f3eeff; --op-graph-fg: #7048e8; --op-graph-line: #e5dbff;
+ --op-upsert-bg: #eafaf1; --op-upsert-fg: #2f7d5b; --op-upsert-line: #d3f0e0;
+}
+
+/* --- Dark: the site's warm inverted tones (--inv-bg #14110f / --inv-fg #fbfaf8) --- */
+.dark {
+ color-scheme: dark;
+
+ --bg: #14110f; /* warm near-black paper */
+ --surface: #1d1916; /* lifted warm panel */
+ --ink: #f4efe8; /* warm off-white */
+ --muted: #a79d94;
+ --faint: #766c64;
+ --line: #2d2723;
+ --accent: #f0714a; /* brighter coral for dark backgrounds */
+ --accent-ink: #f59a7c; /* lighter coral for text/hover on dark */
+ --accent-soft: #2a1712; /* dark coral wash */
+ --ok: #57b98d;
+ --err: #ef8a63;
+
+ --track: #2d2723;
+ --thead: #1a1613;
+ --shadow: rgba(0, 0, 0, .4);
+ --scroll-thumb: #3a332e;
+ --tip-bg: #1d1916;
+ --tip-line: #2d2723;
+
+ --op-search-bg: #1a2233; --op-search-fg: #9db6ff; --op-search-line: #2b3a5c;
+ --op-graph-bg: #221a33; --op-graph-fg: #b9a4f5; --op-graph-line: #3a2e5c;
+ --op-upsert-bg: #142720; --op-upsert-fg: #57b98d; --op-upsert-line: #24463a;
+}
+
html, body { padding: 0; margin: 0; }
body {
- background: theme('colors.bg');
- color: theme('colors.ink');
+ background: var(--bg);
+ color: var(--ink);
font-family: theme('fontFamily.sans');
- border-top: 3px solid theme('colors.accent');
+ border-top: 3px solid var(--accent);
-webkit-font-smoothing: antialiased;
+ transition: background-color .18s ease, color .18s ease;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
-::-webkit-scrollbar-thumb { background: #e0d9d1; border-radius: 6px; }
+::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 6px; }
+
+/* Trace op badges — themed via CSS variables so they flip with .dark */
+.op-badge { background: var(--op-search-bg); color: var(--op-search-fg); border-color: var(--op-search-line); }
+.op-search { background: var(--op-search-bg); color: var(--op-search-fg); border-color: var(--op-search-line); }
+.op-graph_search { background: var(--op-graph-bg); color: var(--op-graph-fg); border-color: var(--op-graph-line); }
+.op-upsert { background: var(--op-upsert-bg); color: var(--op-upsert-fg); border-color: var(--op-upsert-line); }
diff --git a/dashboard/app/layout.tsx b/dashboard/app/layout.tsx
index 33be36c..735422c 100644
--- a/dashboard/app/layout.tsx
+++ b/dashboard/app/layout.tsx
@@ -6,9 +6,17 @@ export const metadata: Metadata = {
description: "Real-time retrieval observability for dynavec — latency, cache, traces.",
};
+// Set the theme class before first paint so there is no light→dark flash.
+// Precedence: ?theme= override (deep-linkable, used for parity screenshots) >
+// saved choice > OS preference; defaults to light (matching the site).
+const themeInit = `(function(){try{var q=new URLSearchParams(location.search).get('theme');var s=localStorage.getItem('dynavec-theme');var d=q?q==='dark':(s?s==='dark':matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.classList.toggle('dark',d);if(q){try{localStorage.setItem('dynavec-theme',d?'dark':'light');}catch(e){}}}catch(e){}})();`;
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
-
+
+
+
+
{children}
);
diff --git a/dashboard/components/LatencyChart.tsx b/dashboard/components/LatencyChart.tsx
index 06fecd1..3b1d01b 100644
--- a/dashboard/components/LatencyChart.tsx
+++ b/dashboard/components/LatencyChart.tsx
@@ -2,9 +2,9 @@
import type { Metrics } from "@/lib/types";
const ROWS: [keyof Metrics, string, string][] = [
- ["p50", "p50", "#2f7d5b"],
- ["p95", "p95", "#e8623b"],
- ["p99", "p99", "#b8472a"],
+ ["p50", "p50", "var(--ok)"],
+ ["p95", "p95", "var(--accent)"],
+ ["p99", "p99", "var(--accent-ink)"],
];
export default function LatencyChart({ m }: { m: Metrics }) {
@@ -23,7 +23,7 @@ export default function LatencyChart({ m }: { m: Metrics }) {
{label}
{Number(v).toFixed(1)} ms
-
diff --git a/dashboard/components/ThemeToggle.tsx b/dashboard/components/ThemeToggle.tsx
new file mode 100644
index 0000000..afce800
--- /dev/null
+++ b/dashboard/components/ThemeToggle.tsx
@@ -0,0 +1,46 @@
+"use client";
+import { useEffect, useState } from "react";
+
+// Toggles the `.dark` class on and persists the choice. The initial
+// class is set pre-paint by the inline script in app/layout.tsx (no flash);
+// this component only syncs its icon to that state and flips it on click.
+export default function ThemeToggle() {
+ const [dark, setDark] = useState(false);
+
+ useEffect(() => {
+ setDark(document.documentElement.classList.contains("dark"));
+ }, []);
+
+ const toggle = () => {
+ const next = !document.documentElement.classList.contains("dark");
+ document.documentElement.classList.toggle("dark", next);
+ try {
+ localStorage.setItem("dynavec-theme", next ? "dark" : "light");
+ } catch {
+ /* storage may be unavailable */
+ }
+ setDark(next);
+ };
+
+ return (
+
+ {dark ? (
+ // sun
+
+
+
+
+ ) : (
+ // moon
+
+
+
+ )}
+
+ );
+}
diff --git a/dashboard/components/TopBar.tsx b/dashboard/components/TopBar.tsx
index 236218b..3191d5c 100644
--- a/dashboard/components/TopBar.tsx
+++ b/dashboard/components/TopBar.tsx
@@ -1,4 +1,5 @@
"use client";
+import ThemeToggle from "@/components/ThemeToggle";
const RANGES = [
{ label: "30m", w: 1800 },
@@ -53,11 +54,12 @@ export default function TopBar({
onClick={onAuto}
className={
"font-mono text-[12px] border-[1.5px] border-ink rounded-lg px-3 py-1.5 " +
- (auto ? "bg-ink text-white" : "bg-surface text-ink")
+ (auto ? "bg-ink text-bg" : "bg-surface text-ink")
}
>
Auto-refresh
+
);
}
diff --git a/dashboard/components/TracesTable.tsx b/dashboard/components/TracesTable.tsx
index 8513850..1797e13 100644
--- a/dashboard/components/TracesTable.tsx
+++ b/dashboard/components/TracesTable.tsx
@@ -1,10 +1,11 @@
"use client";
import type { TraceEvent, TraceFilters } from "@/lib/types";
+// Themed via CSS-variable classes in globals.css so badges flip with `.dark`.
const OP_CLASS: Record = {
- search: "bg-[#eef3ff] text-[#3b5bdb] border-[#dbe3ff]",
- graph_search: "bg-[#f3eeff] text-[#7048e8] border-[#e5dbff]",
- upsert: "bg-[#eafaf1] text-ok border-[#d3f0e0]",
+ search: "op-search",
+ graph_search: "op-graph_search",
+ upsert: "op-upsert",
};
export default function TracesTable({
@@ -54,7 +55,7 @@ export default function TracesTable({
-
+
{["Start", "Op", "Namespace", "Latency", "Results", "Cache", "Rank", "Status"].map((h) => (
{h}
))}
diff --git a/dashboard/components/VolumeChart.tsx b/dashboard/components/VolumeChart.tsx
index d7a036b..efd309e 100644
--- a/dashboard/components/VolumeChart.tsx
+++ b/dashboard/components/VolumeChart.tsx
@@ -17,12 +17,12 @@ export default function VolumeChart({ m }: { m: Metrics }) {
""}
formatter={(v: number) => [v, "queries"]}
/>
-
+
diff --git a/dashboard/tailwind.config.ts b/dashboard/tailwind.config.ts
index 24ca853..fb8ca35 100644
--- a/dashboard/tailwind.config.ts
+++ b/dashboard/tailwind.config.ts
@@ -1,29 +1,35 @@
import type { Config } from "tailwindcss";
// dynavec brand tokens — kept in sync with the landing page (styles.css).
+// Values are driven by CSS variables (see app/globals.css) so a single
+// `.dark` class on flips the whole palette. Light is the default,
+// matching the landing site; dark uses the site's warm inverted tones.
const config: Config = {
+ darkMode: "class",
content: ["./app/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}"],
theme: {
extend: {
colors: {
- bg: "#fbfaf8",
- surface: "#ffffff",
- ink: "#14110f",
- muted: "#6f6862",
- faint: "#a99f97",
- line: "#ece6df",
- accent: "#e8623b",
- "accent-ink": "#b8472a",
- "accent-soft": "#fdeee8",
- ok: "#2f7d5b",
- err: "#b8472a",
+ bg: "var(--bg)",
+ surface: "var(--surface)",
+ ink: "var(--ink)",
+ muted: "var(--muted)",
+ faint: "var(--faint)",
+ line: "var(--line)",
+ accent: "var(--accent)",
+ "accent-ink": "var(--accent-ink)",
+ "accent-soft": "var(--accent-soft)",
+ ok: "var(--ok)",
+ err: "var(--err)",
+ track: "var(--track)",
+ thead: "var(--thead)",
},
fontFamily: {
sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
mono: ['"JetBrains Mono"', 'ui-monospace', 'Menlo', 'monospace'],
},
boxShadow: {
- card: "0 6px 30px rgba(20,17,15,.07)",
+ card: "0 6px 30px var(--shadow)",
},
borderRadius: {
xl2: "14px",
diff --git a/opensource/dynavec/docs/docs.css b/opensource/dynavec/docs/docs.css
index 2a81218..95da8cd 100644
--- a/opensource/dynavec/docs/docs.css
+++ b/opensource/dynavec/docs/docs.css
@@ -1,58 +1,412 @@
-/* dynavec docs — layout on top of the shared brand tokens in ../styles.css */
-
-.docshell { max-width: 1200px; margin: 0 auto; padding: 0 24px; display: grid; grid-template-columns: 250px 1fr; gap: 44px; }
-
-/* sidebar */
-.side { position: sticky; top: 84px; align-self: start; max-height: calc(100vh - 100px); overflow-y: auto; padding: 24px 0 60px; }
-.side__group { margin-bottom: 22px; }
-.side__title { font-family: var(--mono); font-size: 11.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--faint); margin: 0 0 10px; }
-.side__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
-.side__list a { display: block; text-decoration: none; font-size: 14.5px; color: var(--muted); padding: 6px 12px; border-radius: 7px; border-left: 2px solid transparent; transition: background .12s, color .12s; }
-.side__list a:hover { color: var(--fg); background: var(--accent-soft); }
-.side__list a.is-current { color: var(--accent-ink); background: var(--accent-soft); border-left-color: var(--accent); font-weight: 600; }
-
-/* content */
-.doc { min-width: 0; padding: 34px 0 90px; }
-.doc__crumbs { font-family: var(--mono); font-size: 12.5px; color: var(--faint); margin-bottom: 14px; }
-.doc__crumbs a { color: var(--muted); text-decoration: none; }
-.doc__crumbs a:hover { color: var(--accent-ink); }
-.doc h1 { font-size: clamp(28px, 4vw, 40px); letter-spacing: -.02em; margin: 0 0 8px; }
-.doc__sub { font-size: 18px; color: var(--muted); margin: 0 0 26px; }
-.doc h2 { font-size: 22px; letter-spacing: -.01em; margin: 40px 0 12px; padding-top: 8px; }
-.doc h2::before { content: ""; display: block; width: 34px; height: 3px; background: var(--accent); margin-bottom: 14px; }
-.doc h3 { font-size: 17px; margin: 26px 0 8px; }
-.doc p, .doc li { font-size: 16px; color: #322c27; }
-.doc a { color: var(--accent-ink); text-decoration: none; border-bottom: 1px solid var(--line); }
-.doc a:hover { border-bottom-color: var(--accent); }
-.doc code { font-family: var(--mono); font-size: 14px; background: var(--accent-soft); color: var(--accent-ink); padding: 1px 6px; border-radius: 4px; }
-.doc .code { border: 1px solid var(--line); border-radius: 8px; margin: 16px 0; box-shadow: var(--shadow); }
-.doc .code code { background: none; color: #2b2620; padding: 0; }
-.doc ul, .doc ol { padding-left: 22px; }
-.doc li { margin: 5px 0; }
-.doc .callout { border: 1px solid var(--line); border-left: 3px solid var(--accent); background: var(--surface); padding: 14px 18px; border-radius: 8px; margin: 18px 0; font-size: 15px; color: #322c27; }
-.doc .callout strong { color: var(--accent-ink); }
-.doc__params { width: 100%; border-collapse: collapse; margin: 14px 0; font-size: 14.5px; }
-.doc__params th, .doc__params td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
-.doc__params th { font-family: var(--mono); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
-.doc__params td:first-child code { background: none; padding: 0; }
-.doc__next { display: flex; justify-content: space-between; gap: 16px; margin-top: 46px; padding-top: 24px; border-top: 1px solid var(--line); }
-.doc__next a { border: 1px solid var(--line); border-radius: 8px; padding: 14px 18px; text-decoration: none; color: var(--fg); font-weight: 600; font-size: 15px; transition: box-shadow .15s, border-color .15s; }
-.doc__next a:hover { box-shadow: var(--shadow); border-color: #ddd4cb; }
-.doc__next span { display: block; font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--faint); font-weight: 500; margin-bottom: 3px; }
-
-/* feature grid on docs home */
-.docgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; margin-top: 24px; }
-.docgrid a { background: var(--surface); padding: 22px 22px; text-decoration: none; color: var(--fg); border: none; transition: background .12s; }
-.docgrid a:hover { background: var(--accent-soft); }
-.docgrid h3 { margin: 0 0 6px; font-size: 16.5px; }
-.docgrid p { margin: 0; font-size: 14px; color: var(--muted); }
+/* ================================================
+ dynavec docs — layout and components
+ Extends ../styles.css design tokens.
+ ================================================ */
+
+/* ---- Doc shell layout ---- */
+.docshell {
+ max-width: 1140px;
+ margin: 0 auto;
+ padding: 0 24px;
+ display: grid;
+ grid-template-columns: 240px 1fr;
+ gap: 48px;
+ align-items: start;
+}
+
+/* ================================================
+ SIDEBAR
+ ================================================ */
+
+.side {
+ position: sticky;
+ top: calc(var(--nav-h) + 24px);
+ align-self: start;
+ max-height: calc(100vh - var(--nav-h) - 48px);
+ overflow-y: auto;
+ padding: 8px 0 64px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--border) transparent;
+}
+.side::-webkit-scrollbar { width: 4px; }
+.side::-webkit-scrollbar-track { background: transparent; }
+.side::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
+
+.side__group { margin-bottom: 20px; }
+
+.side__title {
+ font-family: var(--mono);
+ font-size: 10.5px;
+ text-transform: uppercase;
+ letter-spacing: 0.09em;
+ color: var(--fg-4);
+ margin: 0 0 6px 8px;
+ font-weight: 600;
+}
+
+.side__list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+}
+
+.side__list a {
+ display: block;
+ text-decoration: none;
+ font-size: 14px;
+ color: var(--fg-3);
+ padding: 6px 10px;
+ border-radius: var(--r);
+ border-left: 2px solid transparent;
+ transition: background var(--t), color var(--t), border-color var(--t);
+ font-weight: 450;
+}
+.side__list a:hover {
+ color: var(--fg);
+ background: var(--subtle);
+}
+.side__list a.is-current {
+ color: var(--fg);
+ background: var(--surface);
+ border-left-color: var(--accent);
+ font-weight: 600;
+}
+
+/* ================================================
+ CONTENT AREA
+ ================================================ */
+
+.doc {
+ min-width: 0;
+ padding: 32px 0 96px;
+}
+
+/* Breadcrumbs */
+.doc__crumbs {
+ font-family: var(--mono);
+ font-size: 12px;
+ color: var(--fg-4);
+ margin-bottom: 20px;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+.doc__crumbs::before {
+ content: "";
+ display: inline-block;
+ width: 16px;
+ height: 1.5px;
+ background: var(--accent);
+}
+.doc__crumbs a {
+ color: var(--fg-3);
+ text-decoration: none;
+ transition: color var(--t);
+ border: none;
+}
+.doc__crumbs a:hover { color: var(--fg); }
+.doc__crumbs span { color: var(--fg-4); }
+
+/* Page heading */
+.doc h1 {
+ font-size: clamp(26px, 3.6vw, 38px);
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ line-height: 1.1;
+ margin: 0 0 10px;
+}
+
+.doc__sub {
+ font-size: 17px;
+ color: var(--fg-2);
+ margin: 0 0 32px;
+ line-height: 1.6;
+}
+
+/* Section headings */
+.doc h2 {
+ font-size: 20px;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ margin: 48px 0 14px;
+ padding-top: 0;
+}
+.doc h2::before {
+ content: "";
+ display: block;
+ width: 24px;
+ height: 2px;
+ background: var(--accent);
+ margin-bottom: 12px;
+ border-radius: 2px;
+}
+
+.doc h3 {
+ font-size: 16px;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ margin: 28px 0 8px;
+ color: var(--fg);
+}
+
+/* Body text */
+.doc p { font-size: 15.5px; color: var(--fg-2); line-height: 1.72; margin: 0 0 14px; }
+.doc ul, .doc ol { padding-left: 22px; margin: 0 0 14px; }
+.doc li { font-size: 15.5px; color: var(--fg-2); line-height: 1.65; margin: 5px 0; }
+
+/* Inline code */
+.doc code {
+ font-family: var(--mono);
+ font-size: 13px;
+ background: var(--accent-bg);
+ color: var(--accent-dk);
+ padding: 2px 6px;
+ border-radius: 4px;
+}
+
+/* Links */
+.doc a {
+ color: var(--fg);
+ text-decoration: none;
+ border-bottom: 1px solid var(--border-md);
+ transition: color var(--t), border-color var(--t);
+}
+.doc a:hover { color: var(--accent); border-bottom-color: var(--accent); }
+
+/* ================================================
+ CODE BLOCKS
+ ================================================ */
+
+.doc .code {
+ position: relative;
+ background: var(--dark);
+ border: 1px solid rgba(255,255,255,.06);
+ border-radius: var(--r-lg);
+ margin: 18px 0;
+ overflow: hidden;
+}
+.doc .code pre,
+.doc .code > code,
+.doc pre.code {
+ display: block;
+ margin: 0;
+ padding: 22px 24px;
+ overflow-x: auto;
+ font-family: var(--mono);
+ font-size: 13.5px;
+ line-height: 1.78;
+ color: #d4d4d4;
+ background: transparent;
+ tab-size: 2;
+}
+.doc .code code {
+ background: none;
+ color: #d4d4d4;
+ padding: 0;
+ font-size: 13.5px;
+ border-radius: 0;
+}
+.doc .code .c-comment { color: #555; font-style: italic; }
+.doc .code .c-str { color: #89c18a; }
+.doc .code .c-kw { color: #dba96e; font-weight: 600; }
+
+/* ================================================
+ COPY BUTTON (docs.js injects this)
+ ================================================ */
+
+.code__copy {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ font-family: var(--mono);
+ font-size: 10.5px;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ border: 1px solid rgba(255,255,255,.12);
+ background: rgba(255,255,255,.06);
+ color: #888;
+ padding: 4px 10px;
+ border-radius: 5px;
+ cursor: pointer;
+ transition: background var(--t), color var(--t), border-color var(--t);
+}
+.code__copy:hover { background: var(--accent); color: #fff; border-color: var(--accent); }
+
+/* ================================================
+ CALLOUTS
+ ================================================ */
+
+.doc .callout {
+ border: 1px solid var(--border);
+ border-left: 3px solid var(--accent);
+ background: var(--surface);
+ padding: 14px 18px;
+ border-radius: var(--r);
+ margin: 20px 0;
+ font-size: 14.5px;
+ color: var(--fg-2);
+ line-height: 1.65;
+}
+.doc .callout strong { color: var(--fg); font-weight: 600; }
+.doc .callout a { color: var(--accent-dk); border-bottom-color: var(--accent-bg); }
+.doc .callout a:hover { color: var(--accent); }
+
+/* ================================================
+ PARAMETER TABLES
+ ================================================ */
+
+.doc__params {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 16px 0;
+ font-size: 14px;
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ overflow: hidden;
+}
+.doc__params th, .doc__params td {
+ text-align: left;
+ padding: 10px 16px;
+ border-bottom: 1px solid var(--border);
+ vertical-align: top;
+}
+.doc__params tr:last-child td { border-bottom: none; }
+.doc__params th {
+ font-family: var(--mono);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--fg-3);
+ background: var(--surface);
+ font-weight: 600;
+}
+.doc__params td:first-child { font-family: var(--mono); font-size: 13px; }
+.doc__params td:first-child code { background: none; padding: 0; color: var(--fg); }
+
+/* ================================================
+ NEXT/PREV NAVIGATION
+ ================================================ */
+
+.doc__next {
+ display: flex;
+ justify-content: space-between;
+ gap: 16px;
+ margin-top: 56px;
+ padding-top: 24px;
+ border-top: 1px solid var(--border);
+}
+.doc__next a {
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ padding: 14px 20px;
+ text-decoration: none;
+ color: var(--fg);
+ font-weight: 600;
+ font-size: 15px;
+ transition: box-shadow var(--t), border-color var(--t);
+ min-width: 0;
+ max-width: 48%;
+}
+.doc__next a:hover { box-shadow: var(--sh); border-color: var(--border-md); }
+.doc__next span {
+ display: block;
+ font-family: var(--mono);
+ font-size: 10.5px;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--fg-4);
+ font-weight: 500;
+ margin-bottom: 4px;
+}
+
+/* ================================================
+ IMAGES
+ ================================================ */
+
+.doc__img {
+ max-width: 100%;
+ height: auto;
+ border-radius: var(--r);
+ border: 1px solid var(--border);
+ margin: 16px 0;
+ display: block;
+}
+
+/* ================================================
+ FEATURE GRID (docs home)
+ ================================================ */
+
+.docgrid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1px;
+ background: var(--border);
+ border: 1px solid var(--border);
+ border-radius: var(--r-lg);
+ overflow: hidden;
+ margin-top: 28px;
+}
+.docgrid a {
+ background: var(--bg);
+ padding: 22px 24px;
+ text-decoration: none;
+ color: var(--fg);
+ border: none;
+ transition: background var(--t);
+}
+.docgrid a:hover { background: var(--surface); border-color: transparent; }
+.docgrid h3 { margin: 0 0 5px; font-size: 15.5px; font-weight: 700; letter-spacing: -0.01em; }
+.docgrid p { margin: 0; font-size: 13.5px; color: var(--fg-3); line-height: 1.5; }
+
+/* ================================================
+ MOBILE SIDEBAR TOGGLE
+ ================================================ */
.side__toggle { display: none; }
+/* ================================================
+ RESPONSIVE
+ ================================================ */
+
@media (max-width: 820px) {
- .docshell { grid-template-columns: 1fr; gap: 0; }
- .side { position: static; max-height: none; border-bottom: 1px solid var(--line); padding: 16px 0; display: none; }
+ .docshell { grid-template-columns: 1fr; gap: 0; padding: 0 20px; }
+
+ .side {
+ position: static;
+ max-height: none;
+ border-bottom: 1px solid var(--border);
+ padding: 12px 0 20px;
+ display: none;
+ overflow: visible;
+ }
.side.is-open { display: block; }
- .side__toggle { display: inline-flex; align-items: center; gap: 8px; margin: 16px 0 0; font-family: var(--mono); font-size: 13px; font-weight: 600; background: var(--surface); border: 1.5px solid var(--line-strong); border-radius: 8px; padding: 9px 14px; cursor: pointer; }
+
+ .side__toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ margin: 20px 0 0;
+ font-family: var(--mono);
+ font-size: 13px;
+ font-weight: 600;
+ background: var(--surface);
+ border: 1px solid var(--border-md);
+ border-radius: var(--r);
+ padding: 9px 14px;
+ cursor: pointer;
+ color: var(--fg);
+ transition: background var(--t);
+ }
+ .side__toggle:hover { background: var(--subtle); }
+
.docgrid { grid-template-columns: 1fr; }
+ .doc { padding: 24px 0 64px; }
+}
+
+@media (max-width: 560px) {
+ .doc__next { flex-direction: column; }
+ .doc__next a { max-width: 100%; }
}
diff --git a/opensource/dynavec/docs/docs.js b/opensource/dynavec/docs/docs.js
index 81df7ea..25e230c 100644
--- a/opensource/dynavec/docs/docs.js
+++ b/opensource/dynavec/docs/docs.js
@@ -34,18 +34,19 @@
if (el.textContent.indexOf("<") === -1) el.innerHTML = highlight(el.textContent);
});
- // copy buttons on code blocks
+ // copy buttons on code blocks — styled via .code__copy in docs.css
document.querySelectorAll(".code").forEach(function (pre) {
var btn = document.createElement("button");
btn.className = "code__copy";
btn.textContent = "copy";
+ btn.setAttribute("aria-label", "Copy code");
btn.addEventListener("click", function () {
- navigator.clipboard.writeText(pre.innerText).then(function () {
- btn.textContent = "copied"; setTimeout(function () { btn.textContent = "copy"; }, 1300);
+ var text = pre.innerText.replace(/\ncopy$/i, "").replace(/\ncopied$/i, "").trim();
+ navigator.clipboard.writeText(text).then(function () {
+ btn.textContent = "copied";
+ setTimeout(function () { btn.textContent = "copy"; }, 1300);
});
});
- pre.style.position = "relative";
- btn.style.cssText = "position:absolute;top:8px;right:8px;font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--line);background:var(--surface);color:var(--muted);padding:4px 9px;border-radius:6px;cursor:pointer;";
pre.appendChild(btn);
});
})();
diff --git a/opensource/dynavec/index.html b/opensource/dynavec/index.html
index 6f0d323..d8f1375 100644
--- a/opensource/dynavec/index.html
+++ b/opensource/dynavec/index.html
@@ -70,19 +70,18 @@ The serverless vector database that lives in
Read the docs
- ▶ Watch the 90-sec explainer
+ ▶ Watch on YouTube
View on GitHub
@@ -202,39 +201,210 @@ How it works
Two AWS primitives, each doing the one job it is best at, joined by a shared key.
-
-
- query
-
-
-
- Embedder (BYO key)
-
-
-
- S3 Vectors
- ANN · nearest keys
-
-
- keys + distance
-
-
- DynamoDB
- BatchGetItem · documents
-
-
-
- rerank · results
-
-
- Graph / ER
- traverse → scope
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ keys + distance
+
+
+
+
+ traverse → scope
+
+
+
+
+
+
+
+
+ query
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Embedder
+ BYO key
+
+ OpenAI · Cohere · BYO
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ S3 Vectors
+ ANN · nearest keys
+
+
+ 90%+ recall
+
+ billions of vectors
+
+
+ AMAZON AWS MANAGED
+
+
+ 2
+
+
+
+
+
+
+
+
+
+ DynamoDB
+ BatchGetItem · documents
+
+ single-digit ms hydration
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Graph / ER
+ optional layer
+
+ KG · adjacency lists
+
+
+
+
+
+
+
+
+ ✓
+
+
+
+
+ rerank · results
+ scored documents
+
+ MMR · RRF · BM25
+
+
+ 4
@@ -260,19 +430,23 @@ Benchmarks
dynavec stays lowest at every point because its storage is priced like S3, not RAM.
-
-
- Monthly cost by scale & dimension. dynavec (red) is the lowest line in every panel.
+
+
+ Monthly cost by scale & dimension · 1536-dim · 1M queries/mo. dynavec (coral) is the lowest at every scale.
-
-
-
- Cost by scale (768-d, 1M queries/mo).
+
+
+
+ Cost by scale (768-d, 1M queries/mo) — log-log scale.
+
+
+
+ Raw float32 footprint — up to ~5.7 TiB at 1B × 1536-d.
-
-
- Raw float32 footprint — up to ~5.7 TB at 1B × 1536-d.
+
+
+ Recall ÷ Latency score — higher recall at lower query latency. Representative estimates; run python -m benchmarks.report to reproduce.
@@ -337,7 +511,7 @@ Run your first query
- Full runnable snippet ↓ or read the Quickstart guide .
+ Full runnable snippet ↓ or read the Quickstart guide .
diff --git a/opensource/dynavec/script.js b/opensource/dynavec/script.js
index f969057..b8b6f3a 100644
Binary files a/opensource/dynavec/script.js and b/opensource/dynavec/script.js differ
diff --git a/opensource/dynavec/styles.css b/opensource/dynavec/styles.css
index 1774496..cf20528 100644
--- a/opensource/dynavec/styles.css
+++ b/opensource/dynavec/styles.css
@@ -1,267 +1,782 @@
-/* dynavec — clean, minimal, with a warm coral identity that matches the charts. */
+/* ================================================
+ dynavec · modern minimal
+ ================================================ */
:root {
- --bg: #fbfaf8; /* warm paper */
- --surface: #ffffff;
- --fg: #14110f; /* warm near-black ink */
- --muted: #6f6862;
- --faint: #a99f97;
- --line: #ece6df;
- --line-strong: #14110f;
+ --bg: #ffffff;
+ --surface: #fafafa;
+ --subtle: #f3f3f3;
- --accent: #e8623b; /* coral — matches the dynavec line in the charts */
- --accent-ink: #b8472a; /* darker coral for text/hover on light */
- --accent-soft: #fdeee8; /* light coral wash */
+ --fg: #0d0d0d;
+ --fg-2: #3a3a3a;
+ --fg-3: #6a6a6a;
+ --fg-4: #9a9a9a;
- --code-bg: #f6f1ec;
- --code-kw: #b8472a; /* coral */
- --code-str: #2f7d5b; /* green */
+ --border: #e8e8e8;
+ --border-md: #cecece;
- --inv-bg: #14110f;
- --inv-fg: #fbfaf8;
+ --accent: #e8623b;
+ --accent-dk: #c44820;
+ --accent-bg: #fff3ef;
- --max: 1280px;
- --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
- --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- --shadow: 0 6px 30px rgba(20, 17, 15, .07);
+ --dark: #0d0d0d;
+ --dark-fg: #f0f0f0;
+ --dark-sub: #6a6a6a;
+
+ --sans: "Inter", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
+ --mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace;
+
+ /* Backward-compat aliases — docs.css and dashboard use these */
+ --muted: var(--fg-3);
+ --faint: var(--fg-4);
+ --accent-ink: var(--accent-dk);
+ --accent-soft: var(--accent-bg);
+ --line: var(--border);
+ --line-strong: var(--border-md);
+ --shadow: var(--sh);
+
+ --max: 1080px;
+ --nav-h: 60px;
+ --r: 8px;
+ --r-lg: 14px;
+
+ --sh-sm: 0 1px 4px rgba(0,0,0,.05);
+ --sh: 0 4px 20px rgba(0,0,0,.08), 0 1px 4px rgba(0,0,0,.04);
+ --sh-lg: 0 16px 56px rgba(0,0,0,.10), 0 4px 14px rgba(0,0,0,.06);
+
+ --ease: cubic-bezier(0.16, 1, 0.3, 1);
+ --t: 0.18s var(--ease);
}
-* { box-sizing: border-box; }
-html { scroll-behavior: smooth; }
+/* ---- Reset ---- */
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
+img, svg { display: block; max-width: 100%; }
+a { color: inherit; }
body {
- margin: 0;
background: var(--bg);
color: var(--fg);
font-family: var(--sans);
- font-size: 17px;
- line-height: 1.6;
- border-top: 3px solid var(--accent);
+ font-size: 16px;
+ line-height: 1.65;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
+ border-top: 2px solid var(--accent);
}
-.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 24px; }
-a { color: inherit; }
+.wrap {
+ width: 100%;
+ max-width: var(--max);
+ margin: 0 auto;
+ padding: 0 28px;
+}
-.skip { position: absolute; left: -999px; top: 0; background: var(--accent); color: #fff; padding: 8px 14px; z-index: 100; }
+.skip {
+ position: absolute; left: -9999px; top: 0;
+ background: var(--accent); color: #fff;
+ padding: 8px 14px; z-index: 200; font-size: 14px;
+}
.skip:focus { left: 8px; top: 8px; }
-/* ---------- buttons ---------- */
+/* ================================================
+ NAVIGATION
+ ================================================ */
+
+.nav {
+ position: sticky; top: 0; z-index: 100;
+ height: var(--nav-h);
+ background: rgba(255,255,255,.92);
+ backdrop-filter: blur(16px) saturate(180%);
+ -webkit-backdrop-filter: blur(16px) saturate(180%);
+ border-bottom: 1px solid transparent;
+ transition: border-color var(--t);
+}
+.nav.is-stuck { border-bottom-color: var(--border); }
+
+.nav__inner { display: flex; align-items: center; height: 100%; }
+
+.brand { display: inline-flex; align-items: center; gap: 9px; text-decoration: none; flex-shrink: 0; }
+.brand__mark { color: var(--accent); }
+.brand__name { font-family: var(--mono); font-weight: 700; font-size: 17px; letter-spacing: -0.03em; }
+
+.nav__links { display: flex; gap: 2px; margin-left: 16px; margin-right: auto; }
+.nav__links a {
+ text-decoration: none;
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--fg-3);
+ padding: 6px 10px;
+ border-radius: 6px;
+ transition: color var(--t), background var(--t);
+}
+.nav__links a:hover { color: var(--fg); background: var(--subtle); }
+
+.star { font-size: 13px; padding: 7px 14px; flex-shrink: 0; }
+.star span[aria-hidden="true"] { color: var(--accent); }
+.star__count { font-family: var(--mono); font-weight: 700; }
+
+/* ================================================
+ BUTTONS
+ ================================================ */
+
.btn {
- display: inline-flex; align-items: center; gap: 8px;
- font-size: 14px; font-weight: 600; letter-spacing: .01em;
- padding: 11px 18px; border: 1.5px solid var(--line-strong);
- text-decoration: none; transition: background .15s ease, color .15s ease, border-color .15s ease, transform .1s ease;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ padding: 10px 20px;
+ border-radius: var(--r);
+ border: 1.5px solid transparent;
+ text-decoration: none;
white-space: nowrap;
+ cursor: pointer;
+ transition: background var(--t), color var(--t), border-color var(--t), transform 0.1s;
}
.btn:active { transform: translateY(1px); }
-.btn--solid { background: var(--accent); color: #fff; border-color: var(--accent); }
-.btn--solid:hover { background: var(--accent-ink); border-color: var(--accent-ink); }
-.btn--ghost { background: transparent; color: var(--fg); }
-.btn--ghost:hover { background: var(--fg); color: var(--bg); }
-/* ---------- nav ---------- */
-.nav {
- position: sticky; top: 0; z-index: 50;
- background: rgba(251, 250, 248, .88); backdrop-filter: saturate(180%) blur(8px);
- border-bottom: 1px solid transparent; transition: border-color .2s ease;
-}
-.nav.is-stuck { border-bottom-color: var(--line); }
-.nav__inner { display: flex; align-items: center; gap: 24px; height: 64px; }
-.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
-.brand__mark { display: block; color: var(--accent); }
-.brand__name { font-family: var(--mono); font-weight: 700; font-size: 18px; letter-spacing: -.02em; }
-.nav__links { display: flex; gap: 22px; margin-left: 8px; margin-right: auto; }
-.nav__links a { text-decoration: none; font-size: 14px; font-weight: 500; color: var(--muted); transition: color .12s; }
-.nav__links a:hover { color: var(--accent-ink); }
-.star { font-size: 13px; padding: 8px 14px; }
-.star span[aria-hidden="true"] { color: var(--accent); }
-.star__count { font-family: var(--mono); font-weight: 700; }
+.btn--solid {
+ background: var(--fg);
+ color: var(--bg);
+ border-color: var(--fg);
+}
+.btn--solid:hover { background: #1e1e1e; border-color: #1e1e1e; }
+
+.btn--ghost {
+ background: transparent;
+ color: var(--fg);
+ border-color: var(--border-md);
+}
+.btn--ghost:hover { background: var(--subtle); }
+
+/* ================================================
+ HERO
+ ================================================ */
-/* ---------- hero ---------- */
.hero {
- padding: 96px 0 72px; border-bottom: 1px solid var(--line);
- background: radial-gradient(1200px 400px at 15% -10%, var(--accent-soft), transparent 60%);
+ padding: 96px 0 80px;
+ border-bottom: 1px solid var(--border);
+}
+
+.hero__grid {
+ display: grid;
+ grid-template-columns: 1.1fr 0.9fr;
+ gap: clamp(32px, 5vw, 72px);
+ align-items: center;
}
+.hero__col { min-width: 0; }
+
.eyebrow {
- font-family: var(--mono); font-size: 12.5px; letter-spacing: .06em;
- text-transform: uppercase; color: var(--accent-ink); margin: 0 0 22px;
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ font-family: var(--mono);
+ font-size: 11px;
+ letter-spacing: 0.09em;
+ text-transform: uppercase;
+ color: var(--fg-3);
+ margin-bottom: 22px;
}
+.eyebrow::before {
+ content: "";
+ display: inline-block;
+ width: 22px;
+ height: 1.5px;
+ background: var(--accent);
+ flex-shrink: 0;
+}
+
.hero__title {
- font-size: clamp(38px, 6vw, 68px); line-height: 1.04; letter-spacing: -.03em;
- font-weight: 700; margin: 0 0 24px; max-width: 16ch;
+ font-size: clamp(38px, 5.2vw, 66px);
+ line-height: 1.04;
+ letter-spacing: -0.04em;
+ font-weight: 800;
+ margin-bottom: 22px;
+ max-width: 16ch;
+}
+.hero__title .u {
+ color: var(--accent);
+ text-decoration: none;
}
-.hero__title .u { text-decoration: underline; text-decoration-color: var(--accent); text-decoration-thickness: 4px; text-underline-offset: 5px; }
-.hero__lede { font-size: 19px; color: #3a332e; max-width: 62ch; margin: 0 0 34px; }
-.hero__lede strong { font-weight: 600; }
-.install { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 30px; }
-.install__row { display: flex; align-items: center; gap: 10px; border: 1.5px solid var(--line-strong); background: var(--surface); }
-.install__row code { font-family: var(--mono); font-size: 14px; padding: 11px 14px; white-space: nowrap; }
+.hero__lede {
+ font-size: 17px;
+ color: var(--fg-2);
+ max-width: 58ch;
+ margin-bottom: 30px;
+ line-height: 1.72;
+}
+.hero__lede strong { color: var(--fg); font-weight: 600; }
+
+.install { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 28px; }
+.install__row {
+ display: inline-flex;
+ align-items: center;
+ background: var(--subtle);
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ overflow: hidden;
+}
+.install__row code {
+ font-family: var(--mono);
+ font-size: 13px;
+ padding: 10px 14px;
+ white-space: nowrap;
+ color: var(--fg);
+}
.copy {
- font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
- border: none; border-left: 1.5px solid var(--line-strong); background: transparent;
- padding: 12px 14px; cursor: pointer; color: var(--muted); align-self: stretch; transition: background .12s, color .12s;
+ font-family: var(--mono);
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ border: none;
+ border-left: 1px solid var(--border);
+ background: transparent;
+ padding: 10px 14px;
+ cursor: pointer;
+ color: var(--fg-4);
+ align-self: stretch;
+ transition: background var(--t), color var(--t);
}
.copy:hover { background: var(--accent); color: #fff; }
-.copy.is-done { color: var(--accent-ink); }
+.copy.is-done { color: var(--accent); background: var(--accent-bg); }
-.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 56px; }
+.hero__cta { display: flex; flex-wrap: wrap; gap: 10px; }
-/* two-column hero: text left, video right */
-.hero__grid { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: clamp(28px, 5vw, 60px); align-items: center; }
-.hero__col { min-width: 0; }
-.hero__grid .hero__title { font-size: clamp(30px, 3.6vw, 52px); }
-.hero__grid .hero__lede { max-width: 100%; }
-.hero__grid .hero__cta { margin-bottom: 0; }
.hero__media { min-width: 0; }
+
+/* YouTube video (kept for explainer page) */
.video-frame {
- position: relative; width: 100%; aspect-ratio: 16 / 9;
- border: 1.5px solid var(--line-strong); border-radius: 14px; overflow: hidden;
- box-shadow: var(--shadow); background: #000;
+ position: relative;
+ width: 100%;
+ aspect-ratio: 16/9;
+ border-radius: var(--r-lg);
+ overflow: hidden;
+ border: 1px solid var(--border);
+ box-shadow: var(--sh-lg);
+ background: #000;
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.hero__stats {
- list-style: none; margin: 48px 0 0; padding: 28px 0 0; border-top: 1px solid var(--line);
- display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px;
-}
-.hero__stats li { display: flex; flex-direction: column; gap: 4px; }
-.stat__n { font-family: var(--mono); font-size: 30px; font-weight: 700; letter-spacing: -.02em; color: var(--accent-ink); }
-.stat__l { font-size: 13px; color: var(--muted); }
-
-/* inline service links in prose */
-.svc-link { text-decoration: none; border-bottom: 2px solid var(--accent); transition: color .12s; }
-.svc-link:hover { color: var(--accent-ink); }
-
-/* ---------- built-on-AWS band ---------- */
-.builton { padding: 40px 0; border-bottom: 1px solid var(--line); background: var(--surface); }
-.builton__eyebrow { font-family: var(--mono); font-size: 12.5px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); margin: 0 0 20px; }
-.stack { display: flex; align-items: stretch; gap: 16px; flex-wrap: wrap; }
-.stack__plus { display: flex; align-items: center; font-family: var(--mono); font-size: 26px; color: var(--faint); }
+ list-style: none;
+ margin-top: 56px;
+ padding-top: 32px;
+ border-top: 1px solid var(--border);
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+}
+.hero__stats li { display: flex; flex-direction: column; gap: 5px; padding-right: 24px; }
+.stat__n { font-family: var(--mono); font-size: 30px; font-weight: 700; letter-spacing: -0.04em; color: var(--fg); line-height: 1; }
+.stat__l { font-size: 12.5px; color: var(--fg-3); }
+
+/* ================================================
+ BUILT ON AWS
+ ================================================ */
+
+.builton {
+ padding: 48px 0;
+ border-bottom: 1px solid var(--border);
+ background: var(--surface);
+}
+.builton__eyebrow {
+ font-family: var(--mono);
+ font-size: 11px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--fg-4);
+ margin-bottom: 20px;
+}
+
+.stack { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
+.stack__plus { font-family: var(--mono); font-size: 22px; color: var(--border-md); line-height: 1; flex-shrink: 0; }
+
.svc {
- flex: 1 1 340px; display: flex; align-items: center; gap: 16px;
- text-decoration: none; color: inherit; padding: 18px 20px;
- border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
- transition: box-shadow .16s ease, transform .16s ease, border-color .16s ease;
+ flex: 1 1 300px;
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ text-decoration: none;
+ color: inherit;
+ padding: 18px 22px;
+ border: 1px solid var(--border);
+ border-radius: var(--r-lg);
+ background: var(--bg);
+ transition: box-shadow var(--t), transform var(--t), border-color var(--t);
}
-.svc:hover { box-shadow: var(--shadow); transform: translateY(-2px); border-color: #ddd4cb; }
-.svc__icon { flex: 0 0 auto; line-height: 0; filter: drop-shadow(0 3px 8px rgba(20,17,15,.16)); }
+.svc:hover { box-shadow: var(--sh); transform: translateY(-2px); border-color: var(--border-md); }
+
+.svc__icon { flex-shrink: 0; line-height: 0; }
.svc__body { display: flex; flex-direction: column; gap: 3px; }
-.svc__name { font-weight: 700; font-size: 17px; letter-spacing: -.01em; }
-.svc__ext { color: var(--muted); font-weight: 500; }
+.svc__name { font-weight: 700; font-size: 15.5px; letter-spacing: -0.02em; }
+.svc__ext { color: var(--fg-4); font-size: 13px; font-weight: 400; }
.svc:hover .svc__ext { color: var(--accent); }
-.svc__role { font-size: 13.5px; color: var(--muted); }
-.builton__note { margin: 20px 0 0; font-size: 14.5px; color: var(--muted); }
-.builton__note strong { color: var(--fg); }
+.svc__role { font-size: 13px; color: var(--fg-3); }
+
+.builton__note { margin-top: 20px; font-size: 14px; color: var(--fg-3); }
+.builton__note strong { color: var(--fg); font-weight: 600; }
+
@media (max-width: 620px) { .stack__plus { display: none; } }
-/* ---------- sections ---------- */
-.section { padding: 84px 0; border-bottom: 1px solid var(--line); }
-.section--alt { background: linear-gradient(180deg, #f7f2ec, var(--bg)); }
-.section__title { font-size: clamp(26px, 3.6vw, 38px); letter-spacing: -.02em; font-weight: 700; margin: 0 0 12px; }
-.section__title::after { content: ""; display: block; width: 46px; height: 3px; background: var(--accent); margin-top: 14px; }
-.section__lede { font-size: 18px; color: var(--muted); max-width: 66ch; margin: 12px 0 44px; }
+.svc-link {
+ text-decoration: none;
+ border-bottom: 1px solid var(--border-md);
+ transition: color var(--t), border-color var(--t);
+}
+.svc-link:hover { color: var(--accent); border-color: var(--accent); }
+
+/* ================================================
+ SECTIONS
+ ================================================ */
+
+.section { padding: 96px 0; border-bottom: 1px solid var(--border); }
+.section--alt { background: var(--surface); }
+
+.section__title {
+ font-size: clamp(26px, 3.8vw, 42px);
+ font-weight: 800;
+ letter-spacing: -0.03em;
+ line-height: 1.08;
+}
+.section__title::after {
+ content: "";
+ display: block;
+ width: 30px;
+ height: 2.5px;
+ background: var(--accent);
+ margin-top: 16px;
+ border-radius: 2px;
+}
+.section__lede { font-size: 17px; color: var(--fg-2); max-width: 62ch; margin: 14px 0 44px; line-height: 1.7; }
+
+/* ================================================
+ CARDS & GRID
+ ================================================ */
-/* ---------- grids / cards ---------- */
-.grid { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); }
+.grid {
+ display: grid;
+ gap: 1px;
+ background: var(--border);
+ border: 1px solid var(--border);
+ border-radius: var(--r-lg);
+ overflow: hidden;
+}
.grid--3 { grid-template-columns: repeat(3, 1fr); }
-.grid--2 { grid-template-columns: repeat(2, 1fr); gap: 24px; background: transparent; border: none; }
-.card { background: var(--surface); padding: 30px 26px; position: relative; transition: box-shadow .16s ease, transform .16s ease; }
-.card:hover { box-shadow: var(--shadow); transform: translateY(-2px); z-index: 1; }
-.card__no { font-family: var(--mono); font-size: 13px; font-weight: 700; color: var(--accent); display: block; margin-bottom: 16px; }
-.card h3 { font-size: 19px; margin: 0 0 10px; letter-spacing: -.01em; }
-.card p { margin: 0; color: #3a332e; font-size: 15.5px; }
-.card code { font-family: var(--mono); font-size: 13.5px; background: var(--accent-soft); color: var(--accent-ink); padding: 1px 5px; }
-.link { display: inline-block; margin-top: 14px; font-weight: 600; font-size: 14.5px; text-decoration: none; color: var(--accent-ink); border-bottom: 1.5px solid var(--accent); padding-bottom: 1px; }
+.grid--2 { grid-template-columns: repeat(2, 1fr); gap: 20px; background: transparent; border: none; border-radius: 0; overflow: visible; }
+
+.card { background: var(--bg); padding: 30px 26px; position: relative; transition: background var(--t); }
+.grid--3 .card:hover { background: var(--surface); }
+
+.grid--2 .card {
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ transition: box-shadow var(--t), border-color var(--t);
+}
+.grid--2 .card:hover { box-shadow: var(--sh); border-color: var(--border-md); }
+
+.card__no { font-family: var(--mono); font-size: 11.5px; font-weight: 700; color: var(--accent); display: block; margin-bottom: 14px; letter-spacing: 0.04em; }
+.card h3 { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 8px; }
+.card p { color: var(--fg-2); font-size: 14.5px; line-height: 1.65; }
+.card > p > code,
+.card > p code {
+ font-family: var(--mono);
+ font-size: 13px;
+ background: var(--accent-bg);
+ color: var(--accent-dk);
+ padding: 1px 5px;
+ border-radius: 3px;
+}
+
+.link {
+ display: inline-block;
+ margin-top: 14px;
+ font-size: 14px;
+ font-weight: 600;
+ text-decoration: none;
+ color: var(--fg);
+ letter-spacing: -0.01em;
+ transition: color var(--t);
+}
+.link::after { content: " →"; }
.link:hover { color: var(--accent); }
-/* ---------- architecture ---------- */
-.arch { border: 1px solid var(--line); background: var(--surface); padding: 24px; margin-bottom: 40px; overflow-x: auto; box-shadow: var(--shadow); }
-.arch__svg { width: 100%; min-width: 720px; height: auto; color: var(--fg); }
-.d-box { fill: #fff; stroke: var(--fg); stroke-width: 1.5; }
-.d-box--em { fill: var(--accent); stroke: var(--accent); }
-.d-box--dash { fill: var(--accent-soft); stroke: var(--accent); stroke-dasharray: 5 4; }
-.d-text { font-family: var(--sans); font-size: 15px; font-weight: 600; fill: var(--fg); }
-.d-sub { font-family: var(--mono); font-size: 11px; fill: var(--muted); }
-.d-label { font-family: var(--mono); font-size: 13px; fill: var(--accent-ink); }
-.d-line { stroke: var(--fg); stroke-width: 1.5; }
-.arch__svg text[text-anchor="middle"][x="490"][y="38"],
-.arch__svg text[text-anchor="middle"][x="490"][y="58"] { fill: #fff; }
-.arch__notes h3 { font-size: 18px; margin: 0 0 8px; }
-.arch__notes p { color: #3a332e; font-size: 15.5px; margin: 0; }
+/* ================================================
+ ARCHITECTURE
+ ================================================ */
+
+.arch {
+ border-radius: 20px;
+ margin-bottom: 40px;
+ overflow-x: auto;
+}
+.arch__svg { width: 100%; min-width: 720px; height: auto; display: block; border-radius: 20px; }
+.d-line { stroke-width: 1.5; }
+
+.arch__notes h3 { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 8px; }
+.arch__notes p { color: var(--fg-2); font-size: 15px; line-height: 1.65; margin: 0; }
.tick::before { content: "— "; color: var(--accent); }
-/* ---------- figures ---------- */
-.fig { margin: 0 0 28px; border: 1px solid var(--line); background: var(--surface); padding: 16px; transition: box-shadow .16s ease; }
-.fig:hover { box-shadow: var(--shadow); }
-.fig img { display: block; width: 100%; height: auto; }
-.fig figcaption { font-size: 13.5px; color: var(--muted); margin-top: 12px; font-family: var(--mono); }
-
-/* ---------- table ---------- */
-.table__title { font-size: 17px; margin: 36px 0 14px; font-weight: 600; }
-.table__title code { font-family: var(--mono); font-size: 14px; color: var(--accent-ink); }
-.table__scroll { overflow-x: auto; border: 1px solid var(--line); border-radius: 2px; }
-.table { width: 100%; border-collapse: collapse; font-size: 15px; min-width: 620px; background: var(--surface); }
-.table th, .table td { text-align: right; padding: 12px 16px; border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums; }
+/* ================================================
+ BENCHMARKS / FIGURES / TABLE
+ ================================================ */
+
+.fig {
+ border: 1px solid var(--border);
+ border-radius: var(--r);
+ background: var(--bg);
+ padding: 20px;
+ margin: 0 0 20px;
+}
+.fig img { width: 100%; height: auto; border-radius: 4px; }
+.fig figcaption { font-family: var(--mono); font-size: 12px; color: var(--fg-3); margin-top: 12px; }
+.fig--donut canvas { display: block; width: 100%; height: 260px; }
+
+/* Canvas benchmark charts */
+.fig--chart { padding: 0; overflow: hidden; border-radius: var(--r); }
+.fig--chart canvas { display: block; border-radius: var(--r) var(--r) 0 0; }
+.fig--chart figcaption { padding: 10px 16px 14px; }
+
+/* 3-column grid for benchmark sub-charts */
+.grid--3-bench { grid-template-columns: 1fr 1fr 1fr; }
+@media (max-width: 820px) { .grid--3-bench { grid-template-columns: 1fr; } }
+
+.table__title { font-size: 16px; font-weight: 700; letter-spacing: -0.02em; margin: 36px 0 14px; }
+.table__title code { font-family: var(--mono); font-size: 13px; background: var(--subtle); color: var(--fg-2); padding: 2px 6px; border-radius: 4px; }
+
+.table__scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r); background: var(--bg); }
+.table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 580px; }
+.table th, .table td { text-align: right; padding: 11px 18px; border-bottom: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.table th:first-child, .table td:first-child { text-align: left; font-family: var(--mono); }
-.table thead th { font-family: var(--mono); font-size: 12.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); background: #faf6f1; }
-.table tbody tr.is-best { background: var(--accent); color: #fff; }
-.table tbody tr.is-best td { border-bottom-color: var(--accent-ink); font-weight: 600; }
-.note { font-size: 13.5px; color: var(--muted); margin-top: 18px; max-width: 74ch; }
-.note code { font-family: var(--mono); color: var(--accent-ink); }
-
-/* ---------- tabs / code ---------- */
-.tabs { border: 1px solid var(--line-strong); background: var(--surface); box-shadow: var(--shadow); }
-.tabs__list { display: flex; flex-wrap: wrap; border-bottom: 1px solid var(--line-strong); background: #faf6f1; }
+.table thead th { font-family: var(--mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-3); background: var(--surface); font-weight: 600; }
+.table tbody tr:last-child td { border-bottom: none; }
+.table tbody tr.is-best { background: var(--dark); color: var(--dark-fg); }
+.table tbody tr.is-best td { border-bottom-color: rgba(255,255,255,.08); font-weight: 600; }
+
+.note { font-size: 13px; color: var(--fg-3); margin-top: 18px; max-width: 72ch; line-height: 1.65; }
+.note code { font-family: var(--mono); font-size: 12px; background: var(--subtle); color: var(--fg-2); padding: 1px 5px; border-radius: 3px; }
+
+/* ================================================
+ TABS & CODE
+ ================================================ */
+
+.tabs { border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; }
+
+.tabs__list {
+ display: flex;
+ flex-wrap: wrap;
+ background: var(--surface);
+ border-bottom: 1px solid var(--border);
+ padding: 10px 10px 0;
+ gap: 4px;
+}
.tab {
- font-family: var(--mono); font-size: 13px; font-weight: 500;
- background: transparent; border: none; border-right: 1px solid var(--line);
- padding: 13px 18px; cursor: pointer; color: var(--muted); transition: background .12s, color .12s;
+ font-family: var(--mono);
+ font-size: 12.5px;
+ font-weight: 500;
+ background: transparent;
+ border: 1px solid transparent;
+ border-bottom: none;
+ border-radius: 6px 6px 0 0;
+ padding: 8px 14px;
+ cursor: pointer;
+ color: var(--fg-3);
+ transition: background var(--t), color var(--t);
+}
+.tab:hover { color: var(--fg); background: rgba(0,0,0,.04); }
+.tab.is-active { background: var(--dark); color: #fff; }
+
+.tabs__panels { background: var(--dark); }
+
+.code {
+ margin: 0;
+ padding: 26px 28px;
+ overflow-x: auto;
+ font-family: var(--mono);
+ font-size: 13.5px;
+ line-height: 1.78;
+ background: var(--dark);
+ color: #d4d4d4;
+ tab-size: 2;
}
-.tab:hover { color: var(--accent-ink); }
-.tab.is-active { background: var(--accent); color: #fff; }
-.code { margin: 0; padding: 24px 26px; overflow-x: auto; font-family: var(--mono); font-size: 13.5px; line-height: 1.7; background: var(--surface); color: #2b2620; }
.code.is-hidden { display: none; }
-.code .c-comment { color: var(--faint); font-style: italic; }
-.code .c-str { color: var(--code-str); }
-.code .c-kw { color: var(--code-kw); font-weight: 700; }
-.code--sm { font-size: 12.5px; padding: 0; border: none; background: transparent; }
-
-/* ---------- features ---------- */
-.features { margin-top: 56px; }
-.features__title { font-size: 18px; margin: 0 0 20px; }
-.features__grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--line); border: 1px solid var(--line); }
-.features__grid li { background: var(--surface); padding: 16px 18px; font-size: 14.5px; transition: background .12s; }
-.features__grid li:hover { background: var(--accent-soft); }
+.code .c-comment { color: #555; font-style: italic; }
+.code .c-str { color: #89c18a; }
+.code .c-kw { color: #dba96e; font-weight: 600; }
+
+.code--sm {
+ font-size: 12.5px;
+ padding: 16px 18px;
+ border: none;
+ border-radius: var(--r);
+ background: var(--dark);
+ color: #d4d4d4;
+ margin-top: 14px;
+ overflow-x: auto;
+}
+/* prevent card code pill styles from leaking into code blocks */
+.code--sm code { background: none !important; color: inherit !important; padding: 0 !important; border-radius: 0 !important; font-size: inherit !important; }
+
+/* ================================================
+ FEATURES LIST
+ ================================================ */
+
+.features { margin-top: 48px; }
+.features__title { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 16px; color: var(--dark-fg); }
+
+.features__grid {
+ list-style: none;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1px;
+ background: rgba(255,255,255,.07);
+ border: 1px solid rgba(255,255,255,.07);
+ border-radius: var(--r);
+ overflow: hidden;
+}
+.features__grid li {
+ background: var(--dark);
+ padding: 12px 16px;
+ font-size: 13.5px;
+ color: #7a7a7a;
+ transition: background var(--t), color var(--t);
+}
+.features__grid li:hover { background: #161616; color: #d8d8d8; }
.features__grid li::before { content: "+"; font-family: var(--mono); font-weight: 700; color: var(--accent); margin-right: 10px; }
-/* ---------- footer ---------- */
-.footer { background: var(--inv-bg); color: var(--inv-fg); padding: 56px 0; }
-.footer .brand__name { color: var(--inv-fg); }
+/* ================================================
+ FOOTER
+ ================================================ */
+
+.footer { background: var(--dark); color: var(--dark-fg); padding: 64px 0; }
+.footer .brand__name { color: var(--dark-fg); }
.footer .brand__name::after { content: "."; color: var(--accent); }
-.footer__tag { color: #b5ada6; font-size: 14px; margin: 10px 0 0; }
-.footer__inner { display: grid; grid-template-columns: 1.4fr 1fr auto; gap: 32px; align-items: start; }
-.footer__links { display: flex; flex-direction: column; gap: 10px; }
-.footer__links a { color: #d8d2cc; text-decoration: none; font-size: 14.5px; transition: color .12s; }
-.footer__links a:hover { color: var(--accent); }
-.footer__legal { color: #8a8079; font-size: 12.5px; margin: 0; align-self: end; }
-
-/* ---------- responsive ---------- */
-@media (max-width: 920px) {
- .hero__grid { grid-template-columns: 1fr; gap: 32px; }
- .hero__media { order: 2; }
+.footer__tag { color: var(--dark-sub); font-size: 14px; margin-top: 8px; }
+.footer__inner { display: grid; grid-template-columns: 1.4fr 1fr auto; gap: 40px; align-items: start; }
+.footer__links { display: flex; flex-direction: column; gap: 8px; }
+.footer__links a { color: var(--dark-sub); text-decoration: none; font-size: 14px; transition: color var(--t); }
+.footer__links a:hover { color: var(--dark-fg); }
+.footer__legal { color: #404040; font-size: 12px; align-self: end; }
+
+/* ================================================
+ RESPONSIVE
+ ================================================ */
+
+@media (max-width: 940px) {
+ .hero__grid { grid-template-columns: 1fr; }
+ .hero__media { max-width: 520px; }
}
@media (max-width: 860px) {
.nav__links { display: none; }
- .grid--3, .grid--2, .features__grid { grid-template-columns: 1fr; }
+ .grid--3 { grid-template-columns: 1fr 1fr; }
+ .grid--2 { grid-template-columns: 1fr; }
+ .features__grid { grid-template-columns: 1fr 1fr; }
.hero__stats { grid-template-columns: repeat(2, 1fr); }
.footer__inner { grid-template-columns: 1fr; }
}
-@media (max-width: 520px) {
- body { font-size: 16px; }
- .hero { padding: 64px 0 48px; }
- .section { padding: 60px 0; }
+@media (max-width: 560px) {
+ .wrap { padding: 0 20px; }
+ .hero { padding: 64px 0 56px; }
+ .section { padding: 64px 0; }
.hero__stats { grid-template-columns: 1fr 1fr; }
+ .grid--3 { grid-template-columns: 1fr; }
+ .features__grid { grid-template-columns: 1fr; }
+ body { font-size: 15px; }
+}
+
+/* ================================================
+ HERO ENTRANCE ANIMATIONS
+ ================================================ */
+
+@keyframes fadeUp {
+ from { opacity: 0; transform: translateY(28px); }
+ to { opacity: 1; transform: none; }
+}
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
}
+
+/* Brand name — coral shimmer sweeps through on load */
+@keyframes brand-shimmer {
+ 0% { background-position: 160% center; }
+ 100% { background-position: -160% center; }
+}
+.nav .brand__name {
+ background: linear-gradient(
+ 90deg,
+ var(--fg) 0%, var(--fg) 25%,
+ var(--accent) 50%,
+ var(--fg) 75%, var(--fg) 100%
+ );
+ background-size: 250% auto;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ animation: brand-shimmer 1.8s cubic-bezier(0.4,0,0.2,1) 0.6s both;
+}
+
+/* Install code — blinking cursor during typewriter */
+@keyframes cursor-blink {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0; }
+}
+.install__cursor {
+ display: inline-block;
+ width: 2px;
+ height: 0.85em;
+ background: var(--accent);
+ vertical-align: -0.08em;
+ margin-left: 1px;
+ border-radius: 1px;
+ animation: cursor-blink 0.65s step-end infinite;
+}
+
+.eyebrow { animation: fadeUp 0.7s var(--ease) 0.05s both; }
+.hero__title { animation: fadeUp 0.75s var(--ease) 0.15s both; }
+.hero__lede { animation: fadeUp 0.75s var(--ease) 0.25s both; }
+.install { animation: fadeUp 0.75s var(--ease) 0.35s both; }
+.hero__cta { animation: fadeUp 0.75s var(--ease) 0.45s both; }
+.hero__canvas-wrap { animation: fadeUp 0.9s var(--ease) 0.2s both; }
+.hero__stats { animation: fadeUp 0.75s var(--ease) 0.55s both; }
+
+/* ================================================
+ 3D CANVAS HERO VISUAL
+ ================================================ */
+
+.hero__canvas-wrap {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 1 / 1;
+ border-radius: var(--r-lg);
+ overflow: hidden;
+ border: 1.5px solid var(--accent);
+ background: var(--subtle);
+ cursor: crosshair;
+ box-shadow: 0 0 0 4px var(--accent-bg), var(--sh-lg);
+ transition: box-shadow var(--t);
+}
+.hero__canvas-wrap:hover {
+ box-shadow: 0 0 0 6px var(--accent-bg), 0 20px 60px rgba(232,98,59,.15);
+}
+
+#hero-canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.canvas-label {
+ position: absolute;
+ bottom: 14px;
+ left: 14px;
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ font-family: var(--mono);
+ font-size: 10.5px;
+ letter-spacing: 0.07em;
+ text-transform: uppercase;
+ color: var(--fg-3);
+ background: rgba(255,255,255,.88);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ padding: 5px 11px;
+ border-radius: 20px;
+ border: 1px solid var(--border);
+ pointer-events: none;
+ user-select: none;
+}
+
+.canvas-label__dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: var(--accent);
+ flex-shrink: 0;
+ animation: pulseDot 2.2s ease-in-out infinite;
+}
+
+@keyframes pulseDot {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.45; transform: scale(0.7); }
+}
+
+/* ================================================
+ SCROLL REVEAL
+ ================================================ */
+
+[data-reveal] {
+ opacity: 0;
+ transform: translateY(36px);
+ transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
+ will-change: opacity, transform;
+}
+[data-reveal="fade"] { transform: none; }
+[data-reveal="left"] { transform: translateX(-36px); }
+[data-reveal="right"] { transform: translateX(36px); }
+[data-reveal="scale"] { transform: scale(0.94) translateY(20px); }
+[data-reveal="up"] { transform: translateY(36px); }
+
+[data-reveal].is-visible {
+ opacity: 1;
+ transform: none;
+}
+
+/* ================================================
+ SECTION UNDERLINE REVEAL
+ ================================================ */
+
+.section__title::after {
+ transform: scaleX(0);
+ transform-origin: left;
+ transition: transform 0.6s var(--ease) 0.3s;
+}
+.section__title.is-visible::after { transform: scaleX(1); }
+
+/* ================================================
+ STAT NUMBER POP
+ ================================================ */
+
+.stat__n {
+ display: inline-block;
+ transition: transform 0.5s var(--ease);
+}
+.hero__stats.is-visible .stat__n { animation: statPop 0.6s var(--ease) both; }
+.hero__stats li:nth-child(1) .stat__n { animation-delay: 0.55s; }
+.hero__stats li:nth-child(2) .stat__n { animation-delay: 0.65s; }
+.hero__stats li:nth-child(3) .stat__n { animation-delay: 0.75s; }
+.hero__stats li:nth-child(4) .stat__n { animation-delay: 0.85s; }
+
+@keyframes statPop {
+ from { opacity: 0; transform: translateY(12px) scale(0.88); }
+ to { opacity: 1; transform: none; }
+}
+
+/* ================================================
+ ARCHITECTURE SVG LINE DRAW
+ ================================================ */
+
+.arch[data-reveal] .d-line {
+ stroke-dasharray: 300;
+ stroke-dashoffset: 300;
+ transition: stroke-dashoffset 1.2s var(--ease);
+}
+.arch.is-visible .d-line { stroke-dashoffset: 0; }
+
+/* ================================================
+ FLOATING ANIMATION (decorative)
+ ================================================ */
+
+@keyframes floatY {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-8px); }
+}
+
+/* ================================================
+ SMOOTH HOVER LIFT (global)
+ ================================================ */
+
+.svc, .fig, .card { transition-property: box-shadow, border-color, transform, background; }
+.svc:hover, .fig:hover { transform: translateY(-3px); }
diff --git a/src/dynavec/__init__.py b/src/dynavec/__init__.py
index 5a3198e..fea57f6 100644
--- a/src/dynavec/__init__.py
+++ b/src/dynavec/__init__.py
@@ -37,7 +37,7 @@
from .hot import HotTier
from .models import Document, SearchResult, UpsertResult
from .namespace import NamespaceView
-from .quantization import ProductQuantizer
+from .quantization import ProductQuantizer, ScalarQuantizer
from .retrieval import (
maximal_marginal_relevance,
reciprocal_rank_fusion,
@@ -61,6 +61,7 @@
"UpsertResult",
"NamespaceView",
"ProductQuantizer",
+ "ScalarQuantizer",
"GraphStore",
"BaseCache",
"SemanticCache",
diff --git a/src/dynavec/quantization.py b/src/dynavec/quantization.py
index 25a80ba..79b63fc 100644
--- a/src/dynavec/quantization.py
+++ b/src/dynavec/quantization.py
@@ -138,3 +138,74 @@ def reconstruction_error(self, vectors: np.ndarray) -> float:
def _check_fitted(self) -> None:
if self._codebooks is None:
raise RuntimeError("ProductQuantizer must be .fit() before use")
+
+
+
+@dataclass
+class ScalarQuantizer:
+ """Per-dimension INT8 scalar quantization."""
+
+ def __post_init__(self):
+ self._mins = None
+ self._scales = None
+
+ @property
+ def is_fitted(self):
+ return self._mins is not None
+
+ @property
+ def code_size_bytes(self):
+ if self._mins is None:
+ raise RuntimeError("ScalarQuantizer is not fitted")
+ return self._mins.shape[0]
+
+ def fit(self, vectors):
+ vectors = np.asarray(vectors, dtype=np.float32)
+
+ if vectors.ndim != 2:
+ raise ValueError("vectors must be a 2D array")
+
+ mins = vectors.min(axis=0)
+ maxs = vectors.max(axis=0)
+
+ scales = (maxs - mins) / 255.0
+ scales = np.where(scales == 0, 1.0, scales)
+
+ self._mins = mins
+ self._scales = scales
+
+ return self
+
+ def encode(self, vectors):
+ self._check_fitted()
+
+ vectors = np.asarray(vectors, dtype=np.float32)
+
+ if vectors.ndim != 2:
+ raise ValueError("vectors must be a 2D array")
+
+ codes = np.round(
+ (vectors - self._mins) / self._scales - 128
+ )
+
+ return np.clip(codes, -128, 127).astype(np.int8)
+
+ def decode(self, codes):
+ self._check_fitted()
+
+ codes = np.asarray(codes, dtype=np.int8)
+
+ return (
+ (codes.astype(np.float32) + 128) * self._scales
+ + self._mins
+ ).astype(np.float32)
+
+ def reconstruction_error(self, vectors):
+ vectors = np.asarray(vectors, dtype=np.float32)
+ reconstructed = self.decode(self.encode(vectors))
+
+ return float(np.mean((vectors - reconstructed) ** 2))
+
+ def _check_fitted(self):
+ if not self.is_fitted:
+ raise RuntimeError("ScalarQuantizer must be .fit() before use")
diff --git a/tests/test_quantization.py b/tests/test_quantization.py
index 7075811..271900c 100644
--- a/tests/test_quantization.py
+++ b/tests/test_quantization.py
@@ -3,7 +3,7 @@
import numpy as np
import pytest
-from dynavec.quantization import ProductQuantizer
+from dynavec.quantization import ProductQuantizer, ScalarQuantizer
@pytest.fixture
@@ -55,3 +55,44 @@ def test_use_before_fit_raises():
pq = ProductQuantizer(m=4)
with pytest.raises(RuntimeError):
pq.encode(np.zeros((1, 16), dtype=np.float32))
+
+
+def test_scalar_fit_encode_shapes(clustered):
+ sq = ScalarQuantizer().fit(clustered)
+ codes = sq.encode(clustered)
+
+ assert codes.shape == clustered.shape
+ assert codes.dtype == np.int8
+
+
+def test_scalar_code_size_and_compression(clustered):
+ sq = ScalarQuantizer().fit(clustered)
+
+ assert sq.code_size_bytes == clustered.shape[1]
+
+ raw = clustered.shape[1] * 4
+ assert raw / sq.code_size_bytes == 4
+
+
+def test_scalar_reconstruction_error_is_reasonable(clustered):
+ sq = ScalarQuantizer().fit(clustered)
+
+ err = sq.reconstruction_error(clustered)
+
+ assert err < 0.01
+
+
+def test_scalar_decode_shape_and_dtype(clustered):
+ sq = ScalarQuantizer().fit(clustered)
+ codes = sq.encode(clustered)
+ recon = sq.decode(codes)
+
+ assert recon.shape == clustered.shape
+ assert recon.dtype == np.float32
+
+
+def test_scalar_use_before_fit_raises():
+ sq = ScalarQuantizer()
+
+ with pytest.raises(RuntimeError):
+ sq.encode(np.zeros((1, 16), dtype=np.float32))