diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html
index 4da0f36..33e1fc0 100644
--- a/web/zebra-spaces.html
+++ b/web/zebra-spaces.html
@@ -3216,7 +3216,8 @@ async function handleSignal(raw){
{
const mm = members.get(m.uuid);
const by = members.get(m.by);
- const victPub = pubHexFromMsg(m, 'pubkey');
+ const victPub = pubHexFromMsg(m, 'pubkey')
+ || (mm && mm.pubkey ? pubHexFromMsg({pubkey:mm.pubkey},'pubkey') : '');
const byPub = pubHexFromMsg(m, 'by_pubkey');
logLine('', idTag(m.uuid, victPub)+' was removed by '+(by||byPub?idTag(m.by, byPub):'a mod'));
if (m.uuid === myUUID){
@@ -3235,6 +3236,19 @@ async function handleSignal(raw){
sfuUnsubscribe().catch(()=>{});
dropMic();
}
+ /* Authoritative tile teardown by pubkey — booted users can't be
+ * publishing anything anymore by definition. The SFU side eviction
+ * (server posts /internal/block, then OnConnectionStateChange ->
+ * removePublisher) will eventually fire 'ended' on subscribers,
+ * but it can lag enough that the camera tile stays visible past
+ * the kick. Drop it here so the boot is visually instant. */
+ if (victPub){
+ try {
+ removeScreenTile(victPub);
+ removeCameraTile(victPub);
+ removeVideoTile('gameshare', victPub);
+ } catch(_){}
+ }
members.delete(m.uuid); tearPeer(m.uuid); handraise.delete(m.uuid);
renderRoom();
}
@@ -3989,8 +4003,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');