Goal: Implement the sign-in screen with Google OAuth via expo-auth-session + expo-web-browser. Acceptance criterion: EAS dev build → sign in → force-close → reopen → watchlist screen appears without re-auth prompt.
Scope:
- `app/(auth)/sign-in.tsx` — three states per UX spec: idle ("Sign in with Google"), loading ("Opening Google sign-in…"), error ("Sign-in failed — please try again.")
- No error shown when result.type === 'dismiss' (user cancelled intentionally)
- AuthSession.makeRedirectUri({ scheme: 'pulseticker' }) — handles Expo Go vs EAS build automatically
- WebBrowser.warmUpAsync() on mount / coolDownAsync() on unmount
- Excluded: root layout deep-link handler (Task 10)
- Manual acceptance test: force-close + reopen shows watchlist — cannot be automated in CI
Branch: `feat/mobile-google-oauth`
Dependency: Tasks 6 + 10 (redirect URLs configured; root layout deep-link handler live)
Test boundary: idle renders button; dismiss → idle with no error; error result → error message; promptAsync called on button press
Risk: HIGH — requires EAS development build; cannot be fully verified in Expo Go. Coordinate EAS secrets with infra before testing.
Goal: Implement the sign-in screen with Google OAuth via expo-auth-session + expo-web-browser. Acceptance criterion: EAS dev build → sign in → force-close → reopen → watchlist screen appears without re-auth prompt.
Scope:
Branch: `feat/mobile-google-oauth`
Dependency: Tasks 6 + 10 (redirect URLs configured; root layout deep-link handler live)
Test boundary: idle renders button; dismiss → idle with no error; error result → error message; promptAsync called on button press
Risk: HIGH — requires EAS development build; cannot be fully verified in Expo Go. Coordinate EAS secrets with infra before testing.