Builds on 07d0af9 (publishSpec wired to imperative flow) — adds an
observer that derives btn-{screen,camera}-share / btn-{kind}-stop
visibility from the publishSpec state instead of from scattered
classList writes in sfuPublish/sfuUnpublish.
The state→UI mapping:
- off / failed → share button visible, stop hidden
- acquiring / negotiating / live / stopping → stop visible, share hidden
(stopping is treated as "still sharing" to avoid flicker during
teardown)
Removed four classList writes (two in sfuPublishScreen+sfuUnpublishScreen,
two in sfuPublishCamera+sfuUnpublishCamera). The buttons now reflect
the FSM rather than whoever last touched them — if the FSM
transitions because of the silent rebuild (sfuRebuildScreenPC) or
the watchPublishPC rebuild path or any future state-driver, the
buttons follow automatically.
Mic has no share/stop button (it's always on for speakers, off for
listeners), so applyPublishStateUI early-returns for that kind.
All 88 fsm + 12 self-listener tests still green.