Three lower-sidebar panels are now wrapped in a shared Collapsible so
mobile readers can fold away sections they're not actively using.
New shared pieces:
- lib/collapsible.ts — readOpen/writeOpen helpers backed by
localStorage under 'panel-open:<name>'. Per-panel-name (not
per-code) because the preference is a panel-level habit; users
don't want different fold state for every symbol.
- components/Collapsible.tsx — button-as-header + chevron + body.
aria-expanded for AT. Body uses 'hidden' instead of conditional
unmount so textarea text, partial alert form input, and
RelatedStocks fetch results all survive collapse cycles.
SSR-safe: useState(defaultOpen) then useEffect-sync to avoid React
hydration mismatch when localStorage diverges from default.
Panel refactors:
- RelatedStocks: outer rounded div + header replaced with Collapsible;
loading/error/empty/data states all rendered inside body (header
stays visible so user can still toggle/recognize the section even
while loading).
- InvestmentNote: same swap. saved.updatedAt drives the subtitle.
- AlertsPanel: same swap. current price drives the subtitle.
Defaults are open=true everywhere; the surface area is the toggle
itself, not a forced collapse. User's first interaction sets the
sticky preference.