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

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 11ddc6fddc3 [refactor](next) fix some issues (#3666)
11ddc6fddc3 is described below

commit 11ddc6fddc32c1fde57c8905bdcb00049093f434
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Sun May 17 23:09:02 2026 -0700

    [refactor](next) fix some issues (#3666)
---
 .github/workflows/manual-deploy-website.yml        |   1 +
 src/components/home-next/NavbarNext.scss           |  22 +--
 src/components/home-next/NavbarNext.tsx            |  19 +-
 .../home-next/sections/EcosystemSection.scss       |   2 +-
 .../home-next/sections/EcosystemSection.tsx        |   6 +-
 .../home-next/sections/FeaturesSection.tsx         | 213 +++++++++++++--------
 src/components/home-next/sections/HeroSection.tsx  |  29 +--
 src/scss/custom.scss                               |  32 +---
 src/theme/DocItem/Layout/MobileSidebarDrawer.tsx   | 105 +++++-----
 src/theme/DocItem/Layout/index.tsx                 |   6 +-
 src/theme/DocItem/Layout/styles.module.css         |  28 +++
 static/js/custom-script.js                         |  31 +++
 12 files changed, 301 insertions(+), 193 deletions(-)

diff --git a/.github/workflows/manual-deploy-website.yml 
b/.github/workflows/manual-deploy-website.yml
index abcb2502f34..d69ed030b0f 100644
--- a/.github/workflows/manual-deploy-website.yml
+++ b/.github/workflows/manual-deploy-website.yml
@@ -42,6 +42,7 @@ jobs:
                   yarn cache clean
                   export NODE_OPTIONS=--max-old-space-size=8192
                   yarn
+                  node ./scripts/update_github_info.js
                   yarn key-features:generate
                   PWA_SERVICE_WORKER_URL=https://doris.apache.org/sw.js yarn 
docusaurus build --locale en --locale zh-CN
                   if [ ! -d "./ja-build" ]; then
diff --git a/src/components/home-next/NavbarNext.scss 
b/src/components/home-next/NavbarNext.scss
index 5481134d3c6..abf1c391f35 100644
--- a/src/components/home-next/NavbarNext.scss
+++ b/src/components/home-next/NavbarNext.scss
@@ -462,15 +462,6 @@
         &--mobile-open &__mobile-panel {
             display: block;
         }
-
-        // With nav hidden, push Ask AI + actions to the right together.
-        &__ask-ai {
-            margin-left: auto;
-        }
-
-        &__actions {
-            margin-left: 0;
-        }
     }
 }
 
@@ -542,11 +533,18 @@
         // Trim star-link horizontal padding (only change needed to make 
everything fit).
         &__star-link {
             padding: 0 8px 0 6px;
+            // Right-anchor the Star+Download+☰ group. Since `__actions` 
collapses
+            // to `display: contents` below, Star is the first member of the
+            // right-side group and inherits the auto-margin job from 
__actions.
+            margin-left: auto;
         }
 
-        // Push Star + Download + ☰ together to the right as one group.
+        // Collapse `__actions` so its children (Ask Me, Star, Download) become
+        // flex children of `__inner` directly. This lets Ask Me wrap to its 
own
+        // full-width second row via `flex: 0 0 100%; order: 10` below — which
+        // is impossible while it's nested inside a non-wrapping flex 
container.
         &__actions {
-            margin-left: auto;
+            display: contents;
         }
 
         // Hamburger follows actions naturally — no extra margin needed.
@@ -554,7 +552,7 @@
             margin-left: 0;
         }
 
-        // Ask AI: full-width second row, icon + text centered both axes.
+        // Ask Me: full-width second row, icon + text centered both axes.
         &__ask-ai {
             order: 10;
             flex: 0 0 100%;
diff --git a/src/components/home-next/NavbarNext.tsx 
b/src/components/home-next/NavbarNext.tsx
index 772a4a4eac0..41043e26498 100644
--- a/src/components/home-next/NavbarNext.tsx
+++ b/src/components/home-next/NavbarNext.tsx
@@ -168,17 +168,16 @@ export function NavbarNext(): JSX.Element {
                     ))}
                 </div>
 
