e6e4f6dcf1
Closes audit #8 first wave. New shared widget wraps Material3's PullToRefreshBox with isRefreshing state managed internally; consumers pass a suspend onRefresh that the wrapper awaits before hiding the indicator (no heuristic delays). ViewModel pattern: refresh() now returns Job so the screen can `.join()` it from the wrapper. Trivial change — adding `: Job =` between the function signature and the existing viewModelScope.launch body. Existing fire-and-forget callers continue to work since they discard the return value. Wired into HomeScreen, AlbumDetailScreen, ArtistDetailScreen. Library tabs + detail / list / admin screens follow in next commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>