readOpen() (reviewer f9027f5 non-blocking note):
- Previously returned v === '1' which collapsed '0' AND any garbage
value (legacy markers, manual edits) into the same false branch,
contradicting the 'fallback to default' comment. Split into three
explicit cases: '1' → true, '0' → false, anything else →
defaultOpen. Behavior unchanged for the writeOpen-only path; only
garbage input is corrected.
RelatedStocks defaultOpen=false:
- Of the three collapsible sidebar panels (AlertsPanel,
InvestmentNote, RelatedStocks), RelatedStocks has the lowest
information density per row and the weakest direct tie to 'this
symbol' (it's other symbols in the same market). Starting collapsed
cuts initial mobile scroll height for first-time visitors; once the
user toggles, localStorage pins their preference.
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.