Before:
- PyQt: inline ETR using chars-based formula
- WebUI: Job.estimated_time_remaining using progress-based formula
(different formulas → different ETR estimates)
After:
- Both UIs call domain.progress.calc_etr_str(elapsed, done, total)
- Same formula, same ETR, single source of truth
- WebUI now stores etr_str on Job and displays it directly
- Job.estimated_time_remaining property kept for backward compat
domain/progress.py: ProgressTracker class + calc_etr_str function
1053 tests pass.