This is an automated email from the ASF dual-hosted git repository.

Xuanwo pushed a commit to branch xuanwo/website-redesign
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit 0a9058bee3266d20883927a51e05cad7e398de77
Author: Xuanwo <[email protected]>
AuthorDate: Sat May 30 00:26:30 2026 +0800

    feat(website): add interactive capabilities explorer and used-by wall
---
 website/src/components/landing/data.js           | 130 ++++++++++---
 website/src/components/landing/sections.jsx      | 125 ++++++++----
 website/src/components/landing/styles.module.css | 238 +++++++++++++----------
 website/static/img/users/cratedb.png             | Bin 0 -> 711 bytes
 website/static/img/users/daft.png                | Bin 0 -> 6745 bytes
 website/static/img/users/databend.png            | Bin 13146 -> 3579 bytes
 website/static/img/users/datafusion-comet.png    | Bin 0 -> 7018 bytes
 website/static/img/users/dify.png                | Bin 7467 -> 2218 bytes
 website/static/img/users/filecodebox.png         | Bin 53339 -> 0 bytes
 website/static/img/users/gravitino.png           | Bin 0 -> 7018 bytes
 website/static/img/users/greptimedb.png          | Bin 0 -> 6539 bytes
 website/static/img/users/hasura.png              | Bin 0 -> 5428 bytes
 website/static/img/users/iceberg-rust.png        | Bin 0 -> 7018 bytes
 website/static/img/users/kubeflow-trainer.png    | Bin 0 -> 9460 bytes
 website/static/img/users/lance.png               | Bin 0 -> 3328 bytes
 website/static/img/users/llamaindex.png          | Bin 0 -> 6890 bytes
 website/static/img/users/lnx.png                 | Bin 0 -> 1895 bytes
 website/static/img/users/loco.png                | Bin 0 -> 5092 bytes
 website/static/img/users/milvus.png              | Bin 15620 -> 0 bytes
 website/static/img/users/octobase.png            | Bin 0 -> 11292 bytes
 website/static/img/users/openraft.png            | Bin 0 -> 3579 bytes
 website/static/img/users/pants.png               | Bin 0 -> 2676 bytes
 website/static/img/users/pathway.png             | Bin 0 -> 20187 bytes
 website/static/img/users/questdb.png             | Bin 26479 -> 3767 bytes
 website/static/img/users/quickwit.png            | Bin 0 -> 3667 bytes
 website/static/img/users/ragflow.png             | Bin 0 -> 9257 bytes
 website/static/img/users/risingwave.png          | Bin 6252 -> 5562 bytes
 website/static/img/users/robustmq.png            | Bin 0 -> 1540 bytes
 website/static/img/users/rustic.png              | Bin 0 -> 12204 bytes
 website/static/img/users/sccache.png             | Bin 4701 -> 1356 bytes
 website/static/img/users/seatunnel.png           | Bin 0 -> 7018 bytes
 website/static/img/users/slatedb.png             | Bin 0 -> 4332 bytes
 website/static/img/users/spiceai.png             | Bin 0 -> 4518 bytes
 website/static/img/users/vaultwarden.png         | Bin 0 -> 18431 bytes
 website/static/img/users/vector.png              | Bin 48196 -> 8211 bytes
 website/static/img/users/walrus.png              | Bin 0 -> 7880 bytes
 website/static/img/users/wrenai.png              | Bin 0 -> 2330 bytes
 website/static/img/users/zino.png                | Bin 0 -> 4517 bytes
 38 files changed, 320 insertions(+), 173 deletions(-)

diff --git a/website/src/components/landing/data.js 
b/website/src/components/landing/data.js
index ac5fa6418..83935b611 100644
--- a/website/src/components/landing/data.js
+++ b/website/src/components/landing/data.js
@@ -142,38 +142,80 @@ export const valueProps = [
   },
 ];
 
