@stacksjs/defaults 0.75.328 → 0.75.330

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/ai/skills/stacks-dashboard/SKILL.md +2 -2
  2. package/ai/skills/stacks-native/CATALOG.md +2 -2
  3. package/ide/vscode/package.json +1 -1
  4. package/package.json +4 -4
  5. package/resources/components/Dashboard/Auth/ForgotPassword.stx +1 -1
  6. package/resources/components/Dashboard/Ci/CiDashboard.stx +7 -7
  7. package/resources/components/Dashboard/Ci/CiRunHistoryDrawer.stx +3 -3
  8. package/resources/components/Dashboard/Commerce/CommerceCategoriesTable.stx +1 -1
  9. package/resources/components/Dashboard/Commerce/CommerceCategoryDetailsDialog.stx +5 -5
  10. package/resources/components/Dashboard/Commerce/CommerceCategoryDialog.stx +2 -2
  11. package/resources/components/Dashboard/Commerce/CommerceCategoryHierarchy.stx +2 -2
  12. package/resources/components/Dashboard/Commerce/CommerceCouponBreakdown.stx +1 -1
  13. package/resources/components/Dashboard/Commerce/CommerceCouponDetailsDialog.stx +9 -9
  14. package/resources/components/Dashboard/Commerce/CommerceCouponDialog.stx +1 -1
  15. package/resources/components/Dashboard/Commerce/CommerceCustomerDetailsDialog.stx +5 -5
  16. package/resources/components/Dashboard/Commerce/CommerceCustomerDialog.stx +2 -2
  17. package/resources/components/Dashboard/Commerce/CommerceCustomersTable.stx +1 -1
  18. package/resources/components/Dashboard/Commerce/CommerceGiftCardBreakdown.stx +1 -1
  19. package/resources/components/Dashboard/Commerce/CommerceGiftCardDetailsDialog.stx +11 -11
  20. package/resources/components/Dashboard/Commerce/CommerceOrderDetailsDialog.stx +13 -13
  21. package/resources/components/Dashboard/Commerce/CommercePaymentsBreakdown.stx +2 -2
  22. package/resources/components/Dashboard/Commerce/CommercePosCart.stx +1 -1
  23. package/resources/components/Dashboard/Commerce/CommercePosCatalog.stx +1 -1
  24. package/resources/components/Dashboard/Commerce/CommercePosReceiptDialog.stx +1 -1
  25. package/resources/components/Dashboard/Commerce/CommerceProductDetailDashboard.stx +8 -8
  26. package/resources/components/Dashboard/Commerce/CommerceProductDialog.stx +1 -1
  27. package/resources/components/Dashboard/Commerce/CommerceProductsTable.stx +2 -2
  28. package/resources/components/Dashboard/Commerce/CommerceReviewsTable.stx +1 -1
  29. package/resources/components/Dashboard/Commerce/CommerceVariantsTable.stx +1 -1
  30. package/resources/components/Dashboard/Commerce/Delivery/DeliveryOverviewDashboard.stx +1 -1
  31. package/resources/components/Dashboard/Commerce/Delivery/DigitalDeliveryDialog.stx +2 -2
  32. package/resources/components/Dashboard/Commerce/PaymentDetailsDialog.stx +3 -3
  33. package/resources/components/Dashboard/Commerce/PrintDeviceDetailsDialog.stx +6 -6
  34. package/resources/components/Dashboard/Commerce/PrintLogBreakdown.stx +1 -1
  35. package/resources/components/Dashboard/Commerce/PrintLogDetailsDialog.stx +2 -2
  36. package/resources/components/Dashboard/Commerce/PrintLogsTable.stx +3 -3
  37. package/resources/components/Dashboard/Content/AuthorsDashboard.stx +1 -1
  38. package/resources/components/Dashboard/Content/CommentsDashboard.stx +1 -1
  39. package/resources/components/Dashboard/Content/ContentTaxonomyDashboard.stx +1 -1
  40. package/resources/components/Dashboard/Content/FileManagerDashboard.stx +53 -61
  41. package/resources/components/Dashboard/Content/FileStoragePanel.stx +99 -0
  42. package/resources/components/Dashboard/Content/FileUploadDialog.stx +1 -1
  43. package/resources/components/Dashboard/Content/PagesDashboard.stx +1 -1
  44. package/resources/components/Dashboard/Content/PostEditorDialog.stx +2 -2
  45. package/resources/components/Dashboard/Content/PostsDashboard.stx +1 -1
  46. package/resources/components/Dashboard/Content/SeoEditorDialog.stx +1 -1
  47. package/resources/components/Dashboard/Deployments/DeploymentPreviewDialog.stx +1 -1
  48. package/resources/components/Dashboard/Email/CapturedMailDetail.stx +1 -1
  49. package/resources/components/Dashboard/Feedback/FeedbackForm.stx +4 -4
  50. package/resources/components/Dashboard/FileViewer/MediaCard.stx +1 -1
  51. package/resources/components/Dashboard/FileViewer/MediaInfoPanel.stx +4 -4
  52. package/resources/components/Dashboard/GlobalSearch.stx +4 -4
  53. package/resources/components/Dashboard/Infrastructure/CloudDashboard.stx +1 -1
  54. package/resources/components/Dashboard/Infrastructure/DnsDashboard.stx +2 -2
  55. package/resources/components/Dashboard/Infrastructure/LogsDashboard.stx +2 -2
  56. package/resources/components/Dashboard/Infrastructure/MailboxesDashboard.stx +3 -3
  57. package/resources/components/Dashboard/Infrastructure/ServerlessDashboard.stx +2 -2
  58. package/resources/components/Dashboard/Infrastructure/ServersDashboard.stx +5 -5
  59. package/resources/components/Dashboard/Kanban/KanbanBoardDashboard.stx +1 -1
  60. package/resources/components/Dashboard/Kanban/KanbanBoardsDashboard.stx +2 -2
  61. package/resources/components/Dashboard/Kanban/KanbanCardDialog.stx +8 -8
  62. package/resources/components/Dashboard/Marketing/AbandonedCartsDashboard.stx +5 -5
  63. package/resources/components/Dashboard/Marketing/CampaignsDashboard.stx +5 -5
  64. package/resources/components/Dashboard/Marketing/MarketingListsDashboard.stx +4 -4
  65. package/resources/components/Dashboard/Marketing/SocialPostsDashboard.stx +4 -4
  66. package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
  67. package/resources/components/Dashboard/Navbar.stx +2 -2
  68. package/resources/components/Dashboard/Operations/AuditDashboard.stx +1 -1
  69. package/resources/components/Dashboard/Operations/OperationHistory.stx +1 -1
  70. package/resources/components/Dashboard/Operations/RecoveryJobList.stx +1 -1
  71. package/resources/components/Dashboard/Remote/RemoteConsole.stx +1 -1
  72. package/resources/components/Dashboard/Settings/SettingsDashboard.stx +1 -1
  73. package/resources/components/Dashboard/UI/Checkbox.stx +1 -1
  74. package/resources/components/Dashboard/UI/MetricCards.stx +1 -1
  75. package/resources/components/Dashboard/UI/Pagination.stx +1 -1
  76. package/resources/components/Dashboard/UI/Tabs.stx +7 -4
  77. package/resources/components/Dashboard/UI/Textarea.stx +2 -2
  78. package/resources/components/Dashboard/UI/Toggle.stx +1 -1