-                <button
-                    type="button"
-                    id="navbar-ask-ai-btn"
-                    className="navbar-next__ask-ai"
-                    aria-label="Ask AI"
-                >
-                    <StarGreenIcon />
-                    <span>Ask AI</span>
-                </button>
-
                 <div className="navbar-next__actions">
+                    <button
+                        type="button"
+                        id="navbar-ask-ai-btn"
+                        className="navbar-next__ask-ai"
+                        aria-label="Ask Me"
+                    >
+                        <StarGreenIcon />
+                        <span>Ask Me</span>
+                    </button>
                     <a
                         href={`https://github.com/${GITHUB_REPO}`}
                         target="_blank"
diff --git a/src/components/home-next/sections/EcosystemSection.scss 
b/src/components/home-next/sections/EcosystemSection.scss
index 394497f57ec..f77abbdbf17 100644
--- a/src/components/home-next/sections/EcosystemSection.scss
+++ b/src/components/home-next/sections/EcosystemSection.scss
@@ -329,7 +329,7 @@
 
     &__logo--metabase .ecosystem-next__logo-image,
     &__logo--grafana .ecosystem-next__logo-image,
-    &__logo--langfuse .ecosystem-next__logo-image {
+    &__logo--litefuse .ecosystem-next__logo-image {
         padding: 0;
     }
 
diff --git a/src/components/home-next/sections/EcosystemSection.tsx 
b/src/components/home-next/sections/EcosystemSection.tsx
index 02549513516..243eaa4d142 100644
--- a/src/components/home-next/sections/EcosystemSection.tsx
+++ b/src/components/home-next/sections/EcosystemSection.tsx
@@ -144,9 +144,9 @@ const CONSUMERS: EcosystemGroup[] = [
                 className: 'ecosystem-next__logo--grafana',
             },
             {
-                name: 'Langfuse',
-                logoSrc: '/images/ecomsystem-log/longfuse.png',
-                className: 'ecosystem-next__logo--langfuse',
+                name: 'Litefuse',
+                logoSrc: '/images/ecomsystem-log/litefuse.png',
+                className: 'ecosystem-next__logo--litefuse',
             },
         ],
     },
diff --git a/src/components/home-next/sections/FeaturesSection.tsx 
b/src/components/home-next/sections/FeaturesSection.tsx
index 22ebc0d80e8..72d393169f3 100644
--- a/src/components/home-next/sections/FeaturesSection.tsx
+++ b/src/components/home-next/sections/FeaturesSection.tsx
@@ -1,4 +1,4 @@
-import React, { CSSProperties, JSX, useEffect, useRef, useState } from 'react';
+import React, { CSSProperties, JSX, useEffect, useMemo, useRef, useState } 
from 'react';
 import './FeaturesSection.scss';
 
 type CapabilityTone = 'green' | 'cream' | 'ink';
@@ -81,10 +81,6 @@ const CAPABILITIES: Capability[] = [
     },
 ];
 
-interface CapabilityCardStyle extends CSSProperties {
-    zIndex: number;
-}
-
 function clamp(value: number, min: number, max: number): number {
     return Math.max(min, Math.min(max, value));
 }
@@ -93,61 +89,12 @@ function easeOut(value: number): number {
     return 1 - Math.pow(1 - value, 2.5);
 }
 
