diff --git a/modules/agents.py b/modules/agents.py
index 662fe9e..7a85180 100644
--- a/modules/agents.py
+++ b/modules/agents.py
@@ -1,137 +1,1098 @@
-from PyQt5.QtWidgets import *
-from PyQt5.QtCore import *
-from PyQt5.QtGui import *
+import json
+import urllib.error
+import urllib.request
+from pathlib import Path
+
+from PyQt5.QtCore import Qt, QTimer, QUrl, pyqtSignal
+from PyQt5.QtGui import QDesktopServices, QFont
+from PyQt5.QtWidgets import (
+    QButtonGroup,
+    QComboBox,
+    QFrame,
+    QGridLayout,
+    QHBoxLayout,
+    QLabel,
+    QLineEdit,
+    QPushButton,
+    QScrollArea,
+    QSizePolicy,
+    QSplitter,
+    QVBoxLayout,
+    QWidget,
+)
+
 from framework.warden_theme import *
-from framework.warden_widgets import *
-from core.agent_manager import agent_manager
+from framework.warden_widgets import WardenBackground
+from modules.agent_workspace import EmbeddedAgentWorkspace
+
+
+ARMOR_ROOT = Path("/home/shire3d/ARMOR")
+REGISTRY_FILE = ARMOR_ROOT / "config/agents/registry.json"
+BONUS_ROOT = Path("/SHiREVault/BONUS_APPS")
+AGENT_HUB_URL = "http://127.0.0.1:8770"
+
+TAILSCALE_IP = "100.116.93.90"
+
+SOURCE_AGENT_MAP = {
+    "FlexiForge_SHIRE_v0.2.0.zip": "flexiforge",
+    "limb-forge-canvas.zip": "covercanvas",
+    "shire-limb-forge.zip": "limbforge",
+    "shire-scan-forge-shire-agent-ready.zip": "scanforge",
+    "SHIRE-Marketing-Forge-Easy-v1.3.0.zip": "marketing-boss",
+    "SHIRE3D-Manager-Standalone-v0.1.0.zip": "shire3d-manager",
+    "SHiRE-Dental-Studio-Independent-v2.0.0.zip": "dental-studio",
+    "shire-link-prime-live-shell.zip": "link-prime",
+}
+
+SYNTHETIC_AGENTS = [
+    {
+        "id": "relay",
+        "name": "RELAY",
+        "nickname": "Remote Thingy",
+        "category": "Infrastructure and Devices",
+        "description": (
+            "Private remote-device manager for SHiRE Mobile OTA releases, "
+            "approved phones, wearables and future SHiRE nodes."
+        ),
+        "declaredStatus": "STAGED_OTA_AND_DEVICE_BRIDGE_REQUIRED",
+        "enabled": False,
+        "route": "/agents/relay",
+        "localUrl": None,
+        "appPath": "agents/apps/relay",
+    },
+    {
+        "id": "shire3d-manager",
+        "name": "SHiRE3D Manager",
+        "category": "Commerce and Operations",
+        "description": (
+            "Orders, marketplace listings, pricing, shipping and product "
+            "operations source recovered from BONUS_APPS."
+        ),
+        "declaredStatus": "SOURCE_AVAILABLE_IN_BONUS_APPS",
+        "enabled": False,
+        "route": "/agents/shire3d-manager",
+        "localUrl": None,
+        "appPath": "BONUS_APPS/SHIRE3D-Manager-Standalone-v0.1.0.zip",
+    },
+    {
+        "id": "dental-studio",
+        "name": "SHiRE Dental Studio",
+        "category": "Specialist Design",
+        "description": (
+            "Independent dental-design workspace source. Clinical and "
+            "manufacturing functions remain safety-gated."
+        ),
+        "declaredStatus": "SOURCE_AVAILABLE_SAFETY_REVIEW_REQUIRED",
+        "enabled": False,
+        "route": "/agents/dental-studio",
+        "localUrl": None,
+        "appPath": "BONUS_APPS/SHiRE-Dental-Studio-Independent-v2.0.0.zip",
+    },
+    {
+        "id": "link-prime",
+        "name": "SHiRE Link Prime",
+        "category": "Companion and Communication",
+        "description": (
+            "Live SHiRE companion shell with chat, device connection, "
+            "printer selection and command-centre concepts."
+        ),
+        "declaredStatus": "SOURCE_AVAILABLE_BASE44_REMOVAL_REQUIRED",
+        "enabled": False,
+        "route": "/agents/link-prime",
+        "localUrl": None,
+        "appPath": "BONUS_APPS/shire-link-prime-live-shell.zip",
+    },
+]
+
+
+def http_json(url, timeout=2.5):
+    try:
+        request = urllib.request.Request(
+            url,
+            headers={"Accept": "application/json"},
+        )
+        with urllib.request.urlopen(request, timeout=timeout) as response:
+            return response.status, json.load(response)
+    except Exception as exc:
+        return 0, {"error": str(exc)}
+
+
+def load_registry():
+    try:
+        payload = json.loads(REGISTRY_FILE.read_text(encoding="utf-8"))
+        return payload.get("agents", [])
+    except Exception:
+        return []
+
+
+def source_files():
+    if not BONUS_ROOT.is_dir():
+        return {}
+
+    result = {}
+    for filename, agent_id in SOURCE_AGENT_MAP.items():
+        path = BONUS_ROOT / filename
+        if path.exists():
+            result.setdefault(agent_id, []).append(path)
+    return result
+
+
+def friendly_status(raw, enabled=False, online=False):
+    value = str(raw or "").upper()
+
+    if online:
+        return "LIVE"
+
+    if "ERROR" in value or "FAILED" in value:
+        return "ERROR"
+
+    if "BLOCK" in value:
+        return "BLOCKED"
+
+    if enabled:
+        return "READY"
+
+    if "READY" in value and "REQUIRED" not in value:
+        return "READY"
+
+    if "SOURCE_AVAILABLE" in value:
+        return "SOURCE"
+
+    if "STAGED" in value or "REQUIRED" in value:
+        return "STAGED"
+
+    return "OFFLINE"
+
+
+def status_colour(status):
+    return {
+        "LIVE": "#20e887",
+        "READY": "#53b7ff",
+        "STAGED": "#b174ff",
+        "SOURCE": "#f2b84b",
+        "BLOCKED": "#ff8c42",
+        "ERROR": "#ff4d5f",
+        "OFFLINE": "#758391",
+    }.get(status, "#758391")
+
+
+# ARMOR_AGENT_EMBED_0007F1
+
+
+def dedicated_workspace_url(data):
+    url = str(
+        data.get("workspaceUrl")
+        or ""
+    ).strip()
+
+    if url:
+        return url
+
+    agent_id = str(
+        data.get("id")
+        or ""
+    )
+
+    registry_path = (
+        ARMOR_ROOT
+        / "config"
+        / "agents"
+        / "registry.json"
+    )
+
+    try:
+        registry = json.loads(
+            registry_path.read_text(
+                encoding="utf-8"
+            )
+        )
+    except Exception:
+        return ""
+
+    for agent in registry.get("agents", []):
+        if agent.get("id") == agent_id:
+            return str(
+                agent.get("workspaceUrl")
+                or ""
+            ).strip()
+
+    return ""
+
+
+class AgentCard(QFrame):
+    selected = pyqtSignal(str)
+    workspace_requested = pyqtSignal(str)
+
+    def __init__(self, agent_id):
+        super().__init__()
+        self.agent_id = agent_id
+        self.data = {}
+        self.setCursor(Qt.PointingHandCursor)
+        self.setMinimumHeight(190)
+        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
+
+        root = QVBoxLayout(self)
+        root.setContentsMargins(17, 15, 17, 15)
+        root.setSpacing(8)
 
