diff --git a/opensource/dynavec/images/contributors/abhishek-gupta.png b/opensource/dynavec/images/contributors/abhishek-gupta.png new file mode 100644 index 0000000..a82aaac Binary files /dev/null and b/opensource/dynavec/images/contributors/abhishek-gupta.png differ diff --git a/opensource/dynavec/images/contributors/ashish-kumar.png b/opensource/dynavec/images/contributors/ashish-kumar.png new file mode 100644 index 0000000..4cbe21f Binary files /dev/null and b/opensource/dynavec/images/contributors/ashish-kumar.png differ diff --git a/opensource/dynavec/images/contributors/henil-bhavsar.png b/opensource/dynavec/images/contributors/henil-bhavsar.png new file mode 100644 index 0000000..cc60de0 Binary files /dev/null and b/opensource/dynavec/images/contributors/henil-bhavsar.png differ diff --git a/opensource/dynavec/images/contributors/isha-zaka.png b/opensource/dynavec/images/contributors/isha-zaka.png new file mode 100644 index 0000000..199cb95 Binary files /dev/null and b/opensource/dynavec/images/contributors/isha-zaka.png differ diff --git a/opensource/dynavec/images/contributors/madhav-sharma.png b/opensource/dynavec/images/contributors/madhav-sharma.png new file mode 100644 index 0000000..1bdded8 Binary files /dev/null and b/opensource/dynavec/images/contributors/madhav-sharma.png differ diff --git a/opensource/dynavec/images/contributors/mohammad-arshad-ali.png b/opensource/dynavec/images/contributors/mohammad-arshad-ali.png new file mode 100644 index 0000000..68c912c Binary files /dev/null and b/opensource/dynavec/images/contributors/mohammad-arshad-ali.png differ diff --git a/opensource/dynavec/images/contributors/sanket-tikhande.png b/opensource/dynavec/images/contributors/sanket-tikhande.png new file mode 100644 index 0000000..406816e Binary files /dev/null and b/opensource/dynavec/images/contributors/sanket-tikhande.png differ diff --git a/opensource/dynavec/images/contributors/shivam-gupta.png b/opensource/dynavec/images/contributors/shivam-gupta.png new file mode 100644 index 0000000..ed97337 Binary files /dev/null and b/opensource/dynavec/images/contributors/shivam-gupta.png differ diff --git a/opensource/dynavec/images/contributors/tanmay-kumar.png b/opensource/dynavec/images/contributors/tanmay-kumar.png new file mode 100644 index 0000000..e5f317d Binary files /dev/null and b/opensource/dynavec/images/contributors/tanmay-kumar.png differ diff --git a/opensource/dynavec/images/contributors/uzma-khan.png b/opensource/dynavec/images/contributors/uzma-khan.png new file mode 100644 index 0000000..3652111 Binary files /dev/null and b/opensource/dynavec/images/contributors/uzma-khan.png differ diff --git a/opensource/dynavec/images/contributors/vardhman-gupta.png b/opensource/dynavec/images/contributors/vardhman-gupta.png new file mode 100644 index 0000000..e14c891 Binary files /dev/null and b/opensource/dynavec/images/contributors/vardhman-gupta.png differ diff --git a/opensource/dynavec/images/team1.png b/opensource/dynavec/images/team1.png new file mode 100644 index 0000000..406816e Binary files /dev/null and b/opensource/dynavec/images/team1.png differ diff --git a/opensource/dynavec/images/team2.jpg b/opensource/dynavec/images/team2.jpg new file mode 100644 index 0000000..a82aaac Binary files /dev/null and b/opensource/dynavec/images/team2.jpg differ diff --git a/opensource/dynavec/index.html b/opensource/dynavec/index.html index 69bdd2b..51d8ddd 100644 --- a/opensource/dynavec/index.html +++ b/opensource/dynavec/index.html @@ -46,7 +46,7 @@ Star - — + 27 @@ -95,8 +95,8 @@

