diff --git a/src/app/components/page/Page.tsx b/src/app/components/page/Page.tsx
index f72b924f..56affa88 100644
--- a/src/app/components/page/Page.tsx
+++ b/src/app/components/page/Page.tsx
@@ -395,8 +395,33 @@ export function PageNavContent({
size="300"
hideTrack
visibility="Hover"
+ // folds `direction="Vertical"` sets `overflow-y: scroll`, which makes
+ // this a *user-scrollable* container even when the list fits (scroll
+ // range 0, permanently pinned at its boundary). On Android any
+ // touch-drag on such a boundary-pinned scroller fires the elastic
+ // overscroll stretch — the list "bounces" though it has nothing to
+ // scroll. `overflow-y: auto` is user-scrollable only when content
+ // actually overflows: short lists become inert (no overscroll), long
+ // lists still scroll normally. Inline style wins over the recipe class.
+ style={{ overflowY: 'auto' }}
>
-
{children}
+ {/* `css.PageNavContent` carries a 32px (`S700`) bottom padding for
+ breathing room below the last row. That padding is part of the
+ scroll content, so once the list grows to within 32px of the
+ viewport it overflows by however much of the padding doesn't fit —
+ the list visually "fits" (a gap shows below it) yet you can still
+ scroll a few px into the leftover padding. On native the curtain
+ list already has its own bottom boundary (the pinned DirectSelfRow /
+ WorkspaceFooter, or the screen edge), so the in-scroll breather is
+ redundant; dropping it there means a list that fits has zero scroll
+ range. Desktop keeps the breather (mouse-scrolling into it is
+ harmless and the last row wants the air at the panel bottom). */}
+
+ {children}
+
);
diff --git a/src/app/components/page/style.css.ts b/src/app/components/page/style.css.ts
index 7c35d4ab..5dea3d8c 100644
--- a/src/app/components/page/style.css.ts
+++ b/src/app/components/page/style.css.ts
@@ -143,7 +143,16 @@ export const PageNavHeader = recipe({
export type PageNavHeaderVariants = RecipeVariants;
export const PageNavContent = style({
- minHeight: '100%',
+ // No `min-height: 100%`. It used to force this padded wrapper to at least
+ // the scroll viewport's height, but the div is transparent (the curtain on
+ // native / the PageNav inner column on web paints the background) so the
+ // fill was invisible — its only real effect was a ~1px scroll overflow:
+ // `100%` resolves against the fractional flex-parent height and rounds UP
+ // while the viewport's clientHeight rounds DOWN, leaving the list
+ // permanently scrollable by a hair even when it fits. That hair is the
+ // "useless" overscroll on native. Letting the wrapper size to its content
+ // means a short list has no scroll range at all (paired with the
+ // `overflow-y: auto` override on the Scroll in Page.tsx).
padding: config.space.S200,
paddingRight: 0,
paddingBottom: config.space.S700,