+        top = QHBoxLayout()
 
-class AgentRow(WardenPanel):
-    def __init__(self, key, data):
-        super().__init__(state_colour(data.get("state", "standby")))
-        self.key = key
-        self.setMinimumHeight(44)
+        self.icon = QLabel("◆")
+        self.icon.setFixedSize(42, 42)
+        self.icon.setAlignment(Qt.AlignCenter)
 
-        layout = QGridLayout()
-        layout.setContentsMargins(6, 3, 6, 3)
-        layout.setSpacing(2)
+        name_box = QVBoxLayout()
+        name_box.setSpacing(1)
 
         self.name = QLabel()
-        self.role = QLabel()
+        self.name.setWordWrap(True)
+
+        self.category = QLabel()
+        self.category.setWordWrap(True)
+
+        name_box.addWidget(self.name)
+        name_box.addWidget(self.category)
+
+        self.status = QLabel()
+        self.status.setAlignment(Qt.AlignCenter)
+        self.status.setMinimumWidth(78)
+
+        top.addWidget(self.icon)
+        top.addLayout(name_box, 1)
+        top.addWidget(self.status)
+
+        self.description = QLabel()
+        self.description.setWordWrap(True)
+        self.description.setMaximumHeight(48)
+
         self.task = QLabel()
-        self.state = QLabel()
-        self.progress = QLabel()
-
-        self.name.setStyleSheet(f"color:{GREEN}; font-size:11px; font-weight:bold; border:none; background:transparent;")
-        self.role.setStyleSheet(f"color:{WHITE}; font-size:8px; font-weight:bold; border:none; background:transparent;")
-        self.task.setStyleSheet(f"color:{MUTED}; font-size:8px; font-weight:bold; border:none; background:transparent;")
-        self.state.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
-        self.progress.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
-
-        layout.addWidget(self.name, 0, 0)
-        layout.addWidget(self.role, 0, 1)
-        layout.addWidget(self.state, 0, 2)
-        layout.addWidget(self.task, 1, 0, 1, 2)
-        layout.addWidget(self.progress, 1, 2)
-
-        self.setLayout(layout)
-        self.refresh()
+        self.task.setWordWrap(True)
 
