Pure client-side tick — 1s setInterval bumps state.tickNow which the
template reads via formatActiveStepElapsed(step). No RPC per tick.
Reads step.date_started_iso (UTC) from the existing payload, parses
to ms, displays elapsed since.
- Green pill (#d1fae5 bg, monospace tabular-nums) on the ACTIVE badge
- Flips red (#fee2e2 + pulse animation) when elapsed > 1.5x
duration_expected — visual cue for the operator that the step is
running long against the recipe target
Cleanup interval on onWillUnmount alongside the existing 15s refresh
interval.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>