-function useContainerProgress(ref: React.RefObject<HTMLElement>): number {
-    const [progress, setProgress] = useState(0);
-
-    useEffect(() => {
-        const el = ref.current;
-        if (!el) return undefined;
-
-        let frame = 0;
-
-        function update() {
-            window.cancelAnimationFrame(frame);
-            frame = window.requestAnimationFrame(() => {
-                const rect = el.getBoundingClientRect();
-                const viewportHeight = window.innerHeight;
-                const scrollable = rect.height - viewportHeight;
-
-                if (scrollable <= 0) {
-                    setProgress(rect.top <= 0 ? 1 : 0);
-                    return;
-                }
-
-                setProgress(clamp(-rect.top / scrollable, 0, 1));
-            });
-        }
-
-        update();
-        window.addEventListener('scroll', update, { passive: true });
-        window.addEventListener('resize', update);
-
-        return () => {
-            window.cancelAnimationFrame(frame);
-            window.removeEventListener('scroll', update);
-            window.removeEventListener('resize', update);
-        };
-    }, [ref]);
-
-    return progress;
-}
-
-function useIsNarrowViewport(): boolean {
-    const [isNarrowViewport, setIsNarrowViewport] = useState(false);
-
-    useEffect(() => {
-        const query = window.matchMedia('(max-width: 820px)');
-        const update = () => setIsNarrowViewport(query.matches);
-
-        update();
-        query.addEventListener('change', update);
-        return () => query.removeEventListener('change', update);
-    }, []);
-
-    return isNarrowViewport;
+interface CardTransform {
+    transform: string;
+    opacity: number;
 }
 
-function getCardStyle(capability: Capability, idx: number, total: number, 
progress: number): CapabilityCardStyle {
+function computeCardTransform(capability: Capability, idx: number, total: 
number, progress: number): CardTransform {
     const transitions = Math.max(1, total - 1);
     const slice = 1 / transitions;
     const activeAt = idx * slice;
@@ -184,12 +131,35 @@ function getCardStyle(capability: Capability, idx: 
number, total: number, progre
     }
 
     return {
-        transform: `translate(-50%, -50%) translateY(${translateY}px) 
scale(${scale}) rotate(${rotation}deg)`,
+        transform: `translate3d(-50%, -50%, 0) translateY(${translateY}px) 
scale(${scale}) rotate(${rotation}deg)`,
         opacity,
-        zIndex: 10 + idx,
     };
 }
 
+const INITIAL_CARD_TRANSFORMS = CAPABILITIES.map((cap, i) =>
+    computeCardTransform(cap, i, CAPABILITIES.length, 0),
+);
+
+const INITIAL_DOT_ON = CAPABILITIES.map((_, i) => {
+    const transitions = Math.max(1, CAPABILITIES.length - 1);
+    return 0 >= i / transitions - 0.001;
+});
+
+function useIsNarrowViewport(): boolean {
+    const [isNarrowViewport, setIsNarrowViewport] = useState(false);
+
+    useEffect(() => {
+        const query = window.matchMedia('(max-width: 820px)');
+        const update = () => setIsNarrowViewport(query.matches);
+
+        update();
+        query.addEventListener('change', update);
+        return () => query.removeEventListener('change', update);
+    }, []);
+
+    return isNarrowViewport;
+}
+
 function ArrowIcon(): JSX.Element {
     return (
         <svg width="14" height="14" viewBox="0 0 24 24" fill="none" 
stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
@@ -280,15 +250,24 @@ interface CapabilityCardProps {
     capability: Capability;
     idx: number;
     total: number;
-    progress: number;
+    cardRef: (el: HTMLElement | null) => void;
     isNarrowViewport: boolean;
 }
 
-function CapabilityCard({ capability, idx, total, progress, isNarrowViewport 
}: CapabilityCardProps): JSX.Element {
+function CapabilityCard({ capability, idx, total, cardRef, isNarrowViewport }: 
CapabilityCardProps): JSX.Element {
+    const initialStyle: CSSProperties | undefined = isNarrowViewport
+        ? undefined
+        : {
+              zIndex: 10 + idx,
+              transform: INITIAL_CARD_TRANSFORMS[idx].transform,
+              opacity: INITIAL_CARD_TRANSFORMS[idx].opacity,
+          };
+
     return (
         <article
+            ref={cardRef}
             className={`features-next__card 
features-next__card--${capability.tone}`}
-            style={isNarrowViewport ? undefined : getCardStyle(capability, 
idx, total, progress)}
+            style={initialStyle}
         >
             <div className="features-next__copy">
                 <div className="features-next__card-num">
@@ -321,8 +300,90 @@ function CapabilityCard({ capability, idx, total, 
progress, isNarrowViewport }:
 
 export function FeaturesSection(): JSX.Element {
     const containerRef = useRef<HTMLDivElement>(null);
+    const cardRefs = useRef<(HTMLElement | null)[]>([]);
+    const dotRefs = useRef<(HTMLSpanElement | null)[]>([]);
     const isNarrowViewport = useIsNarrowViewport();
-    const progress = useContainerProgress(containerRef);
+
+    const cardRefSetters = useMemo(
+        () =>
+            CAPABILITIES.map((_, idx) => (el: HTMLElement | null) => {
+                cardRefs.current[idx] = el;
+            }),
+        [],
+    );
+    const dotRefSetters = useMemo(
+        () =>
+            CAPABILITIES.map((_, idx) => (el: HTMLSpanElement | null) => {
+                dotRefs.current[idx] = el;
+            }),
+        [],
+    );
+
+    useEffect(() => {
+        if (isNarrowViewport) {
+            cardRefs.current.forEach(el => {
+                if (!el) return;
+                el.style.transform = '';
+                el.style.opacity = '';
+            });
+            dotRefs.current.forEach(el => {
+                el?.classList.remove('features-next__stage-dot--on');
+            });
+            return undefined;
+        }
+
+        const containerEl = containerRef.current;
+        if (!containerEl) return undefined;
+
+        let frame = 0;
+        let lastProgress = -1;
+
+        function update() {
+            window.cancelAnimationFrame(frame);
+            frame = window.requestAnimationFrame(() => {
+                const rect = containerEl.getBoundingClientRect();
+                const viewportHeight = window.innerHeight;
+                const scrollable = rect.height - viewportHeight;
+
+                let progress: number;
+                if (scrollable <= 0) {
+                    progress = rect.top <= 0 ? 1 : 0;
+                } else {
+                    progress = clamp(-rect.top / scrollable, 0, 1);
+                }
+
+                if (Math.abs(progress - lastProgress) < 0.0005) return;
+                lastProgress = progress;
+
+                const total = CAPABILITIES.length;
+                for (let i = 0; i < cardRefs.current.length; i++) {
+                    const cardEl = cardRefs.current[i];
+                    if (!cardEl) continue;
+                    const { transform, opacity } = 
computeCardTransform(CAPABILITIES[i], i, total, progress);
+                    cardEl.style.transform = transform;
+                    cardEl.style.opacity = String(opacity);
+                }
+
+                const transitions = Math.max(1, total - 1);
+                for (let i = 0; i < dotRefs.current.length; i++) {
+                    const dotEl = dotRefs.current[i];
+                    if (!dotEl) continue;
+                    const isOn = progress >= i / transitions - 0.001;
+                    dotEl.classList.toggle('features-next__stage-dot--on', 
isOn);
+                }
+            });
+        }
+
+        update();
+        window.addEventListener('scroll', update, { passive: true });
+        window.addEventListener('resize', update);
+
+        return () => {
+            window.cancelAnimationFrame(frame);
+            window.removeEventListener('scroll', update);
+            window.removeEventListener('resize', update);
+        };
+    }, [isNarrowViewport]);
 
     return (
         <section className="features-next">
@@ -345,22 +406,22 @@ export function FeaturesSection(): JSX.Element {
                                 capability={capability}
                                 idx={i}
                                 total={CAPABILITIES.length}
-                                progress={progress}
+                                cardRef={cardRefSetters[i]}
                                 isNarrowViewport={isNarrowViewport}
                             />
                         ))}
                         <div className="features-next__stage-progress" 
aria-hidden="true">
-                            {CAPABILITIES.map((capability, i) => {
-                                const transitions = Math.max(1, 
CAPABILITIES.length - 1);
-                                const isOn = progress >= i / transitions - 
0.001;
-
-                                return (
-                                    <span
-                                        key={capability.num}
-                                        className={isOn ? 
'features-next__stage-dot features-next__stage-dot--on' : 
'features-next__stage-dot'}
-                                    />
-                                );
-                            })}
+                            {CAPABILITIES.map((capability, i) => (
+                                <span
+                                    key={capability.num}
+                                    ref={dotRefSetters[i]}
+                                    className={
+                                        INITIAL_DOT_ON[i]
+                                            ? 'features-next__stage-dot 
features-next__stage-dot--on'
+                                            : 'features-next__stage-dot'
+                                    }
+                                />
+                            ))}
                         </div>
                     </div>
                 </div>
diff --git a/src/components/home-next/sections/HeroSection.tsx 
b/src/components/home-next/sections/HeroSection.tsx
index ef60640e677..4d728808b77 100644
--- a/src/components/home-next/sections/HeroSection.tsx
+++ b/src/components/home-next/sections/HeroSection.tsx
@@ -1,4 +1,5 @@
 import React, { JSX, useState, useEffect, useMemo, useRef } from 'react';
+import { StarGreenIcon } from '@site/src/components/Icons/star-green-icon';
 import './HeroSection.scss';
 
 // ─── SVG atoms 
───────────────────────────────────────────────────────────────
@@ -24,14 +25,6 @@ function LightningSvg({ size = 24, color = '#FFD23F' }: { 
size?: number; color?:
     );
 }
 
-function DownloadIcon(): JSX.Element {
-    return (
-        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" 
stroke="currentColor" strokeWidth="2.5" aria-hidden="true">
-            <path d="M12 4v12m0 0l-5-5m5 5l5-5M4 20h16" />
-        </svg>
-    );
-}
-
 function SlackIcon(): JSX.Element {
     return (
         <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" 
aria-hidden="true">
@@ -897,13 +890,23 @@ export function HeroSection(): JSX.Element {
                         </p>
 
                         <div className="hero-next__ctas">
-                            <a className="hero-next__btn 
hero-next__btn--yellow" href="/download">
-                                <DownloadIcon />
-                                Download
-                            </a>
-                            <a className="hero-next__btn 
hero-next__btn--primary" href={GET_STARTED_HREF}>
+                            <a className="hero-next__btn 
hero-next__btn--yellow" href={GET_STARTED_HREF}>
                                 Get Started
                             </a>
+                            <button
+                                type="button"
+                                className="hero-next__btn 
hero-next__btn--primary"
+                                onClick={() => {
+                                    // Reuse the Kapa modal trigger bound to 
the
+                                    // navbar Ask Me button via 
docusaurus.config.js
+                                    // (data-modal-override-open-selector).
+                                    
document.getElementById('navbar-ask-ai-btn')?.click();
+                                }}
+                                aria-label="Ask Me"
+                            >
+                                <StarGreenIcon />
+                                Ask Me
+                            </button>
                             <a
                                 className="hero-next__btn 
hero-next__btn--ghost"
                                 href="https://doris.apache.org/slack";
diff --git a/src/scss/custom.scss b/src/scss/custom.scss
index 74c66c2854d..5106caa62b0 100644
--- a/src/scss/custom.scss
+++ b/src/scss/custom.scss
@@ -145,33 +145,13 @@ body {
 }
 
 /**
- * Kapa Ask AI modal must overlay the sticky NavbarNext (which sits at
- * z-index 300). Kapa's Mantine modal defaults to a lower stacking value
- * so without this override the dialog gets clipped behind the navbar.
+ * Lift Kapa's shadow-host above the sticky NavbarNext (z-index 300). The
+ * Mantine modal/overlay live inside the shadow root and inherit stacking
+ * from this host, so raising the host is enough.
  */
-[data-kapa-widget-container],
-.mantine-Modal-root,
-.mantine-Modal-inner,
-.mantine-Overlay-root,
-.mantine-Modal-overlay {
+#kapa-widget-container {
     z-index: 9999 !important;
 }
 
-/**
- * Push the Kapa Ask AI modal down from the top of the viewport so it
- * doesn't sit flush against the 64px sticky NavbarNext. Kapa renders its
- * modal flush against whichever container it sits below, so we both
- * override Mantine's yOffset variable on the inner wrapper and add a
- * margin-top on the modal panel itself — whichever Kapa is using, the
- * gap shows up.
- */
-.mantine-Modal-inner,
-[class*="Modal-inner"] {
-    --mantine-modal-y-offset: 24px !important;
-    padding-top: 24px !important;
-}
-
-.mantine-Modal-content,
-[class*="Modal-content"] {
-    margin-top: 24px !important;
-}
+// Centering the Kapa modal itself is done by injecting CSS into its shadow
+// root from static/js/custom-script.js, since light-DOM rules can't pierce it.
diff --git a/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx 
b/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
index a55cca9a745..a366248a27b 100644
--- a/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
+++ b/src/theme/DocItem/Layout/MobileSidebarDrawer.tsx
@@ -1,4 +1,5 @@
 import React, { useEffect, useRef, useState, useCallback } from 'react';
+import { createPortal } from 'react-dom';
 import clsx from 'clsx';
 import { useLocation } from '@docusaurus/router';
 import { useDocsSidebar } from '@docusaurus/plugin-content-docs/client';
@@ -58,6 +59,59 @@ export default function MobileSidebarDrawer(): JSX.Element | 
null {
 
     if (!sidebar) return null;
 
+    const drawer = (
+        <div className={clsx(open && styles.open)} aria-hidden={!open}>
+            <div
+                className={styles.backdrop}
+                onClick={close}
+                role="presentation"
+            />
+            <aside
+                className={styles.drawer}
+                aria-label={isZH ? '文档目录' : 'Docs sidebar'}
+            >
+                <div className={styles.header}>
+                    <span>{isZH ? '目录' : 'Menu'}</span>
+                    <button
+                        type="button"
+                        className={styles.close}
+                        onClick={close}
+                        aria-label={isZH ? '关闭目录' : 'Close docs sidebar'}
+                    >
+                        <svg width="20" height="20" viewBox="0 0 20 20" 
fill="none" aria-hidden="true">
+                            <path
+                                d="M5 5l10 10M15 5L5 15"
+                                stroke="currentColor"
+                                strokeWidth="1.6"
+                                strokeLinecap="round"
+                            />
+                        </svg>
+                    </button>
+                </div>
+                <nav
+                    className={styles.body}
+                    aria-label={isZH ? '文档目录导航' : 'Docs sidebar navigation'}
+                >
+                    <ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 
'menu__list', styles.menu)}>
+                        <DocSidebarItems
+                            items={sidebar.items}
+                            activePath={pathname}
+                            onItemClick={(item) => {
+                                if (item.type === 'link') {
+                                    close();
+                                }
+                                if (item.type === 'category' && item.href) {
+                                    close();
+                                }
+                            }}
+                            level={1}
+                        />
+                    </ul>
+                </nav>
+            </aside>
+        </div>
+    );
+
     return (
         <>
             <div className={styles.toolbar}>
@@ -129,56 +183,7 @@ export default function MobileSidebarDrawer(): JSX.Element 
| null {
                 </button>
             </div>
 
-            <div className={clsx(open && styles.open)} aria-hidden={!open}>
-                <div
-                    className={styles.backdrop}
-                    onClick={close}
-                    role="presentation"
-                />
-                <aside
-                    className={styles.drawer}
-                    aria-label={isZH ? '文档目录' : 'Docs sidebar'}
-                >
-                    <div className={styles.header}>
-                        <span>{isZH ? '目录' : 'Menu'}</span>
-                        <button
-                            type="button"
-                            className={styles.close}
-                            onClick={close}
-                            aria-label={isZH ? '关闭目录' : 'Close docs sidebar'}
-                        >
-                            <svg width="20" height="20" viewBox="0 0 20 20" 
fill="none" aria-hidden="true">
-                                <path
-                                    d="M5 5l10 10M15 5L5 15"
-                                    stroke="currentColor"
-                                    strokeWidth="1.6"
-                                    strokeLinecap="round"
-                                />
-                            </svg>
-                        </button>
-                    </div>
-                    <nav
-                        className={styles.body}
-                        aria-label={isZH ? '文档目录导航' : 'Docs sidebar 
navigation'}
-                    >
-                        <ul 
className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list', 
styles.menu)}>
-                            <DocSidebarItems
-                                items={sidebar.items}
-                                activePath={pathname}
-                                onItemClick={(item) => {
-                                    if (item.type === 'link') {
-                                        close();
-                                    }
-                                    if (item.type === 'category' && item.href) 
{
-                                        close();
-                                    }
-                                }}
-                                level={1}
-                            />
-                        </ul>
-                    </nav>
-                </aside>
-            </div>
+            {typeof document !== 'undefined' && createPortal(drawer, 
document.body)}
         </>
     );
 }
diff --git a/src/theme/DocItem/Layout/index.tsx 
b/src/theme/DocItem/Layout/index.tsx
index 546ec86e702..3a5d033c10d 100644
--- a/src/theme/DocItem/Layout/index.tsx
+++ b/src/theme/DocItem/Layout/index.tsx
@@ -69,8 +69,10 @@ export default function DocItemLayout({ children }: Props): 
JSX.Element {
                 <DocVersionBanner />
                 <div className={styles.docItemContainer}>
                     <article>
-                        <MobileSidebarDrawer />
-                        <DocBreadcrumbs />
+                        <div className={styles.mobileStickyHeader}>
+                            <MobileSidebarDrawer />
+                            <DocBreadcrumbs />
+                        </div>
                         {/* <DocVersionBadge /> */}
                         {docTOC.mobile}
                         <DocItemContent>{children}</DocItemContent>
diff --git a/src/theme/DocItem/Layout/styles.module.css 
b/src/theme/DocItem/Layout/styles.module.css
index e1664cd34b2..a31feeaf8ef 100644
--- a/src/theme/DocItem/Layout/styles.module.css
+++ b/src/theme/DocItem/Layout/styles.module.css
@@ -22,3 +22,31 @@
 .footerBtn:hover {
   border: 1px solid var(--ifm-color-primary);
 }
+
+/* Freeze the search/locale/menu toolbar + breadcrumb at the top on mobile
+   so they remain reachable while scrolling. Sits just below the sticky
+   green NavbarNext (64px tall; ~112px when Ask AI wraps to a second row
+   at ≤480px). z-index stays under the navbar (300) but above content. */
+@media (max-width: 996px) {
+  .mobileStickyHeader {
+    position: sticky;
+    top: 64px;
+    z-index: 200;
+    background-color: #fff;
+    margin-bottom: 0.5rem;
+    padding: 0.5rem 0 0.25rem;
+    box-shadow: 0 4px 8px -4px rgba(0, 0, 0, 0.08);
+  }
+
+  /* Breadcrumb has its own margin-bottom that we want inside the sticky
+     strip's padding rather than escaping it. */
+  .mobileStickyHeader :global(.theme-doc-breadcrumbs) {
+    margin-bottom: 0;
+  }
+}
+
+@media (max-width: 480px) {
+  .mobileStickyHeader {
+    top: 112px;
+  }
+}
diff --git a/static/js/custom-script.js b/static/js/custom-script.js
index e746900d0bf..af8159231e7 100644
--- a/static/js/custom-script.js
+++ b/static/js/custom-script.js
@@ -10,3 +10,34 @@
     y = l.getElementsByTagName(r)[0];
     y.parentNode.insertBefore(t, y);
 })(window, document, 'clarity', 'script', 'kfyqejiz0g');
+
+// Center the Kapa Ask Me modal vertically. Kapa renders inside a Shadow DOM
+// on `#kapa-widget-container`, so light-DOM CSS can't reach it. We inject a
+// <style> into the shadow root and re-inject if Kapa rebuilds it.
+(function centerKapaModal() {
+    var STYLE_ID = 'doris-kapa-center-modal';
+    var CSS_TEXT =
+        '.mantine-Modal-inner{' +
+        'align-items:center !important;' +
+        'padding-top:0 !important;' +
+        'padding-bottom:0 !important;' +
+        '--modal-y-offset:0 !important;' +
+        '}';
+
+    function inject() {
+        var host = document.getElementById('kapa-widget-container');
+        if (!host || !host.shadowRoot) return false;
+        if (host.shadowRoot.getElementById(STYLE_ID)) return true;
+        var style = document.createElement('style');
+        style.id = STYLE_ID;
+        style.textContent = CSS_TEXT;
+        host.shadowRoot.appendChild(style);
+        return true;
+    }
+
+    if (inject()) return;
+    var observer = new MutationObserver(function () {
+        inject();
+    });
+    observer.observe(document.documentElement, { childList: true, subtree: 
true });
+})();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to