-    def refresh(self):
-        data = agent_manager.agents[self.key]
-        state = data.get("state", "standby")
-        colour = state_colour(state)
+        footer = QHBoxLayout()
+
+        self.source_badge = QLabel()
+        self.brain_badge = QLabel()
+        self.open_button = QPushButton("OPEN")
+        self.open_button.clicked.connect(self.open_workspace)
+
+        footer.addWidget(self.source_badge)
+        footer.addWidget(self.brain_badge)
+        footer.addStretch(1)
+        footer.addWidget(self.open_button)
 
-        self.setStyleSheet(f"""
-            QFrame {{
-                background-color: {PANEL_BG};
+        root.addLayout(top)
+        root.addWidget(self.description)
+        root.addWidget(self.task)
+        root.addStretch(1)
+        root.addLayout(footer)
+
+    def mousePressEvent(self, event):
+        self.selected.emit(self.agent_id)
+        super().mousePressEvent(event)
+
+    def update_agent(self, data):
+        self.data = data
+        status = data.get("uiStatus", "OFFLINE")
+        colour = status_colour(status)
+
+        self.setStyleSheet(
+            f"""
+            AgentCard {{
+                background: rgba(7, 13, 22, 244);
                 border: 2px solid {colour};
-                border-radius: 8px;
+                border-radius: 18px;
+            }}
+            AgentCard:hover {{
+                background: rgba(12, 22, 35, 248);
+                border: 2px solid #e8fff2;
+            }}
+            """
+        )
+
+        self.icon.setText(data.get("icon", "◆"))
+        self.icon.setStyleSheet(
+            f"""
+            background: {colour};
+            color: #02040a;
+            border-radius: 21px;
+            font-size: 21px;
+            font-weight: 900;
+            """
+        )
+
+        self.name.setText(data.get("name", self.agent_id))
+        self.name.setStyleSheet(
+            "color:#e8fff2;font-size:16px;font-weight:900;"
+            "background:transparent;border:none;"
+        )
+
+        self.category.setText(data.get("category", "Specialist Agent"))
+        self.category.setStyleSheet(
+            "color:#91a0aa;font-size:10px;font-weight:700;"
+            "background:transparent;border:none;"
+        )
+
+        self.status.setText(status)
+        self.status.setStyleSheet(
+            f"""
+            color:{colour};
+            background:rgba(0,0,0,100);
+            border:1px solid {colour};
+            border-radius:10px;
+            padding:6px 9px;
+            font-size:9px;
+            font-weight:900;
+            """
+        )
+
+        self.description.setText(data.get("description", "No description supplied."))
+        self.description.setStyleSheet(
+            "color:#cbd6dc;font-size:10px;background:transparent;border:none;"
+        )
+
+        detail = data.get("statusText") or data.get("declaredStatus") or "No live status."
+        self.task.setText(detail.replace("_", " ").title())
+        self.task.setStyleSheet(
+            f"""
+            color:{colour};
+            background:rgba(0,0,0,80);
+            border:none;
+            border-radius:8px;
+            padding:6px;
+            font-family:monospace;
+            font-size:9px;
+            """
+        )
+
+        sources = data.get("sourceFiles", [])
+        self.source_badge.setText(f"SOURCE {len(sources)}" if sources else "NO SOURCE")
+        self.source_badge.setStyleSheet(
+            "color:#f2b84b;font-size:8px;font-weight:800;"
+            "background:transparent;border:none;"
+        )
+
+        brain = data.get("brain", {})
+        fast = bool(brain.get("fastAvailable"))
+        deep = bool(brain.get("deepAvailable"))
+        self.brain_badge.setText(
+            "BRAINS ✓✓" if fast and deep else "BRAINS PARTIAL"
+        )
+        self.brain_badge.setStyleSheet(
+            "color:#53b7ff;font-size:8px;font-weight:800;"
+            "background:transparent;border:none;"
+        )
+
+        url = dedicated_workspace_url(data)
+        self.open_button.setEnabled(bool(url))
+        self.open_button.setText("OPEN" if url else "STAGED")
+        self.open_button.setStyleSheet(
+            f"""
+            QPushButton {{
+                color:#02040a;
+                background:{colour if url else '#53606a'};
+                border:none;
+                border-radius:9px;
+                padding:7px 12px;
+                font-size:9px;
+                font-weight:900;
+            }}
+            QPushButton:disabled {{
+                color:#b9c1c6;
+                background:#303942;
             }}
-        """)
+            """
+        )
+
+    def open_workspace(self):
+        if dedicated_workspace_url(self.data):
+            self.workspace_requested.emit(
+                self.agent_id
+            )
+
+
+class AgentDetails(QFrame):
+    refresh_requested = pyqtSignal()
+    workspace_requested = pyqtSignal(str)
+
+    def __init__(self):
+        super().__init__()
+        self.data = {}
+
+        self.setMinimumWidth(380)
+        self.setStyleSheet(
+            """
+            AgentDetails {
+                background:rgba(4,8,14,248);
+                border:1px solid #263847;
+                border-radius:20px;
+            }
+            """
+        )
+
+        root = QVBoxLayout(self)
+        root.setContentsMargins(22, 20, 22, 20)
+        root.setSpacing(12)
+
+        self.heading = QLabel("SELECT AN AGENT")
+        self.heading.setWordWrap(True)
+        self.heading.setStyleSheet(
+            "color:#00ff66;font-size:22px;font-weight:900;"
+            "background:transparent;border:none;"
+        )
+
+        self.subheading = QLabel("Agent details and controls will appear here.")
+        self.subheading.setWordWrap(True)
+        self.subheading.setStyleSheet(
+            "color:#91a0aa;font-size:11px;background:transparent;border:none;"
+        )
+
+        self.status = QLabel("NO AGENT SELECTED")
+        self.status.setAlignment(Qt.AlignCenter)
+
+        self.summary = QLabel()
+        self.summary.setWordWrap(True)
+        self.summary.setTextInteractionFlags(Qt.TextSelectableByMouse)
+        self.summary.setStyleSheet(
+            """
+            color:#dce8ed;
+            background:rgba(0,0,0,80);
+            border:1px solid #1d2b35;
+            border-radius:12px;
+            padding:12px;
+            font-family:monospace;
+            font-size:10px;
+            """
+        )
+
+        self.open_button = QPushButton("OPEN WORKSPACE")
+        self.open_button.clicked.connect(self.open_workspace)
+
+        self.health_button = QPushButton("REFRESH HEALTH")
+        self.health_button.clicked.connect(self.refresh_requested.emit)
+
+        self.source_button = QPushButton("OPEN SOURCE")
+        self.source_button.clicked.connect(self.open_source)
+
+        self.evidence_button = QPushButton("OPEN EVIDENCE")
+        self.evidence_button.clicked.connect(self.open_evidence)
 
