# Call UI + WebRTC Fixes

Applied on 2026-06-03.

## What changed

1. Speaker routing
   - The speaker button is now disabled automatically when the browser does not support `setSinkId()`.
   - The call note now tells users to use the phone/device audio controls when browser-level speaker routing is unavailable.

2. Voice-call stability
   - Added a dedicated hidden `remote-audio` element so voice calls continue to play audio even when the video grid is hidden.
   - Remote video is muted to prevent double audio playback; remote audio is handled by `remote-audio`.
   - Improved media playback retry handling.
   - Improved TURN credential parsing so the app accepts both raw `iceServers` arrays and `{ iceServers: [...] }` responses.
   - Increased ICE candidate pool size and set stable WebRTC bundle/RTCP mux policies.

3. Screen sharing
   - The screen-share button is disabled automatically when the current browser/device does not expose `getDisplayMedia()`.
   - Added a fallback from advanced screen-share constraints to a basic `{ video: true }` request.
   - Screen sharing still requires HTTPS/localhost and browser support. Most mobile browsers do not support full screen sharing.

4. Video-call display
   - Camera/video display now uses `object-fit: contain` instead of `cover`, so the camera feed is not cropped.
   - Local and remote video panels are now full-size/equal panels instead of making the local camera tiny.
   - The call overlay is wider for video calls.

5. Scrollable call overlay
   - The call modal and card are now vertically scrollable with mobile-friendly inertial scrolling.
   - This allows users to move up/down during a call when the screen is small or the keyboard is open.

## Files changed

- `index.php`
- `js/app.js`
- `css/app.css`

## Important note

If voice/video works on the same Wi-Fi but fails on mobile data, another house, office Wi-Fi, or a strict router, configure Cloudflare TURN or another TURN relay correctly in `api/config.php`. Opening `api/cloudflare_turn.php` directly in the browser can show `Not authenticated`; that is expected because the endpoint is protected and should be called only by the logged-in app.