@@ -221,7 +221,7 @@ function another() {
221
221
  </label>
222
222
 
223
223
  <label class="block mb-4">
224
- <span class="block mb-1.5 font-medium text-neutral-700 text-sm dark:text-neutral-200">Details <span class="font-normal text-neutral-400">(optional)</span></span>
224
+ <span class="block mb-1.5 font-medium text-neutral-700 text-sm dark:text-neutral-200">Details <span class="font-normal text-neutral-500 dark:text-neutral-400">(optional)</span></span>
225
225
  <textarea
226
226
  x-model="description"
227
227
  rows="6"
@@ -233,7 +233,7 @@ function another() {
233
233
  </label>
234
234
 
235
235
  <div class="mb-5">
236
- <span class="block mb-1.5 font-medium text-neutral-700 text-sm dark:text-neutral-200">Screenshot <span class="font-normal text-neutral-400">(optional)</span></span>
236
+ <span class="block mb-1.5 font-medium text-neutral-700 text-sm dark:text-neutral-200">Screenshot <span class="font-normal text-neutral-500 dark:text-neutral-400">(optional)</span></span>
237
237
 
238
238
  <div :if="screenshot()" class="flex gap-3 items-center p-3 bg-neutral-50 dark:bg-neutral-800/50 border border-neutral-200 rounded-lg dark:border-neutral-700">
239
239
  <img :src="preview()" alt="The screenshot you chose" class="object-cover h-14 w-14 bg-white dark:bg-neutral-900 border border-neutral-200 rounded dark:border-neutral-700" />
@@ -246,7 +246,7 @@ function another() {
246
246
  <label :else class="flex flex-col items-center justify-center px-4 py-6 text-center border border-dashed border-neutral-300 rounded-lg dark:border-neutral-600 hover:border-blue-400 dark:hover:border-blue-600 transition-colors cursor-pointer" @dragover.prevent @drop.prevent="drop($event)">
247
247
  <span class="h-6 w-6 text-neutral-400 i-hugeicons-image-upload"></span>
248
248
  <span class="mt-2 font-medium text-neutral-700 text-sm dark:text-neutral-200">Choose an image, drop it here, or paste it</span>
249
- <span class="mt-1 text-neutral-400 text-xs">PNG, JPEG, WebP or GIF, up to {{ megabytes(sizeLimit()) }}</span>
249
+ <span class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">PNG, JPEG, WebP or GIF, up to {{ megabytes(sizeLimit()) }}</span>
250
250
  <input
251
251
  ref="feedbackScreenshotInput"
252
252
  type="file"
@@ -269,7 +269,7 @@ function another() {
269
269
  </button>
270
270
  </form>
271
271
 
272
- <p class="mt-4 text-center text-neutral-400 text-xs dark:text-neutral-500">
272
+ <p class="mt-4 text-center text-neutral-500 text-xs dark:text-neutral-500">
273
273
  This link lets you send feedback. It does not sign you in, and it cannot read anything back.
274
274
  </p>
275
275
  </div>
@@ -97,6 +97,6 @@ const badgeClass = derived(() => kind() === 'video'
97
97
  </div>
98
98
  <div class="px-3 py-2.5">
99
99
  <p :title="currentItem().name" class="font-medium text-[13px] text-stone-800 truncate dark:text-white">{{ currentItem().name }}</p>
100
- <p class="mt-0.5 capitalize text-[11px] text-stone-400 dark:text-neutral-500">{{ currentItem().type === 'folder' ? 'Folder' : kind() }}</p>
100
+ <p class="mt-0.5 capitalize text-[11px] text-stone-500 dark:text-neutral-500">{{ currentItem().type === 'folder' ? 'Folder' : kind() }}</p>
101
101
  </div>
102
102
  </div>
@@ -90,10 +90,10 @@ const kindLabel = derived(() => currentItem().type === 'folder' ? 'Folder' : `${
90
90
  </div>
91
91
  <h4 class="mt-4 break-words font-semibold text-[15px] text-stone-800 dark:text-white">{{ currentItem().name }}</h4>
92
92
  <dl class="mt-4 space-y-3 text-[13px]">
93
- <div class="flex gap-4 items-start justify-between"><dt class="text-stone-400 dark:text-neutral-500">Type</dt><dd class="capitalize font-medium text-right text-stone-700 dark:text-neutral-200">{{ kindLabel() }}</dd></div>
94
- <div class="flex gap-4 items-start justify-between"><dt class="text-stone-400 dark:text-neutral-500">Size</dt><dd class="font-medium text-right text-stone-700 dark:text-neutral-200">{{ sizeLabel() }}</dd></div>
95
- <div class="flex gap-4 items-start justify-between"><dt class="text-stone-400 dark:text-neutral-500">Location</dt><dd class="break-all font-medium text-right text-stone-700 dark:text-neutral-200">{{ locationLabel() }}</dd></div>
96
- <div class="flex gap-4 items-start justify-between"><dt class="text-stone-400 dark:text-neutral-500">Modified</dt><dd class="font-medium text-right text-stone-700 dark:text-neutral-200">{{ formatWhen(currentItem().lastModified) }}</dd></div>
93
+ <div class="flex gap-4 items-start justify-between"><dt class="text-stone-500 dark:text-neutral-500">Type</dt><dd class="capitalize font-medium text-right text-stone-700 dark:text-neutral-200">{{ kindLabel() }}</dd></div>
94
+ <div class="flex gap-4 items-start justify-between"><dt class="text-stone-500 dark:text-neutral-500">Size</dt><dd class="font-medium text-right text-stone-700 dark:text-neutral-200">{{ sizeLabel() }}</dd></div>
95
+ <div class="flex gap-4 items-start justify-between"><dt class="text-stone-500 dark:text-neutral-500">Location</dt><dd class="break-all font-medium text-right text-stone-700 dark:text-neutral-200">{{ locationLabel() }}</dd></div>
96
+ <div class="flex gap-4 items-start justify-between"><dt class="text-stone-500 dark:text-neutral-500">Modified</dt><dd class="font-medium text-right text-stone-700 dark:text-neutral-200">{{ formatWhen(currentItem().lastModified) }}</dd></div>
97
97
  </dl>
98
98
  <span :if="currentItem().shared" class="inline-flex gap-1 items-center self-start mt-5 px-2 py-1 font-medium text-[11px] text-indigo-700 dark:text-indigo-300 bg-indigo-100 dark:bg-indigo-500/15 rounded-full"><span class="h-3 w-3 i-hugeicons-share-01"></span>Public disk</span>
99
99
  </div>
@@ -152,7 +152,7 @@ onDestroy(() => {
152
152
  @input="scheduleSearch($event.target.value)"
153
153
  />
154
154
  <span :if="isSearching()" class="shrink-0 h-5 w-5 text-blue-500 animate-spin i-hugeicons-loading-03"></span>
155
- <kbd :if="!isSearching()" class="shrink-0 px-1.5 py-0.5 font-medium text-[10px] text-neutral-400 bg-neutral-100 dark:bg-neutral-700 border border-neutral-200 rounded dark:border-neutral-600">
155
+ <kbd :if="!isSearching()" class="shrink-0 px-1.5 py-0.5 font-medium text-[10px] text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-neutral-700 border border-neutral-200 rounded dark:border-neutral-600">
156
156
  ESC
157
157
  </kbd>
158
158
  </div>
@@ -170,7 +170,7 @@ onDestroy(() => {
170
170
  <template :if="!error() && Object.keys(results()).length > 0">
171
171
  <template :for="group in resultGroups()">
172
172
  <div class="pb-1 pt-3 px-3">
173
- <h3 class="px-1 font-semibold text-neutral-400 text-xs tracking-wider uppercase">
173
+ <h3 class="px-1 font-semibold text-neutral-500 text-xs tracking-wider uppercase dark:text-neutral-400">
174
174
  {{ group.type }}
175
175
  </h3>
176
176
  </div>
@@ -203,11 +203,11 @@ onDestroy(() => {
203
203
  </div>
204
204
 
205
205
  <div :if="!error() && !query().trim() && !isSearching()" class="px-4 py-8 text-center">
206
- <p class="text-neutral-400 text-sm">Search fields declared by each model's useSearch trait.</p>
206
+ <p class="text-neutral-500 text-sm dark:text-neutral-400">Search fields declared by each model's useSearch trait.</p>
207
207
  </div>
208
208
  </div>
209
209
 
210
- <div class="flex gap-4 items-center px-4 py-2.5 text-neutral-400 text-xs border-neutral-200 border-t dark:border-neutral-700">
210
+ <div class="flex gap-4 items-center px-4 py-2.5 text-neutral-500 text-xs dark:text-neutral-400 border-neutral-200 border-t dark:border-neutral-700">
211
211
  <span class="flex gap-1 items-center">
212
212
  <kbd class="px-1 py-0.5 text-[10px] bg-neutral-100 dark:bg-neutral-700 border border-neutral-200 rounded dark:border-neutral-600">↑↓</kbd>
213
213
  Navigate
@@ -243,7 +243,7 @@ onMount(() => {
243
243
  </div>
244
244
  </section>
245
245
 
246
- <footer :if="snapshot()?.generatedAt" class="text-neutral-400 text-right text-xs dark:text-neutral-500">Snapshot {{ formatDate(snapshot()?.generatedAt || null) }}</footer>
246
+ <footer :if="snapshot()?.generatedAt" class="text-neutral-500 text-right text-xs dark:text-neutral-500">Snapshot {{ formatDate(snapshot()?.generatedAt || null) }}</footer>
247
247
 
248
248
  <CloudDetailsDialog :open="Boolean(selectedResource())" :item="selectedResource()" @close="closeResource" />
249
249
  </div>
@@ -254,7 +254,7 @@ onMount(() => {
254
254
  <section class="overflow-hidden bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
255
255
  <header class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700">
256
256
  <div><h2 class="font-semibold text-neutral-900 dark:text-white">Nameservers</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Configured delegation targets.</p></div>
257
- <span class="font-mono text-neutral-400 text-xs">{{ nameservers().length }}</span>
257
+ <span class="font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ nameservers().length }}</span>
258
258
  </header>
259
259
  <div :if="nameservers().length > 0" class="grid gap-3 sm:grid-cols-2 p-5">
260
260
  <div :for="nameserver in nameservers()" class="flex gap-3 items-center p-3 bg-neutral-50 dark:bg-neutral-800 rounded-lg">
@@ -263,7 +263,7 @@ onMount(() => {
263
263
  </div>
264
264
  </div>
265
265
  <p :if="nameservers().length === 0" class="p-5 text-neutral-500 text-sm dark:text-neutral-400">No nameservers configured.</p>
266
- <p :if="generatedAt()" class="px-5 py-3 text-neutral-400 text-xs dark:text-neutral-500 bg-neutral-50 dark:bg-neutral-800/50 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
266
+ <p :if="generatedAt()" class="px-5 py-3 text-neutral-500 text-xs dark:text-neutral-500 bg-neutral-50 dark:bg-neutral-800/50 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
267
267
  </section>
268
268
 
269
269
  <DnsRecordDialog :open="Boolean(selectedRecord())" :record="selectedRecord()" @close="closeRecord" />
@@ -266,7 +266,7 @@ onDestroy(() => {
266
266
  <div class="p-5 bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
267
267
  <div class="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
268
268
  <div><h2 class="font-semibold text-neutral-900 dark:text-white">Query logs</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Search message, project, file, and stack trace.</p></div>
269
- <p :if="generatedAt()" class="text-neutral-400 text-xs dark:text-neutral-500">Snapshot {{ formatDate(generatedAt()) }}</p>
269
+ <p :if="generatedAt()" class="text-neutral-500 text-xs dark:text-neutral-500">Snapshot {{ formatDate(generatedAt()) }}</p>
270
270
  </div>
271
271
  <div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-3 mt-5">
272
272
  <label class="block relative sm:col-span-2 xl:col-span-1">
@@ -296,7 +296,7 @@ onDestroy(() => {
296
296
  <td class="px-5 py-4 text-neutral-500 whitespace-nowrap dark:text-neutral-400">{{ formatDate(record.timestamp) }}</td>
297
297
  <td class="px-5 py-4"><LogLevelBadge :type="record.type" /></td>
298
298
  <td class="px-5 py-4 font-mono text-neutral-700 text-xs dark:text-neutral-300">{{ record.source || 'Not recorded' }}</td>
299
- <td class="px-5 py-4 max-w-md"><p class="font-medium text-neutral-900 dark:text-white">{{ record.message }}</p><p :if="record.file" class="mt-1 font-mono text-neutral-400 text-xs truncate dark:text-neutral-500">{{ record.file }}</p></td>
299
+ <td class="px-5 py-4 max-w-md"><p class="font-medium text-neutral-900 dark:text-white">{{ record.message }}</p><p :if="record.file" class="mt-1 font-mono text-neutral-500 text-xs truncate dark:text-neutral-500">{{ record.file }}</p></td>
300
300
  <td class="px-5 py-4 text-neutral-600 whitespace-nowrap dark:text-neutral-300">{{ record.project || 'Not recorded' }}</td>
301
301
  <td class="px-5 py-4 text-right"><Button variant="ghost" iconOnly :ariaLabel="'Inspect log ' + record.id" @click="openLog(record)"><span class="block h-5 w-5 i-hugeicons-view"></span></Button></td>
302
302
  </tr>
@@ -217,7 +217,7 @@ onMount(() => {
217
217
 
218
218
  <section class="grid gap-4 grid-cols-1 xl:grid-cols-2">
219
219
  <div class="overflow-hidden bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
220
- <header class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700"><div><h2 class="font-semibold text-neutral-900 dark:text-white">Forwarding</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Configured address copies.</p></div><span class="font-mono text-neutral-400 text-xs">{{ forwards().length }}</span></header>
220
+ <header class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700"><div><h2 class="font-semibold text-neutral-900 dark:text-white">Forwarding</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Configured address copies.</p></div><span class="font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ forwards().length }}</span></header>
221
221
  <div :if="forwards().length > 0" class="divide-neutral-100 divide-y dark:divide-neutral-800">
222
222
  <div :for="forward in forwards()" class="px-5 py-4">
223
223
  <p class="font-medium font-mono text-neutral-900 text-sm dark:text-white">{{ forward.source }}</p>
@@ -228,12 +228,12 @@ onMount(() => {
228
228
  </div>
229
229
 
230
230
  <div class="overflow-hidden bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
231
- <header class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700"><div><h2 class="font-semibold text-neutral-900 dark:text-white">Retention</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Configured mail storage policy.</p></div><span class="font-mono text-neutral-400 text-xs">{{ server().storage.length }}</span></header>
231
+ <header class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700"><div><h2 class="font-semibold text-neutral-900 dark:text-white">Retention</h2><p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">Configured mail storage policy.</p></div><span class="font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ server().storage.length }}</span></header>
232
232
  <dl :if="server().storage.length > 0" class="grid gap-4 sm:grid-cols-2 p-5 text-sm">
233
233
  <div :for="item in server().storage"><dt class="capitalize text-neutral-500 dark:text-neutral-400">{{ storageLabel(item) }}</dt><dd class="mt-1 font-medium text-neutral-900 dark:text-white">{{ item.value }}</dd></div>
234
234
  </dl>
235
235
  <p :if="server().storage.length === 0" class="p-5 text-neutral-500 text-sm dark:text-neutral-400">No storage policy configured.</p>
236
- <p :if="generatedAt()" class="px-5 py-3 text-neutral-400 text-xs dark:text-neutral-500 bg-neutral-50 dark:bg-neutral-800/50 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
236
+ <p :if="generatedAt()" class="px-5 py-3 text-neutral-500 text-xs dark:text-neutral-500 bg-neutral-50 dark:bg-neutral-800/50 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
237
237
  </div>
238
238
  </section>
239
239
 
@@ -214,7 +214,7 @@ onMount(() => {
214
214
  <section :if="links().length > 0">
215
215
  <h2 class="font-semibold text-neutral-900 dark:text-white">Manifest relationships</h2>
216
216
  <p class="mt-1 text-neutral-500 text-sm dark:text-neutral-400">Accessible relationship records derived from the same manifests.</p>
217
- <ul class="grid gap-3 md:grid-cols-2 mt-4">
217
+ <ul class="grid gap-3 grid-cols-1 md:grid-cols-2 mt-4">
218
218
  <li :for="link in links()" class="flex gap-3 items-center p-4 bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
219
219
  <span class="font-semibold text-neutral-900 text-sm truncate dark:text-white">{{ relationshipName(link.source) }}</span>
220
220
  <span class="shrink-0 px-2 py-1 text-blue-700 text-xs dark:text-blue-300 bg-blue-50 dark:bg-blue-950/40 rounded-full">{{ link.type }}</span>
@@ -240,7 +240,7 @@ onMount(() => {
240
240
  </div>
241
241
  </section>
242
242
 
243
- <footer :if="generatedAt()" class="text-neutral-400 text-right text-xs dark:text-neutral-500">Snapshot {{ formatDate(generatedAt()) }}</footer>
243
+ <footer :if="generatedAt()" class="text-neutral-500 text-right text-xs dark:text-neutral-500">Snapshot {{ formatDate(generatedAt()) }}</footer>
244
244
 
245
245
  <CloudDetailsDialog :open="Boolean(selectedResource())" :item="selectedResource()" @close="closeResource" />
246
246
  </div>
@@ -268,7 +268,7 @@ onMount(() => {
268
268
  <h2 class="font-semibold text-neutral-900 dark:text-white">Configured request path</h2>
269
269
  <p class="mt-1 text-neutral-500 text-xs dark:text-neutral-400">A structural view of declared routing, with no inferred health data.</p>
270
270
  </div>
271
- <p class="text-neutral-400 text-xs dark:text-neutral-500">Source: config/cloud.ts</p>
271
+ <p class="text-neutral-500 text-xs dark:text-neutral-500">Source: config/cloud.ts</p>
272
272
  </div>
273
273
  <div class="grid gap-3 grid-cols-1 md:grid-cols-[1fr_auto_1fr_auto_1fr] mt-5">
274
274
  <div class="flex gap-3 items-center p-4 bg-rose-50 dark:bg-rose-950/25 border border-rose-100 rounded-xl dark:border-rose-900/50">
@@ -312,7 +312,7 @@ onMount(() => {
312
312
  <div class="overflow-hidden bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
313
313
  <div class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700">
314
314
  <div><h3 class="font-semibold text-neutral-900 text-sm dark:text-white">Definitions</h3><p class="mt-0.5 text-neutral-500 text-xs dark:text-neutral-400">Desired compute configuration</p></div>
315
- <span class="font-mono text-neutral-400 text-xs">{{ filteredServers().length }}</span>
315
+ <span class="font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ filteredServers().length }}</span>
316
316
  </div>
317
317
  <div class="divide-neutral-100 divide-y dark:divide-neutral-800">
318
318
  <button :for="server in filteredServers()" type="button" class="flex gap-4 items-center px-5 py-4 w-full text-left hover:bg-neutral-50 dark:hover:bg-neutral-800/70" @click="inspectServer(server)">
@@ -327,7 +327,7 @@ onMount(() => {
327
327
  <div class="overflow-hidden bg-white dark:bg-neutral-900 border border-neutral-200 rounded-xl dark:border-neutral-700">
328
328
  <div class="flex items-center justify-between px-5 py-4 border-b border-neutral-200 dark:border-neutral-700">
329
329
  <div><h3 class="font-semibold text-neutral-900 text-sm dark:text-white">Deployments</h3><p class="mt-0.5 text-neutral-500 text-xs dark:text-neutral-400">Persisted provider state</p></div>
330
- <span class="font-mono text-neutral-400 text-xs">{{ filteredDeployments().length }}</span>
330
+ <span class="font-mono text-neutral-500 text-xs dark:text-neutral-400">{{ filteredDeployments().length }}</span>
331
331
  </div>
332
332
  <div class="divide-neutral-100 divide-y dark:divide-neutral-800">
333
333
  <button :for="deployment in filteredDeployments()" type="button" class="flex gap-4 items-center px-5 py-4 w-full text-left hover:bg-neutral-50 dark:hover:bg-neutral-800/70" @click="inspectDeployment(deployment)">
@@ -352,7 +352,7 @@ onMount(() => {
352
352
  <article :for="event in events()" class="flex gap-3 px-5 py-4">
353
353
  <span :class="'mt-0.5 h-5 w-5 shrink-0 ' + eventIcon(event)"></span>
354
354
  <div class="flex-1 min-w-0"><p class="font-medium text-neutral-900 text-sm dark:text-white">{{ event.service }}</p><p class="mt-1 text-neutral-500 text-sm dark:text-neutral-400">{{ event.message }}</p></div>
355
- <time class="shrink-0 text-neutral-400 text-xs dark:text-neutral-500">{{ formatDate(event.timestamp) }}</time>
355
+ <time class="shrink-0 text-neutral-500 text-xs dark:text-neutral-500">{{ formatDate(event.timestamp) }}</time>
356
356
  </article>
357
357
  </div>
358
358
  </div>
@@ -366,7 +366,7 @@ onMount(() => {
366
366
  </div>
367
367
  <p :if="environments().length === 0" class="text-neutral-500 text-sm dark:text-neutral-400">No environments configured.</p>
368
368
  </div>
369
- <p :if="generatedAt()" class="mt-5 pt-4 text-neutral-400 text-xs dark:text-neutral-500 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
369
+ <p :if="generatedAt()" class="mt-5 pt-4 text-neutral-500 text-xs dark:text-neutral-500 border-neutral-100 border-t dark:border-neutral-800">Snapshot {{ formatDate(generatedAt()) }}</p>
370
370
  </div>
371
371
  </section>
372
372
 
@@ -376,7 +376,7 @@
376
376
  <div>
377
377
  <h1 class="font-semibold text-2xl text-zinc-900 dark:text-zinc-100" :text="board.name"></h1>
378
378
  <p :if="board.description" class="mt-1 text-sm text-zinc-500" :text="board.description"></p>
379
- <p class="mt-2 text-xs text-zinc-400">
379
+ <p class="mt-2 text-xs text-zinc-500 dark:text-zinc-400">
380
380
  <span :text="columns.length + ' columns'"></span>
381
381
  <span> · </span>
382
382
  <span :text="totalCards() + ' cards'"></span>
@@ -200,7 +200,7 @@
200
200
  class="mt-1 line-clamp-2 text-xs text-zinc-500"
201
201
  :text="board.description"
202
202
  ></p>
203
- <p class="mt-3 text-xs text-zinc-400" :text="cardCountText(board.cardCount)"></p>
203
+ <p class="mt-3 text-xs text-zinc-500 dark:text-zinc-400" :text="cardCountText(board.cardCount)"></p>
204
204
  </div>
205
205
  </StxLink>
206
206
  <span class="absolute right-3 top-4 opacity-0 focus-within:opacity-100 group-hover:opacity-100 transition-opacity">
@@ -232,7 +232,7 @@
232
232
  </div>
233
233
 
234
234
  <div>
235
- <label for="kanban-board-description" class="block mb-1 font-medium text-xs text-zinc-700 dark:text-zinc-300">Description <span class="text-zinc-400">(optional)</span></label>
235
+ <label for="kanban-board-description" class="block mb-1 font-medium text-xs text-zinc-700 dark:text-zinc-300">Description <span class="text-zinc-500 dark:text-zinc-400">(optional)</span></label>
236
236
  <textarea
237
237
  id="kanban-board-description"
238
238
  x-model="draftDescription"
@@ -294,7 +294,7 @@ function formatTimestamp(iso: string | null | undefined): string {
294
294
  </div>
295
295
  <div class="px-2 py-1.5">
296
296
  <p class="text-[10px] text-zinc-500 dark:text-zinc-400" :text="attachment.mimeType"></p>
297
- <p class="text-[10px] text-zinc-400" :text="formatBytes(attachment.sizeBytes)"></p>
297
+ <p class="text-[10px] text-zinc-500 dark:text-zinc-400" :text="formatBytes(attachment.sizeBytes)"></p>
298
298
  </div>
299
299
  </li>
300
300
  </ul>
@@ -317,14 +317,14 @@ function formatTimestamp(iso: string | null | undefined): string {
317
317
  </div>
318
318
  </div>
319
319
 
320
- <div :if="cardComments().length === 0 && !loadingCard()" class="italic text-xs text-zinc-400">No comments yet.</div>
320
+ <div :if="cardComments().length === 0 && !loadingCard()" class="italic text-xs text-zinc-500 dark:text-zinc-400">No comments yet.</div>
321
321
  <ul class="space-y-3">
322
322
  <li :for="comment in cardComments" class="flex gap-3">
323
323
  <span class="inline-flex flex-shrink-0 items-center justify-center h-7 w-7 font-medium text-[10px] text-white bg-violet-500 rounded-full" :text="initialsOf(comment.authorName, comment.authorEmail)"></span>
324
324
  <div class="flex-1 min-w-0">
325
325
  <div class="flex flex-wrap gap-2 items-baseline">
326
326
  <span class="font-medium text-xs text-zinc-700 dark:text-zinc-200" :text="comment.authorName || comment.authorEmail || 'Unknown'"></span>
327
- <span class="text-[10px] text-zinc-400" :text="formatTimestamp(comment.createdAt)"></span>
327
+ <span class="text-[10px] text-zinc-500 dark:text-zinc-400" :text="formatTimestamp(comment.createdAt)"></span>
328
328
  <template :if="editingCommentId() !== comment.id">
329
329
  <Button variant="ghost" size="xs" @click="startCommentEdit(comment.id, comment.body)">Edit</Button>
330
330
  <Button variant="ghost" size="xs" @click="requestDelete('comment', comment.id)"><span class="text-red-500">Delete</span></Button>
@@ -365,7 +365,7 @@ function formatTimestamp(iso: string | null | undefined): string {
365
365
 
366
366
  <div :if="!showLabelPicker()" class="flex flex-wrap gap-1">
367
367
  <span :for="label in cardLabels" :class="`px-2 py-0.5 text-xs font-medium rounded ${labelChipClass(label.color)}`" :text="label.name"></span>
368
- <span :if="cardLabels().length === 0" class="italic text-xs text-zinc-400">None</span>
368
+ <span :if="cardLabels().length === 0" class="italic text-xs text-zinc-500 dark:text-zinc-400">None</span>
369
369
  </div>
370
370
 
371
371
  <div :if="showLabelPicker()" class="space-y-2">
@@ -407,11 +407,11 @@ function formatTimestamp(iso: string | null | undefined): string {
407
407
  <span class="inline-flex items-center justify-center h-6 w-6 font-medium text-[10px] text-white bg-violet-500 rounded-full" :text="initialsOf(assignee.name, assignee.email)"></span>
408
408
  <span :text="assignee.name || assignee.email || 'Unknown'"></span>
409
409
  </div>
410
- <span :if="cardAssignees().length === 0" class="italic text-xs text-zinc-400">Unassigned</span>
410
+ <span :if="cardAssignees().length === 0" class="italic text-xs text-zinc-500 dark:text-zinc-400">Unassigned</span>
411
411
  </div>
412
412
 
413
413
  <div :if="showAssigneePicker()" class="overflow-y-auto space-y-1 max-h-48">
414
- <p :if="kanban.users().length === 0" class="italic text-xs text-zinc-400">No users to assign.</p>
414
+ <p :if="kanban.users().length === 0" class="italic text-xs text-zinc-500 dark:text-zinc-400">No users to assign.</p>
415
415
  <button :for="user in kanban.users" type="button" :aria-pressed="String(cardAssignees().some(assignee => assignee.userId === user.id))" @click="toggleAssignee(user.id)" class="flex gap-2 items-center px-2 py-1.5 w-full text-left text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded">
416
416
  <span class="inline-flex flex-shrink-0 items-center justify-center h-6 w-6 font-medium text-[10px] text-white bg-violet-500 rounded-full" :text="initialsOf(user.name, user.email)"></span>
417
417
  <span class="flex-1 text-zinc-700 truncate dark:text-zinc-200" :text="user.name || user.email"></span>
@@ -426,8 +426,8 @@ function formatTimestamp(iso: string | null | undefined): string {
426
426
  </section>
427
427
 
428
428
  <section class="pt-3 border-t border-zinc-200 dark:border-zinc-800">
429
- <p class="text-[10px] text-zinc-400">Created <span :text="formatTimestamp(openCard()?.createdAt)"></span></p>
430
- <p :if="openCard()?.updatedAt && openCard()?.updatedAt !== openCard()?.createdAt" class="text-[10px] text-zinc-400">Updated <span :text="formatTimestamp(openCard()?.updatedAt)"></span></p>
429
+ <p class="text-[10px] text-zinc-500 dark:text-zinc-400">Created <span :text="formatTimestamp(openCard()?.createdAt)"></span></p>
430
+ <p :if="openCard()?.updatedAt && openCard()?.updatedAt !== openCard()?.createdAt" class="text-[10px] text-zinc-500 dark:text-zinc-400">Updated <span :text="formatTimestamp(openCard()?.updatedAt)"></span></p>
431
431
  </section>
432
432
  </aside>
433
433
  </div>
@@ -165,11 +165,11 @@ onMount(() => {
165
165
  <div :if="loadError()" role="alert" class="flex gap-4 items-center justify-between p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900"><span>{{ loadError() }}</span><Button variant="secondary" size="sm" @click="loadCarts()">Retry</Button></div>
166
166
 
167
167
  <section aria-label="Cart recovery metrics" class="grid gap-3 grid-cols-2 lg:grid-cols-5">
168
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Sitting there</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().open.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Carts abandoned or expired</p></article>
169
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Worth</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ money(summary().openValue) }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ money(summary().averageValue) }} average</p></article>
170
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Chased</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().contacted.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Written to and still cold</p></article>
171
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recovered</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ money(summary().recoveredValue) }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ summary().recovered.toLocaleString() }} carts checked out after an email</p></article>
172
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recovery rate</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ percentage(summary().recoveryRate) }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Of the carts actually chased</p></article>
168
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Sitting there</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().open.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Carts abandoned or expired</p></article>
169
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Worth</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ money(summary().openValue) }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">{{ money(summary().averageValue) }} average</p></article>
170
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Chased</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().contacted.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Written to and still cold</p></article>
171
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recovered</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ money(summary().recoveredValue) }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">{{ summary().recovered.toLocaleString() }} carts checked out after an email</p></article>
172
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recovery rate</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ percentage(summary().recoveryRate) }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Of the carts actually chased</p></article>
173
173
  </section>
174
174
 
175
175
  <section :if="campaigns().length > 0" aria-label="Recovery campaigns" class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
@@ -273,11 +273,11 @@ onMount(() => {
273
273
  <div :if="loadError()" role="alert" class="flex gap-4 items-center justify-between p-4 text-red-700 text-sm dark:text-red-300 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900"><span>{{ loadError() }}</span><Button variant="secondary" size="sm" @click="loadCampaigns()">Retry</Button></div>
274
274
 
275
275
  <section aria-label="Campaign metrics" class="grid gap-3 grid-cols-2 lg:grid-cols-5">
276
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Campaigns</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Persisted records</p></article>
277
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Live</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().live }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Scheduled, sending, or active</p></article>
278
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Completed</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().sent }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Sent or completed</p></article>
279
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recipients</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().recipients.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Persisted successful sends</p></article>
280
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Delivery issues</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().failedDeliveries.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Failed, bounced, or complained</p></article>
276
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Campaigns</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Persisted records</p></article>
277
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Live</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().live }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Scheduled, sending, or active</p></article>
278
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Completed</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().sent }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Sent or completed</p></article>
279
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Recipients</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().recipients.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Persisted successful sends</p></article>
280
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Delivery issues</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().failedDeliveries.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Failed, bounced, or complained</p></article>
281
281
  </section>
282
282
 
283
283
  <section class="space-y-3">
@@ -206,10 +206,10 @@ onMount(() => {
206
206
  </div>
207
207
 
208
208
  <section aria-label="Email list metrics" class="grid gap-3 grid-cols-2 lg:grid-cols-4">
209
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Email lists</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ summary().active }} active</p></article>
210
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Memberships</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().subscribers.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Subscribed and pending</p></article>
211
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">New this week</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().newThisWeek.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Subscribed membership rows</p></article>
212
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Campaigns</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().campaigns.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Linked campaign records</p></article>
209
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Email lists</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">{{ summary().active }} active</p></article>
210
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Memberships</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().subscribers.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Subscribed and pending</p></article>
211
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">New this week</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().newThisWeek.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Subscribed membership rows</p></article>
212
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Campaigns</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().campaigns.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Linked campaign records</p></article>
213
213
  </section>
214
214
 
215
215
  <section class="space-y-3">
@@ -189,10 +189,10 @@ onMount(() => {
189
189
  <div :if="summary().failed > 0" class="flex gap-3 items-start p-4 text-red-800 text-sm dark:text-red-200 bg-red-50 dark:bg-red-950/30 border border-red-200 rounded-lg dark:border-red-900"><span class="shrink-0 mt-0.5 h-5 w-5 i-hugeicons-alert-02"></span><p>{{ summary().failed }} {{ summary().failed === 1 ? 'post has' : 'posts have' }} a failed status and may need review.</p></div>
190
190
 
191
191
  <section aria-label="Social post metrics" class="grid gap-3 grid-cols-2 lg:grid-cols-4">
192
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Social posts</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ summary().published }} published, {{ summary().drafts }} drafts</p></article>
193
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Scheduled</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().scheduled }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Persisted schedule times</p></article>
194
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Reach</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().reach.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Recorded platform reach</p></article>
195
- <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Engagement</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().engagement.toLocaleString() }}</p><p class="mt-1 text-gray-400 text-xs dark:text-neutral-500">Likes, shares, and comments</p></article>
192
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Social posts</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().total }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">{{ summary().published }} published, {{ summary().drafts }} drafts</p></article>
193
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Scheduled</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().scheduled }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Persisted schedule times</p></article>
194
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Reach</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().reach.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Recorded platform reach</p></article>
195
+ <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700"><p class="text-gray-500 text-xs dark:text-neutral-400">Engagement</p><p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ summary().engagement.toLocaleString() }}</p><p class="mt-1 text-gray-500 text-xs dark:text-neutral-500">Likes, shares, and comments</p></article>
196
196
  </section>
197
197
 
198
198
  <section class="space-y-3">
@@ -204,7 +204,7 @@ onMount(() => {
204
204
  <div :if="model.apiUri" class="flex gap-2 items-center mt-4">
205
205
  <span class="inline-flex px-2 py-0.5 font-medium text-green-700 text-xs dark:text-green-300 bg-green-50 dark:bg-green-950/40 rounded">API</span>
206
206
  <code class="text-neutral-500 text-xs truncate dark:text-neutral-400">{{ model.apiUri }}</code>
207
- <span class="ml-auto text-neutral-400 text-xs">{{ model.apiRoutes.length }} routes</span>
207
+ <span class="ml-auto text-neutral-500 text-xs dark:text-neutral-400">{{ model.apiRoutes.length }} routes</span>
208
208
  </div>
209
209
  </StxLink>
210
210
  </div>
@@ -58,12 +58,12 @@ useClickOutside(userMenu, () => setDropdownOpen(false))
58
58
  <div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
59
59
  <StxLink
60
60
  to="/models"
61
- class="flex relative flex-1 items-center pl-8 text-gray-400 sm:text-sm hover:text-gray-600 dark:hover:text-neutral-200 transition-colors"
61
+ class="flex relative flex-1 items-center pl-8 text-gray-500 sm:text-sm dark:text-gray-400 hover:text-gray-600 dark:hover:text-neutral-200 transition-colors"
62
62
  aria-label="Search model records"
63
63
  >
64
64
  <span aria-hidden="true" class="absolute left-0 h-5 w-5 i-hugeicons-search-01"></span>
65
65
  <span>Search model records</span>
66
- <kbd class="ml-auto px-1.5 py-0.5 font-medium text-[10px] text-neutral-400 bg-neutral-100 dark:bg-neutral-700 border border-neutral-200 rounded dark:border-neutral-600">
66
+ <kbd class="ml-auto px-1.5 py-0.5 font-medium text-[10px] text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-neutral-700 border border-neutral-200 rounded dark:border-neutral-600">
67
67
  ⌘K
68
68
  </kbd>
69
69
  </StxLink>
@@ -12,5 +12,5 @@ onMount(() => void load())
12
12
  <PageHeader title="Operations" description="Inspect the append-only operator event stream with actor and operation correlation."><template #actions><Button variant="secondary" :loading="loading()" @click="load"><span :if="!loading()" class="h-4 w-4 i-hugeicons-refresh"></span>Refresh</Button></template></PageHeader>
13
13
  <OperationsNavigation active="audit" />
14
14
  <section class="grid gap-3 sm:grid-cols-2 lg:grid-cols-4"><StatsCard title="Events" :value="String(data()?.summary.events || 0)" icon="i-hugeicons-activity-03" iconBg="primary" :loading="loading()" /><StatsCard title="Operators" :value="String(data()?.summary.operators || 0)" icon="i-hugeicons-user-group" iconBg="neutral" :loading="loading()" /><StatsCard title="Failures" :value="String(data()?.summary.failures || 0)" icon="i-hugeicons-alert-02" iconBg="warning" :loading="loading()" /><StatsCard title="Latest sequence" :value="String(data()?.summary.latestSequence || 0)" icon="i-hugeicons-list-view" iconBg="success" :loading="loading()" /></section>
15
- <section class="overflow-hidden bg-white dark:bg-neutral-900 border border-gray-200 rounded-lg dark:border-neutral-800"><div class="px-4 py-4 border-b border-gray-200 dark:border-neutral-800"><h2 class="font-semibold text-gray-900 dark:text-white">Operator events</h2><p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Events are sequence ordered and retained in the ts-cloud control plane.</p></div><div :if="!loading() && !data()?.events.length" class="px-6 py-12 text-center text-gray-500 text-sm dark:text-neutral-400">No operator events have been recorded.</div><div class="overflow-auto max-h-[42rem] divide-gray-100 divide-y dark:divide-neutral-800"><div :for="event in data()?.events || []" class="grid gap-2 md:grid-cols-[5rem_1fr_10rem_12rem] px-4 py-3"><span class="font-mono text-gray-400 text-xs">#{{ event.sequence }}</span><div><p class="font-medium text-gray-900 text-sm dark:text-white">{{ event.type }}</p><p class="mt-1 font-mono text-gray-500 text-xs dark:text-neutral-400">{{ event.operationId || event.id }}</p></div><span class="text-gray-600 text-sm dark:text-neutral-300">{{ event.actorName }}</span><time class="text-gray-500 text-xs dark:text-neutral-400">{{ date(event.createdAt) }}</time></div></div></section>
15
+ <section class="overflow-hidden bg-white dark:bg-neutral-900 border border-gray-200 rounded-lg dark:border-neutral-800"><div class="px-4 py-4 border-b border-gray-200 dark:border-neutral-800"><h2 class="font-semibold text-gray-900 dark:text-white">Operator events</h2><p class="mt-1 text-gray-500 text-sm dark:text-neutral-400">Events are sequence ordered and retained in the ts-cloud control plane.</p></div><div :if="!loading() && !data()?.events.length" class="px-6 py-12 text-center text-gray-500 text-sm dark:text-neutral-400">No operator events have been recorded.</div><div class="overflow-auto max-h-[42rem] divide-gray-100 divide-y dark:divide-neutral-800"><div :for="event in data()?.events || []" class="grid gap-2 md:grid-cols-[5rem_1fr_10rem_12rem] px-4 py-3"><span class="font-mono text-gray-500 text-xs dark:text-gray-400">#{{ event.sequence }}</span><div><p class="font-medium text-gray-900 text-sm dark:text-white">{{ event.type }}</p><p class="mt-1 font-mono text-gray-500 text-xs dark:text-neutral-400">{{ event.operationId || event.id }}</p></div><span class="text-gray-600 text-sm dark:text-neutral-300">{{ event.actorName }}</span><time class="text-gray-500 text-xs dark:text-neutral-400">{{ date(event.createdAt) }}</time></div></div></section>
16
16
  </div>
@@ -55,7 +55,7 @@ function statusVariant(state: OperatorOperation['state']): 'default' | 'primary'
55
55
  <p class="mt-1 text-gray-500 text-xs dark:text-neutral-400">{{ operation.actorName }} at {{ formatDate(operation.finishedAt || operation.createdAt) }}</p>
56
56
  <p :if="operation.error" class="mt-1 text-red-600 text-xs dark:text-red-400">{{ operation.error }}</p>
57
57
  </div>
58
- <code class="font-mono text-[11px] text-gray-400 dark:text-neutral-500">{{ operation.id }}</code>
58
+ <code class="font-mono text-[11px] text-gray-500 dark:text-neutral-500">{{ operation.id }}</code>
59
59
  </li>
60
60
  </ul>
61
61
  </Card>
@@ -26,7 +26,7 @@ function variant(status: RecoveryJob['status']): 'default' | 'primary' | 'succes
26
26
  <p class="mt-1 text-gray-500 text-xs dark:text-neutral-400">{{ job.progress.phase || 'queued' }} · {{ formatDate(job.finishedAt || job.startedAt || job.createdAt) }}</p>
27
27
  <p :if="job.error" class="mt-1 text-red-600 text-xs dark:text-red-400">{{ job.error }}</p>
28
28
  </div>
29
- <code class="font-mono text-[11px] text-gray-400 dark:text-neutral-500">{{ job.id }}</code>
29
+ <code class="font-mono text-[11px] text-gray-500 dark:text-neutral-500">{{ job.id }}</code>
30
30
  </li>
31
31
  </ul>
32
32
  </Card>
@@ -109,7 +109,7 @@ onMount(() => {
109
109
  <span class="h-4 w-4 i-hugeicons-command-line"></span>
110
110
  Open terminal
111
111
  </Button>
112
- <span :if="!entry.terminal" class="text-gray-400 text-xs dark:text-neutral-500">Named commands only</span>
112
+ <span :if="!entry.terminal" class="text-gray-500 text-xs dark:text-neutral-500">Named commands only</span>
113
113
  </li>
114
114
  </template>
115
115
  </ul>
@@ -220,7 +220,7 @@ void loadFiles()
220
220
  >
221
221
  <span class="flex-none h-4 w-4 i-hugeicons-settings-02"></span>
222
222
  <span class="flex-1 font-medium text-sm truncate">{{ file.title }}</span>
223
- <span class="text-[10px] text-neutral-400">{{ file.size < 1024 ? file.size + ' B' : Math.round(file.size / 1024) + ' KB' }}</span>
223
+ <span class="text-[10px] text-neutral-500 dark:text-neutral-400">{{ file.size < 1024 ? file.size + ' B' : Math.round(file.size / 1024) + ' KB' }}</span>
224
224
  </button>
225
225
  </template>
226
226
  </nav>
@@ -44,7 +44,7 @@ const iconClass = derived(() => [
44
44
  const labelClass = derived(() => [
45
45
  sizeClasses[liveSize()]?.label || sizeClasses.md.label,
46
46
  'font-medium',
47
- liveDisabled() ? 'text-neutral-400 dark:text-neutral-500' : 'text-neutral-700 dark:text-neutral-200',
47
+ liveDisabled() ? 'text-neutral-500 dark:text-neutral-500' : 'text-neutral-700 dark:text-neutral-200',
48
48
  ].join(' '))
49
49
  const descriptionClass = derived(() => [
50
50
  sizeClasses[liveSize()]?.description || sizeClasses.md.description,
@@ -26,7 +26,7 @@ const columns = useReactiveProp('columns', 'grid-cols-2 lg:grid-cols-4')
26
26
  <article class="p-4 bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
27
27
  <p class="text-gray-500 text-xs dark:text-neutral-400">{{ item.label }}</p>
28
28
  <p class="mt-2 font-semibold text-2xl text-gray-900 dark:text-white">{{ item.value }}</p>
29
- <p :if="item.detail" class="mt-1 text-gray-400 text-xs dark:text-neutral-500">{{ item.detail }}</p>
29
+ <p :if="item.detail" class="mt-1 text-gray-500 text-xs dark:text-neutral-500">{{ item.detail }}</p>
30
30
  </article>
31
31
  </template>
32
32
  </section>
@@ -100,7 +100,7 @@ function emitItemsPerPage(event: Event): void {
100
100
  </Button>
101
101
 
102
102
  <template :for="page in visiblePages()">
103
- <span :if="typeof page !== 'number'" class="px-2 text-neutral-400 dark:text-neutral-500">...</span>
103
+ <span :if="typeof page !== 'number'" class="px-2 text-neutral-500 dark:text-neutral-500">...</span>
104
104
  <Button :if="typeof page === 'number' && page === currentPage()" @click="goToPage(page)">
105
105
  <span aria-current="page">{{ page }}</span>
106
106
  </Button>