Adds a thread-safe path from on_sud_changed() (websocket recv thread)
to a modal QMessageBox on the GUI thread via Qt's cross-thread
signal/slot queuing, fired when the State transition lands on
WAIT_USER - which the schedule is already blocked on server-side, so
the dialog just gives it a UI: closing it (OK) sends Confirm.
Deliberately scoped to WAIT_USER transitions, not every UserMessage -
most steps in sude/sud_0010.json inherit the schedule's default
placeholder text ("Put user message here") rather than overriding it,
so popping a dialog for every step would mostly show that placeholder.