diff --git a/DESIGN.md b/DESIGN.md
index 6dd7dec361..2f22c906c4 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -447,16 +447,21 @@ Class names below are examples. Keep the values and structure.
```css
.badge {
- display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 8px;
- border: 1px solid rgb(0 0 0 / 9%); border-radius: 999px; background: rgb(0 0 0 / 4%);
- font-size: 12px; font-weight: 500; line-height: 1; color: var(--ui-fg-dim);
+ display: inline-flex; align-items: center; gap: 6px; height: 24px;
+ font-size: 13px; line-height: 1; color: var(--ui-fg-dim);
}
-.dark .badge { border-color: rgb(255 255 255 / 12%); background: rgb(255 255 255 / 7%); color: var(--ui-fg); }
+.dark .badge { color: var(--ui-fg); }
.badge-dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
```
-- The pill stays neutral. Only the 6px dot has the status color (success,
- warning, error, neutral). Use a pulsing dot for a state in progress.
+- The badge is bare: a 6px colored dot and plain text, with no border, fill,
+ or padding. Only the dot has the status color (success, warning, error,
+ neutral). Use a pulsing dot for a state in progress.
+- Use the bare form everywhere: table rows, cards, headers, and the top bar.
+ Do not wrap a status in a pill.
+- A clickable badge (a status menu or a link) gets a ghost hover: a subtle
+ rounded background drawn 6px outside the badge, so the dot stays aligned
+ with the text around it.
### Empty state
@@ -1235,7 +1240,7 @@ Collections with many rows should use the Cloudflare-inspired table pattern:
- filters, sort, view toggles, and Add on the right;
- 40px header row and roughly 48px data rows;
- subtle row hover;
-- plain text or the shared status badge rather than large colored chips;
+- plain text or the shared bare status badge rather than pills or colored chips;
- compact action at the far right;
- no separate layer card for each item.
diff --git a/resources/css/app.css b/resources/css/app.css
index d806a603dd..96d9bd8f32 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -3179,11 +3179,6 @@ input[type="search"]::-webkit-search-results-decoration {
.table-filter-value.table-filter-badge:hover {
text-decoration-color: transparent;
- border-color: var(--color-neutral-400);
-}
-
-.dark .table-filter-value.table-filter-badge:hover {
- border-color: rgba(255, 255, 255, 0.3);
}
.env-table-grid {
@@ -4259,7 +4254,6 @@ html[data-theme="custom"] .logs-viewer-timestamp {
.logs-section-status-badge {
height: 1.5rem !important;
- padding-inline: 0.5rem !important;
font-size: 0.6875rem !important;
}
diff --git a/resources/js/canvas/environment/Panel.svelte b/resources/js/canvas/environment/Panel.svelte
index b0a1146df1..7933f1436f 100644
--- a/resources/js/canvas/environment/Panel.svelte
+++ b/resources/js/canvas/environment/Panel.svelte
@@ -56,7 +56,7 @@
const listButton = 'flex w-full items-center gap-2 rounded-md px-1.5 py-1 text-left text-[12px] transition-colors hover:bg-neutral-100 dark:hover:bg-white/[0.06]';
const smallButton = danger => `button h-6! shrink-0 px-2! text-[11px]! ${danger ? 'button-error' : ''}`;
const faint = 'text-[12px] text-neutral-400 dark:text-fg-faint';
- const statusPill = 'inline-flex h-6 items-center gap-1.5 rounded-full border border-neutral-200 bg-neutral-100 px-2 text-xs font-medium text-neutral-700 dark:border-white/[0.12] dark:bg-white/[0.07] dark:text-white';
+ const statusPill = 'inline-flex h-6 items-center gap-1.5 text-[13px] text-neutral-700 dark:text-fg';
const titleLink = '[&[href]]:cursor-pointer [&[href]]:underline-offset-2 [&[href]]:hover:underline';
diff --git a/resources/views/components/backup-s3-storage.blade.php b/resources/views/components/backup-s3-storage.blade.php
new file mode 100644
index 0000000000..852ffe8300
--- /dev/null
+++ b/resources/views/components/backup-s3-storage.blade.php
@@ -0,0 +1,21 @@
+@props([
+ 'enabled' => false,
+ 'names' => collect(),
+ 'tooltip' => null,
+])
+
+{{-- S3 is a setting, not a state: plain text keeps the status badge the only pill in the row. --}}
+class('inline-flex max-w-full min-w-0 items-center gap-1.5') }}
+ @if ($tooltip) data-tooltip="{{ $tooltip }}" aria-label="{{ $tooltip }}" tabindex="0" @endif>
+ @if (! $enabled)
+ Local only
+ @elseif ($names->isEmpty())
+
+ Unavailable
+ @else
+ {{ $names->first() }}
+ @if ($names->count() > 1)
+ +{{ $names->count() - 1 }}
+ @endif
+ @endif
+
diff --git a/resources/views/components/status-badge.blade.php b/resources/views/components/status-badge.blade.php
index 6cabaa1a40..c4c50c6283 100644
--- a/resources/views/components/status-badge.blade.php
+++ b/resources/views/components/status-badge.blade.php
@@ -18,15 +18,17 @@
];
$sizeClasses = [
- 'sm' => 'h-4 gap-1 px-1.5 text-[10px]',
- 'md' => 'h-6 gap-1.5 px-2 text-xs',
+ 'sm' => 'gap-1 text-[11px]',
+ 'md' => 'gap-1.5 text-[13px]',
];
- $baseClasses = 'inline-flex max-w-full items-center whitespace-nowrap rounded-full border border-neutral-200 bg-neutral-100 font-medium leading-none text-neutral-700 dark:border-white/[0.12] dark:bg-white/[0.07] dark:text-white ' . ($sizeClasses[$size] ?? $sizeClasses['md']);
+ // Bare: a colored dot and text, no pill. Interactive badges draw their hover background outside the box, so the dot stays aligned with plain text.
+ $baseClasses = 'inline-flex max-w-full items-center whitespace-nowrap text-neutral-700 dark:text-fg ' . ($sizeClasses[$size] ?? $sizeClasses['md']);
+ $interactiveClasses = 'relative isolate before:absolute before:-inset-x-1.5 before:-inset-y-1 before:-z-10 before:rounded-md before:transition-colors hover:before:bg-neutral-100 dark:hover:before:bg-white/[0.07]';
@endphp
@if ($as === 'button')
-