-        self.name.setText(data["name"])
-        self.role.setText(data["role"])
-        self.task.setText(data.get("task", "---"))
-        self.state.setText(state.upper())
-        self.progress.setText(f'{data.get("progress", 0)}%')
+        for button in (
+            self.open_button,
+            self.health_button,
+            self.source_button,
+            self.evidence_button,
+        ):
+            button.setMinimumHeight(40)
+            button.setStyleSheet(
+                """
+                QPushButton {
+                    color:#e8fff2;
+                    background:#17232d;
+                    border:1px solid #385063;
+                    border-radius:10px;
+                    padding:8px;
+                    font-size:10px;
+                    font-weight:900;
+                }
+                QPushButton:hover {
+                    background:#24394a;
+                    border-color:#00ff66;
+                }
+                QPushButton:disabled {
+                    color:#65727c;
+                    background:#111820;
+                    border-color:#27323a;
+                }
+                """
+            )
 
-        self.state.setStyleSheet(f"color:{colour}; font-size:9px; font-weight:bold; border:none; background:transparent;")
-        self.progress.setStyleSheet(f"color:{colour}; font-size:9px; font-weight:bold; border:none; background:transparent;")
+        root.addWidget(self.heading)
+        root.addWidget(self.subheading)
+        root.addWidget(self.status)
+        root.addWidget(self.summary, 1)
+        root.addWidget(self.open_button)
+        root.addWidget(self.health_button)
+        root.addWidget(self.source_button)
+        root.addWidget(self.evidence_button)
+
+    def show_agent(self, data):
+        self.data = data
+        status = data.get("uiStatus", "OFFLINE")
+        colour = status_colour(status)
+
+        self.heading.setText(data.get("name", "Unnamed Agent"))
+
+        nickname = data.get("nickname")
+        subtitle = data.get("category", "Specialist Agent")
+        if nickname:
+            subtitle = f"{subtitle} · {nickname}"
+
+        self.subheading.setText(subtitle)
+
+        self.status.setText(status)
+        self.status.setStyleSheet(
+            f"""
+            color:{colour};
+            background:rgba(0,0,0,100);
+            border:1px solid {colour};
+            border-radius:12px;
+            padding:9px;
+            font-size:11px;
+            font-weight:900;
+            """
+        )
+
+        brain = data.get("brain", {})
+        memory = data.get("memory", {})
+        application = data.get("application", {})
+        source_names = [
+            Path(path).name for path in data.get("sourceFiles", [])
+        ]
+
+        lines = [
+            data.get("description", "No description."),
+            "",
+            f"ID: {data.get('id', 'unknown')}",
+            f"Declared: {data.get('declaredStatus', 'unknown')}",
+            f"Enabled: {data.get('enabled', False)}",
+            f"Route: {data.get('route') or 'not assigned'}",
+            f"Workspace: {dedicated_workspace_url(data) or 'not yet available'}",
+            f"Runtime API: {data.get('localUrl') or 'not yet available'}",
+            "",
+            f"Fast brain: {brain.get('fastModel', 'not declared')}",
+            f"Fast available: {brain.get('fastAvailable', False)}",
+            f"Deep brain: {brain.get('deepModel', 'not declared')}",
+            f"Deep available: {brain.get('deepAvailable', False)}",
+            "",
+            f"Memory root: {memory.get('root', 'not reported')}",
+            f"Memory isolated: {memory.get('isolated', 'unknown')}",
+            f"Application online: {application.get('online', False)}",
+            "",
+            "Source packages:",
+        ]
+
+        lines.extend(
+            [f"• {name}" for name in source_names]
+            if source_names
+            else ["• No matching BONUS_APPS source detected"]
+        )
+
+        lines.extend(
+            [
+                "",
+                "Safety:",
+                "• External actions remain approval-gated",
+                "• Staged Agents are not presented as operational",
+                "• No service controls are enabled by this screen yet",
+            ]
+        )
+
+        self.summary.setText("\n".join(lines))
+
+        self.open_button.setEnabled(
+            bool(
+                dedicated_workspace_url(data)
+            )
+        )
+        self.source_button.setEnabled(bool(data.get("sourceFiles")))
+
+        evidence = (
+            ARMOR_ROOT / "data" / "agents" / str(data.get("id")) / "evidence"
+        )
+        self.evidence_button.setEnabled(evidence.exists())
+
+    def open_workspace(self):
+        agent_id = str(
+            self.data.get("id")
+            or ""
+        )
+
+        if (
+            agent_id
+            and dedicated_workspace_url(
+                self.data
+            )
+        ):
+            self.workspace_requested.emit(
+                agent_id
+            )
+
+    def open_source(self):
+        sources = self.data.get("sourceFiles", [])
+        if sources:
+            QDesktopServices.openUrl(QUrl.fromLocalFile(str(Path(sources[0]).parent)))
+
+    def open_evidence(self):
+        path = ARMOR_ROOT / "data" / "agents" / str(self.data.get("id")) / "evidence"
+        if path.exists():
+            QDesktopServices.openUrl(QUrl.fromLocalFile(str(path)))
 
 
 class AgentsPage(WardenBackground):
     def __init__(self, stack):
         super().__init__()
         self.stack = stack
