diff --git a/CHANGELOG.md b/CHANGELOG.md
index 39c419c..27d0cb1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The interface is rebuilt on [Livewire Material](https://gitea.nonameweb.ch/noNameWEB/livewire-material), a Material 3 Expressive component library, replacing Mary UI and DaisyUI. Every page — upload, share created, download, sign-in, settings, admin and the setup wizard — uses its components, in a colour scheme generated from SealShare's indigo.
- The theme follows the system's light or dark setting until a user picks Light, Dark or System in Settings → Appearance, or from the account menu. A theme chosen in 1.x is kept.
-- A single top app bar replaces the sidebar and header layouts. Signed-in users reach Upload, the admin pages, Settings and Log out from the account menu.
+- A floating toolbar centred at the bottom of every page replaces the sidebar and header layouts. Signed-in users reach Upload and the admin pages from it, and Settings, the theme and Log out from its account menu. The site's name and logo head the upload and download pages.
- Confirmations for deleting a share, removing the logo, clearing the system password and deleting the account are dialogs instead of browser prompts, and "Saved." messages are snackbars.
- The two-factor setup opens full screen on a phone.
- HTTP error pages and Markdown mail (password reset, email verification) use the same Material design and colours. Set `MAIL_MARKDOWN_THEME=default` to get Laravel's mail theme back.
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index 675c514..14f65b6 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -3,13 +3,13 @@
- @empty
+ {{-- Outside the table, so it stays centred on a phone instead of scrolling with the columns. --}}
+ @if ($shares->total() === 0)
+
+ @else
+
+ @endif
diff --git a/resources/views/partials/app-bar.blade.php b/resources/views/partials/app-bar.blade.php
deleted file mode 100644
index ebcf04f..0000000
--- a/resources/views/partials/app-bar.blade.php
+++ /dev/null
@@ -1,47 +0,0 @@
-{{-- The one top app bar on every page: the site's logo and title, then the account menu for a
- signed-in user, or the theme toggle and a way to sign in for everyone else. --}}
-
-@php
- $siteTitle = \App\Models\Setting::get('site_title') ?: config('app.name', 'SealShare');
- $siteLogo = \App\Models\Setting::get('site_logo');
-@endphp
-
-
-
-
- @if ($siteLogo)
-
- @else
-
- @endif
- {{ $siteTitle }}
-
-
-
-
- @auth
-
-
-
- @if (auth()->user()->is_admin)
-
-
- @endif
-
-
-
-
-
-
-
- @else
-
- @if (Route::has('login') && ! request()->routeIs('login'))
-
- @endif
- @endauth
-
-
diff --git a/resources/views/partials/toolbar.blade.php b/resources/views/partials/toolbar.blade.php
new file mode 100644
index 0000000..513f61f
--- /dev/null
+++ b/resources/views/partials/toolbar.blade.php
@@ -0,0 +1,44 @@
+{{-- The one navigation on every page: a floating toolbar centred above the bottom edge, as wide as
+ its buttons. The site's name and logo head the upload and download pages instead of a bar.
+ A signed-in user gets the pages they can reach and the account menu; everyone else gets the
+ upload page, the theme toggle and a way to sign in — without tooltips, so the download page
+ stays free of anchored components. The current page's button is filled. --}}
+
+@php
+ $onUpload = request()->routeIs('upload', 'share.created');
+ $onDashboard = request()->routeIs('admin.dashboard');
+ $onAdminSettings = request()->routeIs('admin.settings');
+@endphp
+
+
diff --git a/tests/Feature/Admin/AdminDashboardTest.php b/tests/Feature/Admin/AdminDashboardTest.php
index 1c07199..d905d69 100644
--- a/tests/Feature/Admin/AdminDashboardTest.php
+++ b/tests/Feature/Admin/AdminDashboardTest.php
@@ -87,3 +87,12 @@ test('the shares table sorts only by its own columns', function () {
expect(Share::query()->count())->toBe(2);
});
+
+test('without shares the dashboard shows an empty state instead of the table', function () {
+ $admin = User::query()->where('is_admin', true)->first();
+
+ $this->actingAs($admin)->get(route('admin.dashboard'))
+ ->assertOk()
+ ->assertSee('No shares yet')
+ ->assertDontSee('