The iOS hierarchy prepends a zero-bounds node before the real root
window, so elements[0] returned undefined and the overlay fell back to
the screenshot's pixel size. Take the first element with positive
extent instead; pre-order puts the root window before any content.
Verified against a real iOS trace in the replay UI.
Tap points are recorded in the hierarchy's coordinate space (iOS points,
Android pixels, web CSS px) while screenshots are device pixels, so the
overlay rendered at 1/3 position on iOS 3x screens. Derive the viewBox
from the root element bounds; natural image size stays the fallback.
* feat(ltl): attribute violations to the obligation origin step
* feat(verifier): label evaluator observations with the runner step index
* feat(trace): carry the causing step in violation witnesses and summary
* feat(replay): move the violation marker to the causing step
* feat(replay-ui): render witness evidence in the violations panel
* feat(replay-ui): wire witnesses and step jump into violation panels
* fix(ltl): treat next obligations as vacuous at run end
* fix(runner): give the finalize trace record its own step index
* refactor: rename inspect to replay across the codebase
Renames inspect-ui/ to replay-ui/, internal/inspect/ to internal/replay/,
the CLI subcommand from `sanderling inspect` to `sanderling replay`, and
updates all references in docs, Makefile, README, and Go comments.
* feat(replay-ui): show spec filename with full path on hover
RunList and RunDetail now render the basename of spec_path (e.g.
login.spec.ts) with the full path available as a title tooltip.