-        self.rows = {}
+        self.cards = {}
+        self.agents = {}
+        self.selected_agent_id = None
+        self.active_workspace = None
+
+        main = QVBoxLayout(self)
+        main.setContentsMargins(18, 14, 18, 14)
+        main.setSpacing(12)
+
+        header = QHBoxLayout()
+
+        title_box = QVBoxLayout()
+        title_box.setSpacing(1)
+
+        title = QLabel("SHiRE AGENT OPERATIONS CENTRE")
+        title.setStyleSheet(
+            "color:#00ff66;font-size:23px;font-weight:900;"
+            "background:transparent;border:none;"
+        )
 
-        main = QVBoxLayout()
-        main.setContentsMargins(8, 3, 8, 3)
-        main.setSpacing(3)
+        subtitle = QLabel(
+            "Live specialist brains, workspaces, source packages and safety states"
+        )
+        subtitle.setStyleSheet(
+            "color:#91a0aa;font-size:10px;font-weight:700;"
+            "background:transparent;border:none;"
+        )
 
-        title = QLabel("♟ AGENT MANAGER")
-        title.setAlignment(Qt.AlignCenter)
-        title.setStyleSheet(f"color:{GREEN}; font-size:18px; font-weight:bold; background:transparent;")
-        main.addWidget(title)
+        title_box.addWidget(title)
+        title_box.addWidget(subtitle)
 
-        subtitle = QLabel("SHIRE WORKER COUNCIL")
-        subtitle.setAlignment(Qt.AlignCenter)
-        subtitle.setStyleSheet(f"color:{WHITE}; font-size:8px; font-weight:bold; background:transparent;")
-        main.addWidget(subtitle)
+        self.hub_status = QLabel("CONNECTING")
+        self.hub_status.setAlignment(Qt.AlignCenter)
+        self.hub_status.setMinimumWidth(145)
 
