8979e0e377
The IntersectionObserver-on-a-sentinel infinite-scroll pattern was copy- pasted in 7 places. New composables/useInfiniteScroll(sentinelRef, cb) owns the observer lifecycle (attach on mount, re-attach when the ref changes, disconnect on unmount). Migrated GalleryGrid, MasonryGrid (gallery+showcase), ArtistPostsTab, ArtistsView, TagsView, SeriesManageView. PostsView left manual on purpose — its anchored mode does bidirectional scroll-position preservation that doesn't fit the simple composable. I4(a) (timestamps) is effectively already done: the UTC displays were converted earlier; the remaining toLocaleString uses already render local time, so they're left as-is rather than churned for format-only consistency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>