# Stage 17: Frontend Page Modules and Shared Components

## Scope

This stage establishes frontend feature ownership without changing page markup, API contracts,
permissions, visual design, or user interaction. The build-free deployment model is retained.

## Page Module Runtime

`static/pages/runtime.js` owns the page lifecycle registry. Each feature registers its views in
`static/pages/<feature>/page.js` and declares named enter and leave actions. The application
injects the existing feature functions into that runtime, so page modules do not reach into
another feature's state or DOM.

The lifecycle boundary now owns:

- page-specific data loading after a successful shell mount;
- member-aware entry for Screener, Mentor, and Wentian;
- auction timer cleanup when leaving Auction;
- canvas, dust, and performance cleanup when leaving Wentian;
- the internal Screener Tracking view's ownership relationship.

`openView` is now a generic coordinator. It validates the route, asks the page runtime to leave
the previous page, mounts through the shared shell, and enters the next page. It contains no
page-name branch chain.

## Shared Components

`static/shared/components.js` is the common rendering boundary for small, stable DOM patterns.
The first migrated component is the empty state used across market rotation, themes,
Dragon-Tiger, review, Screener, Wentian history, alerts, entity details, and administration.
It centralizes escaping and class composition while preserving the exact existing markup.

Collection rendering and text assignment are exposed for later incremental migrations. They
remain dependency-free and use `XiaobaiUI` for safe escaping.

## Enforcement

Frontend boundary tests verify that:

- shared components load after UI primitives and before page code;
- the page runtime loads before every feature registration and before `app.js`;
- every public and internal workspace belongs to exactly one feature page module;
- `openView` contains no feature-specific view comparisons;
- shared empty-state rendering is used by multiple feature families;
- provider requests still exit only through `shared/api.js`.

## Compatibility and Residual Risk

Feature renderers and event handlers still reside in `app.js`; moving them all at once would
create a high-risk rewrite across already accepted workflows. The new lifecycle and component
boundaries let those functions move feature by feature later without changing navigation or
loading behavior. Dedicated mobile composition remains the next governance phase.