-        for key, data in agent_manager.agents.items():
-            row = AgentRow(key, data)
-            self.rows[key] = row
-            main.addWidget(row)
+        back = QPushButton("RETURN HOME")
+        back.setMinimumHeight(42)
+        back.clicked.connect(lambda: self.stack.setCurrentIndex(0))
+        back.setStyleSheet(
+            """
+            QPushButton {
+                color:#02040a;
+                background:#00ff66;
+                border:none;
+                border-radius:12px;
+                padding:9px 16px;
+                font-size:10px;
+                font-weight:900;
+            }
+            QPushButton:hover { background:#78ffaa; }
+            """
+        )
 
-        controls = QGridLayout()
-        controls.setSpacing(3)
+        header.addLayout(title_box, 1)
+        header.addWidget(self.hub_status)
+        header.addWidget(back)
 
-        btn_demo = WardenTouchButton("⚒", "NIGHT", "FORGE", "online")
-        btn_demo.clicked.connect(self.night_forge_demo)
+        main.addLayout(header)
 
-        btn_online = WardenTouchButton("⚡", "START", "ALL", "online")
-        btn_online.clicked.connect(self.start_all)
+        metrics = QHBoxLayout()
+        metrics.setSpacing(9)
 
-        btn_standby = WardenTouchButton("◌", "STANDBY", "ALL", "standby")
-        btn_standby.clicked.connect(self.standby_all)
+        self.total_metric = self.metric("0", "REGISTERED")
+        self.live_metric = self.metric("0", "LIVE")
+        self.ready_metric = self.metric("0", "READY")
+        self.staged_metric = self.metric("0", "STAGED")
+        self.source_metric = self.metric("0", "SOURCE PACKAGES")
 
-        btn_back = WardenTouchButton("↩", "RETURN", "HOME", "online")
-        btn_back.clicked.connect(lambda: self.stack.setCurrentIndex(0))
+        for widget in (
+            self.total_metric,
+            self.live_metric,
+            self.ready_metric,
+            self.staged_metric,
+            self.source_metric,
+        ):
+            metrics.addWidget(widget)
 
-        controls.addWidget(btn_demo, 0, 0)
-        controls.addWidget(btn_online, 0, 1)
-        controls.addWidget(btn_standby, 0, 2)
-        controls.addWidget(btn_back, 0, 3)
+        main.addLayout(metrics)
 
-        main.addLayout(controls)
-        self.setLayout(main)
+        toolbar = QHBoxLayout()
 
-        self.timer = QTimer()
+        self.search = QLineEdit()
+        self.search.setPlaceholderText("Search Agents, categories or capabilities…")
+        self.search.textChanged.connect(self.apply_filters)
+        self.search.setMinimumHeight(40)
+        self.search.setStyleSheet(
+            """
+            QLineEdit {
+                color:#e8fff2;
+                background:#08111a;
+                border:1px solid #304657;
+                border-radius:11px;
+                padding:8px 13px;
+                font-size:11px;
+            }
+            QLineEdit:focus { border-color:#00ff66; }
+            """
+        )
+
+        self.filter = QComboBox()
+        self.filter.addItems(
+            ["ALL STATES", "LIVE", "READY", "STAGED", "SOURCE", "BLOCKED", "ERROR"]
+        )
+        self.filter.currentTextChanged.connect(self.apply_filters)
+        self.filter.setMinimumHeight(40)
+        self.filter.setStyleSheet(
+            """
+            QComboBox {
+                color:#e8fff2;
+                background:#08111a;
+                border:1px solid #304657;
+                border-radius:11px;
+                padding:7px 12px;
+                min-width:145px;
+            }
+            """
+        )
+
+        refresh = QPushButton("REFRESH LIVE STATUS")
+        refresh.clicked.connect(self.refresh)
+        refresh.setMinimumHeight(40)
+        refresh.setStyleSheet(
+            """
+            QPushButton {
+                color:#e8fff2;
+                background:#162736;
+                border:1px solid #3d5b70;
+                border-radius:11px;
+                padding:8px 14px;
+                font-size:10px;
+                font-weight:900;
+            }
+            QPushButton:hover { border-color:#00ff66; }
+            """
+        )
+
+        toolbar.addWidget(self.search, 1)
+        toolbar.addWidget(self.filter)
+        toolbar.addWidget(refresh)
+
+        main.addLayout(toolbar)
+
+        splitter = QSplitter(Qt.Horizontal)
+        splitter.setChildrenCollapsible(False)
+
+        self.scroll = QScrollArea()
+        self.scroll.setWidgetResizable(True)
+        self.scroll.setFrameShape(QFrame.NoFrame)
+        self.scroll.setStyleSheet("background:transparent;border:none;")
+
+        self.card_host = QWidget()
+        self.card_host.setStyleSheet("background:transparent;")
+
+        self.card_grid = QGridLayout(self.card_host)
+        self.card_grid.setContentsMargins(2, 2, 8, 2)
+        self.card_grid.setHorizontalSpacing(13)
+        self.card_grid.setVerticalSpacing(13)
+        self.card_grid.setAlignment(Qt.AlignTop)
+
+        self.scroll.setWidget(self.card_host)
+
+        self.details = AgentDetails()
+        self.details.refresh_requested.connect(self.refresh)
+        self.details.workspace_requested.connect(
+            self.open_agent_workspace
+        )
+
+        splitter.addWidget(self.scroll)
+        splitter.addWidget(self.details)
+        splitter.setStretchFactor(0, 3)
+        splitter.setStretchFactor(1, 1)
+        splitter.setSizes([1150, 410])
+
+        main.addWidget(splitter, 1)
+
+        self.timer = QTimer(self)
         self.timer.timeout.connect(self.refresh)
