diff --git a/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/configure_fingerprint.bin b/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/configure_fingerprint.bin
index f660e87..724898a 100644
--- a/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/configure_fingerprint.bin
+++ b/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/configure_fingerprint.bin
@@ -2,29 +2,29 @@ C/C++ Structured Logl
 j
 h/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/additional_project_files.txtC
 A
-?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint	�����3� �����3i
+?com.android.build.gradle.internal.cxx.io.EncodedFileFingerPrint	�����3� �����3i
 g
-e/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/android_gradle_build.json	�����3� Ͽ���3n
+e/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/android_gradle_build.json	�����3� Ͽ���3n
 l
-j/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/android_gradle_build_mini.json	�����3� �����3[
+j/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/android_gradle_build_mini.json	�����3� �����3[
 Y
-W/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build.ninja	�����3�� �����3_
+W/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build.ninja	�����3�� �����3_
 ]
-[/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build.ninja.txt	�����3d
+[/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build.ninja.txt	�����3d
 b
-`/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build_file_index.txt	�����3� �����3e
+`/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/build_file_index.txt	�����3� �����3e
 c
-a/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/compile_commands.json	�����3�R �����3i
+a/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/compile_commands.json	�����3�R �����3i
 g
-e/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/compile_commands.json.bin	�����3	� �����3o
+e/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/compile_commands.json.bin	�����3	� �����3o
 m
-k/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/metadata_generation_command.txt	�����3
+k/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/metadata_generation_command.txt	�����3
 � �����3b
 `
-^/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/prefab_config.json	�����3� �����3g
+^/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/prefab_config.json	�����3� �����3g
 e
-c/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/symbol_folder_index.txt	�����3� �����3v
+c/home/jracek/code/GeoSus/GeoSusGame/.utmp/RelWithDebInfo/3x4o6w3z/arm64-v8a/symbol_folder_index.txt	�����3� �����3v
 t
-r/home/jracek/code/GeoSus/GeoSusGame/Library/Bee/Android/Prj/IL2CPP/Gradle/unityLibrary/src/main/cpp/CMakeLists.txt	�����3

T �ƿ��3�
+r/home/jracek/code/GeoSus/GeoSusGame/Library/Bee/Android/Prj/IL2CPP/Gradle/unityLibrary/src/main/cpp/CMakeLists.txt	�����3

T �ƿ��3�
 �
-/home/jracek/code/GeoSus/GeoSusGame/Library/Bee/Android/Prj/IL2CPP/Gradle/unityLibrary/src/main/cpp/GameActivity/CMakeLists.txt	�����3� �ƿ��3
\ No newline at end of file
+/home/jracek/code/GeoSus/GeoSusGame/Library/Bee/Android/Prj/IL2CPP/Gradle/unityLibrary/src/main/cpp/GameActivity/CMakeLists.txt	�����3� �ƿ��3
\ No newline at end of file
diff --git a/Assets/GameManager/GameManager.cs b/Assets/GameManager/GameManager.cs
index 00d9637..2479887 100644
--- a/Assets/GameManager/GameManager.cs
+++ b/Assets/GameManager/GameManager.cs
@@ -197,7 +197,6 @@ public class GameManager : MonoBehaviour
         }
     }
 
-    // ── Kill cooldown ─────────────────────────────────────────────────────────
     private float _killCooldownSeconds = 0f;
     private const float KillCooldownDuration = 20f;
 
@@ -206,10 +205,16 @@ public class GameManager : MonoBehaviour
         if (_killCooldownSeconds > 0)
         {
             _killCooldownSeconds -= Time.deltaTime;
+            // Mirror into GameState so UI reads from the single source of truth
+            if (networkSubsystem?.State != null)
+                networkSubsystem.State.KillCooldownRemaining = _killCooldownSeconds;
             uiSubsystem?.SetKillCooldownText($"Kill: {Mathf.CeilToInt(_killCooldownSeconds)}s");
         }
         else
         {
+            _killCooldownSeconds = 0f;
+            if (networkSubsystem?.State != null)
+                networkSubsystem.State.KillCooldownRemaining = 0;
             uiSubsystem?.SetKillCooldownText("");
         }
     }
diff --git a/Assets/GameManager/GameManager_Map.cs b/Assets/GameManager/GameManager_Map.cs
index 12a4aa2..40a36e1 100644
--- a/Assets/GameManager/GameManager_Map.cs
+++ b/Assets/GameManager/GameManager_Map.cs
@@ -411,10 +411,14 @@ namespace Subsystems{
                 var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                 go.name = $"Task_{task.TaskId}";
                 go.transform.parent = _mapCenterPoint.transform;
-                go.transform.position = task.Location.ToLocalVector3(_centerPosition) + Vector3.up * 0.3f;
-                go.transform.localScale = Vector3.one * 0.5f;
+                go.transform.position = task.Location.ToLocalVector3(_centerPosition) + Vector3.up * 1f; // Raised
+                go.transform.localScale = Vector3.one * 8f; // Bigger
                 var mr = go.GetComponent<MeshRenderer>();
                 if (mr) mr.material.color = Color.yellow;
+                var light = go.AddComponent<Light>();
+                light.color = Color.yellow;
+                light.intensity = 2;
+                light.range = 5;
                 _taskMarkers[task.TaskId] = go;
             }
         }
diff --git a/Assets/GameManager/GameManager_Network.cs b/Assets/GameManager/GameManager_Network.cs
index 6eebf9b..cbf27a2 100644
--- a/Assets/GameManager/GameManager_Network.cs
+++ b/Assets/GameManager/GameManager_Network.cs
@@ -14,9 +14,14 @@ namespace Subsystems
         private const string _serverAddress = "geosus.honzuvkod.dev";
         private const int _serverPort = 7777;
         private GameClient _gameClient;
-        private GameManager _manager; // may be null for test clients
+        private GameManager _manager;
         private bool _pendingMapBuild;
 
+        /// <summary>
+        /// Authoritative game state; written here, read by GameManager_UI.
+        /// </summary>
+        public GameState State { get; } = new GameState();
+
         public GameManager_Network(GameClient gameClient, GameManager manager)
         {
             _gameClient = gameClient;
@@ -45,49 +50,40 @@ namespace Subsystems
                 }
                 Debug.Log($"Failed to connect (attempt {retries}). Retrying in {delayMs / 1000}s...");
                 await Task.Delay(delayMs);
-                delayMs = Mathf.Min(delayMs * 2, 30000); // exponential backoff, cap 30s
+                delayMs = Mathf.Min(delayMs * 2, 30000);
             }
         }
 
         public void RegisterEventHandlers()
         {
-            _gameClient.OnConnected += OnConnected;
+            _gameClient.OnConnected    += OnConnected;
             _gameClient.OnDisconnected += OnDisconnected;
-            _gameClient.OnError += OnError;
-            _gameClient.OnMessage += OnMessage;
-            _gameClient.OnGameEvent += OnGameEvent;
+            _gameClient.OnError        += OnError;
+            _gameClient.OnMessage      += OnMessage;
+            _gameClient.OnGameEvent    += OnGameEvent;
         }
 
-        private void OnConnected()
-        {
-            Debug.Log("Successfully connected to the server.");
-        }
+        private void OnConnected()    => Debug.Log("Successfully connected to the server.");
+        private void OnError(string e) => Debug.LogError($"Network error: {e}");
+
         private void OnDisconnected(string reason)
         {
             Debug.Log($"Disconnected: {reason}");
-            // Auto-reconnect unless the app is quitting
             if (reason != "Disposed" && _manager != null)
                 _manager.StartCoroutine(ReconnectAfterDelay(3f));
         }
 
-        private System.Collections.IEnumerator ReconnectAfterDelay(float seconds)
+        private IEnumerator ReconnectAfterDelay(float seconds)
         {
             yield return new UnityEngine.WaitForSeconds(seconds);
             Debug.Log("Attempting to reconnect...");
             OpenConnection();
         }
-        private void OnError(string error)
-        {
-            Debug.LogError($"Network error: {error}");
-        }
 
         private void OnMessage(Message message)
         {
             switch (message.Type)
             {
-                case "GameEvent":
-                    // handled via OnGameEvent
-                    break;
                 case "CreateLobbyResponse":
                     HandleCreateLobbyResponse(message as CreateLobbyResponse);
                     break;
@@ -98,6 +94,7 @@ namespace Subsystems
                     HandlePositionBroadcast(message as PositionBroadcast);
                     break;
                 case "Ack":
+                case "GameEvent":
                     break;
                 default:
                     Debug.Log("Received message of type: " + message.Type);
@@ -107,16 +104,19 @@ namespace Subsystems
 
         private void OnGameEvent(GameEvent gameEvent)
         {
+            // Always sync player list from lobby state after any event
+            SyncPlayersFromLobby();
+
             switch (gameEvent.EventType)
             {
                 case "PlayerJoined":
                 case "PlayerLeft":
                 case "HostChanged":
-                    // SDK already updates CurrentLobbyState; just refresh UI
                     _manager?.uiSubsystem?.NotifyLobbyChanged();
                     break;
 
                 case "GameStarting":
+                    State.Phase = GamePhase.Loading;
                     HandleGameStarting();
                     break;
 
@@ -125,7 +125,7 @@ namespace Subsystems
                     break;
 
                 case "GameStarted":
-                    HandleGameStarted();
+                    State.Phase = GamePhase.Playing;
                     break;
 
                 case "RoleAssigned":
@@ -142,13 +142,17 @@ namespace Subsystems
 
                 case "BodyReported":
                 case "EmergencyMeetingCalled":
-                    HandleMeetingCalled(gameEvent);
+                    Toast("Meeting called! Head to the meeting point.");
                     break;
 
                 case "MeetingStarted":
                     HandleMeetingStarted(gameEvent);
                     break;
 
+                case "PlayerVoted":
+                    HandlePlayerVoted(gameEvent);
+                    break;
+
                 case "VotingClosed":
                     HandleVotingClosed(gameEvent);
                     break;
@@ -167,6 +171,7 @@ namespace Subsystems
 
                 case "SabotageRepaired":
                 case "SabotageMeltdown":
+                    State.ActiveSabotage = null;
                     _manager?.uiSubsystem?.HideSabotageTimer();
                     _manager?.mapSubsystem?.ClearSabotageMarkers();
                     break;
@@ -188,10 +193,8 @@ namespace Subsystems
             if (message == null) return;
             if (message.Success)
             {
-                Debug.Log($"Lobby created. Code: {message.JoinCode}, ID: {message.LobbyId}");
-                // Navigate to the create/waiting scene
+                Debug.Log($"Lobby created. Code: {message.JoinCode}");
                 SceneManager.LoadScene("create", LoadSceneMode.Single);
-                // Mark lobby UI dirty so LobbyDisplayUI refreshes once the scene is loaded
                 _manager?.uiSubsystem?.NotifyLobbyChanged();
             }
             else
@@ -207,7 +210,6 @@ namespace Subsystems
             {
                 Debug.Log($"Joined lobby: {message.LobbyId}");
                 SceneManager.LoadScene("join loading", LoadSceneMode.Single);
-                // Mark lobby UI dirty so LobbyDisplayUI refreshes once the scene is loaded
                 _manager?.uiSubsystem?.NotifyLobbyChanged();
             }
             else
@@ -216,12 +218,24 @@ namespace Subsystems
             }
         }
 
-        // ── Game flow events ──────────────────────────────────────────────────
+        // ── Game flow ─────────────────────────────────────────────────────────
 
         private void HandleGameStarting()
         {
             _pendingMapBuild = false;
-            // SDK sets Phase = Loading; load Client.unity
+            // Reset per-game state
+            State.MyRole                = null;
+            State.IsDead                = false;
+            State.MyTasks               = new List<GameTask>();
+            State.MyCompletedTaskIds    = new HashSet<string>();
+            State.TotalCompleted        = 0;
+            State.TotalRequired         = 0;
+            State.ActiveMeeting         = null;
+            State.LastVoteResult        = null;
+            State.VotedPlayerIds        = new HashSet<string>();
+            State.ActiveSabotage        = null;
+            State.GameEndData           = null;
+            State.KillCooldownRemaining = 0;
             SceneManager.LoadScene("Client", LoadSceneMode.Single);
         }
 
@@ -231,10 +245,6 @@ namespace Subsystems
             TryBuildMapAndMarkers();
         }
 
-        /// <summary>
-        /// Called from GameManager.OnSceneLoaded("Client") after scene objects are bound.
-        /// Ensures map construction still happens even if MapDataReady arrived earlier.
-        /// </summary>
         public void OnClientSceneReady()
         {
             TryBuildMapAndMarkers();
@@ -243,34 +253,40 @@ namespace Subsystems
         private void TryBuildMapAndMarkers()
         {
             if (!_pendingMapBuild) return;
-            if (_manager?.mapSubsystem == null) return;
-            if (!_manager.mapSubsystem.IsSceneReady) return;
+            if (_manager?.mapSubsystem == null || !_manager.mapSubsystem.IsSceneReady) return;
             if (_gameClient?.CurrentLobbyState?.MapData == null) return;
 
             _manager.mapSubsystem.BuildMap();
             _manager.mapSubsystem.CreateTaskMarkers(_gameClient.MyTasks);
             _pendingMapBuild = false;
-            Debug.Log("[Network] Map built and task markers refreshed.");
-        }
-
-        private void HandleGameStarted()
-        {
-            Debug.Log("Game started");
-            // Phase is now Playing; GPS loop will start sending positions
+            Debug.Log("[Network] Map built.");
         }
 
         private void HandleRoleAssigned(GameEvent evt)
         {
             var payload = evt.GetPayload<RoleAssignedPayload>();
             if (payload == null || payload.ClientUuid != _gameClient.ClientUuid) return;
-            Debug.Log($"Role: {payload.Role}, Tasks: {payload.Tasks?.Count ?? 0}");
-            _manager?.taskSubsystem?.Initialize(_gameClient.MyTasks);
+
+            State.MyRole  = payload.Role;
+            State.MyTasks = payload.Tasks ?? new List<GameTask>();
+            State.MyCompletedTaskIds.Clear();
+
+            Debug.Log($"Role: {payload.Role}, Tasks: {State.MyTasks.Count}");
+            _manager?.taskSubsystem?.Initialize(State.MyTasks);
         }
 
         private void HandleTaskCompleted(GameEvent evt)
         {
             var payload = evt.GetPayload<TaskCompletedPayload>();
             if (payload == null) return;
+
+            // Track if it's our task
+            if (payload.ClientUuid == _gameClient.ClientUuid)
+                State.MyCompletedTaskIds.Add(payload.TaskId);
+
+            State.TotalCompleted = payload.TotalCompleted;
+            State.TotalRequired  = payload.TotalTasks;
+
             _manager?.uiSubsystem?.UpdateTaskProgress(payload.TotalCompleted, payload.TotalTasks);
             _manager?.mapSubsystem?.RemoveTaskMarker(payload.TaskId);
         }
@@ -279,28 +295,58 @@ namespace Subsystems
         {
             var payload = evt.GetPayload<PlayerKilledPayload>();
             if (payload == null) return;
-            _manager?.mapSubsystem?.CreateBodyMarker(payload.BodyId, payload.Location);
-            if (payload.VictimId == _gameClient.ClientUuid)
-                _manager?.uiSubsystem?.OnLocalPlayerDied();
-        }
 
-        private void HandleMeetingCalled(GameEvent evt)
-        {
-            _manager?.uiSubsystem?.ShowMeetingAlert();
+            _manager?.mapSubsystem?.CreateBodyMarker(payload.BodyId, payload.Location);
+
+            if (payload.VictimId == _gameClient.ClientUuid)
+            {
+                State.IsDead = true;
+                _manager?.uiSubsystem?.OnLocalPlayerDied();
+            }
+
+            // Update player state in our list
+            var p = State.Players.Find(x => x.ClientUuid == payload.VictimId);
+            if (p != null) p.State = PlayerState.Dead;
         }
 
         private void HandleMeetingStarted(GameEvent evt)
         {
             var payload = evt.GetPayload<MeetingStartedPayload>();
             if (payload == null) return;
-            _manager?.uiSubsystem?.ShowMeetingPanel(_gameClient.CurrentLobbyState?.Players, payload);
+
+            State.Phase          = GamePhase.Meeting;
+            State.ActiveMeeting  = payload;
+            State.VotedPlayerIds = new HashSet<string>();
+            State.LastVoteResult = null;
+
+            SyncPlayersFromLobby();
+            _manager?.uiSubsystem?.ShowMeetingPanel(State.Players, payload);
+        }
+
+        private void HandlePlayerVoted(GameEvent evt)
+        {
+            var payload = evt.GetPayload<PlayerVotedPayload>();
+            if (payload == null) return;
+            State.VotedPlayerIds.Add(payload.VoterId);
         }
 
         private void HandleVotingClosed(GameEvent evt)
         {
             var payload = evt.GetPayload<VotingClosedPayload>();
             if (payload == null) return;
-            _manager?.uiSubsystem?.ShowVoteResult(payload, _gameClient.CurrentLobbyState?.Players);
+
+            State.Phase          = GamePhase.Playing;
+            State.ActiveMeeting  = null;
+            State.LastVoteResult = payload;
+
+            // Mark ejected player dead in our list
+            if (!string.IsNullOrEmpty(payload.EjectedPlayerId))
+            {
+                var p = State.Players.Find(x => x.ClientUuid == payload.EjectedPlayerId);
+                if (p != null) p.State = PlayerState.Dead;
+            }
+
+            _manager?.uiSubsystem?.ShowVoteResult(payload, State.Players);
             _manager?.mapSubsystem?.ClearBodyMarkers();
         }
 
@@ -308,11 +354,16 @@ namespace Subsystems
         {
             var payload = evt.GetPayload<GameEndedPayload>();
             if (payload == null) return;
+
+            State.Phase       = GamePhase.Ended;
+            State.GameEndData = payload;
+
             _manager?.uiSubsystem?.ShowGameEndPanel(payload, _gameClient.ClientUuid);
         }
 
         private void HandleReturnedToLobby()
         {
+            State.Phase = GamePhase.Lobby;
             if (_gameClient.IsOwner)
                 SceneManager.LoadScene("create", LoadSceneMode.Single);
             else
@@ -323,6 +374,9 @@ namespace Subsystems
         {
             var payload = evt.GetPayload<SabotageStartedPayload>();
             if (payload == null) return;
+
+            State.ActiveSabotage = payload;
+
             _manager?.mapSubsystem?.CreateSabotageMarkers(payload.RepairStations);
             if (payload.Type == SabotageType.CriticalMeltdown && payload.Deadline.HasValue)
                 _manager?.uiSubsystem?.ShowSabotageTimer(payload.Deadline.Value);
@@ -336,6 +390,21 @@ namespace Subsystems
             _manager?.mapSubsystem?.UpdatePlayerAvatars(_gameClient.PlayerPositions, _gameClient.ClientUuid);
         }
 
+        // ── Helpers ───────────────────────────────────────────────────────────
+
+        private void SyncPlayersFromLobby()
+        {
+            var lobby = _gameClient.CurrentLobbyState;
+            if (lobby?.Players != null)
+                State.Players = lobby.Players;
+        }
+
+        private void Toast(string message)
+        {
+            State.ToastMessage = message;
+            State.ToastExpiry  = UnityEngine.Time.time + 4f;
+        }
+
         // ── Send helpers ──────────────────────────────────────────────────────
 
         public void CreateLobby(double lat, double lon, double radius = 500, int impostorCount = 1, int taskCount = 5)
@@ -352,6 +421,7 @@ namespace Subsystems
         public void LeaveLobby()
         {
             _gameClient.LeaveLobby();
+            State.Phase = GamePhase.Lobby;
             SceneManager.LoadScene(_manager?.firstMenuScene ?? "main menu asi idk lol", LoadSceneMode.Single);
         }
 
diff --git a/Assets/GameManager/GameManager_Tasks.cs b/Assets/GameManager/GameManager_Tasks.cs
index 11725fa..bbab151 100644
--- a/Assets/GameManager/GameManager_Tasks.cs
+++ b/Assets/GameManager/GameManager_Tasks.cs
@@ -176,12 +176,10 @@ namespace Subsystems
             taskComponent.TaskLocation = (entry.ServerTask.Location.Lat, entry.ServerTask.Location.Lon);
 
             bool done = false;
-            bool exited = false;
             taskComponent.Initialize(t => { done = true; });
-            taskComponent.ExitTask(t => { exited = true; });
 
             // Wait for completion or exit
-            yield return new WaitUntil(() => done || exited);
+            yield return new WaitUntil(() => done);
 
             yield return FinishMinigame(entry, done);
         }
diff --git a/Assets/GameManager/GameManager_UI.cs b/Assets/GameManager/GameManager_UI.cs
index c3337f3..a28c9fb 100644
--- a/Assets/GameManager/GameManager_UI.cs
+++ b/Assets/GameManager/GameManager_UI.cs
@@ -9,168 +9,208 @@ using TMPro;
 namespace Subsystems
 {
     /// <summary>
-    /// Manages UI for the GameManager. Canvas references are only valid in Client.unity;
-    /// Art-menu scenes use their own lightweight UI scripts that read from GameManager.Instance.
+    /// Reads from GameManager_Network.State (the authoritative GameState) and drives
+    /// all in-game canvas panels. No business logic lives here.
     /// </summary>
     public class GameManager_UI
     {
         private GameClient _gameClient;
+        private GameState  _state => GameManager.Instance?.networkSubsystem?.State;
 
-        // Set by GameManager after Client.unity loads (called from GameManager.OnSceneLoaded)
-        public Canvas ClientCreateJoinLobby;  // fallback join-code canvas in Client.unity
-        public Canvas ClientInLobby;          // InLobby canvas in Client.unity (unused now, kept compat)
+        // ── Canvas refs (wired by BindClientScene from Client.unity) ──────────
+        public Canvas ClientCreateJoinLobby;
+        public Canvas ClientInLobby;
         public Canvas ClientLoadingScreen;
-        public Canvas ClientGameScreen;       // parent of all HUD elements
+        public Canvas ClientGameScreen;
 
-        // HUD elements (children of ClientGameScreen, resolved at runtime)
-        private TMP_Text _roleText;
-        private TMP_Text _taskListText;
-        private TMP_Text _taskProgressText;
-        private Button _actionButton;
-        private TMP_Text _actionButtonText;
-        private TMP_Text _killCooldownText;
+        // ── HUD element refs (resolved once in BindClientScene) ───────────────
+        private TMP_Text   _roleText;
+        private TMP_Text   _taskListText;
+        private TMP_Text   _taskProgressText;
+        private Button     _actionButton;
+        private TMP_Text   _actionButtonText;
+        private TMP_Text   _killCooldownText;
         private GameObject _sabotagePanel;
-        private TMP_Text _sabotageTimerText;
+        private TMP_Text   _sabotageTimerText;
         private GameObject _meetingPanel;
+        private TMP_Text   _meetingHeader;
+        private Transform  _meetingScrollContent;
+        private TMP_Text   _meetingFallbackText;
+        private GameObject _voteResultPanel;
+        private TMP_Text   _voteResultText;
         private GameObject _gameEndPanel;
-        private TMP_Text _gameEndText;
+        private TMP_Text   _gameEndText;
+        private RectTransform _returnToLobbyBtn;
+        private TMP_Text   _toastText;
+        private GameObject _toastGO;
 
-        // Runtime state
+        // ── Internal state ────────────────────────────────────────────────────
         private bool _isDead;
         private bool _commsBlackout;
         private DateTime _sabotageMeltdownDeadline;
         private bool _sabotageTimerActive;
-
-        // Lobby-changed flag — set from network thread, consumed in Update
         private volatile bool _lobbyDirty;
 
-        public GameManager_UI(GameClient gameClient)
-        {
-            _gameClient = gameClient;
-        }
+        // Meeting vote-row references rebuilt each meeting
+        private readonly List<GameObject> _voteRows = new List<GameObject>();
+        private string _pendingVoteResultDisplay; // shown after voting
+
+        public GameManager_UI(GameClient gameClient) { _gameClient = gameClient; }
 
-        /// <summary>Called by Network subsystem when lobby player list changes.</summary>
         public void NotifyLobbyChanged() => _lobbyDirty = true;
+        public bool IsCommsBlackout => _commsBlackout;
+        public bool IsPlayerDead    => _isDead;
 
-        // ── Called from GameManager after Client.unity loads ──────────────────
+        // ── Scene binding ─────────────────────────────────────────────────────
 
         public void BindClientScene(Canvas createJoin, Canvas inLobby, Canvas loading, Canvas game)
         {
             ClientCreateJoinLobby = createJoin;
-            ClientInLobby = inLobby;
-            ClientLoadingScreen = loading;
-            ClientGameScreen = game;
+            ClientInLobby         = inLobby;
+            ClientLoadingScreen   = loading;
+            ClientGameScreen      = game;
 
-            EnsureCanvasReady(createJoin);
-            EnsureCanvasReady(inLobby);
-            EnsureCanvasReady(loading);
-            EnsureCanvasReady(game);
+            foreach (var c in new[] { createJoin, inLobby, loading, game })
+                EnsureCanvasReady(c);
 
             if (createJoin) createJoin.gameObject.SetActive(false);
-            if (inLobby) inLobby.gameObject.SetActive(false);
-            if (loading) loading.gameObject.SetActive(false);
-            if (game) game.gameObject.SetActive(false);
+            if (inLobby)    inLobby.gameObject.SetActive(false);
+            if (loading)    loading.gameObject.SetActive(false);
+            if (game)       game.gameObject.SetActive(false);
 
-            if (game != null)
+            if (game == null) return;
+            var t = game.transform;
+
+            _roleText          = FindTMP(t, "Role");
+            _taskListText      = FindTMP(t, "TaskList");
+            _taskProgressText  = FindTMP(t, "TaskProgress");
+            _killCooldownText  = FindTMP(t, "KillCooldown");
+            _sabotageTimerText = FindTMP(t, "SabotageTimer");
+            _gameEndText       = FindTMP(t, "GameEndText");
+            _toastText         = FindTMP(t, "Toast");
+            _meetingHeader     = FindTMP(t, "MeetingHeader");
+            _meetingFallbackText = FindTMP(t, "MeetingPlayerList");
+            _voteResultText    = FindTMP(t, "VoteResult");
+            _meetingScrollContent = FindTransform(t, "MeetingContent");
+
+            var actionGO = t.Find("ActionButton");
+            if (actionGO != null)
             {
-                _roleText           = FindTMP(game.transform, "Role");
-                _taskListText       = FindTMP(game.transform, "TaskList");
-                _taskProgressText   = FindTMP(game.transform, "TaskProgress");
-                _killCooldownText   = FindTMP(game.transform, "KillCooldown");
-                _sabotageTimerText  = FindTMP(game.transform, "SabotageTimer");
-                _gameEndText        = FindTMP(game.transform, "GameEndText");
-
-                var actionGO = game.transform.Find("ActionButton");
-                if (actionGO != null)
-                {
-                    _actionButton = actionGO.GetComponent<Button>();
-                    _actionButtonText = actionGO.GetComponentInChildren<TMP_Text>();
-                }
-
-                var sabGO = game.transform.Find("SabotagePanel");
-                _sabotagePanel = sabGO?.gameObject;
-
-                var meetGO = game.transform.Find("MeetingPanel");
-                _meetingPanel = meetGO?.gameObject;
-                if (_meetingPanel) _meetingPanel.SetActive(false);
-
-                var endGO = game.transform.Find("GameEndPanel");
-                _gameEndPanel = endGO?.gameObject;
-                if (_gameEndPanel) _gameEndPanel.SetActive(false);
+                _actionButton     = actionGO.GetComponent<Button>();
+                _actionButtonText = actionGO.GetComponentInChildren<TMP_Text>();
             }
+
+            _sabotagePanel   = t.Find("SabotagePanel")?.gameObject;
+            _meetingPanel    = t.Find("MeetingPanel")?.gameObject;
+            _gameEndPanel    = t.Find("GameEndPanel")?.gameObject;
+            _voteResultPanel = FindTransformGO(t, "VoteResultPanel");
+            _toastGO         = FindTransformGO(t, "Toast");
+
+            var retBtn = FindTransform(t, "ReturnToLobbyButton");
+            if (retBtn != null) _returnToLobbyBtn = retBtn as RectTransform;
+
+            if (_meetingPanel) _meetingPanel.SetActive(false);
+            if (_gameEndPanel) _gameEndPanel.SetActive(false);
+            if (_voteResultPanel) _voteResultPanel.SetActive(false);
+            if (_toastGO) _toastGO.SetActive(false);
         }
 
-        // ── Main update (called every frame from GameManager.Update) ──────────
+        // ── Update (called every frame from GameManager.Update) ───────────────
 
         public void UpdateLobbyUI()
         {
-            var state = _gameClient.CurrentLobbyState;
-            if (state == null) return;
+            var lobbyState = _gameClient.CurrentLobbyState;
+            if (lobbyState == null) return;
 
-            // Update any LobbyDisplayUI listeners in the current scene
             if (_lobbyDirty)
             {
                 _lobbyDirty = false;
-                LobbyDisplayUI.RefreshAll(state);
+                LobbyDisplayUI.RefreshAll(lobbyState);
             }
 
-            // Only do canvas switches if we are in Client.unity (canvases assigned)
             if (ClientGameScreen == null) return;
 
-            switch (state.Phase)
+            switch (lobbyState.Phase)
             {
                 case GamePhase.Loading:
                     SetCanvases(false, false, true, false);
                     break;
-
                 case GamePhase.Lobby:
                     SetCanvases(false, true, false, false);
                     break;
-
                 case GamePhase.Playing:
                 case GamePhase.Meeting:
                 case GamePhase.Voting:
                     SetCanvases(false, false, false, true);
                     UpdateGameHUD();
                     break;
-
                 case GamePhase.Ended:
-                    // GameEndPanel shown by HandleGameEnded
+                    SetCanvases(false, false, false, true);
                     break;
             }
+
+            TickToast();
         }
 
+        // ── Game HUD tick ─────────────────────────────────────────────────────
+
         private void UpdateGameHUD()
         {
-            if (_roleText != null) _roleText.text = _gameClient.MyRole?.ToString() ?? "";
+            var s = _state;
+            if (s == null) return;
 
-            // Task list
+            // Role
+            if (_roleText != null)
+            {
+                string ghostSuffix = s.IsDead ? " (GHOST)" : "";
+                _roleText.text  = $"{s.MyRole?.ToString() ?? "?"}{ghostSuffix}";
+                _roleText.color = s.MyRole == PlayerRole.Impostor ? new Color(0.9f,0.2f,0.2f) : new Color(0.2f,0.8f,1f);
+            }
+
+            // Task list with checkmarks
             if (_taskListText != null)
             {
                 var sb = new System.Text.StringBuilder();
-                foreach (var t in _gameClient.MyTasks)
-                    sb.AppendLine(t.Name);
+                foreach (var task in s.MyTasks)
+                {
+                    bool done = s.MyCompletedTaskIds.Contains(task.TaskId);
+                    string mark = done ? "<color=#2DB84B>✓</color>" : "○";
+                    sb.AppendLine($"{mark} {task.Name}");
+                }
                 _taskListText.text = sb.ToString();
             }
 
-            // Kill cooldown (managed by GameManager_Tasks via Update)
-            // Sabotage timer
+            // Global task progress
+            if (_taskProgressText != null && s.TotalRequired > 0)
+                _taskProgressText.text = $"Tasks: {s.TotalCompleted}/{s.TotalRequired}";
+
+            // Kill cooldown
+            if (_killCooldownText != null)
+            {
+                bool show = s.KillCooldownRemaining > 0;
+                _killCooldownText.gameObject.SetActive(show);
+                if (show) _killCooldownText.text = $"Kill: {Mathf.CeilToInt(s.KillCooldownRemaining)}s";
+            }
+
+            // Sabotage banner
             if (_sabotageTimerActive && _sabotageTimerText != null)
             {
                 double remaining = (_sabotageMeltdownDeadline - DateTime.UtcNow).TotalSeconds;
-                _sabotageTimerText.text = remaining > 0 ? $"MELTDOWN: {remaining:F0}s" : "MELTDOWN!";
+                _sabotageTimerText.text = remaining > 0 ? $"⚠ MELTDOWN: {remaining:F0}s" : "⚠ MELTDOWN!";
             }
+
+            // Keep meeting voted-indicator rows fresh each frame
+            TickMeetingVoteIndicators();
         }
 
-        // ── Helpers called by Network handlers ────────────────────────────────
+        // ── Kill cooldown helper (called from GameManager) ────────────────────
 
         public void SetKillCooldownText(string text)
         {
-            if (_killCooldownText != null)
-            {
-                _killCooldownText.text = text;
-                _killCooldownText.gameObject.SetActive(!string.IsNullOrEmpty(text));
-            }
+            if (_killCooldownText == null) return;
+            bool show = !string.IsNullOrEmpty(text);
+            _killCooldownText.gameObject.SetActive(show);
+            if (show) _killCooldownText.text = text;
         }
 
         public void UpdateTaskProgress(int completed, int total)
@@ -179,6 +219,8 @@ namespace Subsystems
                 _taskProgressText.text = $"Tasks: {completed}/{total}";
         }
 
+        // ── Action button ─────────────────────────────────────────────────────
+
         public void SetActionButton(string label, bool visible, UnityEngine.Events.UnityAction onClick = null)
         {
             if (_actionButton == null) return;
@@ -191,15 +233,19 @@ namespace Subsystems
             }
         }
 
+        // ── Player state ──────────────────────────────────────────────────────
+
         public void OnLocalPlayerDied()
         {
             _isDead = true;
-            if (_roleText != null) _roleText.text = "GHOST";
+            if (_state != null) _state.IsDead = true;
         }
 
+        // ── Meeting ───────────────────────────────────────────────────────────
+
         public void ShowMeetingAlert()
         {
-            Debug.Log("Meeting called! Run to meeting point.");
+            ShowToast("⚠ Meeting called! Head to the meeting point.");
         }
 
         public void ShowMeetingPanel(List<PlayerInfo> players, MeetingStartedPayload payload)
@@ -207,99 +253,217 @@ namespace Subsystems
             if (_meetingPanel == null) return;
             _meetingPanel.SetActive(true);
 
-            var header = FindTMP(_meetingPanel.transform, "MeetingHeader");
-            if (header != null)
-                header.text = payload.Type == MeetingType.BodyReport ? "BODY REPORTED!" : "EMERGENCY MEETING!";
+            if (_meetingHeader != null)
+                _meetingHeader.text = payload.Type == MeetingType.BodyReport ? "BODY REPORTED!" : "EMERGENCY MEETING!";
 
-            // Build simple text list of players — full vote buttons need prefabs in Unity Editor
-            var playerList = FindTMP(_meetingPanel.transform, "MeetingPlayerList");
-            if (playerList != null && players != null)
+            if (_voteResultPanel) _voteResultPanel.SetActive(false);
+
+            BuildMeetingVoteRows(players);
+        }
+
+        private void BuildMeetingVoteRows(List<PlayerInfo> players)
+        {
+            // Clear old rows
+            foreach (var r in _voteRows) if (r) UnityEngine.Object.Destroy(r);
+            _voteRows.Clear();
+
+            if (_meetingScrollContent == null || players == null)
             {
-                var sb = new System.Text.StringBuilder();
-                foreach (var p in players)
-                    sb.AppendLine($"{p.DisplayName}  [{p.State}]");
-                playerList.text = sb.ToString();
+                // Fall back to text list
+                if (_meetingFallbackText != null)
+                {
+                    _meetingFallbackText.gameObject.SetActive(true);
+                    var sb = new System.Text.StringBuilder();
+                    foreach (var p in players ?? new List<PlayerInfo>())
+                        sb.AppendLine($"{p.DisplayName}  [{p.State}]");
+                    _meetingFallbackText.text = sb.ToString();
+                }
+                return;
             }
 
-            // Wire skip button if it exists
-            var skipBtn = _meetingPanel.transform.Find("SkipButton")?.GetComponent<Button>();
-            if (skipBtn != null)
+            string myId = _gameClient.ClientUuid;
+            bool canVote = !_isDead;
+
+            foreach (var player in players)
             {
-                skipBtn.onClick.RemoveAllListeners();
-                skipBtn.onClick.AddListener(() => GameManager.Instance?.CastVote(null));
+                bool isMe   = player.ClientUuid == myId;
+                bool isAlive = player.State == PlayerState.Alive;
+                var row = BuildVoteRow(player, isMe, isAlive, canVote && isAlive && !isMe);
+                row.transform.SetParent(_meetingScrollContent, false);
+                _voteRows.Add(row);
+            }
+        }
+
+        private GameObject BuildVoteRow(PlayerInfo player, bool isMe, bool isAlive, bool canVote)
+        {
+            const float ROW_H = 110f;
+            var go = new GameObject($"VoteRow_{player.ClientUuid}");
+            var rt = go.AddComponent<RectTransform>();
+            rt.sizeDelta = new Vector2(0, ROW_H);
+            var le = go.AddComponent<LayoutElement>();
+            le.minHeight = le.preferredHeight = ROW_H;
+
+            var bg = go.AddComponent<Image>();
+            bg.color = isMe ? new Color(0.12f,0.18f,0.30f) : new Color(0.10f,0.12f,0.20f);
+
+            // Dead overlay
+            if (!isAlive)
+            {
+                bg.color = new Color(0.08f,0.08f,0.10f,0.7f);
+            }
+
+            // Name label
+            var namRT = MakeChild("Name", rt);
+            namRT.anchorMin = new Vector2(0,0); namRT.anchorMax = new Vector2(0.65f,1);
+            namRT.offsetMin = new Vector2(16,6); namRT.offsetMax = new Vector2(0,-6);
+            var namTmp = namRT.gameObject.AddComponent<TextMeshProUGUI>();
+            namTmp.text      = (player.IsOwner ? "👑 " : "") + (player.DisplayName ?? "???");
+            namTmp.fontSize  = 36;
+            namTmp.color     = !isAlive ? Color.gray : (isMe ? Color.white : new Color(0.73f,0.8f,0.88f));
+            namTmp.fontStyle = isMe ? FontStyles.Bold : FontStyles.Normal;
+            namTmp.alignment = TextAlignmentOptions.MidlineLeft;
+
+            // Vote button
+            var voteBtnRT = MakeChild("VoteBtn", rt);
+            voteBtnRT.anchorMin = new Vector2(0.68f,0.10f); voteBtnRT.anchorMax = new Vector2(0.95f,0.90f);
+            var voteBg = voteBtnRT.gameObject.AddComponent<Image>();
+            voteBg.color = canVote ? new Color(0.2f,0.6f,1f) : new Color(0.2f,0.2f,0.2f,0.5f);
+            var voteBtn = voteBtnRT.gameObject.AddComponent<Button>();
+            voteBtn.targetGraphic = voteBg;
+            voteBtn.interactable  = canVote;
+            string capturedId = player.ClientUuid;
+            voteBtn.onClick.AddListener(() => GameManager.Instance?.CastVote(capturedId));
+            var voteTxtRT = MakeChild("Txt", voteBtnRT);
+            Stretch(voteTxtRT);
+            var voteTmp = voteTxtRT.gameObject.AddComponent<TextMeshProUGUI>();
+            voteTmp.text      = isAlive ? "VOTE" : "DEAD";
+            voteTmp.fontSize  = 28;
+            voteTmp.fontStyle = FontStyles.Bold;
+            voteTmp.color     = Color.white;
+            voteTmp.alignment = TextAlignmentOptions.Center;
+
+            // Voted indicator (hidden by default; shown by TickMeetingVoteIndicators)
+            var votedRT = MakeChild("VotedTick", rt);
+            votedRT.anchorMin = new Vector2(0.95f,0.20f); votedRT.anchorMax = new Vector2(1f,0.80f);
+            var vtTmp = votedRT.gameObject.AddComponent<TextMeshProUGUI>();
+            vtTmp.text = "✓"; vtTmp.fontSize = 34;
+            vtTmp.color = new Color(0.18f,0.75f,0.30f); vtTmp.alignment = TextAlignmentOptions.Center;
+            votedRT.gameObject.SetActive(false);
+
+            return go;
+        }
+
+        private void TickMeetingVoteIndicators()
+        {
+            var s = _state;
+            if (s == null) return;
+            foreach (var row in _voteRows)
+            {
+                if (row == null) continue;
+                // Row name is "VoteRow_<uuid>"
+                string uuid = row.name.Replace("VoteRow_", "");
+                var tick = row.transform.Find("VotedTick")?.gameObject;
+                if (tick != null)
+                    tick.SetActive(s.VotedPlayerIds.Contains(uuid));
             }
         }
 
         public void AppendVoteInstruction()
         {
-            var playerList = FindTMP(_meetingPanel?.transform, "MeetingPlayerList");
-            if (playerList != null)
-                playerList.text += "\n[Tap a name] then press VOTE — or press SKIP";
+            // no-op – vote instructions are embedded in the row buttons
         }
 
         public void ShowVoteResult(VotingClosedPayload payload, List<PlayerInfo> players)
         {
-            if (_meetingPanel == null) return;
-
-            var resultText = FindTMP(_meetingPanel.transform, "VoteResult");
-            if (resultText != null)
+            if (_voteResultPanel != null) _voteResultPanel.SetActive(true);
+            if (_voteResultText != null)
             {
                 if (payload.WasTie)
-                {
-                    resultText.text = "TIE — nobody ejected.";
-                }
+                    _voteResultText.text = "⚖ TIE — nobody ejected.";
                 else if (string.IsNullOrEmpty(payload.EjectedPlayerId))
-                {
-                    resultText.text = "Skip — nobody ejected.";
-                }
+                    _voteResultText.text = "Nobody ejected (skip).";
                 else
                 {
-                    var ejected = players?.Find(p => p.ClientUuid == payload.EjectedPlayerId);
-                    resultText.text = $"{ejected?.DisplayName ?? payload.EjectedPlayerId} ejected!";
+                    var ej = players?.Find(p => p.ClientUuid == payload.EjectedPlayerId);
+                    _voteResultText.text = $"🚪 {ej?.DisplayName ?? payload.EjectedPlayerId} ejected!";
                 }
             }
 
-            // Close panel after 5 seconds — use coroutine via GameManager
-            GameManager.Instance?.StartCoroutine(CloseMeetingPanelAfterDelay(5f));
+            // Auto-close meeting panel after 5 s
+            GameManager.Instance?.StartCoroutine(CloseMeetingAfterDelay(5f));
         }
 
-        private System.Collections.IEnumerator CloseMeetingPanelAfterDelay(float delay)
+        private System.Collections.IEnumerator CloseMeetingAfterDelay(float delay)
         {
             yield return new UnityEngine.WaitForSeconds(delay);
-            if (_meetingPanel != null) _meetingPanel.SetActive(false);
+            if (_meetingPanel) _meetingPanel.SetActive(false);
+            if (_voteResultPanel) _voteResultPanel.SetActive(false);
         }
 
-        public void ShowGameEndPanel(GameEndedPayload payload, string myUuid)
-        {
-            if (_gameEndPanel != null) _gameEndPanel.SetActive(true);
-            bool won = payload.Winners != null && payload.Winners.Contains(myUuid);
-            if (_gameEndText != null)
-                _gameEndText.text = $"{(won ? "VICTORY" : "DEFEAT")}\n{payload.WinningFaction} wins!\n{payload.Reason}";
-        }
+        // ── Sabotage ──────────────────────────────────────────────────────────
 
         public void ShowSabotageTimer(DateTime deadline)
         {
             _sabotageMeltdownDeadline = deadline;
-            _sabotageTimerActive = true;
-            if (_sabotageTimerText != null) _sabotageTimerText.gameObject.SetActive(true);
+            _sabotageTimerActive      = true;
+            if (_sabotagePanel) _sabotagePanel.SetActive(true);
+            if (_sabotageTimerText) _sabotageTimerText.gameObject.SetActive(true);
         }
 
         public void HideSabotageTimer()
         {
             _sabotageTimerActive = false;
-            if (_sabotageTimerText != null) _sabotageTimerText.gameObject.SetActive(false);
+            if (_sabotagePanel) _sabotagePanel.SetActive(false);
             SetCommsBlackout(false);
         }
 
-        public void SetCommsBlackout(bool active)
+        public void SetCommsBlackout(bool active) => _commsBlackout = active;
+
+        // ── Game end ──────────────────────────────────────────────────────────
+
+        public void ShowGameEndPanel(GameEndedPayload payload, string myUuid)
         {
-            _commsBlackout = active;
+            if (_gameEndPanel) _gameEndPanel.SetActive(true);
+            if (_gameEndText != null)
+            {
+                bool won = payload.Winners?.Contains(myUuid) ?? false;
+                string title     = won ? "<color=#FFB800>🏆 VICTORY</color>" : "<color=#C43232>💔 DEFEAT</color>";
+                string faction   = payload.WinningFaction == "Impostor" ? "Impostors win!" : "Crew wins!";
+                _gameEndText.text = $"{title}\n{faction}\n<size=38>{payload.Reason}</size>";
+            }
+
+            // Show "Return to Lobby" only for the host
+            if (_returnToLobbyBtn != null)
+                _returnToLobbyBtn.gameObject.SetActive(_gameClient.IsOwner);
         }
 
-        public bool IsCommsBlackout => _commsBlackout;
-        public bool IsPlayerDead => _isDead;
+        // ── Toast ─────────────────────────────────────────────────────────────
 
-        // ── Utilities ─────────────────────────────────────────────────────────
+        public void ShowToast(string message)
+        {
+            if (_state != null) { _state.ToastMessage = message; _state.ToastExpiry = UnityEngine.Time.time + 4f; }
+            if (_toastGO == null) return;
+            _toastGO.SetActive(true);
+            if (_toastText != null) _toastText.text = message;
+        }
+
+        private void TickToast()
+        {
+            var s = _state;
+            if (_toastGO == null) return;
+
+            if (s != null && !string.IsNullOrEmpty(s.ToastMessage) && UnityEngine.Time.time < s.ToastExpiry)
+            {
+                _toastGO.SetActive(true);
+                if (_toastText != null) _toastText.text = s.ToastMessage;
+            }
+            else
+            {
+                _toastGO.SetActive(false);
+            }
+        }
+
+        // ── Canvas switching ──────────────────────────────────────────────────
 
         private void SetCanvases(bool createJoin, bool inLobby, bool loading, bool game)
         {
@@ -314,14 +478,12 @@ namespace Subsystems
             if (ClientGameScreen)      ClientGameScreen.gameObject.SetActive(game);
         }
 
+        // ── Utilities ─────────────────────────────────────────────────────────
+
         private static void EnsureCanvasReady(Canvas canvas)
         {
             if (canvas == null) return;
-
-            if (!canvas.enabled)
-                canvas.enabled = true;
-
-            // Some scene canvases are saved with zero scale, which makes UI invisible.
+            if (!canvas.enabled) canvas.enabled = true;
             var t = canvas.transform;
             if (t != null)
             {
@@ -335,12 +497,34 @@ namespace Subsystems
         {
             if (root == null) return null;
             foreach (var tmp in root.GetComponentsInChildren<TMP_Text>(true))
-            {
-                if (tmp != null && tmp.name == name)
-                    return tmp;
-            }
+                if (tmp != null && tmp.name == name) return tmp;
             return null;
         }
+
+        private static Transform FindTransform(Transform root, string name)
+        {
+            if (root == null) return null;
+            foreach (Transform t in root.GetComponentsInChildren<Transform>(true))
+                if (t.name == name) return t;
+            return null;
+        }
+
+        private static GameObject FindTransformGO(Transform root, string name)
+            => FindTransform(root, name)?.gameObject;
+
+        private static RectTransform MakeChild(string name, RectTransform parent)
+        {
+            var go = new GameObject(name);
+            var rt = go.AddComponent<RectTransform>();
+            rt.SetParent(parent, false);
+            rt.localScale = Vector3.one;
+            return rt;
+        }
+
+        private static void Stretch(RectTransform rt)
+        {
+            rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one;
+            rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero;
+        }
     }
 }
-
diff --git a/Assets/Scripts/GameState.cs b/Assets/Scripts/GameState.cs
new file mode 100644
index 0000000..08273d8
--- /dev/null
+++ b/Assets/Scripts/GameState.cs
@@ -0,0 +1,42 @@
+using GeoSus.Client;
+using System.Collections.Generic;
+
+/// <summary>
+/// Single source of truth for all in-game state on the client.
+/// Updated exclusively by GameManager_Network; read by GameManager_UI.
+/// </summary>
+public class GameState
+{
+    // ── Phase / Role ──────────────────────────────────────────────────────────
+    public GamePhase Phase         { get; set; } = GamePhase.Lobby;
+    public PlayerRole? MyRole      { get; set; }
+    public bool IsDead             { get; set; }
+
+    // ── Tasks ─────────────────────────────────────────────────────────────────
+    public List<GameTask>    MyTasks             { get; set; } = new List<GameTask>();
+    public HashSet<string>   MyCompletedTaskIds  { get; set; } = new HashSet<string>();
+    public int               TotalCompleted      { get; set; }
+    public int               TotalRequired       { get; set; }
+
+    // ── Players ───────────────────────────────────────────────────────────────
+    public List<PlayerInfo>  Players             { get; set; } = new List<PlayerInfo>();
+
+    // ── Meeting ───────────────────────────────────────────────────────────────
+    public MeetingStartedPayload  ActiveMeeting  { get; set; }
+    public VotingClosedPayload    LastVoteResult { get; set; }
+    public HashSet<string>        VotedPlayerIds { get; set; } = new HashSet<string>();
+
+    // ── Sabotage ──────────────────────────────────────────────────────────────
+    public SabotageStartedPayload ActiveSabotage { get; set; }
+
+    // ── End game ──────────────────────────────────────────────────────────────
+    public GameEndedPayload GameEndData { get; set; }
+
+    // ── Kill cooldown (tracked by GameManager, reflected here for UI) ─────────
+    public float KillCooldownRemaining { get; set; }
+
+    // ── Notification (toast) ─────────────────────────────────────────────────
+    public string ToastMessage  { get; set; }
+    public float  ToastExpiry   { get; set; }   // UnityEngine.Time.time
+}
+
diff --git a/Assets/Scripts/GameState.cs.meta b/Assets/Scripts/GameState.cs.meta
new file mode 100644
index 0000000..abda482
--- /dev/null
+++ b/Assets/Scripts/GameState.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 82b3963a05498b68baf483476d0d81f4
\ No newline at end of file
diff --git a/Assets/Scripts/HostLobbyUI.cs b/Assets/Scripts/HostLobbyUI.cs
index 16ca5a5..82e415a 100644
--- a/Assets/Scripts/HostLobbyUI.cs
+++ b/Assets/Scripts/HostLobbyUI.cs
@@ -3,63 +3,397 @@ using UnityEngine.UI;
 using TMPro;
 
 /// <summary>
-/// Attach to a manager GameObject in host lobby.unity.
-/// Reads radius from the "radius" slider/input and triggers CreateLobby.
-/// Also wires the "vytvořit" button.
+/// Attach to any manager GameObject in the create-lobby scene.
+/// On Start it nukes all Art placeholder children from the Canvas and builds
+/// a complete mobile-portrait lobby-configuration screen entirely in code.
 /// </summary>
 public class HostLobbyUI : MonoBehaviour
 {
-    [Header("Optional refs (auto-found by name if null)")]
-    public Slider radiusSlider;
-    public TMP_InputField radiusInput;
-    public Button createButton;
+    // ── Colours ───────────────────────────────────────────────────────────────
+    static Color H(string hex) { ColorUtility.TryParseHtmlString(hex, out var c); return c; }
+    static readonly Color C_BG      = H("#0D0F1A");
+    static readonly Color C_HDR     = H("#141927");
+    static readonly Color C_CARD    = H("#111525");
+    static readonly Color C_BORDER  = H("#1E2540");
+    static readonly Color C_ACCENT  = H("#3399FF");
+    static readonly Color C_GREEN   = H("#2DB84B");
+    static readonly Color C_MUTED   = new Color(0.47f, 0.53f, 0.67f);
+    static readonly Color C_WHITE   = Color.white;
+    static readonly Color C_INPUT   = H("#0A0D1A");
+
+    // ── Live values ───────────────────────────────────────────────────────────
+    private float _radius       = 500f;
+    private int   _impostors    = 1;
+    private int   _tasks        = 5;
+    private string _playerName  = "";
+
+    // ── UI refs ───────────────────────────────────────────────────────────────
+    private TMP_Text _radiusValueLabel;
+    private TMP_Text _impostorValueLabel;
+    private TMP_Text _taskValueLabel;
+    private TMP_Text _statusText;
 
     void Start()
     {
-        if (radiusSlider == null)
+        // Pre-populate from GameManager defaults
+        var gm = GameManager.Instance;
+        if (gm != null)
         {
-            var go = GameObject.Find("radius");
-            if (go != null) radiusSlider = go.GetComponent<Slider>();
-        }
-        if (radiusInput == null)
-        {
-            var go = GameObject.Find("radius");
-            if (go != null) radiusInput = go.GetComponent<TMP_InputField>();
+            _radius    = (float)gm.pendingRadius;
+            _impostors = gm.pendingImpostorCount;
+            _tasks     = gm.pendingTaskCount;
+            _playerName = gm.displayName ?? "";
         }
 
-        if (createButton == null)
+        var canvasGO = GameObject.Find("Canvas");
+        if (canvasGO == null)
         {
-            // Try all name variants used by the Art team
-            var go = GameObject.Find("stvo\u0159it")     // stvořit
-                  ?? GameObject.Find("stvorit")
-                  ?? GameObject.Find("vytvo\u0159it")    // vytvořit
-                  ?? GameObject.Find("vytvorit");
-            if (go != null)
-            {
-                createButton = go.GetComponent<Button>();
-                // Disable the Art team's direct scene-changer so only our
-                // wired OnCreateClicked fires (navigation is handled by
-                // HandleCreateLobbyResponse after the server confirms).
-                var sceneChanger = go.GetComponent<CudlikZmenaSceny>();
-                if (sceneChanger != null) sceneChanger.enabled = false;
-            }
+            Debug.LogError("[HostLobbyUI] No Canvas found!");
+            return;
         }
 
-        if (createButton != null)
-            createButton.onClick.AddListener(OnCreateClicked);
+        // Nuke all Art placeholder children
+        var kill = new System.Collections.Generic.List<GameObject>();
+        foreach (Transform child in canvasGO.transform)
+            kill.Add(child.gameObject);
+        foreach (var go in kill)
+            DestroyImmediate(go);
+
+        // Disable scene-changer components that bypass our logic
+        foreach (var sc in canvasGO.GetComponentsInChildren<CudlikZmenaSceny>(true))
+            sc.enabled = false;
+
+        Build(canvasGO.GetComponent<RectTransform>() ?? canvasGO.AddComponent<RectTransform>());
     }
 
-    private void OnCreateClicked()
+    // ── Builder ───────────────────────────────────────────────────────────────
+    void Build(RectTransform root)
+    {
+        // Full-screen background
+        var bg = MakeRT("BG", root);
+        Stretch(bg);
+        bg.gameObject.AddComponent<CanvasRenderer>();
+        Img(bg, C_BG);
+
+        // ── Header bar ───────────────────────────────────────────────────────
+        var hdr = MakeRT("Header", root);
+        Anchor(hdr, new Vector2(0,1), new Vector2(1,1), new Vector2(0,-80), new Vector2(0,0));
+        Img(hdr, C_HDR);
+        var hdrTxt = Txt("Create Lobby", hdr, 28, C_WHITE, TextAlignmentOptions.Center, bold: true);
+        Stretch(hdrTxt.rectTransform);
+
+        // ── Scroll body ───────────────────────────────────────────────────────
+        var scroll = MakeRT("Scroll", root);
+        Anchor(scroll, new Vector2(0,0), new Vector2(1,1), new Vector2(0,80), new Vector2(0,-80));
+        var sf = scroll.gameObject.AddComponent<ScrollRect>();
+        sf.horizontal = false;
+        Img(scroll, new Color(0,0,0,0));
+
+        var content = MakeRT("Content", scroll);
+        content.anchorMin = new Vector2(0,1);
+        content.anchorMax = new Vector2(1,1);
+        content.pivot     = new Vector2(0.5f, 1);
+        content.offsetMin = Vector2.zero;
+        content.offsetMax = Vector2.zero;
+        var vlg = content.gameObject.AddComponent<VerticalLayoutGroup>();
+        vlg.padding = new RectOffset(16, 16, 12, 12);
+        vlg.spacing = 12;
+        vlg.childForceExpandWidth  = true;
+        vlg.childForceExpandHeight = false;
+        var csf = content.gameObject.AddComponent<ContentSizeFitter>();
+        csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
+
+        sf.content  = content;
+        sf.viewport = scroll;
+
+        // ── Player name card ─────────────────────────────────────────────────
+        AddSectionLabel("PLAYER NAME", content);
+        var nameCard = AddCard(content, 70);
+        var nameInput = MakeInputField("Your name", nameCard, _playerName);
+        Stretch(nameInput.GetComponent<RectTransform>());
+        nameInput.onEndEdit.AddListener(v =>
+        {
+            _playerName = v.Trim();
+            var gm2 = GameManager.Instance;
+            if (gm2 != null) gm2.displayName = _playerName;
+            PlayerPrefs.SetString("PlayerName", _playerName);
+        });
+
+        // ── Radius card ───────────────────────────────────────────────────────
+        AddSectionLabel("PLAY AREA RADIUS", content);
+        var radCard = AddCard(content, 110);
+        var radRT = radCard.GetComponent<RectTransform>();
+
+        var radLbl = MakeRT("RadLbl", radRT);
+        Anchor(radLbl, new Vector2(0,1), new Vector2(1,1), new Vector2(12,-40), new Vector2(-12,-4));
+        var rt = radLbl.gameObject;
+        var radTmp = rt.AddComponent<TextMeshProUGUI>();
+        radTmp.text = RadiusLabel(_radius);
+        radTmp.fontSize = 20; radTmp.color = C_ACCENT;
+        radTmp.alignment = TextAlignmentOptions.Center;
+        _radiusValueLabel = radTmp;
+
+        var slider = MakeRT("RadSlider", radRT).gameObject.AddComponent<Slider>();
+        var srt = slider.GetComponent<RectTransform>();
+        Anchor(srt, new Vector2(0,0), new Vector2(1,0), new Vector2(16, 14), new Vector2(-16, 44));
+        slider.minValue = 100; slider.maxValue = 2000; slider.value = _radius;
+        BuildSliderVisuals(slider, C_ACCENT);
+        slider.onValueChanged.AddListener(v =>
+        {
+            _radius = v;
+            _radiusValueLabel.text = RadiusLabel(v);
+            var gm2 = GameManager.Instance;
+            if (gm2 != null) gm2.pendingRadius = v;
+        });
+
+        // ── Impostor count card ───────────────────────────────────────────────
+        AddSectionLabel("IMPOSTORS", content);
+        var impCard = AddCard(content, 80);
+        BuildStepper(impCard.GetComponent<RectTransform>(), ref _impostors, 1, 4,
+            v => { var gm2 = GameManager.Instance; if (gm2 != null) gm2.pendingImpostorCount = v; },
+            out _impostorValueLabel);
+
+        // ── Task count card ───────────────────────────────────────────────────
+        AddSectionLabel("TASKS PER PLAYER", content);
+        var taskCard = AddCard(content, 80);
+        BuildStepper(taskCard.GetComponent<RectTransform>(), ref _tasks, 1, 15,
+            v => { var gm2 = GameManager.Instance; if (gm2 != null) gm2.pendingTaskCount = v; },
+            out _taskValueLabel);
+
+        // ── Status text ───────────────────────────────────────────────────────
+        var statusCard = AddCard(content, 40);
+        _statusText = Txt("", statusCard.GetComponent<RectTransform>(), 16, C_MUTED, TextAlignmentOptions.Center);
+        Stretch(_statusText.rectTransform);
+
+        // ── Footer create button ──────────────────────────────────────────────
+        var footer = MakeRT("Footer", root);
+        Anchor(footer, new Vector2(0,0), new Vector2(1,0), new Vector2(0,0), new Vector2(0,80));
+        Img(footer, C_HDR);
+        var btnRT = MakeRT("CreateBtn", footer);
+        Anchor(btnRT, new Vector2(0.1f,0.15f), new Vector2(0.9f,0.85f), Vector2.zero, Vector2.zero);
+        var btn = btnRT.gameObject.AddComponent<Button>();
+        var btnImg = btnRT.gameObject.AddComponent<Image>();
+        btnImg.color = C_GREEN;
+        btn.targetGraphic = btnImg;
+        var cb = btn.colors; cb.pressedColor = C_GREEN * 0.7f; btn.colors = cb;
+        var btnTxt = Txt("CREATE LOBBY", btnRT, 22, C_WHITE, TextAlignmentOptions.Center, bold: true);
+        Stretch(btnTxt.rectTransform);
+        btn.onClick.AddListener(OnCreateClicked);
+
+        // ── Back button ───────────────────────────────────────────────────────
+        var backRT = MakeRT("BackBtn", root);
+        Anchor(backRT, new Vector2(0,1), new Vector2(0,1), new Vector2(8,-72), new Vector2(72,-8));
+        var backBtn = backRT.gameObject.AddComponent<Button>();
+        var backImg = backRT.gameObject.AddComponent<Image>();
+        backImg.color = new Color(1,1,1,0);
+        backBtn.targetGraphic = backImg;
+        var backTxt = Txt("←", backRT, 28, C_MUTED, TextAlignmentOptions.Center);
+        Stretch(backTxt.rectTransform);
+        backBtn.onClick.AddListener(() => UnityEngine.SceneManagement.SceneManager.LoadScene("main menu asi idk lol"));
+    }
+
+    // ── Actions ───────────────────────────────────────────────────────────────
+    void OnCreateClicked()
     {
         var gm = GameManager.Instance;
-        if (gm == null) return;
+        if (gm == null) { SetStatus("GameManager not found!", Color.red); return; }
 
-        // Read radius from slider or input field
-        if (radiusSlider != null)
-            gm.pendingRadius = radiusSlider.value;
-        else if (radiusInput != null && float.TryParse(radiusInput.text, out float r))
-            gm.pendingRadius = r;
+        if (string.IsNullOrWhiteSpace(_playerName))
+        {
+            SetStatus("Enter a player name first.", Color.yellow);
+            return;
+        }
 
+        gm.pendingRadius        = _radius;
+        gm.pendingImpostorCount = _impostors;
+        gm.pendingTaskCount     = _tasks;
+        gm.displayName          = _playerName;
+
+        SetStatus("Connecting…", C_MUTED);
         gm.CreateLobbyButton();
     }
+
+    void SetStatus(string msg, Color col)
+    {
+        if (_statusText == null) return;
+        _statusText.text  = msg;
+        _statusText.color = col;
+    }
+
+    // ── UI helpers ────────────────────────────────────────────────────────────
+    static string RadiusLabel(float v) => $"{Mathf.RoundToInt(v)} m";
+
+    void BuildStepper(RectTransform parent, ref int val, int min, int max,
+        System.Action<int> onChange, out TMP_Text label)
+    {
+        int captured = val; // local copy for closures
+
+        // minus
+        var minusRT = MakeRT("Minus", parent);
+        Anchor(minusRT, new Vector2(0,0), new Vector2(0,1), new Vector2(8,8), new Vector2(72,-8));
+        var minusBtn = minusRT.gameObject.AddComponent<Button>();
+        var minusImg = minusRT.gameObject.AddComponent<Image>(); minusImg.color = C_BORDER;
+        minusBtn.targetGraphic = minusImg;
+        var minusTxt = Txt("−", minusRT, 28, C_WHITE, TextAlignmentOptions.Center);
+        Stretch(minusTxt.rectTransform);
+
+        // label
+        var lblRT = MakeRT("Val", parent);
+        Stretch(lblRT);
+        var lbl = Txt(captured.ToString(), lblRT, 26, C_WHITE, TextAlignmentOptions.Center, bold: true);
+        label = lbl;
+
+        // plus
+        var plusRT = MakeRT("Plus", parent);
+        Anchor(plusRT, new Vector2(1,0), new Vector2(1,1), new Vector2(-72,8), new Vector2(-8,-8));
+        var plusBtn = plusRT.gameObject.AddComponent<Button>();
+        var plusImg = plusRT.gameObject.AddComponent<Image>(); plusImg.color = C_ACCENT;
+        plusBtn.targetGraphic = plusImg;
+        var plusTxt = Txt("+", plusRT, 28, C_WHITE, TextAlignmentOptions.Center);
+        Stretch(plusTxt.rectTransform);
+
+        minusBtn.onClick.AddListener(() =>
+        {
+            captured = Mathf.Max(min, captured - 1);
+            lbl.text = captured.ToString();
+            onChange?.Invoke(captured);
+        });
+        plusBtn.onClick.AddListener(() =>
+        {
+            captured = Mathf.Min(max, captured + 1);
+            lbl.text = captured.ToString();
+            onChange?.Invoke(captured);
+        });
+
+        // write back initial
+        val = captured;
+    }
+
+    static void BuildSliderVisuals(Slider s, Color fillColor)
+    {
+        // Background track
+        var bgRT = MakeRT("Background", s.GetComponent<RectTransform>());
+        Stretch(bgRT);
+        bgRT.gameObject.AddComponent<CanvasRenderer>();
+        var bgImg = bgRT.gameObject.AddComponent<Image>(); bgImg.color = new Color(0.15f,0.18f,0.28f);
+
+        // Fill area
+        var fillArea = MakeRT("Fill Area", s.GetComponent<RectTransform>());
+        Anchor(fillArea, new Vector2(0,0.25f), new Vector2(1,0.75f), new Vector2(5,0), new Vector2(-5,0));
+        var fillRT = MakeRT("Fill", fillArea);
+        fillRT.anchorMin = Vector2.zero; fillRT.anchorMax = Vector2.one;
+        fillRT.offsetMin = Vector2.zero; fillRT.offsetMax = Vector2.zero;
+        fillRT.gameObject.AddComponent<CanvasRenderer>();
+        var fillImg = fillRT.gameObject.AddComponent<Image>(); fillImg.color = fillColor;
+        s.fillRect = fillRT;
+
+        // Handle area
+        var handleArea = MakeRT("Handle Slide Area", s.GetComponent<RectTransform>());
+        Stretch(handleArea);
+        var handleRT = MakeRT("Handle", handleArea);
+        handleRT.sizeDelta = new Vector2(28, 28);
+        handleRT.anchorMin = new Vector2(0, 0.5f); handleRT.anchorMax = new Vector2(0, 0.5f);
+        handleRT.anchoredPosition = Vector2.zero;
+        handleRT.gameObject.AddComponent<CanvasRenderer>();
+        var hImg = handleRT.gameObject.AddComponent<Image>(); hImg.color = Color.white;
+        s.handleRect = handleRT;
+        s.targetGraphic = hImg;
+    }
+
+    static void AddSectionLabel(string text, RectTransform parent)
+    {
+        var rt = MakeRT("Lbl_" + text, parent);
+        var le = rt.gameObject.AddComponent<LayoutElement>(); le.preferredHeight = 28;
+        var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
+        tmp.text = text; tmp.fontSize = 13; tmp.color = new Color(0.47f,0.53f,0.67f);
+        tmp.fontStyle = FontStyles.Bold; tmp.alignment = TextAlignmentOptions.Left;
+    }
+
+    static RectTransform AddCard(RectTransform parent, float height)
+    {
+        var rt = MakeRT("Card", parent);
+        var le = rt.gameObject.AddComponent<LayoutElement>(); le.preferredHeight = height;
+        var img = rt.gameObject.AddComponent<Image>(); img.color = H("#111525");
+        // border via outline
+        var outline = rt.gameObject.AddComponent<Outline>();
+        outline.effectColor = H("#1E2540");
+        outline.effectDistance = new Vector2(1, -1);
+        return rt;
+    }
+
+    static TMP_InputField MakeInputField(string placeholder, RectTransform parent, string initialValue)
+    {
+        var go = new GameObject("InputField");
+        var rt = go.AddComponent<RectTransform>();
+        rt.SetParent(parent, false);
+        rt.localScale = Vector3.one;
+
+        var img = go.AddComponent<Image>(); img.color = H("#0A0D1A");
+        var field = go.AddComponent<TMP_InputField>();
+
+        var textArea = MakeRT("Text Area", rt);
+        Stretch(textArea);
+        textArea.offsetMin = new Vector2(10, 4);
+        textArea.offsetMax = new Vector2(-10, -4);
+        var areaImg = textArea.gameObject.AddComponent<Image>(); areaImg.color = new Color(0,0,0,0);
+        var mask = textArea.gameObject.AddComponent<RectMask2D>();
+
+        var phRT = MakeRT("Placeholder", textArea);
+        Stretch(phRT);
+        var phTmp = phRT.gameObject.AddComponent<TextMeshProUGUI>();
+        phTmp.text = placeholder; phTmp.fontSize = 20;
+        phTmp.color = new Color(0.47f, 0.53f, 0.67f);
+        phTmp.alignment = TextAlignmentOptions.MidlineLeft;
+
+        var txtRT = MakeRT("Text", textArea);
+        Stretch(txtRT);
+        var txtTmp = txtRT.gameObject.AddComponent<TextMeshProUGUI>();
+        txtTmp.fontSize = 20; txtTmp.color = Color.white;
+        txtTmp.alignment = TextAlignmentOptions.MidlineLeft;
+
+        field.textViewport      = textArea;
+        field.textComponent     = txtTmp;
+        field.placeholder       = phTmp;
+        field.text              = initialValue;
+        field.targetGraphic     = img;
+
+        return field;
+    }
+
+    static RectTransform MakeRT(string name, RectTransform parent)
+    {
+        var go = new GameObject(name);
+        var rt = go.AddComponent<RectTransform>();
+        rt.SetParent(parent, false);
+        rt.localScale = Vector3.one;
+        return rt;
+    }
+
+    static void Stretch(RectTransform rt)
+    {
+        rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one;
+        rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero;
+    }
+
+    static void Anchor(RectTransform rt, Vector2 aMin, Vector2 aMax, Vector2 offMin, Vector2 offMax)
+    {
+        rt.anchorMin = aMin; rt.anchorMax = aMax;
+        rt.offsetMin = offMin; rt.offsetMax = offMax;
+    }
+
+    static Image Img(RectTransform rt, Color c)
+    {
+        rt.gameObject.AddComponent<CanvasRenderer>();
+        var img = rt.gameObject.AddComponent<Image>(); img.color = c; return img;
+    }
+
+    static TextMeshProUGUI Txt(string text, RectTransform parent, float size, Color color,
+        TextAlignmentOptions align, bool bold = false)
+    {
+        var rt = MakeRT("T_" + text, parent);
+        var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
+        tmp.text = text; tmp.fontSize = size; tmp.color = color; tmp.alignment = align;
+        if (bold) tmp.fontStyle = FontStyles.Bold;
+        return tmp;
+    }
 }
diff --git a/Assets/Scripts/InGameHUDBuilder.cs b/Assets/Scripts/InGameHUDBuilder.cs
index a172922..cd1224e 100644
--- a/Assets/Scripts/InGameHUDBuilder.cs
+++ b/Assets/Scripts/InGameHUDBuilder.cs
@@ -1,276 +1,291 @@
 using UnityEngine;
 using UnityEngine.UI;
 using TMPro;
+using System.Collections.Generic;
 
 /// <summary>
 /// Programmatically builds the complete in-game HUD inside the InGame canvas (Client.unity).
 ///
-/// Call BuildNow() explicitly from GameManager.OnSceneLoaded BEFORE BindClientScene(),
-/// so that GameManager_UI can find the newly created elements by name.
+/// Call BuildNow() from GameManager.OnSceneLoaded BEFORE BindClientScene so GameManager_UI
+/// can locate named children.
 ///
-/// Named GameObjects created as direct children of InGame canvas (required by Transform.Find):
-///   • ActionButton   — Button + TMP child; shown/hidden by GameManager_Tasks.UpdateProximity()
-///   • SabotagePanel  — warning banner at top (contains "SabotageTimer" TMP_Text)
-///   • MeetingPanel   — voting/meeting overlay (populated by GameManager_UI.ShowMeetingPanel)
-///   • GameEndPanel   — end-of-game overlay (contains "GameEndText" TMP_Text)
-///
-/// Named TMP_Text descendants (found by GameManager_UI.FindTMP — any depth):
-///   • KillCooldown   — shown to impostors during kill cooldown
-///   • TaskList       — crewmate task names
-///   • TaskProgress   — global task completion "X/Y tasks"
-///   • SabotageTimer  — countdown inside SabotagePanel
-///   • GameEndText    — win/lose result text inside GameEndPanel
-///   • (Role already exists in the scene)
-///
-/// Additional elements managed by this script:
-///   • RecenterBtn    — calls MapCameraController.Instance.Recenter()
+/// Named elements expected by GameManager_UI (Transform.Find / FindTMP):
+///   ActionButton         – proxim action button
+///   SabotagePanel        – top-of-screen sabotage banner
+///     SabotageTimer      – TMP countdown text inside SabotagePanel
+///   MeetingPanel         – full-screen voting overlay
+///     MeetingHeader      – TMP title
+///     MeetingPlayerList  – TMP player list (text fallback)
+///     SkipButton         – skip-vote button
+///     VoteResultPanel    – sub-panel shown after voting
+///       VoteResult       – TMP result text
+///   GameEndPanel         – full-screen end-of-game overlay
+///     GameEndText        – TMP result text
+///   KillCooldown         – TMP kill-cooldown label
+///   TaskList             – TMP task name list
+///   TaskProgress         – TMP global task progress
+///   Toast                – TMP toast notification
 /// </summary>
 public class InGameHUDBuilder : MonoBehaviour
 {
-    // ── Color palette ─────────────────────────────────────────────────────────
-    private static readonly Color C_BG        = new Color(0.05f, 0.06f, 0.12f, 0.80f);
-    private static readonly Color C_BAR_BG    = new Color(0.03f, 0.04f, 0.08f, 0.90f);
-    private static readonly Color C_ACCENT    = new Color(0.20f, 0.60f, 1.00f, 1.00f);
-    private static readonly Color C_GREEN     = new Color(0.18f, 0.75f, 0.30f, 1.00f);
-    private static readonly Color C_RED       = new Color(0.76f, 0.19f, 0.19f, 1.00f);
-    private static readonly Color C_ORANGE    = new Color(0.95f, 0.55f, 0.10f, 1.00f);
+    // ── Palette ───────────────────────────────────────────────────────────────
+    static Color H(string hex) { ColorUtility.TryParseHtmlString(hex, out var c); return c; }
+    static readonly Color C_BG       = H("#0D0F1A");
+    static readonly Color C_BAR      = H("#141927");
+    static readonly Color C_ACCENT   = H("#3399FF");
+    static readonly Color C_GREEN    = H("#2DB84B");
+    static readonly Color C_RED      = H("#C43232");
+    static readonly Color C_ORANGE   = H("#F08C1A");
+    static readonly Color C_YELLOW   = H("#FFB800");
+    static readonly Color C_MUTED    = new Color(0.47f, 0.53f, 0.67f);
+    static readonly Color C_ROW_A    = H("#1A2035");
+    static readonly Color C_ROW_B    = H("#161C2E");
 
     private bool _built;
 
-    // ── Entry points ──────────────────────────────────────────────────────────
+    public void BuildNow() { if (!_built) { _built = true; Build(); } }
+    void Start()           { if (!_built) Build(); }
 
-    /// <summary>Called from GameManager.OnSceneLoaded before BindClientScene.</summary>
-    public void BuildNow()
-    {
-        if (_built) return;
-        _built = true;
-        Build();
-    }
-
-    void Start()
-    {
-        if (!_built) Build(); // safety fallback
-    }
-
-    // ── Build ─────────────────────────────────────────────────────────────────
     void Build()
     {
         var rt = GetComponent<RectTransform>();
         if (rt == null) return;
 
-        // ── Top bar: role is already in scene, add kill-cooldown ─────────────
         BuildTopBar(rt);
-
-        // ── Right task panel ──────────────────────────────────────────────────
         BuildTaskPanel(rt);
-
-        // ── Task progress (above bottom bar) ─────────────────────────────────
         BuildTaskProgress(rt);
-
-        // ── Bottom bar: action button + recenter ──────────────────────────────
         BuildBottomBar(rt);
-
-        // ── Action button (DIRECT child — Transform.Find requirement) ─────────
         BuildActionButton(rt);
-
-        // ── Sabotage panel (DIRECT child) ─────────────────────────────────────
         BuildSabotagePanel(rt);
-
-        // ── Meeting panel (DIRECT child) ──────────────────────────────────────
         BuildMeetingPanel(rt);
-
-        // ── Game-end panel (DIRECT child) ─────────────────────────────────────
         BuildGameEndPanel(rt);
+        BuildToast(rt);
     }
 
-    // ── Section builders ──────────────────────────────────────────────────────
-
+    // ── Top bar ───────────────────────────────────────────────────────────────
     void BuildTopBar(RectTransform parent)
     {
-        // Thin semi-transparent header at very top
-        var bar = AddChild("_TopBar", parent);
-        Anchor(bar, new Vector2(0f, 1f), new Vector2(1f, 1f));
-        bar.sizeDelta = new Vector2(0f, 90f);
-        bar.anchoredPosition = new Vector2(0f, 0f);
-        bar.pivot = new Vector2(0.5f, 1f);
-        AddImage(bar.gameObject, C_BAR_BG);
+        var bar = Child("_TopBar", parent);
+        Anchor(bar, new Vector2(0,1), new Vector2(1,1), new Vector2(0,-90f), Vector2.zero);
+        bar.pivot = new Vector2(0.5f,1f);
+        Img(bar, C_BAR);
 
-        // Kill cooldown (right side) — starts hidden
-        var cd = AddChild("KillCooldown", bar);
-        Anchor(cd, new Vector2(0.5f, 0f), new Vector2(1f, 1f));
-        cd.offsetMin = new Vector2(0f, 6f);
-        cd.offsetMax = new Vector2(-12f, -6f);
+        // Kill cooldown – right half, hidden by default
+        var cd = Child("KillCooldown", bar);
+        cd.anchorMin = new Vector2(0.5f, 0); cd.anchorMax = Vector2.one;
+        cd.offsetMin = new Vector2(0, 6); cd.offsetMax = new Vector2(-12, -6);
         var cdTmp = cd.gameObject.AddComponent<TextMeshProUGUI>();
-        cdTmp.text      = "";
-        cdTmp.fontSize  = 32;
-        cdTmp.color     = C_ORANGE;
-        cdTmp.fontStyle = FontStyles.Bold;
-        cdTmp.alignment = TextAlignmentOptions.MidlineRight;
+        cdTmp.text = ""; cdTmp.fontSize = 32; cdTmp.color = C_ORANGE;
+        cdTmp.fontStyle = FontStyles.Bold; cdTmp.alignment = TextAlignmentOptions.MidlineRight;
         cd.gameObject.SetActive(false);
     }
 
+    // ── Task panel (right side) ───────────────────────────────────────────────
     void BuildTaskPanel(RectTransform parent)
     {
-        // Right-side floating panel (always visible during game)
-        var panel = AddChild("_TaskPanel", parent);
-        Anchor(panel, new Vector2(1f, 0.35f), new Vector2(1f, 0.88f));
-        panel.pivot = new Vector2(1f, 0.5f);
-        panel.sizeDelta = new Vector2(280f, 0f);
-        panel.anchoredPosition = Vector2.zero;
-        AddImage(panel.gameObject, C_BG);
+        var panel = Child("_TaskPanel", parent);
+        panel.anchorMin = new Vector2(1,0.35f); panel.anchorMax = new Vector2(1,0.88f);
+        panel.pivot = new Vector2(1,0.5f); panel.sizeDelta = new Vector2(280,0);
+        Img(panel, new Color(0.05f,0.06f,0.12f,0.85f));
 
-        // "MY TASKS" header
-        var hdr = AddChild("_Header", panel);
-        Anchor(hdr, new Vector2(0f, 1f), new Vector2(1f, 1f));
-        hdr.pivot = new Vector2(0.5f, 1f);
-        hdr.sizeDelta = new Vector2(0f, 44f);
-        hdr.anchoredPosition = Vector2.zero;
-        AddImage(hdr.gameObject, C_ACCENT * new Color(1, 1, 1, 0.6f));
-        var hdrTmp = AddTextChild(hdr, "_HeaderTxt", "MY TASKS", 26, FontStyles.Bold, TextAlignmentOptions.Center);
-        hdrTmp.color = Color.white;
+        var hdr = Child("_Hdr", panel);
+        Anchor(hdr, new Vector2(0,1), new Vector2(1,1), new Vector2(0,-44), Vector2.zero);
+        hdr.pivot = new Vector2(0.5f,1f);
+        Img(hdr, new Color(0.2f,0.6f,1f,0.5f));
+        TxtChild(hdr,"MY TASKS",26,Color.white,TextAlignmentOptions.Center,bold:true);
 
-        // Task list body
-        var body = AddChild("TaskList", panel);
-        Anchor(body, new Vector2(0f, 0f), new Vector2(1f, 1f));
-        body.offsetMin = new Vector2(8f, 8f);
-        body.offsetMax = new Vector2(-8f, -48f);
-        var taskTmp = body.gameObject.AddComponent<TextMeshProUGUI>();
-        taskTmp.text      = "";
-        taskTmp.fontSize  = 22;
-        taskTmp.color     = Color.white;
-        taskTmp.alignment = TextAlignmentOptions.TopLeft;
+        var body = Child("TaskList", panel);
+        body.anchorMin = Vector2.zero; body.anchorMax = Vector2.one;
+        body.offsetMin = new Vector2(8,8); body.offsetMax = new Vector2(-8,-48);
+        var t = body.gameObject.AddComponent<TextMeshProUGUI>();
+        t.text = ""; t.fontSize = 22; t.color = Color.white; t.alignment = TextAlignmentOptions.TopLeft;
+        t.enableWordWrapping = true;
     }
 
+    // ── Task progress (above bottom bar) ─────────────────────────────────────
     void BuildTaskProgress(RectTransform parent)
     {
-        var prog = AddChild("TaskProgress", parent);
-        Anchor(prog, new Vector2(0f, 0f), new Vector2(1f, 0f));
-        prog.pivot = new Vector2(0.5f, 0f);
-        prog.sizeDelta = new Vector2(-20f, 40f);
-        prog.anchoredPosition = new Vector2(0f, 120f); // above bottom bar
-        var tmp = prog.gameObject.AddComponent<TextMeshProUGUI>();
-        tmp.text      = "";
-        tmp.fontSize  = 28;
-        tmp.color     = Color.white;
-        tmp.fontStyle = FontStyles.Bold;
-        tmp.alignment = TextAlignmentOptions.Center;
+        var prog = Child("TaskProgress", parent);
+        Anchor(prog, new Vector2(0,0), new Vector2(1,0), new Vector2(-20,120), new Vector2(20,160));
+        var t = prog.gameObject.AddComponent<TextMeshProUGUI>();
+        t.text = ""; t.fontSize = 28; t.color = Color.white;
+        t.fontStyle = FontStyles.Bold; t.alignment = TextAlignmentOptions.Center;
     }
 
+    // ── Bottom bar ────────────────────────────────────────────────────────────
     void BuildBottomBar(RectTransform parent)
     {
-        var bar = AddChild("_BottomBar", parent);
-        Anchor(bar, new Vector2(0f, 0f), new Vector2(1f, 0f));
-        bar.pivot = new Vector2(0.5f, 0f);
-        bar.sizeDelta = new Vector2(0f, 110f);
-        bar.anchoredPosition = Vector2.zero;
-        AddImage(bar.gameObject, C_BAR_BG);
+        var bar = Child("_BottomBar", parent);
+        Anchor(bar, Vector2.zero, new Vector2(1,0), Vector2.zero, new Vector2(0,110));
+        bar.pivot = new Vector2(0.5f,0);
+        Img(bar, C_BAR);
 
-        // Recenter button (bottom-right of bar)
-        var recBtn = AddChild("_RecenterBtn", bar);
-        Anchor(recBtn, new Vector2(0.82f, 0.08f), new Vector2(0.98f, 0.92f));
-        var recBg = AddImage(recBtn.gameObject, C_ACCENT);
+        var recBtn = Child("_RecenterBtn", bar);
+        recBtn.anchorMin = new Vector2(0.82f,0.08f); recBtn.anchorMax = new Vector2(0.98f,0.92f);
+        var recBg = Img(recBtn, C_ACCENT);
         var recButton = recBtn.gameObject.AddComponent<Button>();
-        var recColors = recButton.colors;
-        recColors.pressedColor = new Color(0.1f, 0.4f, 0.8f);
-        recButton.colors = recColors;
         recButton.targetGraphic = recBg;
         recButton.onClick.AddListener(() => MapCameraController.Instance?.Recenter());
-        var recTxt = AddTextChild(recBtn, "_RecTxt", "⊙", 42, FontStyles.Bold, TextAlignmentOptions.Center);
-        recTxt.color = Color.white;
+        TxtChild(recBtn,"⊙",42,Color.white,TextAlignmentOptions.Center,bold:true);
     }
 
+    // ── Action button (DIRECT child so Transform.Find works) ─────────────────
     void BuildActionButton(RectTransform parent)
     {
-        // MUST be a DIRECT child so Transform.Find("ActionButton") works
-        var btn = AddChild("ActionButton", parent);
-        Anchor(btn, new Vector2(0.15f, 0f), new Vector2(0.80f, 0f));
-        btn.pivot = new Vector2(0.5f, 0f);
-        btn.sizeDelta = new Vector2(0f, 90f);
-        btn.anchoredPosition = new Vector2(0f, 12f);
-
-        var bg = AddImage(btn.gameObject, C_GREEN);
+        var btn = Child("ActionButton", parent);
+        Anchor(btn, new Vector2(0.15f,0), new Vector2(0.80f,0), new Vector2(0,12), new Vector2(0,102));
+        btn.pivot = new Vector2(0.5f,0);
+        var bg = Img(btn, C_GREEN);
         var button = btn.gameObject.AddComponent<Button>();
-        var colors = button.colors;
-        colors.normalColor  = C_GREEN;
-        colors.pressedColor = new Color(0.12f, 0.55f, 0.22f);
-        button.colors  = colors;
         button.targetGraphic = bg;
 
-        // TMP child named "Text" so GetComponentInChildren<TMP_Text> finds it
-        var txtRt = AddChild("Text", btn);
+        var txtRt = Child("Text", btn);
         Stretch(txtRt);
         var tmp = txtRt.gameObject.AddComponent<TextMeshProUGUI>();
-        tmp.text      = "ACTION";
-        tmp.fontSize  = 44;
-        tmp.fontStyle = FontStyles.Bold;
-        tmp.color     = Color.white;
-        tmp.alignment = TextAlignmentOptions.Center;
+        tmp.text = "ACTION"; tmp.fontSize = 44; tmp.fontStyle = FontStyles.Bold;
+        tmp.color = Color.white; tmp.alignment = TextAlignmentOptions.Center;
 
-        btn.gameObject.SetActive(false); // hidden until proximity detected
+        btn.gameObject.SetActive(false);
     }
 
+    // ── Sabotage panel (top strip) ────────────────────────────────────────────
     void BuildSabotagePanel(RectTransform parent)
     {
-        // DIRECT child
-        var panel = AddChild("SabotagePanel", parent);
-        Anchor(panel, new Vector2(0f, 0.88f), new Vector2(1f, 1f));
-        panel.offsetMin = new Vector2(0f, -10f);
-        panel.offsetMax = new Vector2(0f, -80f);
-        AddImage(panel.gameObject, C_RED * new Color(1, 1, 1, 0.88f));
+        var panel = Child("SabotagePanel", parent);
+        panel.anchorMin = new Vector2(0,1); panel.anchorMax = new Vector2(1,1);
+        panel.pivot = new Vector2(0.5f,1f); panel.sizeDelta = new Vector2(0,80);
+        Img(panel, new Color(0.76f,0.19f,0.19f,0.92f));
 
-        var timer = AddChild("SabotageTimer", panel);
+        var timer = Child("SabotageTimer", panel);
         Stretch(timer);
-        var tmp = timer.gameObject.AddComponent<TextMeshProUGUI>();
-        tmp.text      = "SABOTAGE!";
-        tmp.fontSize  = 48;
-        tmp.fontStyle = FontStyles.Bold;
-        tmp.color     = Color.white;
-        tmp.alignment = TextAlignmentOptions.Center;
+        var t = timer.gameObject.AddComponent<TextMeshProUGUI>();
+        t.text = "SABOTAGE!"; t.fontSize = 48; t.fontStyle = FontStyles.Bold;
+        t.color = Color.white; t.alignment = TextAlignmentOptions.Center;
 
         panel.gameObject.SetActive(false);
     }
 
+    // ── Meeting panel (full screen overlay) ───────────────────────────────────
     void BuildMeetingPanel(RectTransform parent)
     {
-        // DIRECT child — populated by GameManager_UI.ShowMeetingPanel at runtime
-        var panel = AddChild("MeetingPanel", parent);
-        Anchor(panel, new Vector2(0.05f, 0.10f), new Vector2(0.95f, 0.90f));
-        AddImage(panel.gameObject, new Color(0.04f, 0.05f, 0.14f, 0.96f));
+        var panel = Child("MeetingPanel", parent);
+        Stretch(panel);
+        Img(panel, new Color(0.04f,0.05f,0.14f,0.97f));
 
-        var title = AddChild("_MeetingTitle", panel);
-        Anchor(title, new Vector2(0f, 0.85f), new Vector2(1f, 1f));
-        var titleTmp = title.gameObject.AddComponent<TextMeshProUGUI>();
-        titleTmp.text      = "EMERGENCY MEETING";
-        titleTmp.fontSize  = 44;
-        titleTmp.fontStyle = FontStyles.Bold;
-        titleTmp.color     = C_ORANGE;
-        titleTmp.alignment = TextAlignmentOptions.Center;
+        // Header
+        var hdr = Child("MeetingHeader", panel);
+        Anchor(hdr, new Vector2(0,0.86f), new Vector2(1,1), Vector2.zero, Vector2.zero);
+        var hdrTmp = hdr.gameObject.AddComponent<TextMeshProUGUI>();
+        hdrTmp.text = "EMERGENCY MEETING"; hdrTmp.fontSize = 52;
+        hdrTmp.fontStyle = FontStyles.Bold; hdrTmp.color = C_ORANGE;
+        hdrTmp.alignment = TextAlignmentOptions.Center;
+
+        // Scrollable player vote list
+        var scrollArea = Child("_MeetingScroll", panel);
+        Anchor(scrollArea, new Vector2(0,0.22f), new Vector2(1,0.86f), Vector2.zero, Vector2.zero);
+        BuildMeetingScroll(scrollArea);
+
+        // Text fallback (hidden by default; shown if scroll build fails)
+        var fallback = Child("MeetingPlayerList", panel);
+        Anchor(fallback, new Vector2(0,0.22f), new Vector2(1,0.86f), new Vector2(8,0), new Vector2(-8,0));
+        var fallbackTmp = fallback.gameObject.AddComponent<TextMeshProUGUI>();
+        fallbackTmp.text = ""; fallbackTmp.fontSize = 28; fallbackTmp.color = Color.white;
+        fallbackTmp.alignment = TextAlignmentOptions.TopLeft;
+        fallback.gameObject.SetActive(false); // hidden – scroll list used instead
+
+        // Skip button
+        var skip = Child("SkipButton", panel);
+        Anchor(skip, new Vector2(0.05f,0.04f), new Vector2(0.95f,0.18f), Vector2.zero, Vector2.zero);
+        var skipBg = Img(skip, C_MUTED);
+        var skipBtn = skip.gameObject.AddComponent<Button>();
+        skipBtn.targetGraphic = skipBg;
+        skipBtn.onClick.AddListener(() => GameManager.Instance?.CastVote(null));
+        TxtChild(skip, "⏭  SKIP", 36, Color.white, TextAlignmentOptions.Center, bold: true);
+
+        // Vote-result sub-panel (hidden until voting closes)
+        var resultPanel = Child("VoteResultPanel", panel);
+        Anchor(resultPanel, new Vector2(0,0.04f), new Vector2(1,0.22f), Vector2.zero, Vector2.zero);
+        Img(resultPanel, new Color(0.05f,0.05f,0.15f,0.95f));
+        var resultText = Child("VoteResult", resultPanel);
+        Stretch(resultText);
+        var rtTmp = resultText.gameObject.AddComponent<TextMeshProUGUI>();
+        rtTmp.text = ""; rtTmp.fontSize = 34; rtTmp.color = C_YELLOW;
+        rtTmp.fontStyle = FontStyles.Bold; rtTmp.alignment = TextAlignmentOptions.Center;
+        resultPanel.gameObject.SetActive(false);
 
         panel.gameObject.SetActive(false);
     }
 
+    void BuildMeetingScroll(RectTransform rt)
+    {
+        var sr = rt.gameObject.AddComponent<ScrollRect>();
+
+        var vp = Child("Viewport", rt);
+        Stretch(vp);
+        vp.gameObject.AddComponent<RectMask2D>();
+
+        var content = Child("MeetingContent", vp);
+        content.anchorMin = new Vector2(0,1); content.anchorMax = new Vector2(1,1);
+        content.pivot = new Vector2(0.5f,1);
+        var vlg = content.gameObject.AddComponent<VerticalLayoutGroup>();
+        vlg.childControlWidth = true; vlg.childControlHeight = false;
+        vlg.childForceExpandWidth = true; vlg.spacing = 4;
+        var csf = content.gameObject.AddComponent<ContentSizeFitter>();
+        csf.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
+
+        sr.viewport = vp; sr.content = content;
+        sr.horizontal = false; sr.vertical = true; sr.scrollSensitivity = 60;
+    }
+
+    // ── Game-end panel ────────────────────────────────────────────────────────
     void BuildGameEndPanel(RectTransform parent)
     {
-        // DIRECT child, full-screen overlay
-        var panel = AddChild("GameEndPanel", parent);
+        var panel = Child("GameEndPanel", parent);
         Stretch(panel);
-        AddImage(panel.gameObject, new Color(0f, 0f, 0f, 0.85f));
+        Img(panel, new Color(0,0,0,0.90f));
 
-        var txt = AddChild("GameEndText", panel);
-        Stretch(txt);
+        // Result text (upper half)
+        var txt = Child("GameEndText", panel);
+        Anchor(txt, new Vector2(0,0.4f), new Vector2(1,0.9f), Vector2.zero, Vector2.zero);
         var tmp = txt.gameObject.AddComponent<TextMeshProUGUI>();
-        tmp.text      = "";
-        tmp.fontSize  = 72;
-        tmp.fontStyle = FontStyles.Bold;
-        tmp.color     = Color.white;
-        tmp.alignment = TextAlignmentOptions.Center;
+        tmp.text = ""; tmp.fontSize = 72; tmp.fontStyle = FontStyles.Bold;
+        tmp.color = Color.white; tmp.alignment = TextAlignmentOptions.Center;
+
+        // "Return to lobby" button – owner only (GameManager_UI shows/hides it)
+        var retBtn = Child("ReturnToLobbyButton", panel);
+        Anchor(retBtn, new Vector2(0.15f,0.22f), new Vector2(0.85f,0.36f), Vector2.zero, Vector2.zero);
+        var retBg = Img(retBtn, C_GREEN);
+        var retButton = retBtn.gameObject.AddComponent<Button>();
+        retButton.targetGraphic = retBg;
+        retButton.onClick.AddListener(() => GameManager.Instance?.gameClient?.ReturnToLobby());
+        TxtChild(retBtn, "▶  RETURN TO LOBBY", 38, Color.white, TextAlignmentOptions.Center, bold: true);
+        retBtn.gameObject.SetActive(false); // shown only for host
+
+        // "Leave" button
+        var leaveBtn = Child("LeaveGameButton", panel);
+        Anchor(leaveBtn, new Vector2(0.3f,0.08f), new Vector2(0.7f,0.20f), Vector2.zero, Vector2.zero);
+        var leaveBg = Img(leaveBtn, C_RED);
+        var leaveButton = leaveBtn.gameObject.AddComponent<Button>();
+        leaveButton.targetGraphic = leaveBg;
+        leaveButton.onClick.AddListener(() => GameManager.Instance?.LeaveLobbyButton());
+        TxtChild(leaveBtn, "✕  LEAVE", 34, Color.white, TextAlignmentOptions.Center, bold: true);
 
         panel.gameObject.SetActive(false);
     }
 
+    // ── Toast notification ────────────────────────────────────────────────────
+    void BuildToast(RectTransform parent)
+    {
+        var toast = Child("Toast", parent);
+        Anchor(toast, new Vector2(0.05f,0.88f), new Vector2(0.95f,0.94f), Vector2.zero, Vector2.zero);
+        Img(toast, new Color(0.1f,0.1f,0.2f,0.92f));
+        TxtChild(toast, "", 30, C_YELLOW, TextAlignmentOptions.Center, bold: true);
+        toast.gameObject.SetActive(false);
+    }
+
     // ── Helpers ───────────────────────────────────────────────────────────────
 
-    RectTransform AddChild(string name, RectTransform parent)
+    RectTransform Child(string name, RectTransform parent)
     {
         var go = new GameObject(name);
         var rt = go.AddComponent<RectTransform>();
@@ -279,40 +294,35 @@ public class InGameHUDBuilder : MonoBehaviour
         return rt;
     }
 
-    Image AddImage(GameObject go, Color color)
+    Image Img(RectTransform rt, Color c)
     {
-        var img = go.AddComponent<Image>();
-        img.color = color;
+        var img = rt.gameObject.AddComponent<Image>();
+        img.color = c;
         return img;
     }
 
-    TextMeshProUGUI AddTextChild(RectTransform parent, string name, string text,
-        float size, FontStyles style, TextAlignmentOptions align)
-    {
-        var rt = AddChild(name, parent);
-        Stretch(rt);
-        var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
-        tmp.text      = text;
-        tmp.fontSize  = size;
-        tmp.fontStyle = style;
-        tmp.alignment = align;
-        tmp.color     = Color.white;
-        return tmp;
-    }
-
-    void Anchor(RectTransform rt, Vector2 min, Vector2 max)
-    {
-        rt.anchorMin  = min;
-        rt.anchorMax  = max;
-        rt.offsetMin  = Vector2.zero;
-        rt.offsetMax  = Vector2.zero;
-    }
-
     void Stretch(RectTransform rt)
     {
-        rt.anchorMin  = Vector2.zero;
-        rt.anchorMax  = Vector2.one;
-        rt.offsetMin  = Vector2.zero;
-        rt.offsetMax  = Vector2.zero;
+        rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one;
+        rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero;
+    }
+
+    // min/max by anchor + absolute offset corners
+    void Anchor(RectTransform rt, Vector2 aMin, Vector2 aMax, Vector2 offsetMin, Vector2 offsetMax)
+    {
+        rt.anchorMin = aMin; rt.anchorMax = aMax;
+        rt.offsetMin = offsetMin; rt.offsetMax = offsetMax;
+    }
+
+    TextMeshProUGUI TxtChild(RectTransform parent, string text, float size, Color color,
+        TextAlignmentOptions align, bool bold = false)
+    {
+        var rt = Child("Txt", parent);
+        Stretch(rt);
+        var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
+        tmp.text = text; tmp.fontSize = size; tmp.color = color; tmp.alignment = align;
+        if (bold) tmp.fontStyle = FontStyles.Bold;
+        return tmp;
     }
 }
+
