You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fluss.apache.org by ip...@apache.org on 2026/10/05 04:37:32 UTC

(fluss-blog) branch main updated: [website] Sync blog preview with the main Fluss website (#21)

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

polyzos pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss-blog.git


The following commit(s) were added to refs/heads/main by this push:
     new 8ac98b8  [website] Sync blog preview with the main Fluss website (#21)
8ac98b8 is described below

commit 8ac98b81e9bfce353774471a1fd429d4b583dac8
Author: Jark Wu <ja...@apache.org>
AuthorDate: Mon Oct 5 12:37:26 2026 +0800

    [website] Sync blog preview with the main Fluss website (#21)
    
    Synchronize templates, styles, fonts, and site navigation with the main
    website so local previews match published blog pages. Preserve working
    local release routes and document the synchronization source.
    
    
    AI-Model: gpt-6
    AI-Contributed/Feature: 4159/4159
    AI-Contributed/UT: 0/0
    
    Co-authored-by: Codex <no...@openai.com>
---
 README.md                                          |    6 +-
 docusaurus.config.ts                               |  247 +-
 package.json                                       |   10 +-
 src/css/custom.css                                 | 3197 +++++++++++++++++++-
 src/pages/index.tsx                                |   23 +
 src/theme/BlogListPage/index.tsx                   |  204 ++
 src/theme/BlogListPage/styles.module.css           |  213 ++
 src/theme/BlogPostItem/Header/Title/index.tsx      |   27 -
 .../BlogPostItem/Header/Title/styles.module.css    |   22 -
 src/utils/blogPosts.ts                             |   56 +
 src/utils/prismDark.ts                             |   77 +
 src/utils/prismLight.ts                            |   77 +
 static/img/social/fluss-social-card.png            |  Bin 0 -> 1029903 bytes
 13 files changed, 4012 insertions(+), 147 deletions(-)

diff --git a/README.md b/README.md
index 7ed2583..55c63e6 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,11 @@ npm install
 npm run start
 ```
 
-The site will be available at `http://localhost:3000`.
+The blog will be available at `http://localhost:3000/blog`; the root URL redirects there.
+
+The preview uses the blog templates, shared CSS, Geist fonts, code highlighting, navigation, and footer from [`apache/fluss/website`](https://github.com/apache/fluss/tree/main/website), synchronized at commit `fd7fd47a5112be99371a3e9b7c906ea43c6b8e0f`. Blog links stay local; links to the rest of the website open `fluss.apache.org`. Search and Ask AI use the same hosted services as the published site and require an internet connection.
+
+When the website design changes, synchronize `src/css/custom.css`, `src/theme/BlogListPage/`, `src/theme/BlogPostPage/`, `src/utils/{blogPosts,prismLight,prismDark}.ts`, and the referenced logo/social-card assets from `fluss/website/`. Also align the presentation settings in `docusaurus.config.ts` and font/theme dependencies in `package.json`, preserving `docs: false`, `trailingSlash: false`, and the local blog routes. The local trailing-slash setting lets `npm run serve` handle release  [...]
 
 ## How to Add a Blog Post
 
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index dc860dd..e12b6cc 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -1,31 +1,150 @@
-import {themes as prismThemes} from 'prism-react-renderer';
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Presentation is synchronized from apache/fluss website/.
+// Keep blog routes local; the rest of the site is served by fluss.apache.org.
 import type {Config} from '@docusaurus/types';
 import type * as Preset from '@docusaurus/preset-classic';
+import lightTheme from './src/utils/prismLight';
+import darkTheme from './src/utils/prismDark';
+import {prepareBlogPosts} from './src/utils/blogPosts';
 
 const config: Config = {
-  title: 'Apache Fluss Blog',
-  tagline: 'Streaming Storage for Real-Time Analytics & AI',
+  title: 'Apache Fluss™',
+  tagline: 'The streaming storage layer for real-time analytics and the lakehouse',
   favicon: 'img/logo/fluss_favicon.svg',
 
+  headTags: [
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'description',
+        content:
+          'Apache Fluss is an open-source columnar streaming storage system. Sub-second freshness, primary-key tables, first-class Apache Flink integration, and native tiering to Apache Iceberg and Apache Paimon.',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        property: 'og:title',
+        content: 'Apache Fluss · Streaming Storage for the Real-Time Lakehouse',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        property: 'og:description',
+        content:
+          'Open-source columnar streaming storage with sub-second freshness, primary-key tables, Flink integration, and native tiering to Iceberg and Paimon.',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        property: 'og:type',
+        content: 'website',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'twitter:card',
+        content: 'summary_large_image',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'twitter:title',
+        content: 'Apache Fluss · Streaming Storage for the Real-Time Lakehouse',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'twitter:description',
+        content:
+          'Open-source columnar streaming storage with sub-second freshness, primary-key tables, Flink integration, and native tiering to Iceberg and Paimon.',
+      },
+    },
+    {
+      tagName: 'meta',
+      attributes: {
+        name: 'theme-color',
+        content: '#102856',
+      },
+    },
+  ],
+
+  // kapa.ai "Ask AI" widget. Modelled on the Apache Doris integration.
+  // Privacy: `data-consent-required` makes kapa show its own consent screen and
+  // withhold all data from its backend until the user explicitly consents (once
+  // per device, remembered by kapa); `data-user-analytics-cookie-enabled=false`
+  // disables analytics cookies. Production CSP allowances are maintained in
+  // the main website's .htaccess, coordinated with the ASF privacy team.
+  scripts: [
+    {
+      src: 'https://widget.kapa.ai/kapa-widget.bundle.js',
+      async: true,
+      'data-website-id': '40ccde97-65ed-46d8-81f2-fe8a8a31f9d9',
+      'data-project-name': 'Apache Fluss',
+      'data-project-color': '#06b6d4',
+      // Icon-only (square) mark: the full wordmark gets cropped to "Fl" in
+      // kapa's small logo slot, so use the notext variant.
+      'data-project-logo': '/img/logo/svg/colored_logo_notext.svg',
+      'data-modal-title': 'Ask Apache Fluss AI',
+      'data-modal-image': '/img/logo/svg/colored_logo_notext.svg',
+      'data-modal-disclaimer':
+        'This is a custom LLM with access to the [Apache Fluss documentation](https://fluss.apache.org/docs/). Answers may be inaccurate — always verify against the official docs.',
+      // Hide kapa's own floating button; open the modal from our navbar pill.
+      'data-button-hide': 'true',
+      'data-modal-override-open-selector': '#navbar-ask-ai-btn',
+      // Privacy hardening (see comment above).
+      'data-consent-required': 'true',
+      'data-user-analytics-cookie-enabled': 'false',
+      // Bot protection uses kapa's default reCAPTCHA (CSP: www.google.com,
+      // www.gstatic.com). Do NOT force 'hcaptcha' unless the kapa project is
+      // provisioned for it in the dashboard, or captcha token fetches fail.
+    },
+  ],
+
   url: 'https://fluss.apache.org/',
   baseUrl: '/',
+  // The static preview server misidentifies dotted directories such as 1.0
+  // as files. Flat HTML output keeps release-post URLs accessible on refresh.
+  trailingSlash: false,
 
+  // Documentation routes are not built by this blog-only preview.
   onBrokenLinks: 'warn',
-
   staticDirectories: ['static', 'blog/static'],
 
+  i18n: {
+    defaultLocale: 'en',
+    locales: ['en'],
+  },
+
   markdown: {
+    mermaid: true,
     hooks: {
       onBrokenMarkdownLinks: 'warn',
       onBrokenMarkdownImages: 'warn',
-    }
-  },
-
-
-  i18n: {
-    defaultLocale: 'en',
-    locales: ['en'],
+    },
   },
+  themes: ['@docusaurus/theme-mermaid'],
 
   presets: [
     [
@@ -34,6 +153,9 @@ const config: Config = {
         docs: false,
         blog: {
           showReadingTime: false,
+          // The card index filters the full archive, including older posts.
+          postsPerPage: 'ALL',
+          processBlogPosts: prepareBlogPosts,
           feedOptions: {
             type: ['rss', 'atom'],
             xslt: true,
@@ -52,23 +174,58 @@ const config: Config = {
   ],
 
   themeConfig: {
-    image: 'img/logo/png/colored_logo.png',
+    image: 'img/social/fluss-social-card.png',
     colorMode: {
       defaultMode: 'light',
-      disableSwitch: true,
+      disableSwitch: false,
+      respectPrefersColorScheme: false,
     },
     navbar: {
       title: '',
       logo: {
         alt: 'Fluss',
-        src: 'img/logo/svg/colored_logo.svg',
+        src: 'img/logo/svg/white_color_logo.svg',
+        srcDark: 'img/logo/svg/white_color_logo.svg',
+        href: 'https://fluss.apache.org/',
       },
       items: [
+        {
+          to: 'https://fluss.apache.org/docs/',
+          position: 'left',
+          label: 'Docs',
+        },
         {to: '/blog', label: 'Blog', position: 'left'},
         {
-          href: 'https://fluss.apache.org/',
-          label: 'Main Site',
+          label: 'Learn',
+          position: 'left',
+          type: 'dropdown',
+          items: [
+            {
+              label: 'User Stories',
+              to: 'https://fluss.apache.org/user-stories',
+            },
+            {
+              label: 'Talks',
+              to: 'https://fluss.apache.org/learn/talks',
+            },
+            {
+              label: 'Videos',
+              to: 'https://fluss.apache.org/learn/videos',
+            },
+          ],
+        },
+        {to: 'https://fluss.apache.org/community/welcome', label: 'Community', position: 'left'},
+        {to: 'https://fluss.apache.org/roadmap', label: 'Roadmap', position: 'left'},
+        {to: 'https://fluss.apache.org/downloads', label: 'Downloads', position: 'left'},
+        {
+          // "Ask AI" pill that opens the kapa.ai widget. The kapa bundle
+          // (declared in the top-level `scripts` field above) binds its modal
+          // to this button via `data-modal-override-open-selector`, so a plain
+          // HTML button is all that's needed here.
+          type: 'html',
           position: 'right',
+          value:
+            '<button id="navbar-ask-ai-btn" type="button" class="navbar-ask-ai">Ask AI</button>',
         },
         {
           href: 'https://github.com/apache/fluss',
@@ -80,12 +237,62 @@ const config: Config = {
     },
     footer: {
       style: 'dark',
-      copyright: `<p>Copyright © ${new Date().getFullYear()} The Apache Software Foundation, Licensed under the Apache License, Version 2.0.</p>`,
+      links: [
+        {
+          title: 'Product',
+          items: [
+            {label: 'Documentation', to: 'https://fluss.apache.org/docs/quickstart/flink'},
+            {label: 'Quickstart', to: 'https://fluss.apache.org/docs/quickstart/flink'},
+            {label: 'Roadmap', to: 'https://fluss.apache.org/roadmap'},
+            {label: 'Downloads', to: 'https://fluss.apache.org/downloads'},
+            {label: 'Blog', to: '/blog'},
+          ],
+        },
+        {
+          title: 'Community',
+          items: [
+            {label: 'User Stories', to: 'https://fluss.apache.org/user-stories'},
+            {label: 'GitHub', href: 'https://github.com/apache/fluss'},
+            {label: 'Slack', href: 'https://join.slack.com/t/apache-fluss/shared_invite/zt-473vgmvjr-cmIma~_iAA4cN02o5u2pDQ'},
+            {label: 'Welcome', to: 'https://fluss.apache.org/community/welcome'},
+            {label: 'Contribute', to: 'https://fluss.apache.org/community/welcome'},
+          ],
+        },
+        {
+          title: 'Resources',
+          items: [
+            {label: 'Talks', to: 'https://fluss.apache.org/learn/talks'},
+            {label: 'Videos', to: 'https://fluss.apache.org/learn/videos'},
+            {label: 'Issues', href: 'https://github.com/apache/fluss/issues'},
+            {label: 'Releases', href: 'https://github.com/apache/fluss/releases'},
+          ],
+        },
+        {
+          title: 'Apache',
+          items: [
+            {label: 'Foundation', href: 'https://www.apache.org/'},
+            {label: 'License', href: 'https://www.apache.org/licenses/'},
+            {label: 'Events', href: 'https://events.apache.org'},
+            {label: 'Donate', href: 'https://www.apache.org/foundation/sponsorship.html'},
+            {label: 'Sponsors', href: 'https://www.apache.org/foundation/thanks.html'},
+            {label: 'Security', href: 'https://www.apache.org/security/'},
+            {label: 'Privacy', href: 'https://privacy.apache.org/policies/privacy-policy-public.html'},
+          ],
+        },
+      ],
+      copyright: `<p>Copyright © ${new Date().getFullYear()} The Apache Software Foundation, Licensed under the Apache License, Version 2.0.</p>
+                  <p>Apache, the names of Apache projects, and the feather logo are either registered trademarks or trademarks of the Apache Software Foundation in the United States and/or other countries. All other marks mentioned may be trademarks or registered trademarks of their respective owners.</p>`,
     },
     prism: {
-      theme: prismThemes.github,
-      darkTheme: prismThemes.dracula,
-      additionalLanguages: ['java', 'bash', 'scala'],
+      theme: lightTheme,
+      darkTheme: darkTheme,
+      additionalLanguages: ['java', 'bash', 'scala', 'rust', 'toml', 'cmake']
+    },
+    algolia: {
+      appId: "X8KSGGLJW1",
+      apiKey: "5d0685995a3cb0052f32a59216ad3d35",
+      indexName: "fluss",
+      contextualSearch: true,
     },
   } satisfies Preset.ThemeConfig,
 };
diff --git a/package.json b/package.json
index b0c47fe..1183a24 100644
--- a/package.json
+++ b/package.json
@@ -7,13 +7,15 @@
     "start": "docusaurus start",
     "build": "docusaurus build",
     "clear": "docusaurus clear",
-    "serve": "docusaurus serve"
+    "serve": "docusaurus serve",
+    "typecheck": "tsc --noEmit"
   },
   "dependencies": {
     "@docusaurus/core": "^3.9.2",
     "@docusaurus/preset-classic": "^3.9.2",
-    "@fontsource/inter": "^5.2.8",
-    "@fontsource/roboto": "^5.2.10",
+    "@docusaurus/theme-mermaid": "^3.10.1",
+    "@fontsource-variable/geist": "^5.2.8",
+    "@fontsource-variable/geist-mono": "^5.2.7",
     "@mdx-js/react": "^3.0.0",
     "clsx": "^2.0.0",
     "prism-react-renderer": "^2.3.0",
@@ -24,6 +26,8 @@
     "@docusaurus/module-type-aliases": "^3.9.2",
     "@docusaurus/tsconfig": "^3.9.2",
     "@docusaurus/types": "^3.9.2",
+    "@types/react": "^18.3.0",
+    "@types/react-dom": "^18.3.0",
     "typescript": "~5.5.2"
   },
   "browserslist": {
diff --git a/src/css/custom.css b/src/css/custom.css
index eaa8375..a8625ee 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -21,74 +21,218 @@
  * work well for content-centric websites.
  */
 
-/* Self-hosted fonts via @fontsource (no external requests) */
-@import '@fontsource/inter/400.css';
-@import '@fontsource/inter/500.css';
-@import '@fontsource/inter/600.css';
-@import '@fontsource/inter/700.css';
-@import '@fontsource/inter/800.css';
-@import '@fontsource/roboto/400.css';
-@import '@fontsource/roboto/500.css';
-@import '@fontsource/roboto/700.css';
-
-/* You can override the default Infima variables here. */
+/* Self-hosted variable fonts via @fontsource (no external requests).
+ * Geist is OFL-licensed (https://vercel.com/font); the variable bundles
+ * carry the full 100-900 weight axis in a single woff2 per family, so
+ * picking weights becomes a styling decision rather than a load decision. */
+@import '@fontsource-variable/geist';
+@import '@fontsource-variable/geist-mono';
+
+/* =========================================================================
+   Fluss Design Tokens (2026 redesign)
+   ========================================================================= */
 :root {
-  /* Use Roboto consistently across the platform */
-  --ifm-font-family-base: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
+  /* --- Brand: Navy scale (2026 palette refresh) ---
+     Anchored on six user-provided palette stops:
+       #0A1745 · #102856 · #12325C · #194670 · #1C5078 · #266D95
+     The 300/100/50 stops are derived lighter tints in the same hue family
+     so we keep enough light surfaces / light-text accents on dark grounds. */
+  --fluss-blue-950: #0A1745;
+  --fluss-blue-900: #102856;
+  --fluss-blue-800: #12325C;
+  --fluss-blue-700: #194670;
+  --fluss-blue-600: #1C5078;
+  --fluss-blue-500: #266D95;
+  --fluss-blue-300: #7AAFCB;
+  --fluss-blue-100: #D6E4ED;
+  --fluss-blue-50:  #ECF3F7;
+
+  /* --- Accents (use sparingly) ---
+     The previous bright cyan is replaced with the palette's brightest stop
+     so accents stay within the new navy/teal family. */
+  --fluss-cyan:    #266D95;
+  --fluss-violet:  #7C3AED;
+  --fluss-lime:    #A3E635;
+
+  /* --- Link (docs body prose) ---
+     A brighter, more saturated blue than the navy text/heading palette so
+     in-prose links visibly stand out from body copy ("QuickStart",
+     "Architecture", etc. on the docs intro page). Hover deepens to navy. */
+  --fluss-link:       #0E76C2;
+  --fluss-link-hover: #094E83;
+
+  /* --- Neutrals --- */
+  --fluss-ink-950: #0A0F1C;
+  --fluss-ink-700: #334155;
+  --fluss-ink-500: #64748B;
+  --fluss-ink-300: #CBD5E1;
+  --fluss-ink-100: #E6ECF5;
+  --fluss-paper:   #FFFFFF;
+  --fluss-canvas:  #FFFFFF;
+  --fluss-canvas-soft: #ECF3F7;
+
+  /* --- Semantic --- */
+  --fluss-success: #10B981;
+  --fluss-warning: #F59E0B;
+  --fluss-danger:  #EF4444;
+
+  /* --- Spacing (8px base) --- */
+  --fluss-space-1:  4px;
+  --fluss-space-2:  8px;
+  --fluss-space-3:  12px;
+  --fluss-space-4:  16px;
+  --fluss-space-5:  20px;
+  --fluss-space-6:  24px;
+  --fluss-space-8:  32px;
+  --fluss-space-10: 40px;
+  --fluss-space-12: 48px;
+  --fluss-space-16: 64px;
+  --fluss-space-20: 80px;
+  --fluss-space-24: 96px;
+  --fluss-space-32: 128px;
+
+  /* --- Radius --- */
+  --fluss-radius-sm: 6px;
+  --fluss-radius-md: 10px;
+  --fluss-radius-lg: 14px;
+  --fluss-radius-xl: 20px;
+  --fluss-radius-2xl: 28px;
+  --fluss-radius-pill: 999px;
+
+  /* --- Shadows (cool navy-tinted, palette-driven) --- */
+  --fluss-shadow-sm:   0 1px 2px rgba(10, 23, 69, 0.06), 0 2px 6px rgba(10, 23, 69, 0.04);
+  --fluss-shadow-md:   0 6px 20px rgba(10, 23, 69, 0.10), 0 2px 6px rgba(10, 23, 69, 0.05);
+  --fluss-shadow-lg:   0 24px 60px rgba(10, 23, 69, 0.14), 0 6px 16px rgba(10, 23, 69, 0.06);
+  --fluss-shadow-glow: 0 0 0 1px rgba(28, 80, 120, 0.30), 0 16px 48px rgba(38, 109, 149, 0.28);
 
-  /* Increase line height for a more "airy" and professional feel */
-  --ifm-line-height-base: 1.65;
+  /* --- Motion --- */
+  --fluss-motion-fast: 120ms;
+  --fluss-motion-base: 240ms;
+  --fluss-motion-slow: 480ms;
+  --fluss-ease-out:    cubic-bezier(0.2, 0.8, 0.2, 1);
+  --fluss-ease-inout:  cubic-bezier(0.4, 0, 0.2, 1);
 
-  /* Refine primary colors to a slightly more professional "Brand Blue" */
-  --ifm-color-primary: #0062cc;
-  --ifm-color-primary-dark: #0056b3;
-  --ifm-color-primary-darker: #004d99;
-  --ifm-color-primary-darkest: #003d7a;
-  --ifm-color-primary-light: #1a7ae0;
-  --ifm-color-primary-lighter: #338df2;
-  --ifm-color-primary-lightest: #66abf7;
+  /* --- Typography --- */
+  --fluss-font-sans:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
+  --fluss-font-display: 'Inter', var(--fluss-font-sans);
+  --fluss-font-mono:    ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
 
-  /* Adjust heading weights */
-  --ifm-heading-font-weight: 700;
+  /* --- Layout --- */
+  --fluss-container-max: 1240px;
+  --fluss-container-pad: 24px;
+
+  /* =====================================================================
+     Infima overrides : wire tokens into Docusaurus
+     ===================================================================== */
+  --ifm-font-family-base: var(--fluss-font-sans);
+  --ifm-heading-font-family: var(--fluss-font-display);
+  --ifm-font-family-monospace: var(--fluss-font-mono);
+  --ifm-line-height-base: 1.6;
+  --ifm-heading-font-weight: 600;
+  --ifm-heading-color: var(--fluss-ink-950);
+  --ifm-font-color-base: var(--fluss-ink-700);
+
+  --ifm-color-primary:          #1C5078;
+  --ifm-color-primary-dark:     #194670;
+  --ifm-color-primary-darker:   #12325C;
+  --ifm-color-primary-darkest:  #102856;
+  --ifm-color-primary-light:    #266D95;
+  --ifm-color-primary-lighter:  #7AAFCB;
+  --ifm-color-primary-lightest: #D6E4ED;
 
   --ifm-code-font-size: 90%;
-  --docusaurus-highlighted-code-line-bg: #E2E9F3;
+  --docusaurus-highlighted-code-line-bg: #DDE6EE;
+
+  --ifm-menu-color-background-active: #ECF3F7;
+  --ifm-menu-color-background-hover:  #ECF3F7;
+
+  /* Navbar: a single dark Fluss palette across the WHOLE site (homepage,
+     docs, blog, community) so the navbar reads identically on every
+     page — no per-route transparent / frosted variants.
+     Background uses the deepest palette stop (#0A1745) for a quiet,
+     premium feel. */
+  --ifm-navbar-background-color: rgba(10, 23, 69, 0.92);
+  --ifm-navbar-link-color: #CBD5E1;
+  --ifm-navbar-link-hover-color: #FFFFFF;
+  --ifm-navbar-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
+  --ifm-footer-background-color: var(--fluss-blue-950);
+  --ifm-footer-color: var(--fluss-ink-300);
+  --ifm-footer-link-color: var(--fluss-ink-300);
+  --ifm-footer-title-color: #FFFFFF;
+}
+
+/* =========================================================================
+   Base typographic refinement
+   ========================================================================= */
+html {
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  /* Match the body bg so the browser's default white html surface
+   * doesn't show as a thin line at the top during overscroll / before
+   * the body paint. The variable flips with theme via tokens above. */
+  background: var(--fluss-canvas);
+}
+
+body {
+  background: var(--fluss-canvas);
+}
+
+/* Page scrollbar — keep the thumb bright in dark mode so it stays
+ * visible against the deep-blue canvas. We use only the standard
+ * `scrollbar-color` property (no `::-webkit-scrollbar-*` width rules),
+ * because defining a width on `::-webkit-scrollbar` forces Chrome out
+ * of macOS overlay-scrollbar mode and into a 12px space-taking
+ * scrollbar — which would only apply in dark mode and cause the page
+ * (and the navbar's right-aligned items) to shift 12px on every theme
+ * toggle. */
+[data-theme='dark'] html {
+  scrollbar-color: rgba(255, 255, 255, 0.42) rgba(255, 255, 255, 0.06);
+}
+[data-theme='dark'] body::-webkit-scrollbar-track {
+    background: rgba(255, 255, 255, 0.04);
+}
+[data-theme='dark'] body::-webkit-scrollbar-thumb {
+    background: rgba(255, 255, 255, 0.32);
+    border-radius: 8px;
+    border: 3px solid transparent;
+    background-clip: padding-box;
+}
+[data-theme='dark'] body::-webkit-scrollbar-thumb:hover {
+    background: rgba(255, 255, 255, 0.55);
+    background-clip: padding-box;
+}
 
-  --ifm-menu-color-background-active: #edeefa99;
-  --ifm-menu-color-background-hover: #edeefa99;
+/* Subtle dot-grid texture on the homepage canvas in light mode.
+   Mirrors the dark-mode docs-canvas treatment so the page reads as
+   "designed" instead of flat white. The pattern fades behind hero
+   and dark sections because they paint their own backgrounds. */
+body.fluss-home {
+  background-color: var(--fluss-canvas);
+  background-image:
+    radial-gradient(circle at 1px 1px, rgba(28, 80, 120, 0.10) 1px, transparent 0);
+  background-size: 28px 28px;
+  background-attachment: fixed;
 }
 
+h1, h2, h3, h4, h5, h6 {
+  letter-spacing: -0.011em;
+}
 
 .source_code_button {
   margin-left: 20px;
 }
 
-
+/* =========================================================================
+   Navbar
+   ========================================================================= */
 .navbar__brand {
-  font-family: 'Roboto', sans-serif;
+  font-family: var(--fluss-font-display);
   font-weight: 700;
   letter-spacing: -0.01em;
   color: inherit;
 }
 
-.hero__title {
-  font-family: 'Roboto', sans-serif;
-  font-weight: 700;
-  font-size: 3.5rem;
-  letter-spacing: -0.02em;
-  line-height: 1.1;
-}
-
-.hero__subtitle {
-  font-family: 'Roboto', sans-serif;
-  font-weight: 400;
-  font-size: 1.5rem;
-  opacity: 0.9;
-  max-width: 800px;
-  margin: 0 auto;
-  padding-bottom: 2rem;
-}
-
 .header-github-link:hover {
   opacity: 0.6;
 }
@@ -98,11 +242,13 @@
   width: 24px;
   height: 24px;
   display: flex;
-  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.52 [...]
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' fill='%23E2E8F0' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-. [...]
   no-repeat;
 }
 
-
+/* =========================================================================
+   Sidebar / menu
+   ========================================================================= */
 .menu__list-item {
   font-size: 0.95rem;
   font-weight: 500;
@@ -110,7 +256,7 @@
 
 .menu__link {
   font-weight: 500;
-  color: #57606a;
+  color: var(--fluss-ink-500);
 }
 
 .menu__link--active {
@@ -122,39 +268,35 @@
   background: var(--ifm-menu-link-sublist-icon) 50% / 1.5rem 1.5rem;
 }
 
-
+/* =========================================================================
+   Markdown content
+   ========================================================================= */
 .markdown {
     padding-left: 1rem;
-    h1,
-    h2,
-    h3,
-    h4,
-    h5,
-    h6 {
-        color: #1d1d1d;
+    h1, h2, h3, h4, h5, h6 {
+        color: var(--fluss-ink-950);
         margin-bottom: 0.3125rem;
-        font-weight: 700;
+        font-weight: 600;
     }
 
-    b,
-    strong {
+    b, strong {
         font-weight: 700;
-        color: #1d1d1d;
+        color: var(--fluss-ink-950);
     }
 
-    h1,
-    h1:first-child {
+    h1, h1:first-child {
         font-size: 2.5rem;
         margin-bottom: 1.5rem;
         margin-top: 0;
     }
 
     h2 {
-        font-size: 2rem;
-        margin-bottom: 1.25rem;
-        margin-top: 2rem;
-        padding-top: 2rem;
-        border-top: 1px solid #e6e7e9;
+        font-size: 1.875rem;
+        margin-bottom: 1rem;
+        margin-top: 2.5rem;
+        padding-top: 0;
+        border-top: none;
+        letter-spacing: -0.018em;
     }
 
     h3 {
@@ -162,8 +304,9 @@
         margin-bottom: 1.25rem;
         margin-top: 1rem;
     }
+
     p {
-        line-height: 1.875;
+        line-height: 1.8;
 
         code {
           border-radius: 4px;
@@ -280,16 +423,47 @@
   background: var(--ifm-menu-color-background-active);
 }
 
+/* =========================================================================
+   Footer
+   ========================================================================= */
+.footer {
+  padding: var(--fluss-space-20) 0 var(--fluss-space-12);
+}
+
+.footer__title {
+  font-family: var(--fluss-font-display);
+  font-weight: 600;
+  font-size: 0.875rem;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  color: #FFFFFF;
+  margin-bottom: var(--fluss-space-4);
+}
+
+.footer__link-item {
+  color: #FFFFFF;
+  opacity: 0.88;
+  transition: opacity var(--fluss-motion-fast) var(--fluss-ease-out), color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.footer__link-item:hover {
+  color: #FFFFFF;
+  opacity: 1;
+  text-decoration: none;
+}
+
 .footer__copyright {
-    color: #dfe5f0;
+    color: #FFFFFF;
     font-size: .75rem;
     line-height: 1.8;
-    opacity: .6;
+    opacity: .78;
     text-align: center;
     width: 98%;
 }
 
-/* Hide blog sidebar on individual blog post pages to increase content width */
+/* =========================================================================
+   Blog post layout
+   ========================================================================= */
 .blog-post-page-no-sidebar aside.col.col--3 {
     display: none;
 }
@@ -304,6 +478,2881 @@
     max-width: calc(3 / 12 * 100%);
 }
 
+/* Blog list page: widen the main column from col--7 to col--9
+   so that cards (next to the col--3 sidebar) get more room. */
+.blog-list-page main.col.col--7 {
+    flex-basis: calc(9 / 12 * 100%);
+    max-width: calc(9 / 12 * 100%);
+}
+
+/* MDX pages (e.g. /downloads, /learn/talks, /learn/videos): widen the
+   right-side TOC column from col--2 to col--3 for better readability. */
+.mdx-page .col.col--2 {
+    flex-basis: calc(3 / 12 * 100%);
+    max-width: calc(3 / 12 * 100%);
+}
+
 .hidden {
     display: none !important;
 }
+
+/* =========================================================================
+   Kapa AI widget hide rules
+   ========================================================================= */
+/* Hide every trigger element Kapa injects into its container so the only
+   visible AI entry point is the custom .navbar-ask-ai pill (defined below).
+   Kapa periodically changes its injected DOM, so we use a broad rule and
+   then re-show the actual modal dialog explicitly. */
+#kapa-widget-container > button,
+#kapa-widget-container > div:not([role="dialog"]),
+#kapa-widget-container > a {
+    display: none !important;
+}
+
+/* Belt-and-braces: make sure the modal dialog (when open) is never hidden. */
+#kapa-widget-container > div[role="dialog"] {
+    display: revert !important;
+}
+
+/* =========================================================================
+   "Ask AI" navbar pill
+   ========================================================================= */
+.navbar-ask-ai {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    gap: 7px;
+    margin: 0 8px;
+    padding: 7px 16px;
+    border: 0;
+    border-radius: var(--fluss-radius-sm);
+    /* Bright cyan -> violet: high contrast against the navy navbar and ties to
+       the hero's "Analytics & AI" violet, marking this as the AI entry point. */
+    background: linear-gradient(120deg, #06b6d4 0%, #4f8ff0 50%, var(--fluss-violet) 100%);
+    background-size: 180% 180%;
+    color: #ffffff;
+    font-family: inherit;
+    font-size: 0.9rem;
+    font-weight: 700;
+    letter-spacing: 0.02em;
+    cursor: pointer;
+    box-shadow: 0 2px 12px rgba(124, 58, 237, 0.5),
+                0 2px 10px rgba(6, 182, 212, 0.4),
+                0 0 0 1px rgba(255, 255, 255, 0.12) inset;
+    animation: navbar-ask-ai-shimmer 5s ease-in-out infinite,
+               navbar-ask-ai-pulse 2.6s ease-in-out infinite;
+    transition: transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
+}
+
+.navbar-ask-ai::before {
+    content: '';
+    width: 14px;
+    height: 14px;
+    background-color: #ffffff;
+    -webkit-mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 1.5 L13.6 8.4 L20.5 10 L13.6 11.6 L12 18.5 L10.4 11.6 L3.5 10 L10.4 8.4 Z M19 15 L19.8 17.2 L22 18 L19.8 18.8 L19 21 L18.2 18.8 L16 18 L18.2 17.2 Z' fill='white'/%3E%3C/svg%3E");
+    mask-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 1.5 L13.6 8.4 L20.5 10 L13.6 11.6 L12 18.5 L10.4 11.6 L3.5 10 L10.4 8.4 Z M19 15 L19.8 17.2 L22 18 L19.8 18.8 L19 21 L18.2 18.8 L16 18 L18.2 17.2 Z' fill='white'/%3E%3C/svg%3E");
+    -webkit-mask-repeat: no-repeat;
+    mask-repeat: no-repeat;
+    -webkit-mask-size: contain;
+    mask-size: contain;
+    flex-shrink: 0;
+}
+
+.navbar-ask-ai:hover {
+    transform: translateY(-1px);
+    box-shadow: 0 6px 18px rgba(6, 182, 212, 0.65),
+                0 0 0 1px rgba(255, 255, 255, 0.18) inset;
+    filter: brightness(1.08) saturate(1.1);
+    animation-play-state: paused;
+}
+
+.navbar-ask-ai:focus-visible {
+    outline: 2px solid #ffffff;
+    outline-offset: 2px;
+    box-shadow: 0 0 0 4px rgba(6, 182, 212, 0.5);
+}
+
+@keyframes navbar-ask-ai-shimmer {
+    0%, 100% { background-position: 0% 50%; }
+    50%      { background-position: 100% 50%; }
+}
+
+@keyframes navbar-ask-ai-pulse {
+    0%, 100% { box-shadow: 0 2px 12px rgba(124, 58, 237, 0.5),
+                           0 2px 10px rgba(6, 182, 212, 0.4),
+                           0 0 0 1px rgba(255, 255, 255, 0.12) inset; }
+    50%      { box-shadow: 0 2px 20px rgba(124, 58, 237, 0.8),
+                           0 2px 16px rgba(6, 182, 212, 0.65),
+                           0 0 0 1px rgba(255, 255, 255, 0.16) inset; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+    .navbar-ask-ai { animation: none; }
+}
+
+@media (max-width: 996px) {
+    .navbar-ask-ai {
+        padding: 5px 10px;
+        font-size: 0.85rem;
+    }
+}
+
+/* =========================================================================
+   Global focus ring (accessibility)
+   ========================================================================= */
+*:focus-visible {
+  outline: 2px solid var(--fluss-blue-500);
+  outline-offset: 2px;
+  border-radius: var(--fluss-radius-sm);
+}
+
+/* =========================================================================
+   Reduced-motion safety net
+   ========================================================================= */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.001ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.001ms !important;
+    scroll-behavior: auto !important;
+  }
+}
+
+/* =========================================================================
+   ========================================================================
+   SITE-WIDE CHROME (docs, blog, community, learn, 404)
+   Below this line: theming for everything that is NOT the homepage.
+   ========================================================================
+   ========================================================================= */
+
+/* -------------------------------------------------------------------------
+   Navbar (non-homepage solid state)
+   -------------------------------------------------------------------------
+   Previously had a hard 1px white separator below the navbar which read
+   as a disconnect between the chrome and the page body (Jark feedback,
+   PR #3226). Replaced with a subtle drop shadow so the navbar still has
+   visual separation but flows into the page content. */
+.navbar {
+  height: 64px;
+  border-bottom: 0;
+  box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 2px 8px rgba(15, 23, 42, 0.03);
+}
+
+/* Navbar inner now spans the full viewport width on every page, with the
+   same side padding as page content. Previously capped at 1240px, which
+   left the navbar items visibly inset on docs and other full-width pages
+   (Jark feedback, PR #3226). Same treatment on the homepage so the
+   navbar is visually consistent across the whole site. */
+.navbar__inner {
+  max-width: none;
+  margin: 0;
+  padding: 0 var(--fluss-container-pad);
+}
+
+.navbar__items {
+  gap: 2px;
+}
+
+.navbar__link {
+  font-size: 0.9375rem;
+  font-weight: 500;
+  color: var(--fluss-ink-700);
+  padding: 0 12px;
+  border-radius: var(--fluss-radius-md);
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.navbar__link:hover {
+  color: var(--fluss-blue-500);
+  text-decoration: none;
+}
+
+.navbar__link--active {
+  color: var(--fluss-blue-500);
+  font-weight: 600;
+}
+
+.navbar__title {
+  font-family: var(--fluss-font-display);
+  font-weight: 700;
+  letter-spacing: -0.01em;
+  color: var(--fluss-ink-950);
+}
+
+/* Dropdown menus */
+.dropdown__menu {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-lg);
+  box-shadow: var(--fluss-shadow-lg);
+  padding: 8px;
+  min-width: 200px;
+  background: var(--fluss-paper);
+}
+
+.dropdown__link {
+  border-radius: var(--fluss-radius-sm);
+  padding: 8px 12px;
+  font-size: 0.9375rem;
+  font-weight: 500;
+  color: var(--fluss-ink-700);
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.dropdown__link:hover,
+.dropdown__link--active {
+  color: var(--fluss-blue-700);
+  background: var(--fluss-blue-50);
+}
+
+/* Algolia DocSearch button */
+.DocSearch-Button {
+  height: 36px;
+  padding: 0 12px;
+  border-radius: var(--fluss-radius-md);
+  background: var(--fluss-ink-100);
+  border: 1px solid transparent;
+  transition: border-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.DocSearch-Button:hover {
+  border-color: var(--fluss-blue-300);
+  background: var(--fluss-paper);
+  box-shadow: none;
+}
+
+.DocSearch-Button-Placeholder {
+  font-size: 0.875rem;
+  color: var(--fluss-ink-500);
+}
+
+.DocSearch-Search-Icon {
+  color: var(--fluss-ink-500);
+}
+
+/* Unify right-side navbar spacing so theme-toggle, search button, version
+   dropdown and GitHub icon align on a single baseline with consistent gap
+   (Michael feedback, PR #3226).
+
+   `!important` is required on the margin reset to defeat the
+   `button[class*='colorModeToggle']` selector below, which would
+   otherwise re-introduce a stray horizontal margin on the toggle and
+   throw off the gap. */
+.navbar__items--right {
+  gap: var(--fluss-space-2);
+  align-items: center;
+}
+
+.navbar__items--right > * {
+  margin: 0 !important;
+}
+
+/* Tighten the version dropdown link's horizontal padding when it lives in
+   the right-side cluster, so the visible gap between "Next ▾" and the
+   GitHub icon matches the gap between every other pair of items. */
+.navbar__items--right .navbar__link {
+  padding: 0 4px;
+}
+
+/* Keep navigation readable between the mobile and full desktop layouts. */
+@media (min-width: 997px) and (max-width: 1199px) {
+  .navbar__inner {
+    padding: 0 8px;
+  }
+
+  .navbar__brand {
+    flex-shrink: 0;
+  }
+
+  .navbar__link {
+    padding: 0 8px;
+    white-space: nowrap;
+  }
+
+  .navbar .DocSearch-Button {
+    width: 40px;
+    min-width: 40px;
+    padding: 0;
+    justify-content: center;
+  }
+
+  .navbar .DocSearch-Button-Placeholder,
+  .navbar .DocSearch-Button-Keys {
+    display: none;
+  }
+}
+
+/* Version dropdown badge */
+.navbar .navbar__item--version,
+.navbar .badge {
+  font-family: var(--fluss-font-display);
+  font-size: 0.8125rem;
+  font-weight: 600;
+}
+
+/* -------------------------------------------------------------------------
+   Mobile navbar drawer
+   -------------------------------------------------------------------------
+   The drawer is a light surface (var(--fluss-paper)), so inherited styles
+   from the dark navbar — the white Fluss wordmark, the light-tinted GitHub
+   icon, and the #E2E8F0 navbar link colour — would all be invisible inside
+   it. Re-tint everything for the light drawer below (borzoni feedback,
+   PR #3226). */
+.navbar-sidebar {
+  background: var(--fluss-paper);
+}
+
+.navbar-sidebar__brand {
+  border-bottom: 1px solid var(--fluss-ink-100);
+  background: var(--fluss-paper);
+}
+
+.navbar-sidebar__items .menu__link {
+  font-size: 0.9375rem;
+}
+
+/* Drawer brand: re-tint the white wordmark to brand navy (≈ --fluss-blue-600)
+   so it reads on the light paper background. The leading `brightness(0)`
+   collapses the source to black, the rest of the chain shifts it to the
+   target hue. */
+.navbar-sidebar__brand .navbar__logo img {
+  filter: brightness(0) saturate(100%) invert(13%) sepia(53%) saturate(2243%) hue-rotate(193deg) brightness(96%) contrast(95%);
+}
+
+.navbar-sidebar__brand .navbar__title,
+.navbar-sidebar__brand .navbar__brand {
+  color: var(--fluss-ink-950);
+}
+
+/* Drawer navbar links (e.g. items dropped into the drawer on mobile)
+   inherit color: #E2E8F0 from the dark navbar. Re-tint for the light
+   drawer so the labels are readable. */
+.navbar-sidebar .navbar__link,
+.navbar-sidebar .menu__link {
+  color: var(--fluss-ink-700);
+}
+
+.navbar-sidebar .navbar__link:hover,
+.navbar-sidebar .menu__link:hover {
+  color: var(--fluss-blue-700);
+}
+
+.navbar-sidebar .navbar__link--active,
+.navbar-sidebar .menu__link--active {
+  color: var(--fluss-blue-700);
+}
+
+/* GitHub icon in the drawer: the navbar variant uses fill='#E2E8F0',
+   which vanishes on the light drawer. Swap to a brand-blue fill. The
+   `.navbar-sidebar` ancestor selector already wins on specificity, no
+   `!important` needed. */
+.navbar-sidebar .header-github-link::before {
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%231C5078' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-. [...]
+}
+
+/* Drawer close button (the X) — keep it readable on the light surface. */
+.navbar-sidebar__close {
+  color: var(--fluss-ink-700);
+}
+
+.navbar-sidebar__close:hover {
+  color: var(--fluss-blue-700);
+}
+
+/* -------------------------------------------------------------------------
+   Announcement bar
+   ------------------------------------------------------------------------- */
+div[class*='announcementBar'] {
+  background: linear-gradient(120deg, var(--fluss-blue-700) 0%, var(--fluss-blue-600) 100%);
+  color: #FFFFFF;
+  font-weight: 500;
+  border-bottom: none;
+}
+
+div[class*='announcementBar'] a {
+  color: var(--fluss-blue-100);
+  text-decoration: underline;
+}
+
+/* -------------------------------------------------------------------------
+   Doc page layout : sidebar / main / TOC
+   ------------------------------------------------------------------------- */
+.theme-doc-sidebar-container {
+  border-right: 1px solid var(--fluss-ink-100);
+  background: var(--fluss-canvas-soft);
+}
+
+.theme-doc-sidebar-menu {
+  padding: var(--fluss-space-3) var(--fluss-space-2);
+}
+
+.menu {
+  background: transparent;
+  font-size: 0.9375rem;
+}
+
+.menu__list .menu__list {
+  margin-left: 8px;
+  padding-left: 8px;
+  border-left: 1px solid var(--fluss-ink-100);
+}
+
+.menu__link {
+  border-radius: var(--fluss-radius-sm);
+  padding: 6px 10px;
+  font-weight: 500;
+  color: var(--fluss-ink-700);
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.menu__link:hover {
+  background: var(--fluss-blue-50);
+  color: var(--fluss-blue-700);
+}
+
+.menu__link--active {
+  color: var(--fluss-blue-700);
+  background: var(--fluss-blue-50);
+  font-weight: 600;
+}
+
+.menu__link--sublist {
+  font-weight: 600;
+  color: var(--fluss-ink-950);
+}
+
+.menu__caret:hover {
+  background: var(--fluss-blue-50);
+}
+
+/* Sidebar toggle button at the bottom */
+.theme-doc-sidebar-container button {
+  border-color: var(--fluss-ink-100);
+  color: var(--fluss-ink-500);
+  border-radius: var(--fluss-radius-sm);
+}
+
+.theme-doc-sidebar-container button:hover {
+  border-color: var(--fluss-blue-300);
+  background: var(--fluss-blue-50);
+}
+
+/* -------------------------------------------------------------------------
+   Doc breadcrumbs
+   ------------------------------------------------------------------------- */
+.breadcrumbs__link {
+  font-size: 0.875rem;
+  font-weight: 500;
+  color: var(--fluss-ink-500);
+  border-radius: var(--fluss-radius-sm);
+  padding: 4px 8px;
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+/* Anchor-only hover styles. Category breadcrumb segments without a target
+   page render as a plain element (no <a>); those should not look clickable
+   (borzoni feedback, PR #3226). */
+a.breadcrumbs__link:hover {
+  color: var(--fluss-blue-700);
+  background: var(--fluss-blue-50);
+}
+
+/* Non-link breadcrumb segment (parent category without an index page):
+   render as static text so users don't try to click it. */
+.breadcrumbs__link:not(a) {
+  color: var(--fluss-ink-700);
+  cursor: default;
+}
+
+.breadcrumbs__item--active .breadcrumbs__link {
+  color: var(--fluss-ink-950);
+  font-weight: 600;
+  background: var(--fluss-ink-100);
+}
+
+.breadcrumbs__item:not(:last-child)::after {
+  color: var(--fluss-ink-300);
+}
+
+/* -------------------------------------------------------------------------
+   Table of contents (right rail)
+   ------------------------------------------------------------------------- */
+.table-of-contents {
+  border-left: 1px solid var(--fluss-ink-100);
+  font-size: 0.8125rem;
+  padding-left: var(--fluss-space-3);
+}
+
+.table-of-contents__title,
+.table-of-contents > *:first-child {
+  font-family: var(--fluss-font-display);
+  font-weight: 600;
+  font-size: 0.75rem;
+  letter-spacing: 0.06em;
+  color: var(--fluss-ink-500);
+  margin-bottom: var(--fluss-space-3);
+}
+
+.table-of-contents__link {
+  color: var(--fluss-ink-500);
+  font-weight: 500;
+  padding: 4px 0;
+  display: block;
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.table-of-contents__link:hover {
+  color: var(--fluss-blue-700);
+  text-decoration: none;
+}
+
+.table-of-contents__link--active {
+  color: var(--fluss-blue-700);
+  font-weight: 600;
+}
+
+/* -------------------------------------------------------------------------
+   Doc content typography polish
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown {
+  font-size: 1rem;
+  line-height: 1.7;
+  color: var(--fluss-ink-700);
+}
+
+.theme-doc-markdown a:not(.button):not(.card) {
+  color: var(--fluss-blue-700);
+  text-decoration-color: rgba(28, 80, 120, 0.3);
+  text-decoration-thickness: 1px;
+  text-underline-offset: 3px;
+  transition: text-decoration-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.theme-doc-markdown a:not(.button):not(.card):hover {
+  text-decoration-color: var(--fluss-blue-700);
+}
+
+.markdown a.hash-link {
+  color: var(--fluss-ink-300);
+  text-decoration: none;
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.markdown a.hash-link:hover {
+  color: var(--fluss-blue-600);
+}
+
+/* Doc page title (h1) : refined */
+header.theme-doc-markdown h1,
+.theme-doc-markdown > article > header > h1 {
+  font-family: var(--fluss-font-display);
+  font-size: 2.5rem;
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.1;
+  color: var(--fluss-ink-950);
+}
+
+/* "Edit this page" / last updated row */
+.theme-doc-footer {
+  margin-top: var(--fluss-space-12);
+  padding-top: var(--fluss-space-6);
+  border-top: 1px solid var(--fluss-ink-100);
+}
+
+.theme-edit-this-page {
+  display: inline-flex;
+  align-items: center;
+  gap: 6px;
+  color: var(--fluss-blue-700);
+  font-weight: 500;
+  font-size: 0.875rem;
+}
+
+.theme-edit-this-page:hover {
+  color: var(--fluss-blue-600);
+  text-decoration: underline;
+}
+
+.theme-last-updated {
+  font-size: 0.8125rem;
+  color: var(--fluss-ink-500);
+}
+
+/* -------------------------------------------------------------------------
+   Pagination (prev / next at the bottom of doc pages)
+   ------------------------------------------------------------------------- */
+.pagination-nav__link {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-lg);
+  padding: var(--fluss-space-4) var(--fluss-space-5);
+  background: var(--fluss-paper);
+  transition: transform var(--fluss-motion-base) var(--fluss-ease-out),
+              border-color var(--fluss-motion-base) var(--fluss-ease-out),
+              box-shadow var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+.pagination-nav__link:hover {
+  border-color: var(--fluss-blue-300);
+  box-shadow: var(--fluss-shadow-sm);
+  transform: translateY(-1px);
+  text-decoration: none;
+}
+
+.pagination-nav__sublabel {
+  font-family: var(--fluss-font-display);
+  font-size: 0.75rem;
+  font-weight: 600;
+  letter-spacing: 0.06em;
+  text-transform: uppercase;
+  color: var(--fluss-ink-500);
+  margin-bottom: 4px;
+}
+
+.pagination-nav__label {
+  color: var(--fluss-ink-950);
+  font-weight: 600;
+  font-size: 1rem;
+}
+
+/* -------------------------------------------------------------------------
+   Admonitions (note, tip, info, warning, danger, caution)
+   ------------------------------------------------------------------------- */
+.theme-admonition,
+.alert,
+[class*='admonition_'] {
+  border-radius: var(--fluss-radius-lg);
+  border: 1px solid var(--fluss-ink-100);
+  border-left-width: 4px;
+  background: var(--fluss-paper);
+  padding: var(--fluss-space-4) var(--fluss-space-5);
+  box-shadow: var(--fluss-shadow-sm);
+  margin: var(--fluss-space-6) 0;
+}
+
+.alert--secondary,
+.theme-admonition-note {
+  background: var(--fluss-ink-100);
+  border-color: var(--fluss-ink-300);
+  border-left-color: var(--fluss-ink-500);
+}
+
+.alert--info,
+.theme-admonition-info {
+  background: var(--fluss-blue-50);
+  border-color: var(--fluss-blue-300);
+  border-left-color: var(--fluss-blue-600);
+}
+
+.alert--success,
+.theme-admonition-tip {
+  background: #ECFDF5;
+  border-color: #A7F3D0;
+  border-left-color: var(--fluss-success);
+}
+
+.alert--warning,
+.theme-admonition-warning,
+.theme-admonition-caution {
+  background: #FFFBEB;
+  border-color: #FCD34D;
+  border-left-color: var(--fluss-warning);
+}
+
+.alert--danger,
+.theme-admonition-danger {
+  background: #FEF2F2;
+  border-color: #FCA5A5;
+  border-left-color: var(--fluss-danger);
+}
+
+/* Admonition heading row */
+[class*='admonitionHeading'] {
+  font-family: var(--fluss-font-display);
+  font-weight: 600;
+  font-size: 0.8125rem;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  margin-bottom: 6px;
+  color: var(--fluss-ink-950);
+}
+
+/* -------------------------------------------------------------------------
+   Inline code and code blocks
+   ------------------------------------------------------------------------- */
+code {
+  font-family: var(--fluss-font-mono);
+  font-size: 0.875em;
+  background: var(--fluss-blue-50);
+  border: 1px solid rgba(28, 80, 120, 0.12);
+  color: var(--fluss-blue-800);
+  padding: 1px 6px;
+  border-radius: var(--fluss-radius-sm);
+}
+
+a code,
+.theme-doc-markdown a code {
+  color: inherit;
+  background: rgba(28, 80, 120, 0.12);
+  border-color: transparent;
+}
+
+/* Code block container */
+.theme-code-block,
+div[class*='codeBlockContainer'] {
+  border-radius: var(--fluss-radius-lg);
+  border: 1px solid var(--fluss-ink-100);
+  box-shadow: var(--fluss-shadow-sm);
+  overflow: hidden;
+  margin: var(--fluss-space-5) 0;
+}
+
+div[class*='codeBlockTitle'] {
+  background: var(--fluss-ink-100);
+  border-bottom: 1px solid var(--fluss-ink-300);
+  font-family: var(--fluss-font-mono);
+  font-size: 0.8125rem;
+  color: var(--fluss-ink-700);
+  padding: 8px 14px;
+}
+
+div[class*='codeBlockContent'] pre {
+  background: #F8FAFC;
+}
+
+pre[class*='language-'] {
+  font-size: 0.875rem;
+  line-height: 1.65;
+  padding: var(--fluss-space-4) var(--fluss-space-5);
+}
+
+/* Copy-to-clipboard button */
+button[class*='copyButton'] {
+  background: var(--fluss-paper);
+  border: 1px solid var(--fluss-ink-100);
+  color: var(--fluss-ink-500);
+  border-radius: var(--fluss-radius-sm);
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+button[class*='copyButton']:hover {
+  color: var(--fluss-blue-700);
+  background: var(--fluss-blue-50);
+  border-color: var(--fluss-blue-300);
+}
+
+/* Word-wrap toggle */
+button[class*='wordWrapButton'] {
+  background: var(--fluss-paper);
+  border: 1px solid var(--fluss-ink-100);
+  color: var(--fluss-ink-500);
+  border-radius: var(--fluss-radius-sm);
+}
+
+/* Highlighted lines */
+.theme-code-block-highlighted-line,
+.docusaurus-highlight-code-line {
+  background: rgba(28, 80, 120, 0.08);
+  border-left: 3px solid var(--fluss-blue-600);
+  padding-left: calc(var(--fluss-space-5) - 3px);
+  display: block;
+  margin: 0 calc(-1 * var(--fluss-space-5));
+}
+
+/* Language label corner */
+div[class*='codeBlockTitle']::before {
+  content: '';
+  display: inline-block;
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  background: var(--fluss-cyan);
+  margin-right: 8px;
+  vertical-align: middle;
+}
+
+/* -------------------------------------------------------------------------
+   Tabs (used in docs for SQL / Java / etc. examples)
+   ------------------------------------------------------------------------- */
+.tabs {
+  border-bottom: 1px solid var(--fluss-ink-100);
+  margin-bottom: 0;
+  gap: 4px;
+}
+
+.tabs__item {
+  font-family: var(--fluss-font-display);
+  font-size: 0.875rem;
+  font-weight: 600;
+  color: var(--fluss-ink-500);
+  padding: 10px 14px;
+  border-radius: var(--fluss-radius-sm) var(--fluss-radius-sm) 0 0;
+  border-bottom: 2px solid transparent;
+  margin-bottom: -1px;
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.tabs__item:hover {
+  background: var(--fluss-blue-50);
+  color: var(--fluss-blue-700);
+}
+
+.tabs__item--active {
+  color: var(--fluss-blue-700);
+  border-bottom-color: var(--fluss-blue-600);
+  background: transparent;
+}
+
+/* -------------------------------------------------------------------------
+   Details / Summary (collapsibles)
+   ------------------------------------------------------------------------- */
+details {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-md);
+  background: var(--fluss-paper);
+  padding: var(--fluss-space-3) var(--fluss-space-4);
+  margin: var(--fluss-space-4) 0;
+}
+
+details > summary {
+  cursor: pointer;
+  font-weight: 600;
+  color: var(--fluss-ink-950);
+  font-family: var(--fluss-font-display);
+  list-style: none;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+details > summary::-webkit-details-marker {
+  display: none;
+}
+
+details > summary::before {
+  content: '';
+  width: 8px;
+  height: 8px;
+  border-right: 2px solid var(--fluss-ink-500);
+  border-bottom: 2px solid var(--fluss-ink-500);
+  transform: rotate(-45deg);
+  transition: transform var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+details[open] > summary::before {
+  transform: rotate(45deg);
+}
+
+/* -------------------------------------------------------------------------
+   Headings : anchor offset for sticky navbar
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown h1,
+.theme-doc-markdown h2,
+.theme-doc-markdown h3,
+.theme-doc-markdown h4 {
+  scroll-margin-top: 80px;
+}
+
+/* -------------------------------------------------------------------------
+   Doc cards (used in some index pages)
+   ------------------------------------------------------------------------- */
+article.card,
+.theme-doc-card {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-lg);
+  background: var(--fluss-paper);
+  box-shadow: var(--fluss-shadow-sm);
+  transition: transform var(--fluss-motion-base) var(--fluss-ease-out),
+              box-shadow var(--fluss-motion-base) var(--fluss-ease-out),
+              border-color var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+article.card:hover,
+.theme-doc-card:hover {
+  transform: translateY(-2px);
+  box-shadow: var(--fluss-shadow-md);
+  border-color: var(--fluss-blue-300);
+}
+
+/* -------------------------------------------------------------------------
+   Version banner (unmaintained / unreleased)
+   -------------------------------------------------------------------------
+   Previously rendered as a centred rounded card, which on docs pages
+   (full-width main column) sat visibly inset from the page edges and
+   read as disconnected (Jark feedback, PR #3226). Now rendered as a
+   full-bleed strip across the doc content column: no rounded corners,
+   no side border, top/bottom borders only — aligns with the page chrome
+   above and below it. */
+div[class*='docVersionBanner'] {
+  border: 0;
+  border-top: 1px solid var(--fluss-blue-300);
+  border-bottom: 1px solid var(--fluss-blue-300);
+  border-radius: 0;
+  background: var(--fluss-blue-50);
+  color: var(--fluss-ink-950);
+  padding: var(--fluss-space-3) var(--fluss-space-5);
+  margin: 0 0 var(--fluss-space-6);
+  font-size: 0.9375rem;
+}
+
+div[class*='docVersionBanner'] b {
+  color: var(--fluss-blue-700);
+}
+
+div[class*='docVersionBanner'] a {
+  color: var(--fluss-blue-700);
+  font-weight: 600;
+}
+
+/* -------------------------------------------------------------------------
+   Blog : list page (cards)
+   ------------------------------------------------------------------------- */
+.blog-list-page article,
+article[itemtype='https://schema.org/BlogPosting'] {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-lg);
+  padding: var(--fluss-space-8);
+  background: var(--fluss-paper);
+  margin-bottom: var(--fluss-space-6);
+  box-shadow: var(--fluss-shadow-sm);
+  transition: transform var(--fluss-motion-base) var(--fluss-ease-out),
+              box-shadow var(--fluss-motion-base) var(--fluss-ease-out),
+              border-color var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+.blog-list-page article:hover,
+article[itemtype='https://schema.org/BlogPosting']:hover {
+  transform: translateY(-2px);
+  box-shadow: var(--fluss-shadow-md);
+  border-color: var(--fluss-blue-300);
+}
+
+article[itemtype='https://schema.org/BlogPosting'] header h2 a,
+.blog-list-page article header h2 a {
+  font-family: var(--fluss-font-display);
+  font-weight: 700;
+  color: var(--fluss-ink-950);
+  letter-spacing: -0.01em;
+  text-decoration: none;
+}
+
+article[itemtype='https://schema.org/BlogPosting'] header h2 a:hover,
+.blog-list-page article header h2 a:hover {
+  color: var(--fluss-blue-700);
+}
+
+/* Blog post meta line */
+.blog-post-page-no-sidebar .blog-post-page,
+article[itemtype='https://schema.org/BlogPosting'] header > div {
+  color: var(--fluss-ink-500);
+  font-size: 0.875rem;
+}
+
+/* Blog tags */
+a.tag,
+a[class*='tag_'] {
+  display: inline-flex;
+  align-items: center;
+  height: 26px;
+  padding: 0 10px;
+  border-radius: var(--fluss-radius-pill);
+  background: var(--fluss-blue-50);
+  border: 1px solid transparent;
+  color: var(--fluss-blue-700);
+  font-size: 0.8125rem;
+  font-weight: 600;
+  text-decoration: none;
+  transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+a.tag:hover,
+a[class*='tag_']:hover {
+  background: var(--fluss-blue-100);
+  border-color: var(--fluss-blue-300);
+  color: var(--fluss-blue-800);
+  text-decoration: none;
+}
+
+/* Blog "Read more" button */
+.button.button--secondary {
+  background: var(--fluss-blue-600);
+  color: #FFFFFF;
+  border: none;
+  border-radius: var(--fluss-radius-md);
+  font-weight: 600;
+  padding: 10px 16px;
+  box-shadow: var(--fluss-shadow-sm);
+  transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              transform var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.button.button--secondary:hover {
+  background: var(--fluss-blue-700);
+  color: #FFFFFF;
+  transform: translateY(-1px);
+}
+
+/* -------------------------------------------------------------------------
+   Blog sidebar (recent posts)
+   ------------------------------------------------------------------------- */
+aside.col[class*='blogSidebar'],
+nav[class*='sidebar'] aside {
+  font-size: 0.875rem;
+}
+
+nav[class*='sidebar'] h3 {
+  font-family: var(--fluss-font-display);
+  font-weight: 600;
+  font-size: 0.75rem;
+  text-transform: uppercase;
+  letter-spacing: 0.06em;
+  color: var(--fluss-ink-500);
+  margin-bottom: var(--fluss-space-3);
+}
+
+/* -------------------------------------------------------------------------
+   Blog authors row
+   ------------------------------------------------------------------------- */
+[class*='authorPhoto'] img,
+[class*='authorAvatar'] img {
+  border: 2px solid var(--fluss-paper);
+  box-shadow: 0 0 0 1px var(--fluss-ink-100);
+}
+
+/* -------------------------------------------------------------------------
+   Blog post page header
+   ------------------------------------------------------------------------- */
+.blog-post-page-no-sidebar h1,
+.blog-post-page article > header h1 {
+  font-family: var(--fluss-font-display);
+  font-size: clamp(2rem, 3vw + 1rem, 3rem);
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.1;
+  color: var(--fluss-ink-950);
+}
+
+/* -------------------------------------------------------------------------
+   Markdown tables in docs / blog (override Infima default for dark headers)
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown table,
+.markdown table {
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-md);
+  overflow: hidden;
+  box-shadow: var(--fluss-shadow-sm);
+}
+
+.theme-doc-markdown thead,
+.markdown thead {
+  background: var(--fluss-blue-50);
+}
+
+.theme-doc-markdown thead th,
+.markdown thead th {
+  font-family: var(--fluss-font-display);
+  font-weight: 600;
+  color: var(--fluss-ink-950);
+  border-color: var(--fluss-blue-100);
+}
+
+.theme-doc-markdown tbody tr:nth-child(even),
+.markdown tbody tr:nth-child(even) {
+  background: var(--fluss-blue-50);
+}
+
+/* -------------------------------------------------------------------------
+   Block-quote
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown blockquote,
+.markdown blockquote {
+  border-left: 3px solid var(--fluss-blue-600);
+  background: var(--fluss-blue-50);
+  border-radius: var(--fluss-radius-md);
+  padding: var(--fluss-space-4) var(--fluss-space-5);
+  color: var(--fluss-ink-700);
+  margin: var(--fluss-space-5) 0;
+}
+
+/* -------------------------------------------------------------------------
+   404 page
+   ------------------------------------------------------------------------- */
+main.container .row .col h1.hero__title {
+  font-family: var(--fluss-font-display);
+  font-size: 4rem;
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  color: var(--fluss-ink-950);
+}
+
+/* -------------------------------------------------------------------------
+   Buttons (Infima primary / outline) : global refresh
+   ------------------------------------------------------------------------- */
+.button.button--primary {
+  background: var(--fluss-blue-600);
+  border: none;
+  color: #FFFFFF;
+  border-radius: var(--fluss-radius-md);
+  font-weight: 600;
+  padding: 10px 18px;
+  box-shadow: var(--fluss-shadow-sm);
+  transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              transform var(--fluss-motion-fast) var(--fluss-ease-out),
+              box-shadow var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.button.button--primary:hover {
+  background: var(--fluss-blue-700);
+  color: #FFFFFF;
+  transform: translateY(-1px);
+  box-shadow: var(--fluss-shadow-md);
+}
+
+.button.button--outline.button--primary {
+  background: transparent;
+  border: 1px solid var(--fluss-blue-600);
+  color: var(--fluss-blue-700);
+}
+
+.button.button--outline.button--primary:hover {
+  background: var(--fluss-blue-50);
+  color: var(--fluss-blue-800);
+}
+
+/* -------------------------------------------------------------------------
+   Selection
+   ------------------------------------------------------------------------- */
+::selection {
+  background: rgba(28, 80, 120, 0.18);
+  color: var(--fluss-ink-950);
+}
+
+/* -------------------------------------------------------------------------
+   Scrollbars (subtle, consistent across content areas)
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown pre::-webkit-scrollbar,
+.theme-doc-sidebar-container::-webkit-scrollbar,
+.table-of-contents::-webkit-scrollbar {
+  width: 10px;
+  height: 10px;
+}
+
+.theme-doc-markdown pre::-webkit-scrollbar-thumb,
+.theme-doc-sidebar-container::-webkit-scrollbar-thumb,
+.table-of-contents::-webkit-scrollbar-thumb {
+  background: var(--fluss-ink-300);
+  border-radius: var(--fluss-radius-pill);
+  border: 2px solid transparent;
+  background-clip: padding-box;
+}
+
+.theme-doc-markdown pre::-webkit-scrollbar-thumb:hover,
+.theme-doc-sidebar-container::-webkit-scrollbar-thumb:hover,
+.table-of-contents::-webkit-scrollbar-thumb:hover {
+  background: var(--fluss-ink-500);
+  background-clip: padding-box;
+}
+
+/* -------------------------------------------------------------------------
+   Skip-to-content link (accessibility)
+   ------------------------------------------------------------------------- */
+.skipToContent_node_modules-\@docusaurus-theme-classic-lib-theme-SkipToContent-styles-module {
+  background: var(--fluss-blue-700);
+  color: #FFFFFF;
+}
+
+/* =========================================================================
+   ========================================================================
+   PREMIUM POLISH LAYER
+   Targeted enhancements so docs/blog stop feeling like default Docusaurus.
+   ========================================================================
+   ========================================================================= */
+
+/* -------------------------------------------------------------------------
+   Subtle page background (very faint dot grid for warmth)
+   ------------------------------------------------------------------------- */
+main[class*='docMainContainer'],
+main[class*='blogPostPage'],
+main[class*='docItemContainer'],
+.docs-wrapper,
+.blog-wrapper {
+  position: relative;
+  background:
+    radial-gradient(circle at 1px 1px, rgba(16, 40, 86, 0.04) 1px, transparent 0) 0 0 / 32px 32px,
+    var(--fluss-canvas);
+}
+
+/* -------------------------------------------------------------------------
+   Doc / blog-post page header band (eyebrow + title + breadcrumbs).
+   Scoped to single doc/post pages, NOT to the blog list (where each card
+   also has its own <header> and we don't want this treatment on cards).
+   ------------------------------------------------------------------------- */
+
+
+main[class*='docMainContainer'] article > header:first-of-type h1,
+main[class*='docItemContainer'] article > header:first-of-type h1,
+main[class*='blogPostPage'] article > header:first-of-type h1,
+.blog-post-page-no-sidebar article > header:first-of-type h1,
+.theme-doc-markdown header h1 {
+  font-family: var(--fluss-font-display);
+  font-size: clamp(2.25rem, 2.5vw + 1.5rem, 3.25rem);
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.08;
+  color: var(--fluss-ink-950);
+  margin: var(--fluss-space-3) 0 0;
+}
+
+/* Inline ifm h1 inside the first markdown block also gets the display treatment */
+.markdown > h1:first-child {
+  font-family: var(--fluss-font-display);
+  font-size: clamp(2.25rem, 2.5vw + 1.5rem, 3.25rem);
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.08;
+}
+
+/* -------------------------------------------------------------------------
+   Markdown body : generous reading rhythm
+   ------------------------------------------------------------------------- */
+.theme-doc-markdown,
+.markdown {
+  font-size: 1.0625rem;
+  line-height: 1.75;
+  color: var(--fluss-ink-700);
+}
+
+.markdown p,
+.theme-doc-markdown p {
+  margin-bottom: 1.25em;
+}
+
+.markdown ul,
+.markdown ol,
+.theme-doc-markdown ul,
+.theme-doc-markdown ol {
+  margin-bottom: 1.25em;
+}
+
+.markdown li + li {
+  margin-top: 6px;
+}
+
+/* Heading rhythm + display family */
+.markdown h2,
+.theme-doc-markdown h2 {
+  font-family: var(--fluss-font-display);
+  font-size: 1.75rem;
+  font-weight: 700;
+  letter-spacing: -0.018em;
+  line-height: 1.2;
+  color: var(--fluss-ink-950);
+  margin-top: 3rem;
+  margin-bottom: 1rem;
+  position: relative;
+}
+
+.markdown h2::before,
+.theme-doc-markdown h2::before {
+  content: '';
+  position: absolute;
+  left: -16px;
+  top: 0.55em;
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background: var(--fluss-blue-600);
+  opacity: 0;
+  transition: opacity var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+.markdown h2:hover::before,
+.theme-doc-markdown h2:hover::before {
+  opacity: 0.8;
+}
+
+.markdown h3,
+.theme-doc-markdown h3 {
+  font-family: var(--fluss-font-display);
+  font-size: 1.375rem;
+  font-weight: 600;
+  letter-spacing: -0.014em;
+  color: var(--fluss-ink-950);
+  margin-top: 2.25rem;
+  margin-bottom: 0.625rem;
+}
+
+.markdown h4,
+.theme-doc-markdown h4 {
+  font-family: var(--fluss-font-display);
+  font-size: 1.0625rem;
+  font-weight: 600;
+  color: var(--fluss-ink-950);
+  margin-top: 1.5rem;
+  margin-bottom: 0.5rem;
+}
+
+/* Anchor link reveal on heading hover */
+.markdown h2 .hash-link,
+.markdown h3 .hash-link,
+.markdown h4 .hash-link,
+.theme-doc-markdown h2 .hash-link,
+.theme-doc-markdown h3 .hash-link,
+.theme-doc-markdown h4 .hash-link {
+  margin-left: 6px;
+  opacity: 0;
+  font-weight: 400;
+  transition: opacity var(--fluss-motion-base) var(--fluss-ease-out),
+              transform var(--fluss-motion-base) var(--fluss-ease-out);
+  transform: translateX(-4px);
+  display: inline-block;
+}
+
+.markdown h2:hover .hash-link,
+.markdown h3:hover .hash-link,
+.markdown h4:hover .hash-link,
+.theme-doc-markdown h2:hover .hash-link,
+.theme-doc-markdown h3:hover .hash-link,
+.theme-doc-markdown h4:hover .hash-link {
+  opacity: 1;
+  transform: translateX(0);
+}
+
+/* Animated link underline in body prose. Color is explicit (not inherited
+   from --ifm-color-primary) so docs links read clearly as links against
+   the dark-gray body text. */
+.theme-doc-markdown a:not(.button):not(.card):not(.hash-link),
+.markdown a:not(.button):not(.card):not(.hash-link),
+.mdx-page article a:not(.button):not(.card):not(.hash-link) {
+  color: var(--fluss-link);
+  font-weight: 500;
+  background-image: linear-gradient(0deg, var(--fluss-link) 0, var(--fluss-link) 100%);
+  background-position: 0 100%;
+  background-repeat: no-repeat;
+  background-size: 100% 1px;
+  transition: background-size var(--fluss-motion-base) var(--fluss-ease-out),
+              color var(--fluss-motion-fast) var(--fluss-ease-out);
+  text-decoration: none;
+}
+
+.theme-doc-markdown a:not(.button):not(.card):not(.hash-link):hover,
+.markdown a:not(.button):not(.card):not(.hash-link):hover,
+.mdx-page article a:not(.button):not(.card):not(.hash-link):hover {
+  color: var(--fluss-link-hover);
+  background-image: linear-gradient(0deg, var(--fluss-link-hover) 0, var(--fluss-link-hover) 100%);
+  background-size: 100% 2px;
+  text-decoration: none;
+}
+
+/* External-link indicator (auto, before the link) */
+.theme-doc-markdown a[href^='http']:not([href*='fluss.apache.org'])::after,
+.markdown a[href^='http']:not([href*='fluss.apache.org'])::after,
+.mdx-page article a[href^='http']:not([href*='fluss.apache.org'])::after {
+  content: '';
+  display: inline-block;
+  width: 0.7em;
+  height: 0.7em;
+  margin-left: 4px;
+  background-color: currentColor;
+  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17L17 7M17 7H8M17 7V16'/%3E%3C/svg%3E");
+  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17L17 7M17 7H8M17 7V16'/%3E%3C/svg%3E");
+  -webkit-mask-size: contain;
+  mask-size: contain;
+  -webkit-mask-repeat: no-repeat;
+  mask-repeat: no-repeat;
+  opacity: 0.65;
+  vertical-align: 0.05em;
+}
+
+/* -------------------------------------------------------------------------
+   <kbd> styling
+   ------------------------------------------------------------------------- */
+kbd {
+  display: inline-block;
+  padding: 1px 6px;
+  border-radius: var(--fluss-radius-sm);
+  background: var(--fluss-paper);
+  border: 1px solid var(--fluss-ink-300);
+  border-bottom-width: 2px;
+  color: var(--fluss-ink-700);
+  font-family: var(--fluss-font-mono);
+  font-size: 0.8125em;
+  line-height: 1.4;
+  vertical-align: 0.05em;
+  box-shadow: var(--fluss-shadow-sm);
+}
+
+/* -------------------------------------------------------------------------
+   Premium sidebar : sliding active indicator
+   ------------------------------------------------------------------------- */
+.theme-doc-sidebar-container {
+  background:
+    linear-gradient(180deg, transparent 0, transparent 64px, var(--fluss-canvas-soft) 64px),
+    var(--fluss-canvas-soft);
+}
+
+.menu__list {
+  position: relative;
+}
+
+.menu__link {
+  position: relative;
+  font-size: 0.9rem;
+}
+
+.menu__link--active {
+  background: linear-gradient(90deg, var(--fluss-blue-50) 0%, transparent 100%);
+}
+
+.menu__link--active::before {
+  content: '';
+  position: absolute;
+  left: 0;
+  top: 6px;
+  bottom: 6px;
+  width: 3px;
+  border-radius: 0 var(--fluss-radius-sm) var(--fluss-radius-sm) 0;
+  background: linear-gradient(180deg, var(--fluss-blue-600), var(--fluss-cyan));
+  box-shadow: 0 0 12px rgba(28, 80, 120, 0.4);
+}
+
+/* Top-level items: always bold regardless of whether they are collapsible
+   categories or plain leaf links. Title-Case retained from `_category_.json`
+   (Jark feedback, PR #3226). This is the only visually heavier tier in the
+   sidebar; everything below it is normalized to a single uniform style. */
+.theme-doc-sidebar-menu > .menu__list-item > .menu__link,
+.theme-doc-sidebar-menu > .menu__list-item > .menu__list-item-collapsible > .menu__link {
+  font-family: var(--fluss-font-display);
+  font-weight: 700;
+  font-size: 0.9375rem;
+  letter-spacing: 0;
+  color: var(--fluss-ink-950);
+  padding-top: 14px;
+  padding-bottom: 6px;
+}
+
+/* Nested items at any depth: uniform non-bold styling, regardless of whether
+   the item is a collapsible sub-category or a leaf link. Infima's default
+   makes `.menu__link--sublist` bold, which created an inconsistent hierarchy
+   where nested sub-categories looked as heavy as top-level entries. Match a
+   single weight/size/padding so the only visually distinct tier is the
+   top-level row. */
+.theme-doc-sidebar-menu .menu__list .menu__link,
+.theme-doc-sidebar-menu .menu__list .menu__list-item-collapsible > .menu__link {
+  font-family: inherit;
+  font-weight: 500;
+  font-size: 0.9375rem;
+  letter-spacing: 0;
+  color: var(--fluss-ink-700);
+  padding: 6px 10px;
+}
+
+/* Preserve the active-state highlight for nested items (declared after the
+   uniform nested rule so it wins on equal specificity). */
+.theme-doc-sidebar-menu .menu__list .menu__link--active,
+.theme-doc-sidebar-menu .menu__list .menu__list-item-collapsible > .menu__link--active {
+  color: var(--fluss-blue-700);
+  background: var(--fluss-blue-50);
+  font-weight: 600;
+}
+
+.theme-doc-sidebar-menu > .menu__list-item:not(:first-child) {
+  margin-top: 4px;
+  border-top: 1px dashed var(--fluss-ink-100);
+  padding-top: 4px;
+}
+
+/* -------------------------------------------------------------------------
+   Sticky TOC card (right rail)
+   ------------------------------------------------------------------------- */
+.theme-doc-toc-desktop,
+[class*='tableOfContents'] {
+  border-radius: var(--fluss-radius-lg);
+  background: var(--fluss-paper);
+  border: 1px solid var(--fluss-ink-100);
+  padding: var(--fluss-space-5);
+  box-shadow: var(--fluss-shadow-sm);
+  position: sticky;
+  top: 96px;
+}
+
+.theme-doc-toc-desktop .table-of-contents,
+[class*='tableOfContents'] .table-of-contents {
+  border-left: none;
+  padding-left: 0;
+  margin: 0;
+}
+
+.theme-doc-toc-desktop::before,
+[class*='tableOfContents']::before {
+  content: 'On this page';
+  display: block;
+  font-family: var(--fluss-font-display);
+  font-weight: 700;
+  font-size: 0.72rem;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: var(--fluss-ink-500);
+  margin-bottom: var(--fluss-space-3);
+  padding-bottom: var(--fluss-space-3);
+  border-bottom: 1px solid var(--fluss-ink-100);
+}
+
+.table-of-contents__link {
+  position: relative;
+  padding-left: var(--fluss-space-3);
+  border-left: 2px solid transparent;
+  transition: color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.table-of-contents__link--active {
+  border-left-color: var(--fluss-blue-600);
+  background: var(--fluss-blue-50);
+  border-radius: 0 var(--fluss-radius-sm) var(--fluss-radius-sm) 0;
+}
+
+/* -------------------------------------------------------------------------
+   Doc footer : "edit this page" + last-updated as buttons
+   ------------------------------------------------------------------------- */
+.theme-doc-footer {
+  padding: var(--fluss-space-5);
+  border: 1px solid var(--fluss-ink-100);
+  border-radius: var(--fluss-radius-lg);
+  background: var(--fluss-paper);
+  margin-top: var(--fluss-space-12);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex-wrap: wrap;
+  gap: var(--fluss-space-3);
+}
+
+.theme-edit-this-page {
+  display: inline-flex !important;
+  align-items: center;
+  gap: 6px;
+  height: 36px;
+  padding: 0 14px;
+  border-radius: var(--fluss-radius-md);
+  background: var(--fluss-blue-50);
+  color: var(--fluss-blue-700) !important;
+  font-weight: 600;
+  font-size: 0.875rem;
+  text-decoration: none !important;
+  border: 1px solid transparent;
+  transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+.theme-edit-this-page:hover {
+  background: var(--fluss-blue-100);
+  border-color: var(--fluss-blue-300);
+}
+
+/* -------------------------------------------------------------------------
+   Premium code blocks
+   ------------------------------------------------------------------------- */
+div[class*='codeBlockContainer'] {
+  position: relative;
+  box-shadow: var(--fluss-shadow-sm);
+  transition: box-shadow var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+div[class*='codeBlockContainer']:hover {
+  box-shadow: var(--fluss-shadow-md);
+}
+
+/* Always-visible copy button (instead of fade-on-hover) */
+button[class*='copyButton'] {
+  opacity: 0.85;
+  transition: opacity var(--fluss-motion-fast) var(--fluss-ease-out),
+              color var(--fluss-motion-fast) var(--fluss-ease-out),
+              background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+div[class*='codeBlockContainer'] button[class*='copyButton']:not(:hover) {
+  opacity: 0.85;
+}
+
+/* Language label in the corner : synthetic via data-attr if title not set */
+div[class*='codeBlockContainer']::before {
+  content: attr(data-language);
+  position: absolute;
+  top: 8px;
+  right: 56px;
+  font-family: var(--fluss-font-mono);
+  font-size: 0.6875rem;
+  font-weight: 600;
+  text-transform: uppercase;
+  letter-spacing: 0.08em;
+  color: var(--fluss-ink-500);
+  pointer-events: none;
+  z-index: 1;
+}
+
+/* -------------------------------------------------------------------------
+   Blog list : featured first post + editorial polish
+   ------------------------------------------------------------------------- */
+main[class*='blogListPage'] article:first-of-type,
+.blog-list-page > article:first-of-type {
+  padding: var(--fluss-space-10);
+  background:
+    radial-gradient(800px 300px at 100% 0%, rgba(38, 109, 149, 0.06), transparent 60%),
+    radial-gradient(600px 300px at 0% 100%, rgba(28, 80, 120, 0.06), transparent 60%),
+    var(--fluss-paper);
+  border-color: var(--fluss-blue-100);
+}
+
+main[class*='blogListPage'] article:first-of-type header h2,
+.blog-list-page > article:first-of-type header h2 {
+  font-size: 2.25rem;
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.1;
+}
+
+/* "Featured" eyebrow on the first card */
+main[class*='blogListPage'] article:first-of-type::before,
+.blog-list-page > article:first-of-type::before {
+  content: 'Latest post';
+  display: inline-block;
+  margin-bottom: var(--fluss-space-3);
+  padding: 4px 10px;
+  background: linear-gradient(120deg, var(--fluss-blue-600), var(--fluss-cyan));
+  color: #FFFFFF;
+  border-radius: var(--fluss-radius-pill);
+  font-family: var(--fluss-font-display);
+  font-size: 0.7rem;
+  font-weight: 700;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+}
+
+/* Blog post titles : display font in cards */
+article[itemtype='https://schema.org/BlogPosting'] header h2,
+.blog-list-page article header h2 {
+  font-family: var(--fluss-font-display);
+  letter-spacing: -0.018em;
+}
+
+/* Blog post meta row : compact, calm */
+.blog-list-page article header > div:not([class*='blogPostTags']),
+article[itemtype='https://schema.org/BlogPosting'] header > div:not([class*='blogPostTags']) {
+  font-size: 0.85rem;
+  color: var(--fluss-ink-500);
+}
+
+/* "Read more" link with arrow */
+.button.button--secondary::after,
+a[class*='readMore']::after {
+  content: ' →';
+  display: inline-block;
+  margin-left: 4px;
+  transition: transform var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+.button.button--secondary:hover::after,
+a[class*='readMore']:hover::after {
+  transform: translateX(3px);
+}
+
+/* -------------------------------------------------------------------------
+   Blog post page : editorial header
+   ------------------------------------------------------------------------- */
+.blog-post-page-no-sidebar article > header h1,
+main[class*='blogPostPage'] article > header h1 {
+  font-family: var(--fluss-font-display);
+  font-size: clamp(2.25rem, 3vw + 1rem, 3.5rem);
+  font-weight: 700;
+  letter-spacing: -0.022em;
+  line-height: 1.05;
+  margin-top: var(--fluss-space-3);
+  color: var(--fluss-ink-950);
+}
+
+/* Author row : bigger, card-like */
+.blog-post-page-no-sidebar article > header > div[class*='authorRow'],
+main[class*='blogPostPage'] article > header > div[class*='authorRow'] {
+  margin-top: var(--fluss-space-5);
+  padding-top: var(--fluss-space-4);
+  border-top: 1px solid var(--fluss-ink-100);
+}
+
+/* -------------------------------------------------------------------------
+   Pagination card refinement
+   ------------------------------------------------------------------------- */
+.pagination-nav {
+  margin-top: var(--fluss-space-12);
+}
+
+.pagination-nav__link {
+  position: relative;
+  overflow: hidden;
+}
+
+.pagination-nav__link::before {
+  content: '';
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 3px;
+  height: 100%;
+  background: linear-gradient(180deg, var(--fluss-blue-600), var(--fluss-cyan));
+  opacity: 0;
+  transition: opacity var(--fluss-motion-base) var(--fluss-ease-out);
+}
+
+.pagination-nav__link:hover::before {
+  opacity: 1;
+}
+
+.pagination-nav__link--prev .pagination-nav__sublabel::before {
+  content: '← ';
+}
+
+.pagination-nav__link--next .pagination-nav__sublabel::after {
+  content: ' →';
+}
+
+/* -------------------------------------------------------------------------
+   Admonition icons (color tinting)
+   ------------------------------------------------------------------------- */
+[class*='admonitionIcon'] svg {
+  filter: saturate(1.1);
+}
+
+/* -------------------------------------------------------------------------
+   Featured quote / pull-quote (use blockquote with .pull class via mdx)
+   Falls back gracefully for any blockquote.
+   ------------------------------------------------------------------------- */
+.markdown blockquote,
+.theme-doc-markdown blockquote {
+  position: relative;
+  padding-left: var(--fluss-space-8);
+}
+
+.markdown blockquote::before,
+.theme-doc-markdown blockquote::before {
+  content: '\201C';
+  position: absolute;
+  left: var(--fluss-space-3);
+  top: -0.1em;
+  font-family: var(--fluss-font-display);
+  font-size: 3rem;
+  font-weight: 700;
+  color: var(--fluss-blue-300);
+  line-height: 1;
+}
+
+/* -------------------------------------------------------------------------
+   Page-load fade-in for main content
+   ------------------------------------------------------------------------- */
+@keyframes flussFadeUp {
+  from { opacity: 0; transform: translateY(6px); }
+  to   { opacity: 1; transform: translateY(0); }
+}
+
+main[class*='docMainContainer'] article,
+main[class*='blogPostPage'] article,
+main[class*='blogListPage'] article {
+  animation: flussFadeUp var(--fluss-motion-slow) var(--fluss-ease-out) both;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  main[class*='docMainContainer'] article,
+  main[class*='blogPostPage'] article,
+  main[class*='blogListPage'] article {
+    animation: none;
+  }
+}
+
+/* -------------------------------------------------------------------------
+   Increase content max-width slightly for editorial breathability
+   (only inside docs / blog, not on the homepage)
+   ------------------------------------------------------------------------- */
+@media (min-width: 997px) {
+  main[class*='docItemContainer'] article,
+  main[class*='blogPostPage'] article {
+    max-width: 760px;
+    margin-left: auto;
+    margin-right: auto;
+  }
+}
+
+/* -------------------------------------------------------------------------
+   Inline code refinements (override generic Infima)
+   ------------------------------------------------------------------------- */
+.markdown p code,
+.markdown li code,
+.theme-doc-markdown p code,
+.theme-doc-markdown li code {
+  font-size: 0.85em;
+  padding: 1px 6px;
+  background: var(--fluss-blue-50);
+  border: 1px solid rgba(28, 80, 120, 0.15);
+  color: var(--fluss-blue-800);
+  border-radius: var(--fluss-radius-sm);
+}
+
+/* Code blocks (pre > code) should never inherit inline-code box styling
+   even when nested inside list/paragraph/table elements. */
+.markdown pre code,
+.theme-doc-markdown pre code {
+  background: transparent;
+  border: 0;
+  padding: 0;
+  font-size: inherit;
+  color: inherit;
+  border-radius: 0;
+}
+
+/* -------------------------------------------------------------------------
+   Tag cloud / blog tags page
+   ------------------------------------------------------------------------- */
+ul[class*='tag'] {
+  display: flex;
+  flex-wrap: wrap;
+  gap: var(--fluss-space-2);
+  list-style: none;
+  padding-left: 0;
+}
+
+ul[class*='tag'] li {
+  margin: 0;
+  list-style: none;
+}
+
+/* =========================================================================
+   ========================================================================
+   GLOBAL NAVBAR : same look on every page (homepage, docs, blog, etc.)
+   No transparent-on-hero variant, no homepage-frosted variant — the
+   navbar is rendered as a single consistent dark pane site-wide.
+   ========================================================================
+   ========================================================================= */
+
+.navbar {
+  background-color: var(--ifm-navbar-background-color);
+  box-shadow: var(--ifm-navbar-shadow);
+}
+
+.navbar__brand,
+.navbar__title,
+.navbar__link {
+  color: #E2E8F0;
+}
+
+.navbar__link:hover {
+  color: var(--fluss-blue-300);
+}
+
+.navbar__link--active {
+  color: var(--fluss-blue-300);
+}
+
+/* Hide the navbar hamburger / toggle button on desktop. Explicit product
+   decision: the icon to the left of the Fluss logo is not desired on any
+   page (homepage, docs, blog, community, learn). On docs pages the
+   in-doc sidebar toggle is a separate element and remains available. */
+@media (min-width: 997px) {
+    .navbar__toggle {
+        display: none !important;
+    }
+}
+
+/* On mobile, keep the navbar toggle in its natural flex position at the
+   start of the navbar so it stays reachable at every viewport width.
+   Previously it was `position: absolute; right: 4rem` which made it
+   disappear on intermediate widths where the right-side cluster pushed
+   past 4rem (borzoni feedback, PR #3226). */
+@media (max-width: 996px) {
+    .navbar__toggle {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        width: 36px;
+        height: 36px;
+        margin-right: var(--fluss-space-2);
+        padding: 0;
+        border-radius: var(--fluss-radius-md);
+        background: transparent;
+        border: 1px solid transparent;
+        color: #E2E8F0;
+        transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+                    border-color var(--fluss-motion-fast) var(--fluss-ease-out);
+    }
+
+    .navbar__toggle:hover,
+    .navbar__toggle:focus-visible {
+        background: rgba(255, 255, 255, 0.08);
+        border-color: rgba(255, 255, 255, 0.16);
+    }
+}
+
+/* =========================================================================
+   ========================================================================
+   COLOR MODE TOGGLE : branded sun/moon button
+   ========================================================================
+   ========================================================================= */
+
+.theme-toggle,
+button[class*='colorModeToggle'],
+button.clean-btn[class*='toggle'] {
+  border-radius: var(--fluss-radius-pill);
+  background: var(--fluss-ink-100);
+  border: 1px solid transparent;
+  color: var(--fluss-ink-700);
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  margin: 0 6px;
+  transition: background-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              border-color var(--fluss-motion-fast) var(--fluss-ease-out),
+              transform var(--fluss-motion-fast) var(--fluss-ease-out),
+              color var(--fluss-motion-fast) var(--fluss-ease-out);
+}
+
+button[class*='colorModeToggle']:hover,
+button.clean-btn[class*='toggle']:hover {
+  background: var(--fluss-blue-50);
+  border-color: var(--fluss-blue-300);
+  color: var(--fluss-blue-700);
+  transform: translateY(-1px);
+}
+
+button[class*='colorModeToggle'] svg,
+button.clean-btn[class*='toggle'] svg {
+  width: 18px;
+  height: 18px;
+}
+
+/* The colour-mode toggle is omitted from the DOM on the landing page via
+   the swizzled `src/theme/ColorModeToggle/index.js` wrapper, so no CSS
+   hiding rule is needed here. Docs / blog / community pages keep the
+   toggle. */
+
+/* =========================================================================
+   ========================================================================
+   DARK MODE : full token remap + component overrides
+   Triggered by [data-theme='dark'] on <html>.
+   ========================================================================
+   ========================================================================= */
+
+[data-theme='dark'] {
+  /* Surfaces : deepest two palette stops give a quiet, premium dark canvas. */
+  --fluss-canvas: #0A1745;
+  --fluss-paper:  #102856;
+
+  /* Inks (inverted) */
+  --fluss-ink-950: #F8FAFC;
+  --fluss-ink-700: #CBD5E1;
+  --fluss-ink-500: #94A3B8;
+  --fluss-ink-300: rgba(203, 213, 225, 0.22);
+  --fluss-ink-100: rgba(203, 213, 225, 0.10);
+
+  /* Blue tints recalibrated for legibility on dark, anchored on the new
+     navy/teal palette. */
+  --fluss-blue-50:  rgba(38, 109, 149, 0.14);
+  --fluss-blue-100: rgba(38, 109, 149, 0.22);
+  --fluss-blue-300: #7AAFCB;
+  --fluss-blue-500: #266D95;
+  --fluss-blue-600: #1C5078;
+  --fluss-blue-700: #7AAFCB;
+  --fluss-blue-800: #D6E4ED;
+
+  --fluss-link: #7AAFCB;
+  --fluss-link-hover: #D6E4ED;
+
+  /* Shadows on dark : softer, warmer */
+  --fluss-shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.4);
+  --fluss-shadow-md:   0 4px 16px rgba(0, 0, 0, 0.4);
+  --fluss-shadow-lg:   0 16px 48px rgba(0, 0, 0, 0.5);
+  --fluss-shadow-glow: 0 0 0 1px rgba(122, 175, 203, 0.35), 0 12px 40px rgba(38, 109, 149, 0.35);
+
+  /* Wire into Infima dark vars */
+  --ifm-background-color: #0A1745;
+  --ifm-background-surface-color: #102856;
+  --ifm-color-primary:          #7AAFCB;
+  --ifm-color-primary-dark:     #266D95;
+  --ifm-color-primary-darker:   #1C5078;
+  --ifm-color-primary-darkest:  #194670;
+  --ifm-color-primary-light:    #7AAFCB;
+  --ifm-color-primary-lighter:  #B1CEDF;
+  --ifm-color-primary-lightest: #D6E4ED;
+
+  --ifm-heading-color: #F8FAFC;
+  /* Body text bumped from #CBD5E1 (slate-300) to #F1F5F9 (slate-100) for
+     stronger readability in dark mode without going to pure white, which
+     causes halation on dark backgrounds in long-form reading. */
+  --ifm-font-color-base: #F1F5F9;
+
+  --ifm-navbar-background-color: rgba(10, 23, 69, 0.92);
+  --ifm-navbar-shadow: 0 1px 0 rgba(255, 255, 255, 0.06);
+  --ifm-navbar-link-color: #CBD5E1;
+
+  --ifm-toc-border-color: rgba(255, 255, 255, 0.08);
+  --ifm-color-emphasis-200: rgba(255, 255, 255, 0.08);
+  --ifm-color-emphasis-300: rgba(255, 255, 255, 0.12);
+  --ifm-color-emphasis-600: #94A3B8;
+  --ifm-color-emphasis-700: #CBD5E1;
+  --ifm-color-emphasis-800: #F8FAFC;
+
+  --ifm-menu-color-background-active: rgba(28, 80, 120, 0.14);
+  --ifm-menu-color-background-hover:  rgba(28, 80, 120, 0.1);
+
+  --docusaurus-highlighted-code-line-bg: rgba(28, 80, 120, 0.18);
+}
+
+/* ----- Backgrounds & body ----- */
+[data-theme='dark'] body,
+[data-theme='dark'] body.fluss-home {
+  background: var(--fluss-canvas);
+  background-image: none;
+  color: var(--fluss-ink-700);
+}
+
+[data-theme='dark'] main[class*='docMainContainer'],
+[data-theme='dark'] main[class*='blogPostPage'],
+[data-theme='dark'] main[class*='docItemContainer'] {
+  background:
+    radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.04) 1px, transparent 0) 0 0 / 32px 32px,
+    var(--fluss-canvas);
+}
+
+/* ----- Navbar ----- */
+[data-theme='dark'] .navbar {
+  border-bottom: 0;
+  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04), 0 2px 8px rgba(0, 0, 0, 0.3);
+}
+
+[data-theme='dark'] .navbar__link {
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .navbar__link:hover {
+  color: var(--fluss-blue-300);
+}
+
+[data-theme='dark'] .navbar__link--active {
+  color: var(--fluss-blue-300);
+}
+
+[data-theme='dark'] .navbar__title {
+  color: #F8FAFC;
+}
+
+/* DocSearch button (dark) */
+[data-theme='dark'] .DocSearch-Button {
+  background: rgba(255, 255, 255, 0.06);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .DocSearch-Button:hover {
+  background: rgba(255, 255, 255, 0.1);
+  border-color: rgba(122, 175, 203, 0.4);
+}
+
+[data-theme='dark'] .DocSearch-Button-Placeholder,
+[data-theme='dark'] .DocSearch-Search-Icon {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] .DocSearch-Button-Keys {
+  background: rgba(255, 255, 255, 0.05);
+  border-color: rgba(255, 255, 255, 0.1);
+  color: #CBD5E1;
+}
+
+/* GitHub icon (dark) */
+[data-theme='dark'] .header-github-link::before {
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23F8FAFC' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-. [...]
+}
+
+/* ----- Sidebar (dark) ----- */
+[data-theme='dark'] .theme-doc-sidebar-container {
+  background: var(--fluss-canvas);
+  border-right-color: rgba(255, 255, 255, 0.06);
+}
+
+[data-theme='dark'] .menu__link {
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .menu__link:hover {
+  background: rgba(122, 175, 203, 0.12);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] .menu__link--active {
+  background: linear-gradient(90deg, rgba(122, 175, 203, 0.18) 0%, transparent 100%);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] .menu__list .menu__list {
+  border-left-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .theme-doc-sidebar-menu > .menu__list-item:not(:first-child) {
+  border-top-color: rgba(255, 255, 255, 0.06);
+}
+
+/* ----- Breadcrumbs (dark) ----- */
+[data-theme='dark'] .breadcrumbs__link {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] a.breadcrumbs__link:hover {
+  color: #D6E4ED;
+  background: rgba(122, 175, 203, 0.12);
+}
+
+[data-theme='dark'] .breadcrumbs__link:not(a) {
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .breadcrumbs__item--active .breadcrumbs__link {
+  color: #F8FAFC;
+  background: rgba(255, 255, 255, 0.06);
+}
+
+/* ----- TOC card (dark) ----- */
+[data-theme='dark'] .theme-doc-toc-desktop,
+[data-theme='dark'] [class*='tableOfContents'] {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .table-of-contents__link {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] .table-of-contents__link:hover {
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] .table-of-contents__link--active {
+  color: #D6E4ED;
+  background: rgba(122, 175, 203, 0.12);
+}
+
+/* ----- Doc body (dark) ----- */
+[data-theme='dark'] .theme-doc-markdown,
+[data-theme='dark'] .markdown {
+  color: #F1F5F9;
+}
+
+[data-theme='dark'] .markdown h1,
+[data-theme='dark'] .markdown h2,
+[data-theme='dark'] .markdown h3,
+[data-theme='dark'] .markdown h4,
+[data-theme='dark'] .markdown h5,
+[data-theme='dark'] .markdown h6,
+[data-theme='dark'] .theme-doc-markdown h1,
+[data-theme='dark'] .theme-doc-markdown h2,
+[data-theme='dark'] .theme-doc-markdown h3 {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] .markdown b,
+[data-theme='dark'] .markdown strong {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] .theme-doc-markdown a:not(.button):not(.card):not(.hash-link),
+[data-theme='dark'] .markdown a:not(.button):not(.card):not(.hash-link),
+[data-theme='dark'] .mdx-page article a:not(.button):not(.card):not(.hash-link) {
+  color: #B1CEDF;
+  background-image: linear-gradient(0deg, #B1CEDF 0, #B1CEDF 100%);
+}
+
+[data-theme='dark'] .markdown a.hash-link {
+  color: rgba(255, 255, 255, 0.18);
+}
+
+[data-theme='dark'] .markdown a.hash-link:hover {
+  color: #B1CEDF;
+}
+
+/* ----- Tables (dark) ----- */
+[data-theme='dark'] .markdown table,
+[data-theme='dark'] .theme-doc-markdown table {
+  border-color: rgba(255, 255, 255, 0.08);
+  background: var(--fluss-paper);
+}
+
+[data-theme='dark'] .markdown thead,
+[data-theme='dark'] .theme-doc-markdown thead {
+  background: rgba(122, 175, 203, 0.1);
+}
+
+/* The nested .markdown rule (light section) sets `table thead tr` bg to
+   #f6f8fa, which has higher specificity than the dark `thead` rule above
+   and therefore bleeds into dark mode, producing a near-white header row.
+   Override the `tr` explicitly for dark mode. */
+[data-theme='dark'] .markdown table thead tr,
+[data-theme='dark'] .theme-doc-markdown table thead tr {
+  background: rgba(122, 175, 203, 0.1);
+}
+
+[data-theme='dark'] .markdown thead th,
+[data-theme='dark'] .theme-doc-markdown thead th,
+[data-theme='dark'] .markdown table thead th,
+[data-theme='dark'] .theme-doc-markdown table thead th {
+  color: #F8FAFC;
+  background: transparent;
+  border-color: rgba(255, 255, 255, 0.08);
+  font-weight: 600;
+}
+
+[data-theme='dark'] .markdown tbody tr,
+[data-theme='dark'] .theme-doc-markdown tbody tr {
+  background: transparent;
+}
+
+[data-theme='dark'] .markdown tbody tr:nth-child(even),
+[data-theme='dark'] .theme-doc-markdown tbody tr:nth-child(even) {
+  background: rgba(255, 255, 255, 0.03);
+}
+
+[data-theme='dark'] .markdown table tr td,
+[data-theme='dark'] .markdown table tr th {
+  border-color: rgba(255, 255, 255, 0.06);
+}
+
+[data-theme='dark'] .markdown p code,
+[data-theme='dark'] .markdown li code,
+[data-theme='dark'] .markdown td code,
+[data-theme='dark'] .theme-doc-markdown p code,
+[data-theme='dark'] .theme-doc-markdown li code,
+[data-theme='dark'] .theme-doc-markdown td code,
+[data-theme='dark'] code {
+  border-color: rgba(122, 175, 203, 0.25);
+  color: #D6E4ED;
+}
+
+/* ----- Blockquotes (dark) ----- */
+[data-theme='dark'] .markdown blockquote,
+[data-theme='dark'] .theme-doc-markdown blockquote {
+  background: rgba(122, 175, 203, 0.08);
+  border-left-color: #7AAFCB;
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .markdown blockquote::before,
+[data-theme='dark'] .theme-doc-markdown blockquote::before {
+  color: rgba(122, 175, 203, 0.4);
+}
+
+/* ----- Admonitions (dark) ----- */
+[data-theme='dark'] .theme-admonition,
+[data-theme='dark'] .alert,
+[data-theme='dark'] [class*='admonition_'] {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.1);
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .alert--secondary,
+[data-theme='dark'] .theme-admonition-note {
+  background: rgba(255, 255, 255, 0.04);
+  border-left-color: #94A3B8;
+}
+
+[data-theme='dark'] .alert--info,
+[data-theme='dark'] .theme-admonition-info {
+  background: rgba(28, 80, 120, 0.12);
+  border-left-color: #7AAFCB;
+}
+
+[data-theme='dark'] .alert--success,
+[data-theme='dark'] .theme-admonition-tip {
+  background: rgba(16, 185, 129, 0.12);
+  border-left-color: #34D399;
+}
+
+[data-theme='dark'] .alert--warning,
+[data-theme='dark'] .theme-admonition-warning,
+[data-theme='dark'] .theme-admonition-caution {
+  background: rgba(245, 158, 11, 0.12);
+  border-left-color: #FBBF24;
+}
+
+[data-theme='dark'] .alert--danger,
+[data-theme='dark'] .theme-admonition-danger {
+  background: rgba(239, 68, 68, 0.12);
+  border-left-color: #F87171;
+}
+
+/* ----- Code blocks (dark) ----- */
+[data-theme='dark'] div[class*='codeBlockContainer'] {
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] div[class*='codeBlockContent'] pre {
+  background: #102856;
+}
+
+[data-theme='dark'] div[class*='codeBlockTitle'] {
+  background: rgba(122, 175, 203, 0.08);
+  border-bottom: 1px solid rgba(122, 175, 203, 0.18);
+  color: #F1F5F9;
+  font-weight: 500;
+}
+
+/* The cyan-dot ::before is defined in the light section and inherits into
+   dark; bump its luminance so it stays visible on the darker title bg. */
+[data-theme='dark'] div[class*='codeBlockTitle']::before {
+  background: var(--fluss-cyan);
+  box-shadow: 0 0 0 2px rgba(122, 175, 203, 0.18);
+}
+
+[data-theme='dark'] button[class*='copyButton'] {
+  background: rgba(255, 255, 255, 0.06);
+  border-color: rgba(255, 255, 255, 0.1);
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] button[class*='copyButton']:hover {
+  background: rgba(122, 175, 203, 0.18);
+  border-color: rgba(122, 175, 203, 0.45);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] .theme-code-block-highlighted-line,
+[data-theme='dark'] .docusaurus-highlight-code-line {
+  background: rgba(122, 175, 203, 0.14);
+  border-left-color: #7AAFCB;
+}
+
+/* ----- Tabs (dark) ----- */
+[data-theme='dark'] .tabs {
+  border-bottom-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .tabs__item {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] .tabs__item:hover {
+  background: rgba(122, 175, 203, 0.12);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] .tabs__item--active {
+  color: #D6E4ED;
+  border-bottom-color: #7AAFCB;
+}
+
+/* ----- Pagination (dark) ----- */
+[data-theme='dark'] .pagination-nav__link {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .pagination-nav__link:hover {
+  border-color: rgba(122, 175, 203, 0.45);
+}
+
+[data-theme='dark'] .pagination-nav__sublabel {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] .pagination-nav__label {
+  color: #F8FAFC;
+}
+
+/* ----- Doc footer (dark) ----- */
+[data-theme='dark'] .theme-doc-footer {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] .theme-edit-this-page {
+  background: rgba(122, 175, 203, 0.12);
+  color: #B1CEDF !important;
+}
+
+[data-theme='dark'] .theme-edit-this-page:hover {
+  background: rgba(122, 175, 203, 0.2);
+  border-color: rgba(122, 175, 203, 0.4);
+}
+
+[data-theme='dark'] .theme-last-updated {
+  color: #94A3B8;
+}
+
+/* ----- Blog list / blog post (dark) ----- */
+[data-theme='dark'] article[itemtype='https://schema.org/BlogPosting'],
+[data-theme='dark'] .blog-list-page article {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] article[itemtype='https://schema.org/BlogPosting'] header h2 a,
+[data-theme='dark'] .blog-list-page article header h2 a {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] article[itemtype='https://schema.org/BlogPosting'] header h2 a:hover,
+[data-theme='dark'] .blog-list-page article header h2 a:hover {
+  color: #B1CEDF;
+}
+
+[data-theme='dark'] main[class*='blogListPage'] article:first-of-type,
+[data-theme='dark'] .blog-list-page > article:first-of-type {
+  background:
+    radial-gradient(800px 300px at 100% 0%, rgba(38, 109, 149, 0.1), transparent 60%),
+    radial-gradient(600px 300px at 0% 100%, rgba(28, 80, 120, 0.12), transparent 60%),
+    var(--fluss-paper);
+  border-color: rgba(122, 175, 203, 0.3);
+}
+
+[data-theme='dark'] a.tag,
+[data-theme='dark'] a[class*='tag_'] {
+  background: rgba(122, 175, 203, 0.14);
+  color: #D6E4ED;
+  border-color: transparent;
+}
+
+[data-theme='dark'] a.tag:hover,
+[data-theme='dark'] a[class*='tag_']:hover {
+  background: rgba(122, 175, 203, 0.22);
+  border-color: rgba(122, 175, 203, 0.4);
+  color: #D6E4ED;
+}
+
+/* ----- Doc cards (dark) ----- */
+[data-theme='dark'] article.card,
+[data-theme='dark'] .theme-doc-card {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] article.card:hover,
+[data-theme='dark'] .theme-doc-card:hover {
+  border-color: rgba(122, 175, 203, 0.45);
+}
+
+/* ----- Version banner (dark) ----- */
+[data-theme='dark'] div[class*='docVersionBanner'] {
+  background: rgba(122, 175, 203, 0.12);
+  border-color: rgba(122, 175, 203, 0.4);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] div[class*='docVersionBanner'] b,
+[data-theme='dark'] div[class*='docVersionBanner'] a {
+  color: #D6E4ED;
+}
+
+/* ----- Dropdown menus (dark) ----- */
+[data-theme='dark'] .dropdown__menu {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.1);
+}
+
+[data-theme='dark'] .dropdown__link {
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .dropdown__link:hover,
+[data-theme='dark'] .dropdown__link--active {
+  background: rgba(122, 175, 203, 0.14);
+  color: #D6E4ED;
+}
+
+/* ----- KBD (dark) ----- */
+[data-theme='dark'] kbd {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.16);
+  color: #D6E4ED;
+}
+
+/* ----- Color toggle (dark) ----- */
+[data-theme='dark'] button[class*='colorModeToggle'],
+[data-theme='dark'] button.clean-btn[class*='toggle'] {
+  background: rgba(255, 255, 255, 0.08);
+  color: #D6E4ED;
+}
+
+[data-theme='dark'] button[class*='colorModeToggle']:hover,
+[data-theme='dark'] button.clean-btn[class*='toggle']:hover {
+  background: rgba(122, 175, 203, 0.18);
+  border-color: rgba(122, 175, 203, 0.4);
+  color: #D6E4ED;
+}
+
+/* ----- Mobile drawer (dark) ----- */
+[data-theme='dark'] .navbar-sidebar {
+  background: var(--fluss-canvas);
+}
+
+[data-theme='dark'] .navbar-sidebar__brand {
+  border-bottom-color: rgba(255, 255, 255, 0.06);
+  background: var(--fluss-canvas);
+}
+
+/* In dark mode the drawer is dark again, so undo the light-mode inversion
+   on the brand wordmark and restore the light-on-dark GitHub icon. */
+[data-theme='dark'] .navbar-sidebar__brand .navbar__logo img {
+  filter: none;
+}
+
+[data-theme='dark'] .navbar-sidebar__brand .navbar__title,
+[data-theme='dark'] .navbar-sidebar__brand .navbar__brand {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] .navbar-sidebar .navbar__link,
+[data-theme='dark'] .navbar-sidebar .menu__link {
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] .navbar-sidebar .navbar__link:hover,
+[data-theme='dark'] .navbar-sidebar .menu__link:hover {
+  color: var(--fluss-blue-300);
+}
+
+[data-theme='dark'] .navbar-sidebar .header-github-link::before {
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23F8FAFC' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-. [...]
+}
+
+/* ----- Footer logo invert if needed (dark) ----- */
+[data-theme='dark'] .footer__logo {
+  filter: brightness(1.1);
+}
+
+/* ----- Scrollbars (dark) ----- */
+[data-theme='dark'] .theme-doc-markdown pre::-webkit-scrollbar-thumb,
+[data-theme='dark'] .theme-doc-sidebar-container::-webkit-scrollbar-thumb,
+[data-theme='dark'] .table-of-contents::-webkit-scrollbar-thumb {
+  background: rgba(255, 255, 255, 0.16);
+}
+
+[data-theme='dark'] .theme-doc-markdown pre::-webkit-scrollbar-thumb:hover,
+[data-theme='dark'] .theme-doc-sidebar-container::-webkit-scrollbar-thumb:hover,
+[data-theme='dark'] .table-of-contents::-webkit-scrollbar-thumb:hover {
+  background: rgba(255, 255, 255, 0.3);
+}
+
+/* ----- Selection (dark) ----- */
+[data-theme='dark'] ::selection {
+  background: rgba(122, 175, 203, 0.32);
+  color: #F8FAFC;
+}
+
+/* =========================================================================
+   ========================================================================
+   RESPONSIVE : small viewport refinements (any device)
+   ========================================================================
+   ========================================================================= */
+
+@media (max-width: 996px) {
+  /* Sidebar / TOC card padding tightened */
+  .theme-doc-toc-desktop,
+  [class*='tableOfContents'] {
+    padding: var(--fluss-space-4);
+  }
+
+  /* Doc/blog content has full width on mobile */
+  main[class*='docItemContainer'] article,
+  main[class*='blogPostPage'] article {
+    max-width: 100%;
+  }
+}
+
+@media (max-width: 768px) {
+  /* Tighten section vertical padding on small screens */
+  .theme-doc-markdown,
+  .markdown {
+    font-size: 1rem;
+    line-height: 1.7;
+  }
+
+  /* Heading dot indicator hidden on mobile (no margin-room) */
+  .markdown h2::before,
+  .theme-doc-markdown h2::before {
+    display: none;
+  }
+
+  /* Pagination cards stack neatly */
+  .pagination-nav {
+    grid-template-columns: 1fr;
+    gap: var(--fluss-space-3);
+  }
+
+  /* Let long labels wrap & shrink instead of overflowing the card on
+     narrow viewports (borzoni feedback, PR #3226). */
+  .pagination-nav__link {
+    min-width: 0;
+    padding: var(--fluss-space-3) var(--fluss-space-4);
+  }
+
+  .pagination-nav__label {
+    font-size: 0.9375rem;
+    line-height: 1.35;
+    overflow-wrap: anywhere;
+  }
+
+  /* Footer columns: tighter gap */
+  .footer .col {
+    margin-bottom: var(--fluss-space-6);
+  }
+
+  /* Doc footer wraps */
+  .theme-doc-footer {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+
+  /* Smaller anchor offset */
+  .theme-doc-markdown h1,
+  .theme-doc-markdown h2,
+  .theme-doc-markdown h3,
+  .theme-doc-markdown h4 {
+    scroll-margin-top: 72px;
+  }
+}
+
+@media (max-width: 600px) {
+  /* Smaller hero internal title in case window is very narrow */
+  .navbar__title {
+    font-size: 0.95rem;
+  }
+
+  /* Hide kbd from being too cramped */
+  kbd {
+    font-size: 0.75em;
+  }
+
+  /* Tag pill smaller */
+  a.tag,
+  a[class*='tag_'] {
+    height: 22px;
+    font-size: 0.75rem;
+    padding: 0 8px;
+  }
+
+  /* Compare/markdown tables become horizontally scrollable comfortably */
+  .markdown table,
+  .theme-doc-markdown table {
+    display: block;
+    overflow-x: auto;
+    white-space: nowrap;
+  }
+}
+
+/* Very small screens : squeeze the navbar items so they don't overflow */
+@media (max-width: 480px) {
+  .navbar__items--right .navbar-ask-ai {
+    padding: 4px 8px;
+    font-size: 0.75rem;
+  }
+}
+
+/* =========================================================================
+   ========================================================================
+   HOMEPAGE : dark-mode adjustments for sections that hardcode blue-950
+   ========================================================================
+   ========================================================================= */
+
+/* In dark mode, the canvas itself is --fluss-blue-950, so sections that
+   intentionally use that color blend in. Lift them slightly so they read
+   as distinct surfaces. CSS module class names are matched by suffix. */
+[data-theme='dark'] section[class*='sectionDark'] {
+  background: linear-gradient(180deg, #102856 0%, #0A1745 100%);
+  border-top: 1px solid rgba(255, 255, 255, 0.05);
+  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+}
+
+/* Comparison table on dark mode: paper bg, refined column highlight */
+[data-theme='dark'] div[class*='compareWrap'] {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+[data-theme='dark'] table[class*='compareTable'] thead th {
+  background: rgba(255, 255, 255, 0.04);
+  color: #F8FAFC;
+  border-bottom-color: rgba(255, 255, 255, 0.1);
+}
+
+[data-theme='dark'] table[class*='compareTable'] thead th[class*='colHighlight'] {
+  background: linear-gradient(180deg, var(--fluss-blue-700) 0%, var(--fluss-blue-600) 100%);
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody td {
+  border-top-color: rgba(255, 255, 255, 0.05);
+  color: #CBD5E1;
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody td:first-child {
+  background: rgba(255, 255, 255, 0.03);
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody td[class*='colHighlight'] {
+  background: rgba(122, 175, 203, 0.1);
+  color: #D6E4ED;
+}
+
+/* Code card in dark mode : slightly lifted so it stands above canvas */
+[data-theme='dark'] div[class*='codeCard'] {
+  background: linear-gradient(180deg, #102856 0%, #0A1745 100%);
+  border-color: rgba(255, 255, 255, 0.08);
+}
+
+/* Section eyebrows / titles / leads in dark mode */
+[data-theme='dark'] span[class*='eyebrow'] {
+  color: #B1CEDF;
+}
+
+[data-theme='dark'] h2[class*='sectionTitle'] {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] p[class*='sectionLead'] {
+  color: #CBD5E1;
+}
+
+/* Dark-mode hover/border on hover-lift cards */
+[data-theme='dark'] div[class*='card']:hover,
+[data-theme='dark'] article[class*='card']:hover {
+  border-color: rgba(122, 175, 203, 0.45);
+}
+
+[data-theme='dark'] div[class*='cardIcon'],
+[data-theme='dark'] div[class*='iconWrap'] {
+  background: rgba(122, 175, 203, 0.14);
+  color: #D6E4ED;
+}
+
+/* Version-badge chips on Flink section */
+[data-theme='dark'] span[class*='badge'] {
+  background: rgba(122, 175, 203, 0.18);
+  color: #D6E4ED;
+}
+
+/* Multiple Systems Tax section in dark mode */
+[data-theme='dark'] section[class*='taxSection'] {
+  background:
+    radial-gradient(900px 500px at 50% 0%, rgba(122, 175, 203, 0.14), transparent 60%),
+    var(--fluss-canvas);
+  border-color: rgba(255, 255, 255, 0.06);
+}
+
+[data-theme='dark'] div[class*='taxStackItem'] {
+  background: var(--fluss-paper);
+  border-color: rgba(255, 255, 255, 0.08);
+  border-left-color: #FB7185;
+}
+
+[data-theme='dark'] div[class*='taxStackTitle'] {
+  color: #F8FAFC;
+}
+
+[data-theme='dark'] div[class*='taxStackSub'] {
+  color: #94A3B8;
+}
+
+[data-theme='dark'] span[class*='taxStackIndex'] {
+  color: rgba(255, 255, 255, 0.22);
+}
+
+[data-theme='dark'] span[class*='taxLabelBefore'] {
+  background: rgba(244, 63, 94, 0.14);
+  color: #FDA4AF;
+  border-color: rgba(244, 63, 94, 0.3);
+}
+
+[data-theme='dark'] p[class*='taxFootnote'] {
+}
+
+/* =========================================================================
+   Light-mode-only enhancements that need to be neutralised in dark mode
+   ========================================================================= */
+
+/* The card resting white-tint gradient is meant for light mode. In dark
+   mode it would lighten the card top awkwardly — strip it. */
+[data-theme='dark'] section[class*='introduce'] div[class*='card'],
+[data-theme='dark'] section[class*='features'] article[class*='card'] {
+  background: var(--fluss-paper);
+}
+
+/* Section background washes use white-leaning gradients in light mode.
+   In dark mode the canvas already provides the deep blue, but we want
+   subtle blue-tinted radial highlights on plain sections instead. */
+[data-theme='dark'] section[class*='introduce'],
+[data-theme='dark'] section[class*='features'] {
+  background:
+    radial-gradient(1100px 480px at 100% 0%, rgba(122, 175, 203, 0.1), transparent 60%),
+    radial-gradient(900px 460px at 0% 100%, rgba(122, 175, 203, 0.08), transparent 60%),
+    var(--fluss-canvas);
+  border-top-color: rgba(255, 255, 255, 0.06);
+}
+
+/* Generic .section radial wash on light mode — recolour for dark */
+[data-theme='dark'] section[class*='section'][class*='index_section'],
+[data-theme='dark'] section[class*='index_section'] {
+  background:
+    radial-gradient(1100px 480px at 80% 0%, rgba(122, 175, 203, 0.08), transparent 60%),
+    radial-gradient(900px 460px at 0% 100%, rgba(122, 175, 203, 0.08), transparent 60%),
+    var(--fluss-canvas);
+}
+
+/* Eyebrow chip override for dark mode — the auto-mapped background is
+   already a translucent blue, but the border needs darkening too. */
+[data-theme='dark'] span[class*='eyebrow'] {
+  border-color: rgba(122, 175, 203, 0.28);
+}
+
+/* Card icon gradient is meant for light. Re-tone for dark. */
+[data-theme='dark'] div[class*='cardIcon'],
+[data-theme='dark'] div[class*='iconWrap'] {
+  background: linear-gradient(135deg, rgba(122, 175, 203, 0.18) 0%, rgba(38, 109, 149, 0.18) 100%);
+  box-shadow: inset 0 0 0 1px rgba(122, 175, 203, 0.22);
+  color: #D6E4ED;
+}
+
+/* Comparison-table accent bar visible against dark surface */
+[data-theme='dark'] div[class*='compareWrap']::before {
+  background: linear-gradient(90deg, var(--fluss-blue-500), var(--fluss-cyan));
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody tr:hover td {
+  background-color: rgba(122, 175, 203, 0.08);
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody tr:hover td:first-child {
+  background-color: rgba(122, 175, 203, 0.12);
+}
+
+[data-theme='dark'] table[class*='compareTable'] tbody tr:hover td[class*='colHighlight'] {
+  background-color: rgba(122, 175, 203, 0.2);
+}
+
+html[data-theme='light'] .navbar__search {
+    opacity: 1;
+}
+
+/* =========================================================================
+   Mermaid diagrams
+   ========================================================================= */
+
+/* Mermaid sizes node/edge label boxes assuming its own line-height of 1.5,
+   but the site's paragraph line-height (1.8) also applies inside the SVG's
+   HTML labels — multi-line labels overflow and clip at the bottom. Pin the
+   label line-height back to what mermaid measured with. */
+.docusaurus-mermaid-container foreignObject p {
+  line-height: 1.5;
+  margin: 0;
+}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
new file mode 100644
index 0000000..f099708
--- /dev/null
+++ b/src/pages/index.tsx
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import {Redirect} from '@docusaurus/router';
+
+export default function Home(): React.JSX.Element {
+  return <Redirect to="/blog" />;
+}
diff --git a/src/theme/BlogListPage/index.tsx b/src/theme/BlogListPage/index.tsx
new file mode 100644
index 0000000..b0fce63
--- /dev/null
+++ b/src/theme/BlogListPage/index.tsx
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React, {useMemo, useState} from 'react';
+import clsx from 'clsx';
+import Link from '@docusaurus/Link';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import useIsBrowser from '@docusaurus/useIsBrowser';
+import {useHistory, useLocation} from '@docusaurus/router';
+import Layout from '@theme/Layout';
+import SearchMetadata from '@theme/SearchMetadata';
+import BlogListPageStructuredData from '@theme/BlogListPage/StructuredData';
+import type {Props} from '@theme/BlogListPage';
+
+import styles from './styles.module.css';
+
+type Post = Props['items'][number]['content'];
+type Tag = Post['metadata']['tags'][number];
+const PAGE_SIZE = 9;
+const TAG_ORDER = ['Announcement', 'Case Study', 'Engineering', 'Guides'];
+const dateFormat = new Intl.DateTimeFormat('en', {
+  month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC',
+});
+
+function PostImage({post, featured = false}: {post: Post; featured?: boolean}): React.JSX.Element {
+  const [failed, setFailed] = useState(false);
+  const cover = post.assets.image ?? post.metadata.frontMatter.image;
+  const imageUrl = useBaseUrl(cover ?? '/img/logo/svg/white_color_logo.svg');
+  const logoUrl = useBaseUrl('/img/logo/svg/white_color_logo.svg');
+  const hasCover = Boolean(cover) && !failed;
+
+  return (
+    <Link to={post.metadata.permalink} className={clsx(styles.imageLink, !hasCover && styles.fallback)}
+      tabIndex={-1} aria-hidden="true">
+      <img src={hasCover ? imageUrl : logoUrl} alt="" loading={featured ? 'eager' : 'lazy'}
+        decoding="async" onError={hasCover ? () => setFailed(true) : undefined} />
+      {!hasCover && <span className={styles.fallbackLabel}>THE FLUSS BLOG</span>}
+    </Link>
+  );
+}
+
+function PostMeta({post}: {post: Post}): React.JSX.Element {
+  const {authors, date} = post.metadata;
+  return (
+    <div className={styles.meta}>
+      <span>
+        {authors.map((author, index) => (
+          <React.Fragment key={index}>
+            {index > 0 && ', '}
+            {author.url
+              ? <Link href={author.url}>{author.name}</Link>
+              : author.name}
+          </React.Fragment>
+        ))}
+      </span>
+      {authors.length > 0 && <span aria-hidden="true">·</span>}
+      <time dateTime={date}>{dateFormat.format(new Date(date))}</time>
+    </div>
+  );
+}
+
+function PostTags({tags, onSelect}: {tags: readonly Tag[]; onSelect: (label: string) => void}): React.JSX.Element {
+  return (
+    <div className={styles.tags}>
+      {tags.map((tag) => (
+        <button type="button" key={tag.permalink} onClick={() => onSelect(tag.label)}
+          aria-label={`Filter by ${tag.label}`} className={styles.tag}>
+          {tag.label}
+        </button>
+      ))}
+      {tags.length === 0 && <span className={styles.tag}>Article</span>}
+    </div>
+  );
+}
+
+function PostCard({post, featured = false, onSelect}: {
+  post: Post; featured?: boolean; onSelect: (label: string) => void;
+}): React.JSX.Element {
+  const {title, permalink, description, tags} = post.metadata;
+  const Heading = featured ? 'h2' : 'h3';
+  return (
+    <article className={clsx(styles.card, featured && styles.featured)}>
+      <PostImage post={post} featured={featured} />
+      <div className={styles.cardBody}>
+        <Heading className={styles.cardTitle}><Link to={permalink}>{title}</Link></Heading>
+        <PostMeta post={post} />
+        <p className={styles.description}>{description}</p>
+        <PostTags tags={tags} onSelect={onSelect} />
+      </div>
+    </article>
+  );
+}
+
+function PostGrid({posts, selectedTag, onSelect}: {
+  posts: Post[]; selectedTag: string; onSelect: (label: string) => void;
+}): React.JSX.Element {
+  const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
+  return (
+    <section aria-label={selectedTag ? `${selectedTag} articles` : 'All articles'} className={styles.archive}>
+      <div className={styles.archiveHeader}>
+        <p role="status">{posts.length} {posts.length === 1 ? 'article' : 'articles'}</p>
+      </div>
+      <div className={styles.grid}>
+        {posts.slice(0, visibleCount).map((post) => (
+          <PostCard key={post.metadata.permalink} post={post} onSelect={onSelect} />
+        ))}
+      </div>
+      {posts.length === 0 && <p className={styles.empty}>No articles with this tag. <button type="button" onClick={() => onSelect('')}>View all articles</button></p>}
+      {visibleCount < posts.length && (
+        <div className={styles.loadMore}>
+          <button type="button" onClick={() => setVisibleCount((count) => count + PAGE_SIZE)}>
+            Load more articles <span aria-hidden="true">↓</span>
+          </button>
+          <span>Showing {Math.min(visibleCount, posts.length)} of {posts.length}</span>
+        </div>
+      )}
+    </section>
+  );
+}
+
+export default function BlogListPage(props: Props): React.JSX.Element {
+  const history = useHistory();
+  const location = useLocation();
+  const isBrowser = useIsBrowser();
+  const selectedTag = isBrowser ? new URLSearchParams(location.search).get('tag') ?? '' : '';
+  const posts = useMemo(() => props.items.map(({content}) => content)
+    .sort((a, b) => Date.parse(b.metadata.date) - Date.parse(a.metadata.date)), [props.items]);
+  const tags = useMemo(() => {
+    const byLabel = new Map<string, {label: string; count: number}>();
+    posts.forEach((post) => post.metadata.tags.forEach(({label}) => {
+      const tag = byLabel.get(label) ?? {label, count: 0};
+      tag.count += 1;
+      byLabel.set(label, tag);
+    }));
+    return [...byLabel.values()].sort((a, b) => {
+      const aIndex = TAG_ORDER.indexOf(a.label);
+      const bIndex = TAG_ORDER.indexOf(b.label);
+      return (aIndex < 0 ? TAG_ORDER.length : aIndex)
+        - (bIndex < 0 ? TAG_ORDER.length : bIndex)
+        || a.label.localeCompare(b.label);
+    });
+  }, [posts]);
+
+  function selectTag(label: string): void {
+    const params = new URLSearchParams(location.search);
+    if (label) {
+      params.set('tag', label);
+    } else {
+      params.delete('tag');
+    }
+    history.push({...location, search: params.toString() ? `?${params}` : ''});
+  }
+
+  const filteredPosts = selectedTag
+    ? posts.filter((post) => post.metadata.tags.some(({label}) => label === selectedTag))
+    : posts.slice(1);
+
+  return (
+    <Layout title="Blog" description={props.metadata.blogDescription}>
+      <SearchMetadata tag="blog_posts_list" />
+      <BlogListPageStructuredData {...props} />
+      <main className={styles.page}>
+        <header className={styles.pageHeader}>
+          <div>
+            <h1>The Fluss Blog<span>.</span></h1>
+            <p>Engineering, ideas, and stories from the streaming frontier.</p>
+          </div>
+        </header>
+
+        {posts[0] && <PostCard post={posts[0]} featured onSelect={selectTag} />}
+
+        <div className={styles.filters} role="group" aria-label="Filter articles by tag">
+          <button type="button" className={clsx(styles.filter, !selectedTag && styles.activeFilter)}
+            aria-pressed={!selectedTag} onClick={() => selectTag('')}>
+            All posts <span>{posts.length}</span>
+          </button>
+          {tags.map(({label, count}) => (
+            <button type="button" key={label}
+              className={clsx(styles.filter, selectedTag === label && styles.activeFilter)}
+              aria-pressed={selectedTag === label} onClick={() => selectTag(label)}>
+              {label} <span>{count}</span>
+            </button>
+          ))}
+        </div>
+
+        <PostGrid key={selectedTag} posts={filteredPosts} selectedTag={selectedTag} onSelect={selectTag} />
+      </main>
+    </Layout>
+  );
+}
diff --git a/src/theme/BlogListPage/styles.module.css b/src/theme/BlogListPage/styles.module.css
new file mode 100644
index 0000000..362d70f
--- /dev/null
+++ b/src/theme/BlogListPage/styles.module.css
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.page {
+  --blog-text: #102856;
+  --blog-muted: #53667d;
+  --blog-border: #dce5ed;
+  --blog-surface: #fff;
+  --blog-soft: #f1f5f8;
+  --blog-accent: #266d95;
+  --blog-banner-ratio: 1200 / 510;
+  width: min(1280px, calc(100% - 64px));
+  margin: 0 auto;
+  padding: 32px 0 80px;
+  color: var(--blog-text);
+  font-family: 'Geist Variable', sans-serif;
+}
+
+.page h1, .page h2, .page h3 {
+  color: var(--blog-text);
+  font-family: inherit;
+}
+
+.pageHeader {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 24px;
+  margin-bottom: 24px;
+}
+
+.pageHeader h1 {
+  margin: 0 0 8px;
+  font-size: clamp(2rem, 3.4vw, 3rem);
+  font-weight: 650;
+  letter-spacing: -0.045em;
+  line-height: 1.1;
+}
+
+.pageHeader h1 span { color: var(--blog-accent); }
+.pageHeader p { margin: 0; color: var(--blog-muted); font-size: 15px; }
+
+.card {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  border: 1px solid var(--blog-border);
+  border-radius: 12px;
+  background: var(--blog-surface);
+  transition: border-color 180ms ease, box-shadow 180ms ease;
+}
+
+.card:hover {
+  border-color: var(--blog-accent);
+  box-shadow: 0 8px 24px rgb(16 40 86 / 7%);
+}
+
+.imageLink {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  aspect-ratio: var(--blog-banner-ratio);
+  overflow: hidden;
+  background: var(--blog-soft);
+}
+
+.imageLink img { display: block; width: 100%; height: 100%; object-fit: contain; }
+.cardBody { display: flex; flex: 1; flex-direction: column; align-items: flex-start; padding: 22px; }
+.cardTitle { margin: 0 0 12px; font-size: 21px; font-weight: 600; line-height: 1.3; letter-spacing: -0.025em; }
+.cardTitle a { color: inherit; text-decoration: none; }
+.cardTitle a:hover { color: var(--blog-accent); }
+
+.meta {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: baseline;
+  gap: 4px 8px;
+  margin-bottom: 13px;
+  color: var(--blog-muted);
+  font-size: 12px;
+  line-height: 1.6;
+}
+
+.meta a { color: inherit; }
+.meta time { white-space: nowrap; }
+.description {
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 3;
+  overflow: hidden;
+  margin: 0 0 18px;
+  color: var(--blog-muted);
+  font-size: 14px;
+  line-height: 1.6;
+}
+
+.tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: auto; }
+.tag {
+  border: 0;
+  border-radius: 6px;
+  padding: 6px 10px;
+  background: var(--blog-soft);
+  color: var(--blog-muted);
+  font: inherit;
+  font-size: 13px;
+  line-height: 1.4;
+}
+
+button.tag { cursor: pointer; }
+button.tag:hover { color: var(--blog-text); background: var(--blog-border); }
+
+.featured { display: grid; grid-template-columns: 1.05fr 1fr; margin-bottom: 32px; }
+.featured .imageLink { align-self: center; }
+.featured .cardBody { justify-content: center; padding: 24px 32px; }
+.featured .cardTitle { font-size: clamp(1.6rem, 2.1vw, 1.875rem); line-height: 1.2; letter-spacing: -0.035em; }
+.featured .description { -webkit-line-clamp: 2; font-size: 15px; }
+.featured .tags { margin-top: 0; }
+
+.fallback {
+  position: relative;
+  flex-direction: column;
+  gap: 22px;
+  background-color: #102856;
+  background-image: radial-gradient(circle at 80% 20%, #266d9566, transparent 65%),
+    repeating-linear-gradient(125deg, transparent 0 50px, #ffffff0a 50px 51px);
+}
+.fallback img { width: 44%; height: auto; max-height: 110px; object-fit: contain; }
+.fallbackLabel { color: #b3cee0; font-size: 10px; letter-spacing: 0.24em; }
+
+.filters { display: flex; flex-wrap: wrap; gap: 8px; padding-bottom: 26px; border-bottom: 1px solid var(--blog-border); }
+.filter {
+  display: inline-flex;
+  align-items: center;
+  gap: 10px;
+  min-height: 44px;
+  padding: 10px 14px;
+  border: 1px solid var(--blog-border);
+  border-radius: 6px;
+  background: var(--blog-surface);
+  color: var(--blog-muted);
+  cursor: pointer;
+  font: inherit;
+  font-size: 14px;
+  line-height: 1.4;
+  transition: background 150ms ease, color 150ms ease;
+}
+.filter span { font-size: 12px; opacity: 0.75; }
+.filter:hover { color: var(--blog-text); border-color: var(--blog-accent); }
+.activeFilter, .activeFilter:hover { color: #fff; border-color: #102856; background: #102856; }
+.archive { padding-top: 26px; }
+.archiveHeader { display: flex; justify-content: flex-end; margin-bottom: 16px; }
+.archiveHeader p { margin: 0; color: var(--blog-muted); font-size: 12px; white-space: nowrap; }
+.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; }
+.loadMore { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-top: 36px; }
+.loadMore button, .empty button { border: 1px solid var(--blog-border); border-radius: 6px; background: var(--blog-surface); color: var(--blog-text); padding: 11px 20px; font: inherit; font-size: 13px; cursor: pointer; }
+.loadMore button:hover, .empty button:hover { background: var(--blog-soft); border-color: var(--blog-accent); }
+.loadMore button span { margin-left: 18px; }
+.loadMore > span { color: var(--blog-muted); font-size: 12px; }
+.empty { padding: 32px 0; color: var(--blog-muted); }
+.page :is(a, button):focus-visible { outline: 2px solid var(--blog-accent); outline-offset: 4px; }
+
+:global([data-theme='dark']) .page {
+  --blog-text: #e0eaf4;
+  --blog-muted: #a7b8cd;
+  --blog-border: #2b3c54;
+  --blog-surface: #102139;
+  --blog-soft: #1b3049;
+  --blog-accent: #7aafcb;
+}
+:global([data-theme='dark']) .activeFilter { background: #d6e4ed; color: #102856; border-color: #d6e4ed; }
+
+@media (max-width: 996px) {
+  .page { width: calc(100% - 40px); padding-top: 32px; }
+  .featured .cardBody { padding: 24px; }
+  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
+}
+
+@media (max-width: 800px) {
+  .featured { grid-template-columns: 1fr; }
+}
+
+@media (max-width: 640px) {
+  .page { width: calc(100% - 32px); padding-top: 26px; }
+  .pageHeader { align-items: flex-start; gap: 16px; margin-bottom: 24px; }
+  .pageHeader h1 { font-size: 30px; }
+  .pageHeader p { font-size: 13px; }
+  .featured { margin-bottom: 24px; }
+  .featured .cardBody { padding: 22px; }
+  .featured .cardTitle { font-size: 25px; }
+  .filters { flex-wrap: nowrap; overflow-x: auto; padding: 2px 2px 18px; }
+  .filter { flex-shrink: 0; }
+  .archive { padding-top: 20px; }
+  .grid { grid-template-columns: 1fr; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+  .card, .filter { transition: none; }
+}
diff --git a/src/theme/BlogPostItem/Header/Title/index.tsx b/src/theme/BlogPostItem/Header/Title/index.tsx
deleted file mode 100644
index a1f55b0..0000000
--- a/src/theme/BlogPostItem/Header/Title/index.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react';
-import clsx from 'clsx';
-import Link from '@docusaurus/Link';
-import {useBlogPost} from '@docusaurus/plugin-content-blog/client';
-import styles from './styles.module.css';
-
-type Props = {
-  className?: string;
-};
-
-export default function BlogPostItemHeaderTitle({className}: Props): JSX.Element {
-  const {metadata, isBlogPostPage} = useBlogPost();
-  const {permalink, title, frontMatter} = metadata;
-  const subtitle = (frontMatter as {subtitle?: string}).subtitle;
-  const TitleHeading = isBlogPostPage ? 'h1' : 'h2';
-
-  return (
-    <>
-      <TitleHeading className={clsx(styles.title, className)} itemProp="headline">
-        {isBlogPostPage ? title : <Link to={permalink} itemProp="url">{title}</Link>}
-      </TitleHeading>
-      {subtitle && (
-        <p className={clsx(styles.subtitle, 'blog-post-subtitle')}>{subtitle}</p>
-      )}
-    </>
-  );
-}
diff --git a/src/theme/BlogPostItem/Header/Title/styles.module.css b/src/theme/BlogPostItem/Header/Title/styles.module.css
deleted file mode 100644
index b4c9534..0000000
--- a/src/theme/BlogPostItem/Header/Title/styles.module.css
+++ /dev/null
@@ -1,22 +0,0 @@
-.title {
-  font-size: 3rem;
-}
-
-.subtitle {
-  font-size: 1.5rem;
-  font-weight: 400;
-  color: var(--ifm-color-emphasis-700);
-  margin-top: -0.25rem;
-  margin-bottom: 1rem;
-  line-height: 1.3;
-}
-
-@media (max-width: 576px) {
-  .title {
-    font-size: 2rem;
-  }
-
-  .subtitle {
-    font-size: 1.15rem;
-  }
-}
diff --git a/src/utils/blogPosts.ts b/src/utils/blogPosts.ts
new file mode 100644
index 0000000..3056db5
--- /dev/null
+++ b/src/utils/blogPosts.ts
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {ProcessBlogPostsFn} from '@docusaurus/plugin-content-blog';
+import {createExcerpt} from '@docusaurus/utils';
+
+// Older posts often put a banner at the start of the body without setting
+// front matter. Let Docusaurus bundle that image just like an explicit cover.
+// Only use a leading image, so diagrams inside an article do not become covers.
+export const prepareBlogPosts: ProcessBlogPostsFn = async ({blogPosts}) =>
+  blogPosts.map((post) => {
+    const body = post.content.trimStart();
+    const introduction = body.replace(/^(?:#{1,6}[ \t]+[^\n]*\r?\n\s*)+/, '');
+    const banner = introduction.match(/^!\[[^\]]*\]\(([^\s)]+)\)/)?.[1];
+    const cover = post.metadata.frontMatter.image ?? banner;
+    // Docusaurus only bundles image assets beginning with "./". This also
+    // makes parent-relative covers ("../assets/...") work for release posts.
+    const image = cover && !/^(?:[a-z][a-z\d+.-]*:|\/|\.\/)/i.test(cover)
+      ? `./${cover}`
+      : cover;
+
+    // Docusaurus can use an image's alt text (often just "Banner") as the
+    // default excerpt. Skip standalone images and headings for card summaries.
+    const prose = body
+      .replace(/<!--[\s\S]*?-->/g, '')
+      .replace(/^\s*!\[[^\n]*\]\([^\n]*\)\s*$/gm, '')
+      .replace(/^#{1,6}\s+.*$/gm, '')
+      .replace(/^\*\*[^*\r\n]+:\*\*[ \t]*$/gm, '')
+      .replace(/^[ \t]*>[ \t]?/gm, '');
+    const paragraph = prose.split(/\r?\n\s*\r?\n/).find((text) => createExcerpt(text));
+    const description = post.metadata.frontMatter.description
+      ?? createExcerpt((paragraph ?? '').replace(/\r?\n/g, ' '))
+      ?? post.metadata.description;
+    return {
+      ...post,
+      metadata: {
+        ...post.metadata,
+        description,
+        frontMatter: {...post.metadata.frontMatter, image},
+      },
+    };
+  });
diff --git a/src/utils/prismDark.ts b/src/utils/prismDark.ts
new file mode 100644
index 0000000..db395ec
--- /dev/null
+++ b/src/utils/prismDark.ts
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {PrismTheme} from 'prism-react-renderer';
+
+/**
+ * Dark Prism theme matching the Apache Fluss deep-blue palette.
+ * Used for code blocks rendered in dark contexts (e.g. dark code cards).
+ */
+const darkTheme: PrismTheme = {
+  plain: {
+    color: '#D6E4ED',           // --fluss-blue-100
+    backgroundColor: '#102856', // --fluss-blue-900
+  },
+  styles: [
+    {
+      types: ['comment', 'prolog', 'doctype', 'cdata'],
+      style: {color: '#64748B', fontStyle: 'italic'},
+    },
+    {
+      types: ['namespace'],
+      style: {opacity: 0.7},
+    },
+    {
+      types: ['string', 'attr-value', 'char', 'inserted'],
+      style: {color: '#A3E635'}, // --fluss-lime
+    },
+    {
+      types: ['number', 'boolean'],
+      style: {color: '#FBBF24'},
+    },
+    {
+      types: ['keyword', 'atrule', 'selector'],
+      style: {color: '#B1CEDF'}, // --fluss-blue-300
+    },
+    {
+      types: ['function', 'class-name', 'tag'],
+      style: {color: '#7AAFCB'}, // --fluss-cyan
+    },
+    {
+      types: ['builtin', 'constant', 'variable', 'property'],
+      style: {color: '#C4B5FD'},
+    },
+    {
+      types: ['operator', 'punctuation'],
+      style: {color: '#94A3B8'},
+    },
+    {
+      types: ['regex', 'important', 'deleted'],
+      style: {color: '#FB7185'},
+    },
+    {
+      types: ['attr-name'],
+      style: {color: '#7AAFCB'},
+    },
+    {
+      types: ['symbol', 'url'],
+      style: {color: '#67E8F9'},
+    },
+  ],
+};
+
+export default darkTheme;
diff --git a/src/utils/prismLight.ts b/src/utils/prismLight.ts
new file mode 100644
index 0000000..cca01ec
--- /dev/null
+++ b/src/utils/prismLight.ts
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type {PrismTheme} from 'prism-react-renderer';
+
+/**
+ * Light Prism theme tuned to the Apache Fluss blue palette.
+ * Pairs with --fluss-* tokens defined in src/css/custom.css.
+ */
+const lightTheme: PrismTheme = {
+  plain: {
+    color: '#0A0F1C',           // --fluss-ink-950
+    backgroundColor: '#F8FAFC',
+  },
+  styles: [
+    {
+      types: ['comment', 'prolog', 'doctype', 'cdata'],
+      style: {color: '#64748B', fontStyle: 'italic'},
+    },
+    {
+      types: ['namespace'],
+      style: {opacity: 0.7},
+    },
+    {
+      types: ['string', 'attr-value', 'char', 'inserted'],
+      style: {color: '#0E7C66'},
+    },
+    {
+      types: ['number', 'boolean'],
+      style: {color: '#B45309'},
+    },
+    {
+      types: ['keyword', 'atrule', 'selector'],
+      style: {color: '#194670'}, // --fluss-blue-700
+    },
+    {
+      types: ['function', 'class-name', 'tag'],
+      style: {color: '#7C3AED'}, // --fluss-violet
+    },
+    {
+      types: ['builtin', 'constant', 'variable', 'property'],
+      style: {color: '#12325C'}, // --fluss-blue-800
+    },
+    {
+      types: ['operator', 'punctuation'],
+      style: {color: '#475569'},
+    },
+    {
+      types: ['regex', 'important', 'deleted'],
+      style: {color: '#BE123C'},
+    },
+    {
+      types: ['attr-name'],
+      style: {color: '#1C5078'}, // --fluss-blue-600
+    },
+    {
+      types: ['symbol', 'url'],
+      style: {color: '#0E7490'},
+    },
+  ],
+};
+
+export default lightTheme;
diff --git a/static/img/social/fluss-social-card.png b/static/img/social/fluss-social-card.png
new file mode 100644
index 0000000..6a9d7d4
Binary files /dev/null and b/static/img/social/fluss-social-card.png differ