The serverless vector database
that lives in
  • —GitHub stars
  • -
  • $469/mo at 1B vectors*
  • -
  • 100%in your account
  • +
  • $469/mo at 1B vectors*
  • +
  • 100%in your account
  • 0servers to run
  • @@ -165,35 +165,35 @@

    The serverless vector database
    that lives in
    -

    Why dynavec

    +

    Why dynavec

    -
    - 01 +
    +

    Cost-effective

    No always-on cluster, no managed-service premium. You pay S3-priced vector storage plus DynamoDB on-demand. Idle cost approaches storage only.

    -
    - 02 +
    +

    Lowest latency

    S3 Vectors returns the nearest keys; the actual documents are hydrated from DynamoDB in single-digit milliseconds via BatchGetItem.

    -
    - 03 +
    +

    Billion-scale

    Amazon S3 Vectors searches across billions of vectors with 90%+ recall — the serverless ANN engine, managed by AWS.

    -
    - 04 +
    +

    Data compliance

    Everything stays in your account, your region, your availability zones. dynavec only ever calls AWS with your credentials.

    -
    - 05 +
    +

    Secure & elastic

    Serverless primitives scale to zero and back automatically. IAM is the only access boundary; assume-role and per-tenant namespaces built in.

    -
    - 06 +
    +

    Framework-native

    LangChain and LlamaIndex vector stores plus a framework-agnostic retriever tool for LangGraph, CrewAI, and Strands.

    @@ -204,10 +204,10 @@

    Framework-native

    -

    How it works

    +

    How it works

    Two AWS primitives, each doing the one job it is best at, joined by a shared key.

    -
    +
    @@ -431,28 +431,36 @@

    DynamoDB — the speed & meaning tier

    -

    Benchmarks

    +

    Benchmarks

    Cost across every common embedding dimension (384–3072) and 100K → 1 billion vectors. dynavec stays lowest at every point because its storage is priced like S3, not RAM.

    - +
    + +
    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) — log-log scale.
    - +
    + +
    Raw float32 footprint — up to ~5.7 TiB at 1B × 1536-d.
    - +
    + +
    Recall ÷ Latency score — higher recall at lower query latency. Representative estimates; run python -m benchmarks.report to reproduce.
    @@ -475,38 +483,97 @@

    1536-dim (e.g. OpenAI text-embedding-3-small -
    -

    Estimate your own cost

    -
    - - - - + +
    +
    +

    Estimate your cost

    +
    + + + +
    -
    - Estimated: $0.00 / mo -
    + +
    + +
    +
    +
    + Vectors stored + 1 M +
    + +
    +
    +
    + Dimensions + 768 +
    + +
    +
    +
    + Queries / month + 1 M +
    + +
    +
    +
    + Writes / month + 100 K +
    + +
    +
    + + +
    +
    +
    + +
    +
    $0.00
    +
    dynavec / mo
    +
    +
    +
    vs
    +
    + +
    +
    $0.00
    +
    Pinecone est. / mo
    +
    +
    +
    +
    You save ~0%
    + +
    +
    + S3 storage +
    + $0.00 +
    +
    + S3 reads +
    + $0.00 +
    +
    + DDB reads +
    + $0.00 +
    +
    + DDB writes +
    + $0.00 +
    +
    +
    +

    Estimates based on public AWS list prices · verify before quoting

    - -

    - Cost is computed by the repository's transparent cost model from public list prices — - order-of-magnitude, verify before quoting. Recall and latency figures in the repo are - representative until a live AWS run replaces them. Reproduce everything with - python -m benchmarks.report. -

    @@ -514,7 +581,7 @@

    Estimate your own cost

    -

    Quick start

    +

    Quick start

    Three steps to your first semantic search — everything runs inside your own AWS account.

    @@ -552,7 +619,7 @@

    Run your first query

    -

    Pythonic by design

    +

    Pythonic by design

    Clean, explicit, and framework-friendly. Bring your own embedder and API key, or your own vectors.

    @@ -670,7 +737,7 @@

    Everything in the box

    -

    Contribute

    +

    Contribute

    dynavec is Apache-2.0 and community-built. Issues, PRs, and ideas are welcome.

    @@ -698,6 +765,24 @@

    Star & share

    + + +
    +
    +

    Built by the community

    +

    Every commit, fix, and idea counts. Meet the people who make dynavec better.

    + +

    Core contributors

    +
    + +

    All contributors

    +
    + + +
    +
    @@ -717,6 +802,7 @@

    Star & share

    + diff --git a/opensource/dynavec/script.js b/opensource/dynavec/script.js index e69de29..5e2f11a 100644 --- a/opensource/dynavec/script.js +++ b/opensource/dynavec/script.js @@ -0,0 +1,681 @@ +/* ================================================ + dynavec landing page — script.js + 1. Animated dot-node network background + 2. Benchmark charts (Chart.js) + 3. Cost calculator + ================================================ */ + +/* ---- 1. Background node network canvas ---- */ +(function () { + const canvas = document.getElementById("bg-canvas"); + if (!canvas) return; + const ctx = canvas.getContext("2d"); + + const NODES = 70; + const MAX_DIST = 160; + const NODE_RADIUS = 2.2; + const SPEED = 0.35; + const CORAL = "#e05a3a"; + const DOT_COLOR = "rgba(180,180,200,0.55)"; + const LINE_COLOR = "rgba(180,180,200,"; + + let W, H, nodes, dpr; + + function resize() { + dpr = window.devicePixelRatio || 1; + W = window.innerWidth; + H = window.innerHeight; + canvas.width = Math.round(W * dpr); + canvas.height = Math.round(H * dpr); + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + function makeNode() { + const angle = Math.random() * Math.PI * 2; + const speed = SPEED * (0.4 + Math.random() * 0.6); + return { + x: Math.random() * W, + y: Math.random() * H, + vx: Math.cos(angle) * speed, + vy: Math.sin(angle) * speed, + accent: Math.random() < 0.08, + }; + } + + function init() { + resize(); + nodes = Array.from({ length: NODES }, makeNode); + } + + function tick() { + ctx.clearRect(0, 0, W, H); + + nodes.forEach((n) => { + n.x += n.vx; + n.y += n.vy; + if (n.x < 0 || n.x > W) n.vx *= -1; + if (n.y < 0 || n.y > H) n.vy *= -1; + }); + + // draw edges + for (let i = 0; i < nodes.length; i++) { + for (let j = i + 1; j < nodes.length; j++) { + const dx = nodes[i].x - nodes[j].x; + const dy = nodes[i].y - nodes[j].y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < MAX_DIST) { + const alpha = (1 - dist / MAX_DIST) * 0.45; + ctx.beginPath(); + ctx.moveTo(nodes[i].x, nodes[i].y); + ctx.lineTo(nodes[j].x, nodes[j].y); + ctx.strokeStyle = LINE_COLOR + alpha + ")"; + ctx.lineWidth = 1; + ctx.stroke(); + } + } + } + + // draw nodes + nodes.forEach((n) => { + ctx.beginPath(); + ctx.arc( + n.x, + n.y, + n.accent ? NODE_RADIUS * 1.6 : NODE_RADIUS, + 0, + Math.PI * 2 + ); + ctx.fillStyle = n.accent ? CORAL : DOT_COLOR; + ctx.fill(); + }); + + requestAnimationFrame(tick); + } + + window.addEventListener("resize", () => { + resize(); // re-applies dpr scale transform + nodes.forEach((n) => { + n.x = Math.min(n.x, W); + n.y = Math.min(n.y, H); + }); + }); + + init(); + tick(); +})(); + +/* ---- 2. Benchmark charts ---- */ +window.addEventListener("DOMContentLoaded", function () { + if (typeof Chart === "undefined") return; + + const CORAL = "#e05a3a"; + const GRAY1 = "#9ca3af"; + const GRAY2 = "#c4c9d4"; + const GRAY3 = "#d1d5db"; + const GRAY4 = "#e5e7eb"; + + const font = { family: "'Inter', sans-serif", size: 12 }; + Chart.defaults.font = font; + Chart.defaults.color = "#6a6a6a"; + + // --- bench-matrix: grouped bar — cost by scale at 1536-dim --- + const matrixCtx = document.getElementById("bench-matrix"); + if (matrixCtx) { + const scales = ["100K", "1M", "10M", "100M", "1B"]; + new Chart(matrixCtx, { + type: "bar", + data: { + labels: scales, + datasets: [ + { + label: "dynavec", + data: [3, 3, 8, 50, 469], + backgroundColor: CORAL, + }, + { + label: "Pinecone", + data: [9, 10, 27, 197, 1897], + backgroundColor: GRAY1, + }, + { + label: "Qdrant", + data: [160, 160, 960, 8640, 85920], + backgroundColor: GRAY2, + }, + { + label: "Weaviate", + data: [175, 175, 1050, 9450, 93975], + backgroundColor: GRAY3, + }, + { + label: "OpenSearch", + data: [701, 701, 877, 8423, 83708], + backgroundColor: GRAY4, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { position: "top" }, + title: { + display: true, + text: "Monthly cost ($/mo) — 1536-dim, 1M queries/mo", + font: { size: 13 }, + }, + tooltip: { + callbacks: { + label: (ctx) => + ` ${ctx.dataset.label}: $${ctx.parsed.y.toLocaleString()}`, + }, + }, + }, + scales: { + y: { + type: "logarithmic", + title: { display: true, text: "$/month (log scale)" }, + ticks: { callback: (v) => "$" + v.toLocaleString() }, + }, + x: { title: { display: true, text: "Vector count" } }, + }, + }, + }); + } + + // --- bench-scale: line — cost by scale at 768-dim --- + const scaleCtx = document.getElementById("bench-scale"); + if (scaleCtx) { + const pts = ["100K", "1M", "10M", "100M", "1B"]; + new Chart(scaleCtx, { + type: "line", + data: { + labels: pts, + datasets: [ + { + label: "dynavec", + data: [2, 2, 5, 32, 295], + borderColor: CORAL, + backgroundColor: CORAL + "22", + tension: 0.35, + fill: true, + }, + { + label: "Pinecone", + data: [9, 10, 27, 197, 1897], + borderColor: GRAY1, + backgroundColor: "transparent", + tension: 0.35, + }, + { + label: "Qdrant", + data: [80, 80, 480, 4320, 42960], + borderColor: GRAY2, + backgroundColor: "transparent", + tension: 0.35, + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { position: "top" }, + title: { + display: true, + text: "Cost by scale (768-d)", + font: { size: 12 }, + }, + }, + scales: { + y: { + type: "logarithmic", + ticks: { callback: (v) => "$" + v.toLocaleString() }, + }, + }, + }, + }); + } + + // --- bench-storage: bar — raw storage footprint --- + const storageCtx = document.getElementById("bench-storage"); + if (storageCtx) { + new Chart(storageCtx, { + type: "bar", + data: { + labels: ["100K", "1M", "10M", "100M", "1B"], + datasets: [ + { + label: "Storage (GiB)", + data: [0.57, 5.7, 57, 573, 5730], + backgroundColor: CORAL + "cc", + }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { display: false }, + title: { + display: true, + text: "Float32 storage footprint (1536-d)", + font: { size: 12 }, + }, + }, + scales: { + y: { + type: "logarithmic", + title: { display: true, text: "GiB (log)" }, + ticks: { callback: (v) => v + " GiB" }, + }, + }, + }, + }); + } + + // --- quality-chart: doughnut — recall/latency score --- + const qualityCtx = document.getElementById("quality-chart"); + if (qualityCtx) { + new Chart(qualityCtx, { + type: "doughnut", + data: { + labels: ["dynavec", "Pinecone", "Qdrant", "Weaviate", "OpenSearch"], + datasets: [ + { + data: [92, 88, 85, 82, 74], + backgroundColor: [CORAL, GRAY1, GRAY2, GRAY3, GRAY4], + borderWidth: 2, + borderColor: "#fff", + }, + ], + }, + options: { + responsive: true, + cutout: "62%", + plugins: { + legend: { position: "bottom", labels: { boxWidth: 12 } }, + title: { + display: true, + text: "Recall ÷ Latency score", + font: { size: 12 }, + }, + tooltip: { + callbacks: { label: (ctx) => ` ${ctx.label}: ${ctx.parsed}` }, + }, + }, + }, + }); + } +}); + +/* ---- 3. Cost calculator v2 ---- */ +(function () { + function init() { + const slVectors = document.getElementById("sl-vectors"); + const slDim = document.getElementById("sl-dim"); + const slQpm = document.getElementById("sl-qpm"); + const slWpm = document.getElementById("sl-wpm"); + if (!slVectors) return; + + const DIM_OPTS = [384, 768, 1536, 3072]; + + function getVectors() { + return Math.round(Math.pow(10, parseFloat(slVectors.value))); + } + function getDim() { + return DIM_OPTS[parseInt(slDim.value)]; + } + function getQpm() { + return Math.round(Math.pow(10, parseFloat(slQpm.value))); + } + function getWpm() { + return Math.round(Math.pow(10, parseFloat(slWpm.value))); + } + + function fmtN(n) { + if (n >= 1e9) return (n / 1e9).toFixed(1).replace(/\.0$/, "") + " B"; + if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, "") + " M"; + if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, "") + " K"; + return String(n); + } + function fmtUSD(n) { + if (n >= 1000) + return "$" + n.toFixed(0).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return "$" + n.toFixed(2); + } + + const PRESETS = { + starter: { vectors: 5, dim: 1, qpm: 5, wpm: 4 }, + growth: { vectors: 6, dim: 2, qpm: 6.7, wpm: 5.7 }, + scale: { vectors: 7, dim: 2, qpm: 7.7, wpm: 6.7 }, + }; + + document.querySelectorAll(".calc2__preset").forEach((btn) => { + btn.addEventListener("click", () => { + document + .querySelectorAll(".calc2__preset") + .forEach((b) => b.classList.remove("is-active")); + btn.classList.add("is-active"); + const p = PRESETS[btn.dataset.preset]; + slVectors.value = p.vectors; + slDim.value = p.dim; + slQpm.value = p.qpm; + slWpm.value = p.wpm; + compute(); + }); + }); + + function setBar(id, amtId, cost, max) { + const fill = document.getElementById(id); + const amt = document.getElementById(amtId); + if (fill) + fill.style.width = (max > 0 ? Math.min(cost / max, 1) * 100 : 0) + "%"; + if (amt) amt.textContent = fmtUSD(cost); + } + + function compute() { + const vectors = getVectors(); + const dim = getDim(); + const qpm = getQpm(); + const wpm = getWpm(); + + const lblV = document.getElementById("lbl-vectors"); + const lblD = document.getElementById("lbl-dim"); + const lblQ = document.getElementById("lbl-qpm"); + const lblW = document.getElementById("lbl-wpm"); + if (lblV) lblV.textContent = fmtN(vectors); + if (lblD) lblD.textContent = dim; + if (lblQ) lblQ.textContent = fmtN(qpm); + if (lblW) lblW.textContent = fmtN(wpm); + + const storedGB = (vectors * dim * 4) / 1e9; + const storageCost = storedGB * 0.04; + const readCost = (qpm / 1e6) * 0.04; + const dynRead = qpm * 0.00000013; + const dynWrite = wpm * 0.00000065; + const dvTotal = storageCost + readCost + dynRead + dynWrite; + + const pnTotal = + storedGB * 0.096 + (qpm / 1e6) * 0.1 + (wpm / 1e6) * 0.5 + 20; + const saving = + pnTotal > 0 ? Math.round((1 - dvTotal / pnTotal) * 100) : 0; + + const dvEl = document.getElementById("calc-dv-price"); + const pnEl = document.getElementById("calc-pn-price"); + const svEl = document.getElementById("calc-savings"); + if (dvEl) dvEl.textContent = fmtUSD(dvTotal) + " /mo"; + if (pnEl) pnEl.textContent = "~" + fmtUSD(pnTotal) + " /mo"; + if (svEl) + svEl.textContent = + saving > 0 + ? `You save ~${saving}% vs Pinecone` + : "Similar cost to Pinecone"; + + const maxCost = Math.max(storageCost, readCost, dynRead, dynWrite, 0.01); + setBar("bar-storage", "amt-storage", storageCost, maxCost); + setBar("bar-reads", "amt-reads", readCost, maxCost); + setBar("bar-ddb-r", "amt-ddb-r", dynRead, maxCost); + setBar("bar-ddb-w", "amt-ddb-w", dynWrite, maxCost); + } + + [slVectors, slDim, slQpm, slWpm].forEach((el) => + el.addEventListener("input", compute) + ); + compute(); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); + +/* ---- 4. Sticky nav ---- */ +(function () { + const nav = document.getElementById("nav"); + if (!nav) return; + const onScroll = () => nav.classList.toggle("is-stuck", window.scrollY > 10); + window.addEventListener("scroll", onScroll, { passive: true }); + onScroll(); +})(); + +/* ---- 5. Scroll reveal (IntersectionObserver) ---- */ +(function () { + const targets = document.querySelectorAll( + "[data-reveal], .section__title, .hero__stats" + ); + if (!targets.length || !("IntersectionObserver" in window)) { + targets.forEach((el) => el.classList.add("is-visible")); + return; + } + const io = new IntersectionObserver( + (entries) => { + entries.forEach((e) => { + if (e.isIntersecting) { + e.target.classList.add("is-visible"); + io.unobserve(e.target); + } + }); + }, + { threshold: 0.12 } + ); + targets.forEach((el) => io.observe(el)); +})(); + +/* ---- 6. Tab switching ---- */ +(function () { + const tabsEl = document.querySelector("[data-tabs]"); + if (!tabsEl) return; + const tabs = tabsEl.querySelectorAll(".tab"); + const panels = tabsEl.querySelectorAll('[role="tabpanel"]'); + + tabs.forEach((tab) => { + tab.addEventListener("click", () => { + tabs.forEach((t) => { + t.classList.remove("is-active"); + t.setAttribute("aria-selected", "false"); + }); + panels.forEach((p) => p.classList.add("is-hidden")); + tab.classList.add("is-active"); + tab.setAttribute("aria-selected", "true"); + const target = document.getElementById(tab.dataset.tab); + if (target) target.classList.remove("is-hidden"); + }); + }); +})(); + +/* ---- 7. Copy buttons ---- */ +(function () { + document.querySelectorAll(".copy[data-copy]").forEach((btn) => { + btn.addEventListener("click", () => { + navigator.clipboard.writeText(btn.dataset.copy).then(() => { + btn.textContent = "Copied!"; + btn.classList.add("is-done"); + setTimeout(() => { + btn.textContent = "copy"; + btn.classList.remove("is-done"); + }, 1800); + }); + }); + }); +})(); + +/* ---- 8. YouTube facade ---- */ +(function () { + const facade = document.getElementById("yt-facade"); + if (!facade) return; + facade.addEventListener("click", () => { + const iframe = document.createElement("iframe"); + iframe.src = "https://www.youtube.com/embed/UJ9MBALD380?autoplay=1&rel=0"; + iframe.allow = + "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"; + iframe.setAttribute("allowfullscreen", ""); + iframe.style.cssText = + "position:absolute;inset:0;width:100%;height:100%;border:0;"; + // Clear thumbnail/play button and drop in the iframe + facade.innerHTML = ""; + facade.style.cursor = "default"; + facade.appendChild(iframe); + }); + facade.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") facade.click(); + }); +})(); + +/* ---- 9. Contributors grid ---- */ +(function () { + const coreEl = document.getElementById("contrib-core"); + const allEl = document.getElementById("contrib-all"); + if (!coreEl || !allEl) return; + + const CORE_NAMES = [ + "Abhishek Gupta", + "Sanket Tikhande", + "Vardhman Gupta", + "Shivam Gupta", + "Isha Zaka", + ]; + + // To add a contributor: append their name here. + // Drop a photo as images/contributors/.png — auto-detected. + const ALL_NAMES = [ + "Abhishek Gupta", + "Sanket Tikhande", + "Vardhman Gupta", + "Shivam Gupta", + "Isha Zaka", + "Uzma Khan", + "Ashish Kumar", + "Madhav Sharma", + "Tanmay Kumar", + "Henil Bhavsar", + "Mohammad Arshad Ali", + "vaishnavk09", + "be-student", + "redcode333", + "R3108", + "AyushhVatsal", + "ramashishmaurya", + "Lawliet2004", + "wang1408", + "theman6660", + "osamashabih6960", + ]; + + function nameToPhotoPath(name) { + return ( + "images/contributors/" + + name.trim().toLowerCase().replace(/\s+/g, "-") + + ".png" + ); + } + + const PALETTE = [ + "#e8623b", + "#4f8ef7", + "#2db87c", + "#9b67e0", + "#e8a43b", + "#e84f7a", + "#3bbde8", + ]; + + function initialsAvatar(name, idx) { + const words = name.trim().split(/[\s_\-]+/); + const initials = + words.length >= 2 + ? (words[0][0] + words[1][0]).toUpperCase() + : name.slice(0, 2).toUpperCase(); + const color = PALETTE[idx % PALETTE.length]; + const svg = ` + + ${initials} + `; + return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg); + } + + function makeCard(name, idx, isCore) { + const photo = nameToPhotoPath(name); + + const card = document.createElement("div"); + card.className = "cg-card" + (isCore ? " cg-card--core" : ""); + + const photoWrap = document.createElement("div"); + photoWrap.className = "cg-card__photo"; + + const img = document.createElement("img"); + img.src = photo; + img.alt = name; + img.loading = "lazy"; + img.onerror = () => { + img.onerror = null; + img.src = initialsAvatar(name, idx); + }; + + photoWrap.appendChild(img); + + const info = document.createElement("div"); + info.className = "cg-card__info"; + + const nameEl = document.createElement("div"); + nameEl.className = "cg-card__name"; + nameEl.textContent = name; + + info.appendChild(nameEl); + + card.appendChild(photoWrap); + card.appendChild(info); + return card; + } + + const coreSet = new Set(CORE_NAMES); + CORE_NAMES.forEach((name, i) => coreEl.appendChild(makeCard(name, i, true))); + + const restNames = ALL_NAMES.filter((n) => !coreSet.has(n)); + const INITIAL_SHOW = 5; + + // Render first 5 immediately + restNames + .slice(0, INITIAL_SHOW) + .forEach((name, i) => allEl.appendChild(makeCard(name, i, false))); + + // If there are more, add a "Show more" button + if (restNames.length > INITIAL_SHOW) { + const showMoreBtn = document.createElement("button"); + showMoreBtn.className = "contrib-show-more"; + showMoreBtn.textContent = `Show ${ + restNames.length - INITIAL_SHOW + } more contributors`; + allEl.after(showMoreBtn); + + showMoreBtn.addEventListener("click", () => { + restNames + .slice(INITIAL_SHOW) + .forEach((name, i) => + allEl.appendChild(makeCard(name, INITIAL_SHOW + i, false)) + ); + showMoreBtn.remove(); + }); + } +})(); + +/* ---- 10. GitHub star count ---- */ +(function () { + fetch("https://api.github.com/repos/codeforstartups/dynavec") + .then((r) => r.json()) + .then((data) => { + const count = data.stargazers_count; + if (!count) return; + const fmt = + count >= 1000 + ? (count / 1000).toFixed(1).replace(/\.0$/, "") + "k" + : String(count); + document.querySelectorAll("[data-stars]").forEach((el) => { + el.textContent = fmt; + }); + }) + .catch(() => {}); +})(); diff --git a/opensource/dynavec/styles.css b/opensource/dynavec/styles.css index 7010105..6c508e5 100644 --- a/opensource/dynavec/styles.css +++ b/opensource/dynavec/styles.css @@ -555,6 +555,126 @@ body { .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; } +/* ── Cost calculator v2 ── */ +.calc2 { + margin-top: 40px; + background: var(--surface); + border: 1.5px solid var(--border-md); + border-radius: 20px; + padding: 32px 36px 24px; + box-shadow: var(--sh-lg); +} +.calc2__header { + display: flex; align-items: center; justify-content: space-between; + flex-wrap: wrap; gap: 12px; margin-bottom: 28px; +} +.calc2__title { + font-size: 18px; font-weight: 700; letter-spacing: -0.03em; color: var(--fg); +} +.calc2__presets { display: flex; gap: 8px; } +.calc2__preset { + font-family: var(--mono); font-size: 11px; font-weight: 600; + padding: 6px 16px; border-radius: 999px; + border: 1.5px solid var(--border-md); background: transparent; + color: var(--fg-3); cursor: pointer; + transition: background var(--t), border-color var(--t), color var(--t); +} +.calc2__preset:hover { border-color: var(--accent); color: var(--accent); } +.calc2__preset.is-active { + background: var(--accent); border-color: var(--accent); color: #fff; +} +.calc2__body { + display: grid; grid-template-columns: 1fr 1fr; gap: 32px; +} +@media (max-width: 780px) { .calc2__body { grid-template-columns: 1fr; } } + +/* sliders */ +.calc2__sliders { display: flex; flex-direction: column; gap: 22px; } +.calc2__row-label { + display: flex; justify-content: space-between; align-items: baseline; + font-size: 13px; color: var(--fg-2); margin-bottom: 8px; +} +.calc2__val { + font-family: var(--mono); font-size: 13px; font-weight: 700; color: var(--accent); +} +.calc2__slider { + -webkit-appearance: none; appearance: none; + width: 100%; height: 5px; border-radius: 999px; + background: var(--border-md); outline: none; cursor: pointer; +} +.calc2__slider::-webkit-slider-thumb { + -webkit-appearance: none; appearance: none; + width: 18px; height: 18px; border-radius: 50%; + background: var(--accent); border: 2px solid #fff; + box-shadow: 0 2px 8px rgba(232,98,59,0.4); cursor: pointer; + transition: transform 0.1s; +} +.calc2__slider:active::-webkit-slider-thumb { transform: scale(1.2); } +.calc2__slider::-moz-range-thumb { + width: 18px; height: 18px; border-radius: 50%; + background: var(--accent); border: 2px solid #fff; + box-shadow: 0 2px 8px rgba(232,98,59,0.4); cursor: pointer; +} + +/* results panel */ +.calc2__results { display: flex; flex-direction: column; gap: 20px; } +.calc2__totals { display: flex; align-items: center; gap: 16px; } +.calc2__brand-total { + flex: 1; display: flex; align-items: center; gap: 12px; + background: var(--accent-bg); border: 1.5px solid var(--accent); + border-radius: 14px; padding: 14px 16px; +} +.calc2__brand-total--alt { + background: var(--subtle); border-color: var(--border-md); +} +.calc2__brand-logo { + width: 36px; height: 36px; border-radius: 10px; flex-shrink: 0; + background: var(--accent); color: #fff; + font-family: var(--mono); font-size: 11px; font-weight: 800; + display: flex; align-items: center; justify-content: center; +} +.calc2__brand-logo--alt { background: var(--fg-3); } +.calc2__price { + font-size: 22px; font-weight: 800; font-family: var(--mono); + color: var(--accent); letter-spacing: -0.04em; line-height: 1; +} +.calc2__price--alt { color: var(--fg-2); font-size: 18px; } +.calc2__price-label { font-size: 11px; color: var(--fg-3); margin-top: 3px; } +.calc2__vs { + font-family: var(--mono); font-size: 11px; font-weight: 700; + color: var(--fg-4); flex-shrink: 0; +} +.calc2__savings { + text-align: center; font-family: var(--mono); font-size: 12px; font-weight: 700; + color: #2db87c; background: rgba(45,184,124,0.1); + border: 1px solid rgba(45,184,124,0.3); + border-radius: 999px; padding: 6px 16px; align-self: center; +} + +/* breakdown bars */ +.calc2__bars { display: flex; flex-direction: column; gap: 10px; } +.calc2__bar-row { + display: grid; grid-template-columns: 80px 1fr 52px; + align-items: center; gap: 10px; +} +.calc2__bar-label { font-size: 11px; font-family: var(--mono); color: var(--fg-3); } +.calc2__bar-track { + height: 7px; border-radius: 999px; background: var(--border-md); overflow: hidden; +} +.calc2__bar-fill { + height: 100%; border-radius: 999px; + background: linear-gradient(90deg, var(--accent), #f5a200); + width: 0%; transition: width 0.45s cubic-bezier(.22,1,.36,1); +} +.calc2__bar-amt { + font-family: var(--mono); font-size: 11px; font-weight: 700; + color: var(--fg-2); text-align: right; +} +.calc2__note { + margin-top: 20px; font-size: 11px; color: var(--fg-4); + text-align: center; padding-top: 16px; border-top: 1px solid var(--border); +} + /* ================================================ TABS & CODE ================================================ */ @@ -921,3 +1041,140 @@ body { .svc, .fig, .card { transition-property: box-shadow, border-color, transform, background; } .svc:hover, .fig:hover { transform: translateY(-3px); } + +/* ================================================ + CARD ICONS + ================================================ */ + +.card__icon { + display: flex; + align-items: center; + justify-content: flex-start; + margin-bottom: 16px; + width: 44px; + height: 44px; + background: var(--accent-bg); + border-radius: 10px; + padding: 8px; +} +.card__icon svg { width: 28px; height: 28px; } + +/* Stagger card reveal delays */ +.grid--3 .card[data-reveal]:nth-child(1) { transition-delay: 0.05s; } +.grid--3 .card[data-reveal]:nth-child(2) { transition-delay: 0.12s; } +.grid--3 .card[data-reveal]:nth-child(3) { transition-delay: 0.19s; } +.grid--3 .card[data-reveal]:nth-child(4) { transition-delay: 0.26s; } +.grid--3 .card[data-reveal]:nth-child(5) { transition-delay: 0.33s; } +.grid--3 .card[data-reveal]:nth-child(6) { transition-delay: 0.40s; } + +/* Stat accent colour */ +.stat__n--accent { color: var(--accent); } + +/* Benchmark grid border fix */ +.grid--3-bench { + display: grid; + gap: 16px; + background: transparent; + border: none; + border-radius: 0; + overflow: visible; + margin-bottom: 24px; +} + +/* ================================================ + CONTRIBUTORS CAROUSEL + ================================================ */ + +.contributors-section { overflow: visible; padding-bottom: 80px; } + +/* ── Section group label ── */ +.contrib-group-label { + font-family: var(--mono); font-size: 11px; font-weight: 700; + text-transform: uppercase; letter-spacing: 0.1em; color: var(--fg-3); + margin-bottom: 24px; +} + +/* ── Card grid — both core and all use identical columns/sizing ── */ +.contrib-grid, +.contrib-grid--core { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 24px; +} + +@media (max-width: 900px) { + .contrib-grid, .contrib-grid--core { grid-template-columns: repeat(3, 1fr); } +} +@media (max-width: 600px) { + .contrib-grid, .contrib-grid--core { grid-template-columns: repeat(2, 1fr); } +} + +/* ── Card ── */ +.cg-card { + border-radius: 14px; + border: 1.5px solid var(--border); + background: var(--surface); + overflow: hidden; + transition: transform var(--t), box-shadow var(--t), border-color var(--t); + cursor: default; +} +.cg-card:hover { + transform: translateY(-4px); + box-shadow: var(--sh-lg); + border-color: var(--border-md); +} +.cg-card--core { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-bg); +} +.cg-card--core:hover { + box-shadow: 0 0 0 3px var(--accent-bg), var(--sh-lg); +} + +/* Photo area — fixed aspect ratio */ +.cg-card__photo { + width: 100%; + aspect-ratio: 4 / 4; + overflow: hidden; + background: var(--subtle); +} +.cg-card__photo img { + width: 100%; height: 100%; + object-fit: cover; object-position: center top; + display: block; + transition: transform 0.4s ease; +} +.cg-card:hover .cg-card__photo img { transform: scale(1.04); } + +/* Info area */ +.cg-card__info { + padding: 14px 16px 16px; + display: flex; flex-direction: column; gap: 6px; +} +.cg-card__name { + font-size: 14px; font-weight: 700; + color: var(--fg); line-height: 1.3; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.cg-card--core .cg-card__name { color: var(--accent); } +.cg-card__badge { + display: inline-flex; align-items: center; + font-family: var(--mono); font-size: 10px; font-weight: 700; + color: var(--accent); background: var(--accent-bg); + border: 1px solid var(--accent); border-radius: 999px; + padding: 2px 8px; align-self: flex-start; + text-transform: uppercase; letter-spacing: 0.06em; +} + +.contrib-show-more { + display: block; width: 100%; margin-top: 20px; + padding: 14px 24px; border-radius: 10px; + border: 1.5px dashed var(--border-md); background: transparent; + font-family: var(--mono); font-size: 13px; font-weight: 600; + color: var(--fg-3); cursor: pointer; + transition: border-color var(--t), color var(--t), background var(--t); +} +.contrib-show-more:hover { + border-color: var(--accent); color: var(--accent); + background: var(--accent-bg); +}