diff --git a/test/towerops_web/components/layouts_test.exs b/test/towerops_web/components/layouts_test.exs
new file mode 100644
index 00000000..b84b9139
--- /dev/null
+++ b/test/towerops_web/components/layouts_test.exs
@@ -0,0 +1,167 @@
+defmodule ToweropsWeb.LayoutsTest do
+ use ToweropsWeb.ConnCase
+
+ import Phoenix.LiveViewTest
+
+ setup :register_and_log_in_user
+
+ setup %{conn: conn, user: user} do
+ {:ok, organization} =
+ Towerops.Organizations.create_organization(
+ %{name: "Test Org", use_sites: true},
+ user.id
+ )
+
+ conn = Plug.Conn.put_session(conn, :current_organization_id, organization.id)
+
+ %{conn: conn, organization: organization}
+ end
+
+ describe "authenticated layout - sidebar navigation" do
+ test "renders sidebar with grouped navigation sections", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ # Sidebar container
+ assert html =~ ~s(id="app-sidebar")
+ assert html =~ ~s(id="sidebar-wrapper")
+
+ # Section headers
+ assert html =~ "Monitor"
+ assert html =~ "Respond"
+ assert html =~ "Analyze"
+
+ # Monitor section links
+ assert html =~ ~s(href="/dashboard")
+ assert html =~ ~s(href="/devices")
+ assert html =~ ~s(href="/sites")
+ assert html =~ ~s(href="/network-map")
+
+ # Respond section links
+ assert html =~ ~s(href="/alerts")
+ assert html =~ ~s(href="/maintenance")
+ assert html =~ ~s(href="/schedules")
+
+ # Analyze section links
+ assert html =~ ~s(href="/insights")
+ assert html =~ ~s(href="/trace")
+ assert html =~ ~s(href="/activity")
+ end
+
+ test "renders slim top bar with search, alerts, help, and user menu", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ # Logo in top bar
+ assert html =~ "Towerops"
+
+ # Search trigger
+ assert html =~ ~s(id="global-search-trigger")
+ assert html =~ "⌘K"
+
+ # Alert bell
+ assert html =~ ~s(hero-bell)
+
+ # Help icon
+ assert html =~ ~s(href="/help")
+
+ # User menu button
+ assert html =~ ~s(id="org-menu-button")
+ end
+
+ test "renders sidebar collapse toggle", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(data-sidebar-toggle)
+ assert html =~ "hero-chevron-double-left"
+ end
+
+ test "highlights active page in sidebar", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ # Dashboard link should have active styling
+ assert html =~ ~r/bg-blue-50.*href="\/dashboard"/s
+ end
+
+ test "renders org switcher in sidebar bottom", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(id="sidebar-org-switcher-button")
+ assert html =~ "Test Org"
+ assert html =~ "Switch Organization"
+ assert html =~ "Organization Settings"
+ end
+
+ test "renders settings link in sidebar", %{conn: conn, organization: org} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(href="/orgs/#{org.slug}/settings")
+ end
+
+ test "hides sites link when use_sites is false", %{conn: conn, organization: org} do
+ # Disable sites on the existing org
+ Towerops.Organizations.update_organization(org, %{use_sites: false})
+
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ # Sites should NOT appear as a sidebar nav link
+ refute html =~ ~s(href="/sites")
+ end
+
+ test "no horizontal More dropdown exists", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ refute html =~ ~s(id="more-nav-button")
+ refute html =~ ~s(id="more-nav-menu")
+ end
+ end
+
+ describe "authenticated layout - mobile menu" do
+ test "renders mobile menu with grouped sections", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(id="mobile-menu")
+ assert html =~ ~s(id="mobile-menu-button")
+
+ # Mobile menu contains all nav sections
+ # (Mobile menu has same links as sidebar)
+ assert html =~ "hero-home"
+ assert html =~ "hero-server"
+ assert html =~ "hero-bell-alert"
+ assert html =~ "hero-wrench-screwdriver"
+ assert html =~ "hero-light-bulb"
+ assert html =~ "hero-clock"
+ end
+ end
+
+ describe "authenticated layout - user menu" do
+ test "renders user menu items", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(id="org-menu")
+ assert html =~ "User Settings"
+ assert html =~ "My Data"
+ assert html =~ "What's New"
+ assert html =~ "Log out"
+ end
+ end
+
+ describe "authenticated layout - beta banner" do
+ test "renders beta testing banner", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ ~s(id="beta-banner")
+ assert html =~ "Thanks for testing Towerops!"
+ assert html =~ "hi@towerops.net"
+ end
+ end
+
+ describe "authenticated layout - footer" do
+ test "renders footer with copyright and links", %{conn: conn} do
+ {:ok, _view, html} = live(conn, ~p"/dashboard")
+
+ assert html =~ "Copyright"
+ assert html =~ "Towerops"
+ assert html =~ "Privacy Policy"
+ assert html =~ "Terms of Service"
+ end
+ end
+end