db6e7726eb
Task #476. On iPad Safari, opening the keyboard while writing a new note or replying inside the note thread dialog covered the bottom of the dialog (textarea + send button). The dialog used `top:50%; translate(-50%,-50%)` against the layout viewport and never reacted to the visual viewport shrinking when the keyboard appeared. Changes: - artifacts/tx-os/index.html: added `interactive-widget=resizes-content` to the viewport meta so iOS 17+ resizes the layout viewport when the software keyboard opens. - artifacts/tx-os/src/hooks/use-visual-viewport.ts: new hook that subscribes to `window.visualViewport` `resize` / `scroll` / `orientationchange` (rAF-throttled) and exposes the visible height, the visual viewport `offsetTop`, and the bottom keyboard inset. - artifacts/tx-os/src/components/ui/dialog.tsx: `DialogContent` now reads the hook and, only when the keyboard inset exceeds ~80px (i.e. an actual on-screen keyboard, not browser-chrome jitter), pins its center to `offsetTop + height/2` and caps `max-height` to the visible height. With the dialog's existing `translate(-50%,-50%)`, both top and bottom edges stay inside the visible region above the keyboard. Desktop renders unchanged (inset is 0 → no inline style applied beyond what the caller passes). Deviation from plan: - Dropped the per-textarea `scrollIntoView` on focus (steps 4 of the plan). Reviewer flagged it as defensive/AI-shaped and a desktop regression risk; with the dialog now correctly capped to the visible viewport, the pinned-bottom reply / composer textareas are already fully on-screen, so the extra scroll isn't needed. Verification: - `tsc --noEmit` clean. - `notes-thread-dialog` e2e still passes.