Fix App Router scroll padding visibility (#96308)
## Summary
- Treat the root `scroll-padding-top` as the lower boundary of the
usable viewport during App Router navigations, so content obscured by a
sticky header is not incorrectly considered visible.
- Preserve the merged Fragment scroll-ownership state machine: empty
Fragments remain unavailable targets, while real targets are classified
against the padding-aware viewport.
- Resolve pixel and percentage values and use the same visibility rule
in both the legacy element handler and the Fragment-ref handler.
- Add regression coverage showing that Next scrolls when the destination
is hidden inside the padding boundary, while preserving the current
scroll position when the destination is genuinely visible below it.
## Performance
The root computed style is resolved lazily, only after the candidate
produces client rects. Empty Fragments and hash navigations do not
perform this lookup. For a real route-scroll target, the resolved value
is cached locally and reused by the second geometry check after
`scrollTop = 0`.
This does not add work to scroll events or every render, and keeping the
value local allows responsive CSS, root classes, and custom properties
to change between navigations. In a 50-iteration dirty-style Chromium
probe, both the existing and updated paths reported 50 style
recalculations and 50 layouts, indicating that the lookup reused the
style/layout update already required by the handler in that test.
## Related work
#96342 is now merged into `canary` and handles empty Fragment scroll
ownership. This PR composes with its `NoClientRects` / `InViewport` /
`OutOfViewport` state machine by changing only the visible-region
boundary for real targets.
## Verification
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts -t "scroll
padding"` (3/3)
- `HEADLESS=true __NEXT_EXPERIMENTAL_APP_NEW_SCROLL_HANDLER=false pnpm
test-dev-turbo
test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts -t "scroll
padding"` (3/3)
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-webpack
test/e2e/app-dir/parallel-routes-scroll-owner/parallel-routes-scroll-owner.test.ts`
(8/8)
- `HEADLESS=true pnpm test-dev-turbo
test/e2e/app-dir/navigation-focus/navigation-focus.test.ts` (5/5)
- Manually compared the same sticky-header reproduction before and after
the change.
<!-- NEXT_JS_LLM -->