85a8ce1136
Fixes a real defect and reworks the calibration/settings flow per Jay's on-device review. Bug: the second capture hung on 'capturing'. The completion code wrote capture=null (a LaunchedEffect key) then awaited the suspend setSurfaceCalibration BEFORE setting phase=COMPLETE; at the suspension Compose cancelled the effect mid-write, stranding the transition. The effect body is now fully synchronous and persistence runs on an independent scope.launch. Verified end-to-end on device. Settings screen (new): reached from the Level header gear (was: gear jumped straight into calibration). Calibration is a sleek button in its own section; Units, Haptics, and Reduce-motion prefs get a home; Done is pinned to the bottom while the list scrolls independently. Calibration screen: two stages. OVERVIEW shows the current correction (Pitch/Roll offsets), the Thoroughness selector, and Calibrate / Reset to phone defaults / Done. CAPTURING shows ONLY capture + Cancel; Cancel restores the prior calibration (saved value is never touched mid-flow). Instructions centered; outlined buttons given visible borders. Thoroughness: Simple (2-point, 0/180) or Thorough (4-point, 0/90/180/270). Generalized deriveSurfaceFromSamples averages a symmetric rotation set - the true tilt sums to zero, so the mean is the device bias; 4-point also cancels each axis twice and averages more noise. Two new unit tests (2-point equivalence, 4-point bias recovery). 61 tests passing; assembleDebug clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>