-// What OpenDAL can do, grouped for the capabilities section. Grounded in the
-// core read/write/manage features (ranges, concurrency, multipart, listing …).
-export const capabilityGroups = [
-  {
-    title: "Read",
-    items: [
-      "Whole-object or byte-range reads",
-      "Concurrent, chunked fetching",
-      "Auto-merge nearby ranges",
-      "Resume interrupted reads",
-      "Conditional & versioned reads",
-    ],
+// What OpenDAL can do — grouped for the capabilities explorer. Each item pairs
+// a minimal, real snippet with its docs.rs reference. Grounded in the core
+// read / write / manage APIs (verified against types/operator + 
types/options).
+const RS = "https://docs.rs/opendal/latest/opendal";;
+const opDoc = (method) => `${RS}/struct.Operator.html#method.${method}`;
+
+export const capabilityThemes = [
+  {
+    title: "Read in parallel",
+    blurb: "Fetch a byte range, or a whole object in concurrent chunks.",
+    doc: opDoc("read_with"),
+    code: `let op = Operator::new(S3::default().bucket("data"))?.finish();
+
+// Read just the bytes you need.
+let head = op.read_with("logs/today").range(0..64 * 1024).await?;
+
+// Or pull a large object in parallel chunks.
+let full = op
+    .read_with("big.parquet")
+    .concurrent(8)
+    .chunk(8 * 1024 * 1024)
+    .await?;`,
   },
   {
-    title: "Write",
-    items: [
-      "Streaming writes of any size",
-      "Multipart upload",
-      "Concurrent part uploads",
-      "Append to existing objects",
-      "Atomic create & overwrite guards",
-    ],
+    title: "Upload in parts",
+    blurb: "Stream writes of any size as concurrent, multipart uploads.",
+    doc: opDoc("writer_with"),
+    code: `let op = Operator::new(S3::default().bucket("data"))?.finish();
+
+// Open a multipart writer with 8 concurrent parts.
+let mut w = op
+    .writer_with("big.bin")
+    .concurrent(8)
+    .chunk(8 * 1024 * 1024)
+    .await?;
+
+// Stream any number of buffers; close flushes the rest.
+w.write(part_one).await?;
+w.write(part_two).await?;
+w.close().await?;`,
   },
   {
-    title: "Manage",
-    items: [
-      "Stat metadata without the body",
-      "Lazy, recursive, paginated listing",
-      "Batch & recursive delete",
-      "Server-side copy & rename",
-      "Presigned URLs for direct access",
-    ],
+    title: "Recover from failure",
+    blurb: "Resume on retry, write atomically, and pin a version.",
+    doc: `${RS}/layers/struct.RetryLayer.html`,
+    code: `// Retries automatically resume interrupted transfers.
+let op = Operator::new(S3::default().bucket("data"))?
+    .layer(RetryLayer::new())
+    .finish();
+
+// Create only if absent.
+op.write_with("once.json", data).if_not_exists(true).await?;
+// Read only if unchanged.
+let doc = op.read_with("doc").if_match(etag).await?;
+// Pin an exact version.
+let pinned = op.read_with("doc").version(version_id).await?;`,
+  },
+  {
+    title: "Work with files",
+    blurb: "Inspect, list, move, and share — without moving bytes.",
+    doc: opDoc("list_with"),
+    code: `// Inspect a file without downloading it.
+let meta = op.stat("report.csv").await?;
+// List a prefix, recursing lazily through the tree.
+let mut entries = op.lister_with("logs/").recursive(true).await?;
+while let Some(entry) = entries.try_next().await? {
+    println!("{}", entry.path());
+}
+// Copy on the server — no download.
+op.copy("draft.md", "final.md").await?;
+// Recursively delete a subtree.
+op.delete_with("tmp/").recursive(true).await?;
+// Presign a temporary, shareable URL.
+let url = op.presign_read("report.csv", Duration::from_secs(3600)).await?;`,
   },
 ];
 
@@ -181,15 +223,43 @@ export const capabilityGroups = [
 // GitHub star count (highest first). Logos are the projects' GitHub org
 // avatars, self-hosted under static/img/users/. Refresh with scripts as the
 // ecosystem grows. The logo wall shows a responsive subset by viewport width.
+// Public projects with 1,000+ GitHub stars that depend on OpenDAL, sorted by
+// stars. Sourced via crates.io reverse deps + GitHub code search; logos are 
the
+// owner avatars, mirrored locally under static/img/users to avoid runtime 
calls.
 export const usedBy = [
   { name: "Dify", icon: "/img/users/dify.png", href: 
"https://github.com/langgenius/dify"; },
-  { name: "Milvus", icon: "/img/users/milvus.png", href: 
"https://github.com/milvus-io/milvus"; },
+  { name: "RAGFlow", icon: "/img/users/ragflow.png", href: 
"https://github.com/infiniflow/ragflow"; },
+  { name: "Pathway", icon: "/img/users/pathway.png", href: 
"https://github.com/pathwaycom/pathway"; },
+  { name: "Vaultwarden", icon: "/img/users/vaultwarden.png", href: 
"https://github.com/dani-garcia/vaultwarden"; },
+  { name: "LlamaIndex", icon: "/img/users/llamaindex.png", href: 
"https://github.com/run-llama/llama_index"; },
+  { name: "Hasura", icon: "/img/users/hasura.png", href: 
"https://github.com/hasura/graphql-engine"; },
   { name: "Vector", icon: "/img/users/vector.png", href: 
"https://github.com/vectordotdev/vector"; },
   { name: "QuestDB", icon: "/img/users/questdb.png", href: 
"https://github.com/questdb/questdb"; },
+  { name: "WrenAI", icon: "/img/users/wrenai.png", href: 
"https://github.com/Canner/WrenAI"; },
+  { name: "Quickwit", icon: "/img/users/quickwit.png", href: 
"https://github.com/quickwit-oss/quickwit"; },
+  { name: "SeaTunnel", icon: "/img/users/seatunnel.png", href: 
"https://github.com/apache/seatunnel"; },
   { name: "Databend", icon: "/img/users/databend.png", href: 
"https://github.com/databendlabs/databend"; },
   { name: "RisingWave", icon: "/img/users/risingwave.png", href: 
"https://github.com/risingwavelabs/risingwave"; },
-  { name: "FileCodeBox", icon: "/img/users/filecodebox.png", href: 
"https://github.com/vastsa/FileCodeBox"; },
+  { name: "Loco", icon: "/img/users/loco.png", href: 
"https://github.com/loco-rs/loco"; },
   { name: "sccache", icon: "/img/users/sccache.png", href: 
"https://github.com/mozilla/sccache"; },
+  { name: "Lance", icon: "/img/users/lance.png", href: 
"https://github.com/lance-format/lance"; },
+  { name: "GreptimeDB", icon: "/img/users/greptimedb.png", href: 
"https://github.com/GreptimeTeam/greptimedb"; },
+  { name: "Daft", icon: "/img/users/daft.png", href: 
"https://github.com/Eventual-Inc/Daft"; },
+  { name: "CrateDB", icon: "/img/users/cratedb.png", href: 
"https://github.com/crate/crate"; },
+  { name: "Pants", icon: "/img/users/pants.png", href: 
"https://github.com/pantsbuild/pants"; },
+  { name: "rustic", icon: "/img/users/rustic.png", href: 
"https://github.com/rustic-rs/rustic"; },
+  { name: "SlateDB", icon: "/img/users/slatedb.png", href: 
"https://github.com/slatedb/slatedb"; },
+  { name: "Gravitino", icon: "/img/users/gravitino.png", href: 
"https://github.com/apache/gravitino"; },
+  { name: "Spice.ai", icon: "/img/users/spiceai.png", href: 
"https://github.com/spiceai/spiceai"; },
+  { name: "Kubeflow Trainer", icon: "/img/users/kubeflow-trainer.png", href: 
"https://github.com/kubeflow/trainer"; },
+  { name: "OctoBase", icon: "/img/users/octobase.png", href: 
"https://github.com/toeverything/OctoBase"; },
+  { name: "Openraft", icon: "/img/users/openraft.png", href: 
"https://github.com/databendlabs/openraft"; },
+  { name: "Walrus", icon: "/img/users/walrus.png", href: 
"https://github.com/nubskr/walrus"; },
+  { name: "RobustMQ", icon: "/img/users/robustmq.png", href: 
"https://github.com/robustmq/robustmq"; },
+  { name: "lnx", icon: "/img/users/lnx.png", href: 
"https://github.com/lnx-search/lnx"; },
+  { name: "Iceberg Rust", icon: "/img/users/iceberg-rust.png", href: 
"https://github.com/apache/iceberg-rust"; },
+  { name: "DataFusion Comet", icon: "/img/users/datafusion-comet.png", href: 
"https://github.com/apache/datafusion-comet"; },
+  { name: "zino", icon: "/img/users/zino.png", href: 
"https://github.com/zino-rs/zino"; },
 ];
 
 // Where people add their own project (PR to the users list).
diff --git a/website/src/components/landing/sections.jsx 
b/website/src/components/landing/sections.jsx
index cfb948b66..cfb1d7d16 100644
--- a/website/src/components/landing/sections.jsx
+++ b/website/src/components/landing/sections.jsx
@@ -17,9 +17,10 @@
  * under the License.
  */
 
-import React from "react";
+import React, { useState } from "react";
 import Link from "@docusaurus/Link";
 import { useBaseUrlUtils } from "@docusaurus/useBaseUrl";
+import CodeBlock from "@theme/CodeBlock";
 import CodeTabs from "./CodeTabs";
 import styles from "./styles.module.css";
 import {
@@ -29,7 +30,7 @@ import {
   heroStats,
   codeSamples,
   valueProps,
-  capabilityGroups,
+  capabilityThemes,
   usedBy,
   USERS_LIST_URL,
   serviceGroups,
@@ -93,34 +94,40 @@ export function Hero() {
 export function UsedBy() {
   const { withBaseUrl } = useBaseUrlUtils();
   return (
-    <section className={styles.usedBy}>
+    <section className={`${styles.section} ${styles.sectionSubtle}`}>
       <div className="odl-container">
-        <div className={styles.usedByHead}>
+        <div className={styles.sectionHead}>
           <span className="odl-eyebrow">Used by</span>
-          <Link className={styles.addLogo} to={USERS_LIST_URL}>
-            + add your logo
-          </Link>
+          <h2 className={styles.sectionTitle}>
+            Powering AI, analytics, and real-time data
+          </h2>
+          <p className={styles.sectionLede}>
+            OpenDAL runs in production across the open-source ecosystem. These
+            are some of the projects that build on it.
+          </p>
         </div>
-        <div className={styles.logoWall}>
+        <ul className={styles.logoWall}>
           {usedBy.map((u) => (
-            <Link
-              key={u.name}
-              className={styles.logoItem}
-              to={u.href}
-              title={u.name}
-            >
-              <img
-                className={styles.logoMark}
-                src={withBaseUrl(u.icon)}
-                alt=""
-                width="24"
-                height="24"
-                loading="lazy"
-              />
-              <span className={styles.logoName}>{u.name}</span>
-            </Link>
+            <li key={u.name}>
+              <Link className={styles.logoItem} to={u.href} title={u.name}>
+                <img
+                  className={styles.logoMark}
+                  src={withBaseUrl(u.icon)}
+                  alt=""
+                  width="28"
+                  height="28"
+                  loading="lazy"
+                />
+                <span className={styles.logoName}>{u.name}</span>
+              </Link>
+            </li>
           ))}
-        </div>
+          <li>
+            <Link className={styles.addLogo} to={USERS_LIST_URL}>
+              + add your logo
+            </Link>
+          </li>
+        </ul>
       </div>
     </section>
   );
@@ -155,6 +162,7 @@ export function ValueProps() {
 }
 
 export function Capabilities() {
+  const [active, setActive] = useState(capabilityThemes[0]);
   return (
     <section className={`${styles.section} ${styles.sectionSubtle}`}>
       <div className="odl-container">
@@ -162,24 +170,61 @@ export function Capabilities() {
           <span className="odl-eyebrow">Capabilities</span>
           <h2 className={styles.sectionTitle}>Configure once. Access 
anything.</h2>
           <p className={styles.sectionLede}>
-            One Operator gives you a full toolkit for real-world data —
-            streaming, concurrency, multipart uploads, conditionals and
-            server-side moves — working the same way on every backend.
+            One Operator is a full toolkit for real-world data — read and write
+            at scale, recover from failures, and work with files — the same way
+            on every backend.
           </p>
         </div>
-        <div className={styles.capabilityGrid}>
-          {capabilityGroups.map((group) => (
-            <div className={styles.capabilityGroup} key={group.title}>
-              <h3 className={styles.capabilityGroupTitle}>{group.title}</h3>
-              <ul className={styles.capabilityList}>
-                {group.items.map((item) => (
-                  <li className={styles.capabilityItem} key={item}>
-                    {item}
-                  </li>
-                ))}
-              </ul>
+        <div className={styles.capabilityExplorer}>
+          <ul className={styles.capabilityNav}>
+            {capabilityThemes.map((theme) => {
+              const selected = active.title === theme.title;
+              return (
+                <li key={theme.title}>
+                  <Link
+                    className={`${styles.capabilityItem} ${
+                      selected ? styles.capabilityItemActive : ""
+                    }`}
+                    to={theme.doc}
+                    target="_blank"
+                    rel="noreferrer"
+                    aria-current={selected ? "true" : undefined}
+                    onMouseEnter={() => setActive(theme)}
+                    onFocus={() => setActive(theme)}
+                  >
+                    <span className={styles.capabilityText}>
+                      <span className={styles.capabilityItemTitle}>
+                        {theme.title}
+                      </span>
+                      <span className={styles.capabilityItemBlurb}>
+                        {theme.blurb}
+                      </span>
+                    </span>
+                    <span className={styles.capabilityArrow} 
aria-hidden="true">
+                      ↗
+                    </span>
+                  </Link>
+                </li>
+              );
+            })}
+          </ul>
+          <div className={styles.capabilityPreview}>
+            <div className={styles.codeWindow}>
+              <div className={styles.windowBar}>
+                <div className={styles.windowDots} aria-hidden="true">
+                  <span />
+                  <span />
+                  <span />
+                </div>
+                <span className={styles.windowTitle}>{active.title}</span>
+              </div>
+              <div className={`${styles.codeBody} 
${styles.capabilityCodeBody}`}>
+                <div className={styles.capabilityCodeFade} key={active.title}>
+                  <CodeBlock language="rust">{active.code}</CodeBlock>
+                </div>
+              </div>
             </div>
-          ))}
+          </div>
         </div>
       </div>
     </section>
diff --git a/website/src/components/landing/styles.module.css 
b/website/src/components/landing/styles.module.css
index 19e5f8a3e..b76032d14 100644
--- a/website/src/components/landing/styles.module.css
+++ b/website/src/components/landing/styles.module.css
@@ -273,61 +273,42 @@
 }
 
 /* ===== Used-by wall ===================================================== */
-.usedBy {
-  padding-block: clamp(2.25rem, 1.5rem + 3vw, 3.25rem);
-  border-top: 1px solid var(--odl-border);
-}
-/* Top row: "Used by" eyebrow (left) + "+ add your logo" (right) over a rule */
-.usedByHead {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  gap: var(--odl-space-4);
-  padding-bottom: var(--odl-space-4);
-  border-bottom: 1px solid var(--odl-border);
-}
-.addLogo {
-  font-family: var(--odl-font-mono);
-  font-size: var(--odl-text-xs);
-  letter-spacing: 0.04em;
-  color: var(--odl-fg-subtle);
-  text-decoration: none;
-  white-space: nowrap;
-  transition: color var(--odl-dur) var(--odl-ease);
-}
-.addLogo:hover {
-  color: var(--odl-accent);
-  text-decoration: none;
-}
-/* Logo wall: grayscale adopter logos that color on hover. Logos are sorted by
-   GitHub stars; the count shown adapts to viewport width via the nth-child
-   rules below (4 → 6 → 8). */
 .logoWall {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(12.5rem, 1fr));
+  gap: var(--odl-space-3);
+}
+.logoWall > li {
   display: flex;
-  flex-wrap: wrap;
-  align-items: center;
-  justify-content: space-between;
-  gap: var(--odl-space-5) var(--odl-space-6);
-  margin-top: var(--odl-space-6);
 }
 .logoItem {
-  display: inline-flex;
+  flex: 1;
+  display: flex;
   align-items: center;
   gap: var(--odl-space-3);
+  padding: var(--odl-space-4);
+  background: var(--odl-surface);
+  border: 1px solid var(--odl-border);
+  border-radius: var(--odl-radius-md);
   text-decoration: none;
   filter: grayscale(1);
-  opacity: 0.62;
+  opacity: 0.72;
   transition: opacity var(--odl-dur) var(--odl-ease),
-    filter var(--odl-dur) var(--odl-ease);
+    filter var(--odl-dur) var(--odl-ease),
+    background-color var(--odl-dur) var(--odl-ease);
 }
 .logoItem:hover {
+  background: var(--odl-surface-2);
   filter: grayscale(0);
   opacity: 1;
   text-decoration: none;
 }
 .logoMark {
-  width: 26px;
-  height: 26px;
+  width: 28px;
+  height: 28px;
   object-fit: contain;
   border-radius: var(--odl-radius-sm);
   flex: none;
@@ -338,24 +319,31 @@
   font-weight: 500;
   color: var(--odl-fg-muted);
   white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
-
-/* Responsive count: 4 on mobile, 6 on tablet, 8 on desktop. */
-.logoItem:nth-child(n + 5) {
-  display: none;
-}
-@media (min-width: 600px) {
-  .logoItem:nth-child(n + 5) {
-    display: inline-flex;
-  }
-  .logoItem:nth-child(n + 7) {
-    display: none;
-  }
+/* "+ add your logo" rendered as the final, dashed tile in the wall. */
+.addLogo {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: var(--odl-space-4);
+  border: 1px dashed var(--odl-border);
+  border-radius: var(--odl-radius-md);
+  font-family: var(--odl-font-mono);
+  font-size: var(--odl-text-xs);
+  letter-spacing: 0.04em;
+  color: var(--odl-fg-subtle);
+  text-decoration: none;
+  white-space: nowrap;
+  transition: color var(--odl-dur) var(--odl-ease),
+    border-color var(--odl-dur) var(--odl-ease);
 }
-@media (min-width: 980px) {
-  .logoItem:nth-child(n + 7) {
-    display: inline-flex;
-  }
+.addLogo:hover {
+  color: var(--odl-accent);
+  border-color: var(--odl-accent);
+  text-decoration: none;
 }
 
 /* ===== Value props ====================================================== */
@@ -400,63 +388,98 @@
   margin: 0;
 }
 
-/* ===== Capabilities ===================================================== */
-.capabilityGrid {
+/* ===== Capabilities (explorer) ========================================== */
+.capabilityExplorer {
   display: grid;
-  grid-template-columns: repeat(3, 1fr);
-  gap: 1px;
-  background: var(--odl-border);
-  border: 1px solid var(--odl-border);
-  border-radius: var(--odl-radius-lg);
-  overflow: hidden;
-}
-.capabilityGroup {
-  background: var(--odl-surface);
-  padding: clamp(1.25rem, 1rem + 1.5vw, 2rem);
-}
-.capabilityGroupTitle {
-  display: flex;
-  align-items: center;
-  gap: var(--odl-space-3);
-  font-family: var(--odl-font-mono);
-  font-size: var(--odl-text-xs);
-  font-weight: 500;
-  letter-spacing: var(--odl-tracking-label);
-  text-transform: uppercase;
-  color: var(--odl-fg-subtle);
-  margin: 0 0 var(--odl-space-2);
-}
-.capabilityGroupTitle::before {
-  content: "";
-  width: 0.875rem;
-  height: 0.375rem;
-  background: var(--odl-accent);
-  flex: none;
+  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
+  gap: clamp(2rem, 1rem + 3vw, 3.5rem);
+  align-items: start;
 }
-.capabilityList {
+.capabilityNav {
   list-style: none;
   margin: 0;
   padding: 0;
+  display: flex;
+  flex-direction: column;
+  gap: var(--odl-space-2);
 }
 .capabilityItem {
   display: flex;
-  align-items: baseline;
+  align-items: flex-start;
+  justify-content: space-between;
   gap: var(--odl-space-3);
-  padding: var(--odl-space-3) 0;
-  font-size: var(--odl-text-base);
-  color: var(--odl-fg);
-  border-top: 1px solid var(--odl-border);
+  padding: var(--odl-space-4);
+  border: 1px solid transparent;
+  border-radius: var(--odl-radius-md);
+  text-decoration: none;
+  transition: background-color var(--odl-dur) var(--odl-ease),
+    border-color var(--odl-dur) var(--odl-ease);
 }
-.capabilityItem:first-child {
-  border-top: 0;
+.capabilityItem:hover {
+  background: var(--odl-surface);
+  text-decoration: none;
 }
-.capabilityItem::before {
-  content: "";
-  width: 0.6rem;
-  height: 2px;
-  margin-top: 0.6em;
-  background: var(--odl-border-strong);
-  flex: none;
+.capabilityItemActive {
+  background: var(--odl-surface);
+  border-color: var(--odl-border);
+  box-shadow: var(--odl-shadow-sm);
+}
+.capabilityText {
+  display: flex;
+  flex-direction: column;
+  gap: 3px;
+  min-width: 0;
+}
+.capabilityItemTitle {
+  font-size: var(--odl-text-md);
+  font-weight: 650;
+  letter-spacing: var(--odl-tracking-tight);
+  color: var(--odl-fg-strong);
+}
+.capabilityItemBlurb {
+  font-size: var(--odl-text-sm);
+  line-height: var(--odl-leading-snug);
+  color: var(--odl-fg-muted);
+}
+.capabilityArrow {
+  font-family: var(--odl-font-mono);
+  font-size: var(--odl-text-sm);
+  color: var(--odl-accent);
+  opacity: 0;
+  transition: opacity var(--odl-dur) var(--odl-ease);
+}
+.capabilityItem:hover .capabilityArrow,
+.capabilityItemActive .capabilityArrow {
+  opacity: 1;
+}
+
+/* Sticky code preview that tracks the active capability */
+.capabilityPreview {
+  position: sticky;
+  top: 5rem;
+}
+.capabilityCodeBody {
+  height: 18rem;
+  overflow: auto;
+  background: var(--odl-code-bg);
+}
+/* Fade only the code itself; the panel background stays put, so switching
+   snippets never flashes the window surface behind it. */
+.capabilityCodeFade {
+  animation: odlFade var(--odl-dur) var(--odl-ease);
+}
+@keyframes odlFade {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+@media (prefers-reduced-motion: reduce) {
+  .capabilityCodeFade {
+    animation: none;
+  }
 }
 
 /* ===== Services ========================================================= */
@@ -729,9 +752,17 @@
 @media (max-width: 996px) {
   .heroInner,
   .layersInner,
-  .capabilityGrid {
+  .capabilityExplorer {
     grid-template-columns: 1fr;
   }
+  .capabilityPreview {
+    display: none;
+  }
+  .capabilityNav {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: var(--odl-space-3);
+  }
   .serviceGroups {
     grid-template-columns: repeat(2, 1fr);
   }
@@ -742,7 +773,8 @@
 
 @media (max-width: 640px) {
   .valueGrid,
-  .serviceGroups {
+  .serviceGroups,
+  .capabilityNav {
     grid-template-columns: 1fr;
   }
   .heroStats {
diff --git a/website/static/img/users/cratedb.png 
b/website/static/img/users/cratedb.png
new file mode 100644
index 000000000..f65bfd245
Binary files /dev/null and b/website/static/img/users/cratedb.png differ
diff --git a/website/static/img/users/daft.png 
b/website/static/img/users/daft.png
new file mode 100644
index 000000000..cd845656c
Binary files /dev/null and b/website/static/img/users/daft.png differ
diff --git a/website/static/img/users/databend.png 
b/website/static/img/users/databend.png
index 27fface5c..908975ca6 100644
Binary files a/website/static/img/users/databend.png and 
b/website/static/img/users/databend.png differ
diff --git a/website/static/img/users/datafusion-comet.png 
b/website/static/img/users/datafusion-comet.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/datafusion-comet.png 
differ
diff --git a/website/static/img/users/dify.png 
b/website/static/img/users/dify.png
index d08be7198..d4c3195c1 100644
Binary files a/website/static/img/users/dify.png and 
b/website/static/img/users/dify.png differ
diff --git a/website/static/img/users/filecodebox.png 
b/website/static/img/users/filecodebox.png
deleted file mode 100644
index ba251b3d8..000000000
Binary files a/website/static/img/users/filecodebox.png and /dev/null differ
diff --git a/website/static/img/users/gravitino.png 
b/website/static/img/users/gravitino.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/gravitino.png differ
diff --git a/website/static/img/users/greptimedb.png 
b/website/static/img/users/greptimedb.png
new file mode 100644
index 000000000..de5d5eb61
Binary files /dev/null and b/website/static/img/users/greptimedb.png differ
diff --git a/website/static/img/users/hasura.png 
b/website/static/img/users/hasura.png
new file mode 100644
index 000000000..bfd043810
Binary files /dev/null and b/website/static/img/users/hasura.png differ
diff --git a/website/static/img/users/iceberg-rust.png 
b/website/static/img/users/iceberg-rust.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/iceberg-rust.png differ
diff --git a/website/static/img/users/kubeflow-trainer.png 
b/website/static/img/users/kubeflow-trainer.png
new file mode 100644
index 000000000..8cc9ba859
Binary files /dev/null and b/website/static/img/users/kubeflow-trainer.png 
differ
diff --git a/website/static/img/users/lance.png 
b/website/static/img/users/lance.png
new file mode 100644
index 000000000..ed288e5b1
Binary files /dev/null and b/website/static/img/users/lance.png differ
diff --git a/website/static/img/users/llamaindex.png 
b/website/static/img/users/llamaindex.png
new file mode 100644
index 000000000..731c902e4
Binary files /dev/null and b/website/static/img/users/llamaindex.png differ
diff --git a/website/static/img/users/lnx.png b/website/static/img/users/lnx.png
new file mode 100644
index 000000000..dfae753ef
Binary files /dev/null and b/website/static/img/users/lnx.png differ
diff --git a/website/static/img/users/loco.png 
b/website/static/img/users/loco.png
new file mode 100644
index 000000000..e952fa04e
Binary files /dev/null and b/website/static/img/users/loco.png differ
diff --git a/website/static/img/users/milvus.png 
b/website/static/img/users/milvus.png
deleted file mode 100644
index 8e3e38be8..000000000
Binary files a/website/static/img/users/milvus.png and /dev/null differ
diff --git a/website/static/img/users/octobase.png 
b/website/static/img/users/octobase.png
new file mode 100644
index 000000000..aaf33dfe3
Binary files /dev/null and b/website/static/img/users/octobase.png differ
diff --git a/website/static/img/users/openraft.png 
b/website/static/img/users/openraft.png
new file mode 100644
index 000000000..908975ca6
Binary files /dev/null and b/website/static/img/users/openraft.png differ
diff --git a/website/static/img/users/pants.png 
b/website/static/img/users/pants.png
new file mode 100644
index 000000000..b9ebd5fbf
Binary files /dev/null and b/website/static/img/users/pants.png differ
diff --git a/website/static/img/users/pathway.png 
b/website/static/img/users/pathway.png
new file mode 100644
index 000000000..8939308f2
Binary files /dev/null and b/website/static/img/users/pathway.png differ
diff --git a/website/static/img/users/questdb.png 
b/website/static/img/users/questdb.png
index 1ae55a66a..663fb3c66 100644
Binary files a/website/static/img/users/questdb.png and 
b/website/static/img/users/questdb.png differ
diff --git a/website/static/img/users/quickwit.png 
b/website/static/img/users/quickwit.png
new file mode 100644
index 000000000..230241887
Binary files /dev/null and b/website/static/img/users/quickwit.png differ
diff --git a/website/static/img/users/ragflow.png 
b/website/static/img/users/ragflow.png
new file mode 100644
index 000000000..5bc5c186f
Binary files /dev/null and b/website/static/img/users/ragflow.png differ
diff --git a/website/static/img/users/risingwave.png 
b/website/static/img/users/risingwave.png
index aed6574ee..ece3bd7d5 100644
Binary files a/website/static/img/users/risingwave.png and 
b/website/static/img/users/risingwave.png differ
diff --git a/website/static/img/users/robustmq.png 
b/website/static/img/users/robustmq.png
new file mode 100644
index 000000000..d13539b64
Binary files /dev/null and b/website/static/img/users/robustmq.png differ
diff --git a/website/static/img/users/rustic.png 
b/website/static/img/users/rustic.png
new file mode 100644
index 000000000..53e2a2773
Binary files /dev/null and b/website/static/img/users/rustic.png differ
diff --git a/website/static/img/users/sccache.png 
b/website/static/img/users/sccache.png
index 070e3df52..e5a7b2fc0 100644
Binary files a/website/static/img/users/sccache.png and 
b/website/static/img/users/sccache.png differ
diff --git a/website/static/img/users/seatunnel.png 
b/website/static/img/users/seatunnel.png
new file mode 100644
index 000000000..c8aaed47d
Binary files /dev/null and b/website/static/img/users/seatunnel.png differ
diff --git a/website/static/img/users/slatedb.png 
b/website/static/img/users/slatedb.png
new file mode 100644
index 000000000..16b3d3e9c
Binary files /dev/null and b/website/static/img/users/slatedb.png differ
diff --git a/website/static/img/users/spiceai.png 
b/website/static/img/users/spiceai.png
new file mode 100644
index 000000000..19e0eceef
Binary files /dev/null and b/website/static/img/users/spiceai.png differ
diff --git a/website/static/img/users/vaultwarden.png 
b/website/static/img/users/vaultwarden.png
new file mode 100644
index 000000000..394dcae93
Binary files /dev/null and b/website/static/img/users/vaultwarden.png differ
diff --git a/website/static/img/users/vector.png 
b/website/static/img/users/vector.png
index 8420ae221..b8a7bf4d9 100644
Binary files a/website/static/img/users/vector.png and 
b/website/static/img/users/vector.png differ
diff --git a/website/static/img/users/walrus.png 
b/website/static/img/users/walrus.png
new file mode 100644
index 000000000..c87b21729
Binary files /dev/null and b/website/static/img/users/walrus.png differ
diff --git a/website/static/img/users/wrenai.png 
b/website/static/img/users/wrenai.png
new file mode 100644
index 000000000..5de193a81
Binary files /dev/null and b/website/static/img/users/wrenai.png differ
diff --git a/website/static/img/users/zino.png 
b/website/static/img/users/zino.png
new file mode 100644
index 000000000..df460333e
Binary files /dev/null and b/website/static/img/users/zino.png differ

Reply via email to