# Overscroll Control

## Objective

Stop scrolling inside a panel from taking the whole page with it.

Explicit scroll-chaining rules for nested scrollable surfaces such as dialogs, drawers, menus, and side panels.

## Before You Begin

This feature is being added to an application that already exists and already
works. Do not scaffold a new project, and do not assume a blank slate.

Inspect the codebase first and establish:

- The existing application structure and where code of this kind already lives.
- The framework and version in use.
- The existing design system — colours, spacing, typography, and component conventions.
- Existing UI components you can reuse instead of writing new ones.
- The existing database structure, if this feature needs to persist anything.
- The existing authentication and authorization system, if this feature is user-scoped.
- Dependencies already installed, so you don't add a library that duplicates one.
- The existing test setup and conventions.

Only start writing code once you understand the above. If the application
already implements part of this feature, extend it rather than replacing it.

## Implementation Instructions

1. Identify the nested scrolling surfaces where continuing past the end currently drags the page behind them: dialogs, drawers, dropdown menus, chat panes, sidebars, and long tables. Where a surface is both sticky and scrollable, the offsets belong to Safe Sticky Positioning and only the chaining rules belong here.
2. Contain the scroll within those surfaces so reaching the end stops the gesture rather than handing it to the page. Contain it rather than forbidding it — the surface itself must still scroll normally throughout.
3. Apply the same rule to every input route. Wheel, trackpad momentum, touch drag, and keyboard paging all arrive at the boundary and all must behave identically when they do.
4. Preserve and restore the underlying page position whenever a full-screen surface opens and closes, so dismissing a dialog does not return the user to the top of a long list.
5. Do not lock scrolling globally by freezing the document while an overlay is open. That approach loses the scroll position, misbehaves on mobile as the address bar collapses, and can leave keyboard users unable to move within the overlay itself.

## UI and UX Requirements

Match the application's existing design system exactly. Reuse its components,
spacing, and typography. This feature should look like it was always there.

## Responsive Requirements

Works on mobile, tablet, and desktop. Touch targets are large enough to hit on a
phone, and nothing overflows horizontally at 320px.

## Accessibility Requirements

- Fully keyboard navigable.
- Correct semantic elements and ARIA roles.
- Visible focus states.
- Meets WCAG AA contrast.
- Dynamic changes are announced to screen readers.
- Respects prefers-reduced-motion.

## Edge Cases

- Containment must apply only where a surface genuinely scrolls. Applied indiscriminately it makes ordinary page scrolling stop dead wherever the pointer happens to be resting.
- Suppressing overscroll at the top of the document also disables pull-to-refresh and, on some platforms, back-swipe navigation, so scope the rule to the nested surfaces rather than to the document.
- Trackpad momentum keeps delivering events after the finger has lifted, so a boundary reached mid-flick will chain unless containment is expressed as a property of the scrolling surface rather than by cancelling individual events.
- A keyboard user paging through an overlay must still reach its end and then move focus out. Containment applies to scrolling, never to focus movement or to dismissing the surface.
- Bounce at the end of a list is meaningful feedback on some platforms, and removing it makes the surface feel broken. Suppress the chaining without suppressing the bounce.
- A surface that grows after opening — a menu whose list is filtered, a dialog that gains a validation summary — must recompute whether it scrolls at all.
- Zooming changes what overflows: a layout with no nested scrolling at default zoom often acquires it at two hundred percent, and the rules must hold there too.

## Testing

Exercise the feature end to end in the running application. Cover every edge case
above, then run the existing test suite and confirm nothing regressed.

## Acceptance Criteria

- [ ] Reaching the end of a nested scrolling surface does not scroll the page behind it.
- [ ] Nested surfaces continue to scroll normally within their own bounds.
- [ ] Wheel, trackpad momentum, touch, and keyboard paging all respect the same boundary.
- [ ] Pull-to-refresh and browser navigation gestures still work outside the contained surfaces.
- [ ] Opening and closing an overlay preserves the underlying page scroll position.
- [ ] Keyboard focus can always leave a contained surface.
- [ ] The feature matches the existing design system.
- [ ] No existing functionality is broken.

## Adaptation Rules

- Match the existing design system. Do not introduce a new colour palette,
  spacing scale, or component library.
- Reuse existing components and utilities wherever they fit.
- Follow the naming, file layout, and code style already present.
- Do not upgrade, replace, or remove existing dependencies to make this
  feature fit. Adapt the feature to the app, not the app to the feature.
- Do not break existing functionality. If a change is genuinely required in
  existing code, make the smallest one that works and say so.
- If something in these instructions conflicts with how the application is
  built, follow the application and explain the deviation.

## Final Verification

Before you report the work as done:

1. Re-read the acceptance criteria above and check each one against what you
   actually built.
2. Run the application and exercise the feature end to end.
3. Run the existing test suite and confirm you have broken nothing.
4. Check the feature on mobile, tablet, and desktop widths.
5. Check keyboard navigation and focus handling.
6. Summarize what changed: files added, files modified, and anything you
   deliberately did differently because of how this application is built.

If any acceptance criterion is unmet, fix it before reporting completion.