-        self.timer.start(1000)
+        self.timer.start(10000)
+
         self.refresh()
 
-    def paintEvent(self, event):
-        painter = QPainter(self)
-        painter.setRenderHint(QPainter.Antialiasing)
-        self.paint_warden_background(painter)
-        super().paintEvent(event)
+    @staticmethod
+    def metric(value, label):
+        frame = QFrame()
+        frame.setStyleSheet(
+            """
+            QFrame {
+                background:rgba(5,11,18,238);
+                border:1px solid #263c4c;
+                border-radius:13px;
+            }
+            """
+        )
 
-    def night_forge_demo(self):
-        agent_manager.start_night_forge_demo()
-        self.refresh()
+        layout = QVBoxLayout(frame)
+        layout.setContentsMargins(13, 8, 13, 8)
+        layout.setSpacing(0)
 
-    def start_all(self):
-        for key in list(agent_manager.agents.keys()):
-            agent_manager.activate_agent(key)
-        self.refresh()
+        number = QLabel(value)
+        number.setObjectName("value")
+        number.setAlignment(Qt.AlignCenter)
+        number.setStyleSheet(
+            "color:#e8fff2;font-size:18px;font-weight:900;"
+            "background:transparent;border:none;"
+        )
 
-    def standby_all(self):
-        agent_manager.standby_all()
-        self.refresh()
+        caption = QLabel(label)
+        caption.setAlignment(Qt.AlignCenter)
+        caption.setStyleSheet(
+            "color:#91a0aa;font-size:8px;font-weight:900;"
+            "background:transparent;border:none;"
+        )
+
+        layout.addWidget(number)
+        layout.addWidget(caption)
+
+        return frame
+
+    @staticmethod
+    def set_metric(frame, value):
+        label = frame.findChild(QLabel, "value")
+        if label:
+            label.setText(str(value))
+
+    def build_agent_data(self):
+        registry_agents = load_registry()
+        source_map = source_files()
+
+        status_code, live_payload = http_json(
+            f"{AGENT_HUB_URL}/api/v1/agents",
+            timeout=2.5,
+        )
+
+        live_map = {}
+        if status_code == 200:
+            for item in live_payload.get("agents", []):
+                live_map[str(item.get("id"))] = item
+
+        combined = {}
+
+        for declared in registry_agents:
+            agent_id = str(declared.get("id"))
+            merged = dict(declared)
+            merged.update(live_map.get(agent_id, {}))
+
+            application = merged.get("application") or {}
+            online = bool(application.get("online"))
+
+            merged["description"] = declared.get(
+                "description",
+                merged.get("description", "Specialist SHiRE Agent."),
+            )
+            merged["category"] = declared.get(
+                "category",
+                merged.get("category", "Specialist Agent"),
+            )
+            merged["appPath"] = declared.get("appPath")
+            merged["sourceFiles"] = [
+                str(path) for path in source_map.get(agent_id, [])
+            ]
+            merged["uiStatus"] = friendly_status(
+                merged.get("declaredStatus") or declared.get("status"),
+                enabled=bool(merged.get("enabled")),
+                online=online,
+            )
+            merged["statusText"] = (
+                application.get("health", {}).get("app")
+                if isinstance(application.get("health"), dict)
+                else None
+            ) or merged.get("declaredStatus") or declared.get("status")
+
+            combined[agent_id] = merged
+
+        for synthetic in SYNTHETIC_AGENTS:
+            agent_id = synthetic["id"]
+            if agent_id in combined:
+                continue
+
+            merged = dict(synthetic)
+            merged["sourceFiles"] = [
+                str(path) for path in source_map.get(agent_id, [])
+            ]
+            merged["uiStatus"] = friendly_status(
+                merged.get("declaredStatus"),
+                enabled=False,
+                online=False,
+            )
+            merged["brain"] = {
+                "fastModel": "shire-mini-fast",
+                "fastAvailable": True,
+                "deepModel": "shire-mini-deep",
+                "deepAvailable": True,
+            }
+            merged["memory"] = {
+                "root": str(ARMOR_ROOT / "data" / "agents" / agent_id),
+                "isolated": True,
+            }
+            combined[agent_id] = merged
+
+        return combined, status_code == 200
 
     def refresh(self):
