Visual polish

This commit is contained in:
2026-04-12 02:44:40 +06:00
parent 000318f77e
commit 46c43b95c2

View File

@ -168,45 +168,48 @@
<main class="flex min-w-0 flex-1 flex-col overflow-hidden">
<!-- Header -->
<header class="flex shrink-0 flex-col gap-4 border-b border-[var(--color-border)] bg-[var(--color-bg-1)] px-6 py-5">
<div class="flex items-start justify-between">
<header class="relative shrink-0 border-b border-[var(--color-border)] bg-[var(--color-bg-1)]">
<!-- Subtle gradient wash behind header for depth -->
<div class="absolute inset-0 bg-gradient-to-b from-[var(--color-accent)]/[0.02] to-transparent pointer-events-none"></div>
<div class="relative flex items-start justify-between px-8 pt-7 pb-5">
<div>
<h1 class="font-serif text-[1.75rem] leading-none tracking-[-0.03em] text-[var(--color-text-bright)]">
<h1 class="font-serif text-page leading-none tracking-[-0.03em] text-[var(--color-text-bright)]">
Hosts
</h1>
<p class="mt-1.5 text-ui text-[var(--color-text-tertiary)]">
<p class="mt-2 text-ui text-[var(--color-text-tertiary)]">
Platform and BYOC compute across all teams.
</p>
</div>
{#if activeTab === 'platform'}
<button
onclick={() => { showCreate = true; createError = null; createForm = { provider: '', availability_zone: '' }; }}
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-4 py-2 text-ui font-semibold text-white shadow-sm transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0"
class="group flex items-center gap-2.5 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-5 py-2.5 text-ui font-semibold text-white shadow-sm transition-all duration-200 hover:shadow-[0_0_20px_var(--color-accent-glow-mid)] hover:brightness-115 hover:-translate-y-px active:translate-y-0"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="transition-transform duration-200 group-hover:rotate-90"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Add Host
</button>
{/if}
</div>
<!-- Stat pills -->
<!-- Stat strip — bolder presence -->
{#if !loading && !error}
<div class="flex items-center gap-2">
<div class="flex items-baseline gap-1 rounded-[var(--radius-button)] border border-[var(--color-border)] bg-[var(--color-bg-2)] px-2.5 py-1">
<span class="font-mono font-semibold text-ui tabular-nums text-[var(--color-text-bright)]">{totalCount}</span>
<div class="relative flex items-center gap-3 px-8 pb-5">
<div class="stat-pill border-[var(--color-border)] bg-[var(--color-bg-2)]">
<span class="font-mono text-body font-bold tabular-nums text-[var(--color-text-bright)]">{totalCount}</span>
<span class="text-label text-[var(--color-text-muted)]">total</span>
</div>
<div class="flex items-baseline gap-1.5 rounded-[var(--radius-button)] border border-[var(--color-accent)]/25 bg-[var(--color-accent)]/8 px-2.5 py-1">
<span class="relative mt-px flex h-1.5 w-1.5 shrink-0 self-center">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-[var(--color-accent)] opacity-60"></span>
<span class="relative inline-flex h-1.5 w-1.5 rounded-full bg-[var(--color-accent)]"></span>
<div class="stat-pill border-[var(--color-accent)]/25 bg-[var(--color-accent)]/8 gap-2">
<span class="relative flex h-2 w-2 shrink-0">
<span class="animate-status-ping absolute inline-flex h-full w-full rounded-full bg-[var(--color-accent)] opacity-60"></span>
<span class="relative inline-flex h-2 w-2 rounded-full bg-[var(--color-accent)]"></span>
</span>
<span class="font-mono font-semibold text-ui tabular-nums text-[var(--color-accent-bright)]">{onlineCount}</span>
<span class="font-mono text-body font-bold tabular-nums text-[var(--color-accent-bright)]">{onlineCount}</span>
<span class="text-label text-[var(--color-accent-bright)]/70">online</span>
</div>
{#if pendingCount > 0}
<div class="flex items-baseline gap-1 rounded-[var(--radius-button)] border border-[var(--color-amber)]/25 bg-[var(--color-amber)]/8 px-2.5 py-1">
<span class="font-mono font-semibold text-ui tabular-nums text-[var(--color-amber)]">{pendingCount}</span>
<div class="stat-pill border-[var(--color-amber)]/25 bg-[var(--color-amber)]/8">
<span class="font-mono text-body font-bold tabular-nums text-[var(--color-amber)]">{pendingCount}</span>
<span class="text-label text-[var(--color-amber)]/70">pending</span>
</div>
{/if}
@ -214,30 +217,32 @@
{/if}
</header>
<!-- Tabs -->
<div class="flex shrink-0 border-b border-[var(--color-border)] bg-[var(--color-bg-1)] px-6">
<!-- Tabs — heavier presence -->
<div class="flex shrink-0 border-b border-[var(--color-border)] bg-[var(--color-bg-1)] px-8">
{#each [['platform', 'Platform', platformHosts.length], ['byoc', 'BYOC', byocHosts.length]] as [id, label, count] (id)}
<button
onclick={() => { activeTab = id as 'platform' | 'byoc'; if (id === 'byoc') byocPage = 0; }}
class="relative py-3 pr-5 text-ui transition-colors duration-150 {activeTab === id
? 'font-medium text-[var(--color-text-bright)]'
class="tab-button {activeTab === id
? 'text-[var(--color-text-bright)] font-medium'
: 'text-[var(--color-text-tertiary)] hover:text-[var(--color-text-secondary)]'}"
>
{label}
{#if activeTab === id}
<span class="absolute bottom-0 left-0 right-5 h-[2px] rounded-t-full bg-[var(--color-accent)]"></span>
{/if}
{#if !loading}
<span class="ml-2 rounded-full bg-[var(--color-bg-4)] px-1.5 py-0.5 text-label text-[var(--color-text-muted)]">
<span class="ml-2 rounded-full px-2 py-0.5 text-label tabular-nums transition-colors duration-200 {activeTab === id
? 'bg-[var(--color-accent)]/12 text-[var(--color-accent-bright)]'
: 'bg-[var(--color-bg-4)] text-[var(--color-text-muted)]'}">
{count}
</span>
{/if}
{#if activeTab === id}
<span class="absolute bottom-0 left-0 right-0 h-[2px] rounded-t-full bg-[var(--color-accent)]"></span>
{/if}
</button>
{/each}
</div>
<!-- Body -->
<div class="flex-1 overflow-y-auto p-6">
<div class="flex-1 overflow-y-auto px-8 py-6">
{#if loading}
{@render skeletonRows()}
{:else if error}
@ -251,16 +256,16 @@
{#if byocHosts.length === 0}
{@render emptyState('byoc')}
{:else}
<div class="space-y-5">
<div class="space-y-6">
{#each byocGroups as group (group.teamId ?? '__none__')}
{@const groupPageHosts = byocPageHosts.filter(h => h.team_id === group.teamId || (group.teamId === null && !h.team_id))}
{#if groupPageHosts.length > 0}
<div>
<div class="mb-2.5 flex items-center gap-2.5">
<div class="mb-3 flex items-center gap-2.5">
<span class="text-label font-semibold uppercase tracking-[0.08em] text-[var(--color-text-tertiary)]">
{group.teamName}
</span>
<span class="rounded-full bg-[var(--color-bg-3)] px-1.5 py-0.5 font-mono text-label text-[var(--color-text-muted)]">
<span class="rounded-full bg-[var(--color-bg-3)] px-2 py-0.5 font-mono text-label tabular-nums text-[var(--color-text-muted)]">
{group.hosts.length}
</span>
</div>
@ -271,22 +276,22 @@
<!-- Pagination -->
{#if byocPageCount > 1}
<div class="flex items-center justify-between pt-2">
<div class="flex items-center justify-between pt-3">
<span class="text-meta text-[var(--color-text-muted)]">
Page {byocPage + 1} of {byocPageCount} · {byocHosts.length} hosts
Page <span class="font-mono tabular-nums">{byocPage + 1}</span> of <span class="font-mono tabular-nums">{byocPageCount}</span> · <span class="font-mono tabular-nums">{byocHosts.length}</span> hosts
</span>
<div class="flex items-center gap-2">
<button
onclick={() => (byocPage = Math.max(0, byocPage - 1))}
disabled={byocPage === 0}
class="rounded-[var(--radius-button)] border border-[var(--color-border)] px-3 py-1.5 text-meta text-[var(--color-text-secondary)] transition-colors duration-150 hover:border-[var(--color-border-mid)] hover:text-[var(--color-text-primary)] disabled:cursor-not-allowed disabled:opacity-40"
class="rounded-[var(--radius-button)] border border-[var(--color-border)] px-3.5 py-1.5 text-meta text-[var(--color-text-secondary)] transition-all duration-150 hover:border-[var(--color-border-mid)] hover:text-[var(--color-text-primary)] disabled:cursor-not-allowed disabled:opacity-40"
>
← Previous
</button>
<button
onclick={() => (byocPage = Math.min(byocPageCount - 1, byocPage + 1))}
disabled={byocPage >= byocPageCount - 1}
class="rounded-[var(--radius-button)] border border-[var(--color-border)] px-3 py-1.5 text-meta text-[var(--color-text-secondary)] transition-colors duration-150 hover:border-[var(--color-border-mid)] hover:text-[var(--color-text-primary)] disabled:cursor-not-allowed disabled:opacity-40"
class="rounded-[var(--radius-button)] border border-[var(--color-border)] px-3.5 py-1.5 text-meta text-[var(--color-text-secondary)] transition-all duration-150 hover:border-[var(--color-border-mid)] hover:text-[var(--color-text-primary)] disabled:cursor-not-allowed disabled:opacity-40"
>
Next →
</button>
@ -301,34 +306,34 @@
</div>
{#snippet skeletonRows()}
<div class="rounded-[var(--radius-card)] border border-[var(--color-border)] bg-[var(--color-bg-1)] overflow-hidden">
<div class="rounded-[var(--radius-card)] border border-[var(--color-border)] bg-[var(--color-bg-1)] overflow-hidden shadow-sm">
<table class="w-full">
<thead>
<tr class="border-b border-[var(--color-border)]">
<th class="px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Host</th>
<th class="px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Status</th>
<th class="hidden px-4 py-3 md:table-cell text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Specs</th>
<th class="hidden px-4 py-3 lg:table-cell text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Last Heartbeat</th>
<th class="px-4 py-3"></th>
<tr class="border-b border-[var(--color-border)] bg-[var(--color-bg-0)]/40">
<th class="table-header">Host</th>
<th class="table-header">Status</th>
<th class="table-header hidden md:table-cell">Specs</th>
<th class="table-header hidden lg:table-cell">Last Heartbeat</th>
<th class="table-header w-20"></th>
</tr>
</thead>
<tbody>
{#each Array(5) as _, i}
<tr class="border-b border-[var(--color-border)] last:border-0" style="animation-delay: {i * 60}ms">
<td class="px-4 py-3.5">
<tr class="table-row-animate border-b border-[var(--color-border)] last:border-0" style="animation-delay: {i * 60}ms">
<td class="px-5 py-3.5">
<div class="skeleton mb-1.5 h-3 w-28 rounded"></div>
<div class="skeleton h-2.5 w-20 rounded"></div>
</td>
<td class="px-4 py-3.5">
<td class="px-5 py-3.5">
<div class="skeleton h-3 w-16 rounded-full"></div>
</td>
<td class="hidden px-4 py-3.5 md:table-cell">
<td class="hidden px-5 py-3.5 md:table-cell">
<div class="skeleton h-3 w-24 rounded"></div>
</td>
<td class="hidden px-4 py-3.5 lg:table-cell">
<td class="hidden px-5 py-3.5 lg:table-cell">
<div class="skeleton h-3 w-20 rounded"></div>
</td>
<td class="px-4 py-3.5 text-right">
<td class="px-5 py-3.5 text-right">
<div class="skeleton ml-auto h-6 w-14 rounded"></div>
</td>
</tr>
@ -342,26 +347,28 @@
{#if hosts.length === 0}
{@render emptyState('platform')}
{:else}
<div class="rounded-[var(--radius-card)] border border-[var(--color-border)] bg-[var(--color-bg-1)] overflow-hidden">
<div class="rounded-[var(--radius-card)] border border-[var(--color-border)] bg-[var(--color-bg-1)] overflow-hidden shadow-sm">
<table class="w-full">
<thead>
<tr class="border-b border-[var(--color-border)]">
<th class="px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Host</th>
<th class="px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)]">Status</th>
<th class="hidden px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)] md:table-cell">Specs</th>
<th class="hidden px-4 py-3 text-left text-label font-semibold uppercase tracking-[0.06em] text-[var(--color-text-tertiary)] lg:table-cell">Last Heartbeat</th>
<th class="px-4 py-3"></th>
<tr class="border-b border-[var(--color-border)] bg-[var(--color-bg-0)]/40">
<th class="table-header">Host</th>
<th class="table-header">Status</th>
<th class="table-header hidden md:table-cell">Specs</th>
<th class="table-header hidden lg:table-cell">Last Heartbeat</th>
<th class="table-header w-20"></th>
</tr>
</thead>
<tbody>
{#each hosts as host (host.id)}
{#each hosts as host, i (host.id)}
<tr
class="row-entry border-b border-[var(--color-border)] last:border-0 transition-colors duration-200
class="table-row-animate border-b border-[var(--color-border)] last:border-0 transition-colors duration-200
{host.id === newHostId ? 'new-row' : ''}
{flashHostId === host.id ? 'bg-[var(--color-accent-glow)]' : 'hover:bg-[var(--color-bg-2)]'}"
{flashHostId === host.id ? 'bg-[var(--color-accent-glow)]' : 'hover:bg-[var(--color-bg-2)]'}
{host.status === 'online' ? 'host-row-online' : ''}"
style="animation-delay: {i * 30}ms"
>
<td class="px-4 py-3.5">
<div class="font-mono text-meta text-[var(--color-text-primary)]">{host.id}</div>
<td class="px-5 py-3.5">
<div class="font-mono text-ui font-medium text-[var(--color-text-bright)]">{host.id}</div>
{#if host.address}
<div class="mt-0.5 font-mono text-label text-[var(--color-text-muted)]">{host.address}</div>
{/if}
@ -371,31 +378,31 @@
</div>
{/if}
</td>
<td class="px-4 py-3.5">
<span class="flex items-center gap-1.5 text-meta font-medium" style="color: {statusColor(host.status)}">
<td class="px-5 py-3.5">
<span class="flex items-center gap-2 text-meta font-semibold" style="color: {statusColor(host.status)}">
{#if host.status === 'online'}
<span class="relative flex h-1.5 w-1.5 shrink-0">
<span class="absolute inline-flex h-full w-full animate-ping rounded-full opacity-60" style="background: {statusColor(host.status)}"></span>
<span class="relative inline-flex h-1.5 w-1.5 rounded-full" style="background: {statusColor(host.status)}"></span>
<span class="relative flex h-2 w-2 shrink-0">
<span class="animate-status-ping absolute inline-flex h-full w-full rounded-full opacity-60" style="background: {statusColor(host.status)}"></span>
<span class="relative inline-flex h-2 w-2 rounded-full" style="background: {statusColor(host.status)}"></span>
</span>
{:else}
<span class="h-1.5 w-1.5 shrink-0 rounded-full" style="background: {statusColor(host.status)}"></span>
<span class="h-2 w-2 shrink-0 rounded-full" style="background: {statusColor(host.status)}"></span>
{/if}
{host.status}
</span>
</td>
<td class="hidden px-4 py-3.5 md:table-cell">
<span class="text-meta text-[var(--color-text-secondary)]">{formatSpecs(host)}</span>
<td class="hidden px-5 py-3.5 md:table-cell">
<span class="font-mono text-meta tabular-nums text-[var(--color-text-secondary)]">{formatSpecs(host)}</span>
</td>
<td class="hidden px-4 py-3.5 lg:table-cell">
<td class="hidden px-5 py-3.5 lg:table-cell">
<span class="text-meta text-[var(--color-text-muted)]" title={host.last_heartbeat_at ? formatDate(host.last_heartbeat_at) : undefined}>
{host.last_heartbeat_at ? timeAgo(host.last_heartbeat_at) : '—'}
</span>
</td>
<td class="px-4 py-3.5 text-right">
<td class="px-5 py-3.5 text-right">
<button
onclick={() => openDeleteConfirm(host)}
class="rounded-[var(--radius-button)] px-3 py-1.5 text-meta text-[var(--color-text-tertiary)] transition-colors duration-150 hover:bg-[var(--color-red)]/10 hover:text-[var(--color-red)]"
class="rounded-[var(--radius-button)] px-3 py-1.5 text-meta text-[var(--color-text-tertiary)] transition-all duration-150 hover:bg-[var(--color-red)]/10 hover:text-[var(--color-red)]"
>
Delete
</button>
@ -409,18 +416,31 @@
{/snippet}
{#snippet emptyState(type: 'platform' | 'byoc')}
<div class="flex flex-col items-center justify-center py-24 text-center">
<div class="mb-5 flex h-16 w-16 items-center justify-center rounded-[var(--radius-card)] border border-[var(--color-border)] bg-[var(--color-bg-2)]">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--color-text-muted)]"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>
<div class="flex flex-col items-center justify-center py-28 text-center">
<!-- Floating icon with glow ring -->
<div class="relative mb-7">
<div class="absolute -inset-3 rounded-2xl bg-[var(--color-accent-glow)] blur-xl"></div>
<div class="empty-icon-float relative flex h-18 w-18 items-center justify-center rounded-xl border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] shadow-card">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" class="text-[var(--color-accent-mid)]"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>
</div>
</div>
<p class="font-serif text-[1.125rem] leading-snug text-[var(--color-text-secondary)]">
{type === 'platform' ? 'No platform hosts yet.' : 'No BYOC hosts across any team.'}
<p class="font-serif text-heading leading-snug text-[var(--color-text-secondary)]">
{type === 'platform' ? 'No platform hosts yet' : 'No BYOC hosts across any team'}
</p>
<p class="mt-1.5 text-ui text-[var(--color-text-muted)]">
<p class="mt-2 max-w-[320px] text-ui text-[var(--color-text-muted)]">
{type === 'platform'
? 'Add a host to start scheduling capsules onto your own compute.'
: 'Teams that register their own compute will appear here.'}
</p>
{#if type === 'platform'}
<button
onclick={() => { showCreate = true; createError = null; createForm = { provider: '', availability_zone: '' }; }}
class="mt-6 flex items-center gap-2 rounded-[var(--radius-button)] border border-[var(--color-accent)]/30 bg-[var(--color-accent)]/10 px-4 py-2 text-ui font-medium text-[var(--color-accent-bright)] transition-all duration-200 hover:bg-[var(--color-accent)]/20 hover:border-[var(--color-accent)]/50"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Add your first host
</button>
{/if}
</div>
{/snippet}
@ -435,10 +455,14 @@
onkeydown={(e) => { if (e.key === 'Escape' && !creating) showCreate = false; }}
></div>
<div
class="relative w-full max-w-[420px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] p-6 shadow-xl"
class="relative w-full max-w-[420px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] shadow-dialog"
style="animation: fadeUp 0.18s cubic-bezier(0.25,1,0.5,1) both"
>
<h2 class="font-serif text-[1.375rem] leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
<!-- Top accent edge -->
<div class="h-[2px] rounded-t-[var(--radius-card)] bg-gradient-to-r from-transparent via-[var(--color-accent)] to-transparent"></div>
<div class="p-6">
<h2 class="font-serif text-heading leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
Add Platform Host
</h2>
<p class="mt-1.5 text-ui text-[var(--color-text-tertiary)]">
@ -491,7 +515,7 @@
<button
onclick={handleCreatePlatform}
disabled={creating}
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-5 py-2 text-ui font-semibold text-white transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0 disabled:opacity-50 disabled:hover:translate-y-0"
class="group flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-5 py-2.5 text-ui font-semibold text-white transition-all duration-200 hover:shadow-[0_0_20px_var(--color-accent-glow-mid)] hover:brightness-115 hover:-translate-y-px active:translate-y-0 disabled:opacity-50 disabled:hover:translate-y-0 disabled:hover:shadow-none"
>
{#if creating}
<svg class="animate-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
@ -501,6 +525,7 @@
{/if}
</button>
</div>
</div>
</div>
</div>
{/if}
@ -510,11 +535,15 @@
<div class="fixed inset-0 z-50 flex items-center justify-center">
<div class="absolute inset-0 bg-black/60"></div>
<div
class="relative w-full max-w-[500px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] p-6 shadow-xl"
class="relative w-full max-w-[500px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] shadow-dialog"
style="animation: fadeUp 0.18s cubic-bezier(0.25,1,0.5,1) both"
>
<!-- Success accent edge -->
<div class="h-[2px] rounded-t-[var(--radius-card)] bg-gradient-to-r from-transparent via-[var(--color-accent-bright)] to-transparent"></div>
<div class="p-6">
<!-- Animated checkmark -->
<div class="mb-5 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-accent-glow)]">
<div class="mb-5 flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-accent-glow-mid)]">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent-bright)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline
points="20 6 9 17 4 12"
@ -524,7 +553,7 @@
</svg>
</div>
<h2 class="font-serif text-[1.375rem] leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
<h2 class="font-serif text-heading leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
Host registered
</h2>
<p class="mt-1.5 text-ui text-[var(--color-text-tertiary)]">
@ -558,11 +587,12 @@
<div class="mt-6">
<button
onclick={closeTokenReveal}
class="w-full rounded-[var(--radius-button)] bg-[var(--color-bg-4)] px-4 py-2.5 text-ui font-medium text-[var(--color-text-primary)] transition-colors duration-150 hover:bg-[var(--color-bg-5)]"
class="w-full rounded-[var(--radius-button)] bg-[var(--color-bg-4)] px-4 py-2.5 text-ui font-medium text-[var(--color-text-primary)] transition-all duration-150 hover:bg-[var(--color-bg-5)]"
>
Done
</button>
</div>
</div>
</div>
</div>
{/if}
@ -578,10 +608,14 @@
onkeydown={(e) => { if (e.key === 'Escape' && !deleting) deleteTarget = null; }}
></div>
<div
class="relative w-full max-w-[420px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] p-6 shadow-xl"
class="relative w-full max-w-[420px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] shadow-dialog"
style="animation: fadeUp 0.18s cubic-bezier(0.25,1,0.5,1) both"
>
<h2 class="font-serif text-[1.375rem] leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
<!-- Danger accent edge -->
<div class="h-[2px] rounded-t-[var(--radius-card)] bg-gradient-to-r from-transparent via-[var(--color-red)] to-transparent"></div>
<div class="p-6">
<h2 class="font-serif text-heading leading-tight tracking-[-0.02em] text-[var(--color-text-bright)]">
Delete Host
</h2>
<p class="mt-1.5 text-ui text-[var(--color-text-tertiary)]">
@ -621,7 +655,7 @@
<button
onclick={handleDelete}
disabled={deleting || deletePreviewLoading}
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-red)] px-5 py-2 text-ui font-semibold text-white transition-all duration-150 hover:brightness-110 disabled:opacity-50"
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-red)] px-5 py-2.5 text-ui font-semibold text-white transition-all duration-200 hover:shadow-[0_0_16px_rgba(207,129,114,0.25)] hover:brightness-110 disabled:opacity-50"
>
{#if deleting}
<svg class="animate-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
@ -631,6 +665,7 @@
{/if}
</button>
</div>
</div>
</div>
</div>
{/if}
@ -676,4 +711,54 @@
.checkmark-drawn {
stroke-dashoffset: 0;
}
/* Stat pill — shared base */
.stat-pill {
display: flex;
align-items: baseline;
gap: 6px;
border-radius: var(--radius-button);
border-width: 1px;
padding: 6px 12px;
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.stat-pill:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}
/* Table header */
.table-header {
padding: 10px 20px;
text-align: left;
font-size: var(--text-label);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-tertiary);
}
/* Staggered row entrance */
.table-row-animate {
animation: fadeUp 0.25s ease both;
}
/* Tab button */
.tab-button {
position: relative;
padding: 14px 20px 14px 0;
font-size: var(--text-ui);
transition: color 0.15s ease;
cursor: pointer;
}
/* Online host row — subtle left accent */
.host-row-online {
box-shadow: inset 3px 0 0 var(--color-accent);
}
/* Empty state icon float */
.empty-icon-float {
animation: iconFloat 3s ease-in-out infinite;
}
</style>