-        for row in self.rows.values():
-            row.refresh()
+        self.agents, hub_online = self.build_agent_data()
+
+        if hub_online:
+            self.hub_status.setText("AGENT HUB LIVE")
+            self.hub_status.setStyleSheet(
+                """
+                color:#20e887;
+                background:rgba(0,0,0,100);
+                border:1px solid #20e887;
+                border-radius:12px;
+                padding:10px;
+                font-size:10px;
+                font-weight:900;
+                """
+            )
+        else:
+            self.hub_status.setText("AGENT HUB OFFLINE")
+            self.hub_status.setStyleSheet(
+                """
+                color:#ff4d5f;
+                background:rgba(0,0,0,100);
+                border:1px solid #ff4d5f;
+                border-radius:12px;
+                padding:10px;
+                font-size:10px;
+                font-weight:900;
+                """
+            )
+
+        for agent_id, data in self.agents.items():
+            card = self.cards.get(agent_id)
+
+            if card is None:
+                card = AgentCard(agent_id)
+                card.selected.connect(self.select_agent)
+                card.workspace_requested.connect(
+                    self.open_agent_workspace
+                )
+                self.cards[agent_id] = card
+
+            card.update_agent(data)
+
+        for agent_id in list(self.cards):
+            if agent_id not in self.agents:
+                self.cards[agent_id].deleteLater()
+                del self.cards[agent_id]
+
+        statuses = [
+            data.get("uiStatus", "OFFLINE")
+            for data in self.agents.values()
+        ]
+
+        self.set_metric(self.total_metric, len(self.agents))
+        self.set_metric(self.live_metric, statuses.count("LIVE"))
+        self.set_metric(self.ready_metric, statuses.count("READY"))
+        self.set_metric(self.staged_metric, statuses.count("STAGED"))
+        self.set_metric(
+            self.source_metric,
+            sum(len(data.get("sourceFiles", [])) for data in self.agents.values()),
+        )
+
+        self.apply_filters()
+
+        if self.selected_agent_id in self.agents:
+            self.details.show_agent(self.agents[self.selected_agent_id])
+        elif self.agents:
+            first = sorted(self.agents)[0]
+            self.select_agent(first)
+
+    def select_agent(self, agent_id):
+        self.selected_agent_id = agent_id
+        data = self.agents.get(agent_id)
+        if data:
+            self.details.show_agent(data)
+
+    def open_agent_workspace(self, agent_id):
+        data = self.agents.get(agent_id)
+
+        if not data:
+            return
+
+        workspace_url = dedicated_workspace_url(
+            data
+        )
+
+        if not workspace_url:
+            return
+
+        if self.active_workspace is not None:
+            self.stack.setCurrentWidget(
+                self.active_workspace
+            )
+            return
+
+        workspace_data = dict(data)
+        workspace_data["workspaceUrl"] = (
+            workspace_url
+        )
+
+        workspace = EmbeddedAgentWorkspace(
+            self.stack,
+            self,
+            workspace_data,
+        )
+
+        self.active_workspace = workspace
+
+        workspace.destroyed.connect(
+            lambda *_: setattr(
+                self,
+                "active_workspace",
+                None,
+            )
+        )
+
+        self.stack.addWidget(workspace)
+        self.stack.setCurrentWidget(workspace)
+
+    def apply_filters(self):
+        query = self.search.text().strip().lower()
+        selected_state = self.filter.currentText()
+
+        visible = []
+
+        for agent_id, card in self.cards.items():
+            data = self.agents.get(agent_id, {})
+            haystack = " ".join(
+                [
+                    str(data.get("name", "")),
+                    str(data.get("category", "")),
+                    str(data.get("description", "")),
+                    str(data.get("declaredStatus", "")),
+                    agent_id,
+                ]
+            ).lower()
+
+            state_match = (
+                selected_state == "ALL STATES"
+                or data.get("uiStatus") == selected_state
+            )
+            text_match = not query or query in haystack
+
+            if state_match and text_match:
+                visible.append(card)
+            else:
+                card.hide()
+
+        while self.card_grid.count():
+            item = self.card_grid.takeAt(0)
+            if item.widget():
+                item.widget().setParent(self.card_host)
+
+        columns = 2
+
+        for index, card in enumerate(visible):
+            row = index // columns
+            column = index % columns
+            self.card_grid.addWidget(card, row, column)
+            card.show()
+
+        for column in range(columns):
+            self.card_grid.setColumnStretch(column, 1)
