vestrycms 0.0.0-stage → 0.1.0

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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +348 -2
  3. package/dist/config/fields.d.ts +30 -0
  4. package/dist/config/fields.js +75 -0
  5. package/dist/config/index.d.ts +9 -0
  6. package/dist/config/index.js +18 -0
  7. package/dist/config/types.d.ts +403 -0
  8. package/dist/config/types.js +10 -0
  9. package/dist/core/api.d.ts +67 -0
  10. package/dist/core/api.js +538 -0
  11. package/dist/core/auth.d.ts +1561 -0
  12. package/dist/core/auth.js +234 -0
  13. package/dist/core/cache.d.ts +27 -0
  14. package/dist/core/cache.js +52 -0
  15. package/dist/core/entries.d.ts +19 -0
  16. package/dist/core/entries.js +127 -0
  17. package/dist/core/index-types.d.ts +20 -0
  18. package/dist/core/index-types.js +1 -0
  19. package/dist/core/index.d.ts +13 -0
  20. package/dist/core/index.js +13 -0
  21. package/dist/core/mail.d.ts +28 -0
  22. package/dist/core/mail.js +62 -0
  23. package/dist/core/paths.d.ts +35 -0
  24. package/dist/core/paths.js +148 -0
  25. package/dist/core/roles.d.ts +24 -0
  26. package/dist/core/roles.js +15 -0
  27. package/dist/core/service.d.ts +102 -0
  28. package/dist/core/service.js +478 -0
  29. package/dist/core/source.d.ts +82 -0
  30. package/dist/core/source.js +75 -0
  31. package/dist/core/sources/github-app.d.ts +26 -0
  32. package/dist/core/sources/github-app.js +161 -0
  33. package/dist/core/sources/github.d.ts +40 -0
  34. package/dist/core/sources/github.js +278 -0
  35. package/dist/core/sources/local.d.ts +16 -0
  36. package/dist/core/sources/local.js +98 -0
  37. package/dist/core/values.d.ts +22 -0
  38. package/dist/core/values.js +123 -0
  39. package/dist/env.d.ts +19 -0
  40. package/dist/index.d.ts +18 -0
  41. package/dist/index.js +167 -0
  42. package/dist/integration/build-index.d.ts +8 -0
  43. package/dist/integration/build-index.js +67 -0
  44. package/dist/integration/client-deps.d.ts +10 -0
  45. package/dist/integration/client-deps.js +43 -0
  46. package/dist/integration/dev-middleware.d.ts +6 -0
  47. package/dist/integration/dev-middleware.js +48 -0
  48. package/dist/integration/formatter.d.ts +7 -0
  49. package/dist/integration/formatter.js +56 -0
  50. package/dist/mdx/expressions.d.ts +14 -0
  51. package/dist/mdx/expressions.js +141 -0
  52. package/dist/mdx/index.d.ts +4 -0
  53. package/dist/mdx/index.js +4 -0
  54. package/dist/mdx/parse.d.ts +6 -0
  55. package/dist/mdx/parse.js +378 -0
  56. package/dist/mdx/serialize.d.ts +9 -0
  57. package/dist/mdx/serialize.js +396 -0
  58. package/dist/mdx/types.d.ts +40 -0
  59. package/dist/mdx/types.js +6 -0
  60. package/dist/pages/admin.astro +27 -0
  61. package/dist/pages/api.d.ts +3 -0
  62. package/dist/pages/api.js +31 -0
  63. package/dist/ui/App.d.ts +4 -0
  64. package/dist/ui/App.js +70 -0
  65. package/dist/ui/api.d.ts +83 -0
  66. package/dist/ui/api.js +97 -0
  67. package/dist/ui/auth-client.d.ts +27 -0
  68. package/dist/ui/auth-client.js +29 -0
  69. package/dist/ui/components/AssetPicker.d.ts +27 -0
  70. package/dist/ui/components/AssetPicker.js +97 -0
  71. package/dist/ui/components/Icon.d.ts +11 -0
  72. package/dist/ui/components/Icon.js +46 -0
  73. package/dist/ui/components/Layout.d.ts +15 -0
  74. package/dist/ui/components/Layout.js +87 -0
  75. package/dist/ui/components/editor/EditorContext.d.ts +15 -0
  76. package/dist/ui/components/editor/EditorContext.js +9 -0
  77. package/dist/ui/components/editor/NodeViews.d.ts +4 -0
  78. package/dist/ui/components/editor/NodeViews.js +127 -0
  79. package/dist/ui/components/editor/RichTextEditor.d.ts +24 -0
  80. package/dist/ui/components/editor/RichTextEditor.js +253 -0
  81. package/dist/ui/components/editor/extensions.d.ts +20 -0
  82. package/dist/ui/components/editor/extensions.js +156 -0
  83. package/dist/ui/components/fields/CodeInput.d.ts +10 -0
  84. package/dist/ui/components/fields/CodeInput.js +8 -0
  85. package/dist/ui/components/fields/CodeMirrorInput.d.ts +2 -0
  86. package/dist/ui/components/fields/CodeMirrorInput.js +44 -0
  87. package/dist/ui/components/fields/FieldRenderer.d.ts +41 -0
  88. package/dist/ui/components/fields/FieldRenderer.js +299 -0
  89. package/dist/ui/components/fields/ImageTools.d.ts +31 -0
  90. package/dist/ui/components/fields/ImageTools.js +296 -0
  91. package/dist/ui/components/fields/MarkdownField.d.ts +7 -0
  92. package/dist/ui/components/fields/MarkdownField.js +16 -0
  93. package/dist/ui/components/fields/TreeInput.d.ts +13 -0
  94. package/dist/ui/components/fields/TreeInput.js +221 -0
  95. package/dist/ui/components/ui.d.ts +63 -0
  96. package/dist/ui/components/ui.js +113 -0
  97. package/dist/ui/context.d.ts +17 -0
  98. package/dist/ui/context.js +13 -0
  99. package/dist/ui/lib/format.d.ts +5 -0
  100. package/dist/ui/lib/format.js +11 -0
  101. package/dist/ui/lib/utils.d.ts +24 -0
  102. package/dist/ui/lib/utils.js +255 -0
  103. package/dist/ui/main.d.ts +1 -0
  104. package/dist/ui/main.js +11 -0
  105. package/dist/ui/pages/Assets.d.ts +1 -0
  106. package/dist/ui/pages/Assets.js +69 -0
  107. package/dist/ui/pages/CollectionList.d.ts +6 -0
  108. package/dist/ui/pages/CollectionList.js +115 -0
  109. package/dist/ui/pages/Dashboard.d.ts +1 -0
  110. package/dist/ui/pages/Dashboard.js +57 -0
  111. package/dist/ui/pages/EntryEditor.d.ts +3 -0
  112. package/dist/ui/pages/EntryEditor.js +313 -0
  113. package/dist/ui/pages/Login.d.ts +6 -0
  114. package/dist/ui/pages/Login.js +88 -0
  115. package/dist/ui/pages/Users.d.ts +1 -0
  116. package/dist/ui/pages/Users.js +102 -0
  117. package/dist/ui/styles.css +2536 -0
  118. package/package.json +121 -3
@@ -0,0 +1,2536 @@
1
+ /* Vestry control panel design system. Scoped under #vestry-root. */
2
+
3
+ :root {
4
+ --v-font:
5
+ Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
6
+ sans-serif;
7
+ --v-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
8
+ --v-serif: Georgia, "Times New Roman", serif;
9
+
10
+ /* Outer body (behind the sidebar) and the inner panel the pages sit on. */
11
+ --v-bg: #e9ecf1;
12
+ --v-panel: #f8f9fb;
13
+ --v-surface: #ffffff;
14
+ --v-surface-muted: #f9fafb;
15
+ --v-border: #e5e7eb;
16
+ --v-border-strong: #d1d5db;
17
+ --v-text: #111827;
18
+ --v-text-muted: #6b7280;
19
+ --v-text-faint: #9ca3af;
20
+ --v-primary: #2563eb;
21
+ --v-primary-hover: #1d4ed8;
22
+ --v-primary-soft: #eff6ff;
23
+ --v-primary-ring: rgba(37, 99, 235, 0.25);
24
+ --v-success: #16a34a;
25
+ --v-success-soft: #ecfdf5;
26
+ --v-warning: #d97706;
27
+ --v-warning-soft: #fffbeb;
28
+ --v-danger: #dc2626;
29
+ --v-danger-soft: #fef2f2;
30
+ --v-radius: 8px;
31
+ --v-radius-sm: 6px;
32
+ --v-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
33
+ --v-shadow:
34
+ 0 1px 3px rgba(16, 24, 40, 0.08), 0 1px 2px rgba(16, 24, 40, 0.04);
35
+ --v-shadow-lg: 0 20px 40px -12px rgba(16, 24, 40, 0.25);
36
+ --v-sidebar-width: 248px;
37
+ --v-topbar-height: 56px;
38
+ --v-chrome: #0f172a;
39
+ --v-chrome-text: #e2e8f0;
40
+ --v-chrome-muted: #94a3b8;
41
+ --v-chrome-height: 52px;
42
+ --v-frame-inset: 8px;
43
+ }
44
+
45
+ html,
46
+ body {
47
+ margin: 0;
48
+ padding: 0;
49
+ height: 100%;
50
+ background: var(--v-chrome);
51
+ }
52
+
53
+ #vestry-root {
54
+ font-family: var(--v-font);
55
+ font-size: 14px;
56
+ line-height: 1.5;
57
+ color: var(--v-text);
58
+ min-height: 100vh;
59
+ -webkit-font-smoothing: antialiased;
60
+ }
61
+
62
+ #vestry-root *,
63
+ #vestry-root *::before,
64
+ #vestry-root *::after {
65
+ box-sizing: border-box;
66
+ }
67
+
68
+ #vestry-root a {
69
+ color: var(--v-primary);
70
+ text-decoration: none;
71
+ }
72
+ #vestry-root a:hover {
73
+ text-decoration: underline;
74
+ }
75
+ /* Links styled as other components keep their own colours. */
76
+ #vestry-root
77
+ a:where(
78
+ .v-btn,
79
+ .v-nav__link,
80
+ .v-dash-card,
81
+ .v-table__title,
82
+ .v-brand,
83
+ .v-folder
84
+ ) {
85
+ color: inherit;
86
+ }
87
+ #vestry-root a:where(.v-btn--primary) {
88
+ color: #fff;
89
+ }
90
+ #vestry-root a:where(.v-btn--danger) {
91
+ color: var(--v-danger);
92
+ }
93
+ #vestry-root a:where(.v-nav__link.is-active) {
94
+ color: var(--v-primary);
95
+ }
96
+
97
+ /* Zero-specificity reset so component classes (e.g. .v-btn--primary) win. */
98
+ :where(
99
+ #vestry-root button,
100
+ #vestry-root input,
101
+ #vestry-root select,
102
+ #vestry-root textarea
103
+ ) {
104
+ font: inherit;
105
+ color: inherit;
106
+ }
107
+
108
+ /* ------------------------------------------------------------- layout */
109
+ /* Dark chrome bar across the top; the app sits below it in a light panel
110
+ with rounded top corners, Statamic-style. */
111
+ /* App-like layout: the bar and the outer body fill the viewport and only
112
+ the inner panel scrolls, so both sets of rounded corners stay in place. */
113
+ .v-app {
114
+ height: 100vh;
115
+ display: flex;
116
+ flex-direction: column;
117
+ background: var(--v-chrome);
118
+ overflow: hidden;
119
+ }
120
+
121
+ .v-chrome {
122
+ position: relative;
123
+ z-index: 30;
124
+ background: var(--v-chrome);
125
+ height: var(--v-chrome-height);
126
+ display: flex;
127
+ align-items: center;
128
+ gap: 8px;
129
+ padding: 0 14px 0 16px;
130
+ color: var(--v-chrome-text);
131
+ flex: none;
132
+ }
133
+ .v-chrome__brand {
134
+ display: flex;
135
+ align-items: center;
136
+ gap: 10px;
137
+ color: #fff;
138
+ font-weight: 600;
139
+ font-size: 14px;
140
+ text-decoration: none !important;
141
+ min-width: 0;
142
+ }
143
+ .v-chrome__mark {
144
+ width: 28px;
145
+ height: 28px;
146
+ border-radius: 7px;
147
+ background: var(--v-primary);
148
+ color: #fff;
149
+ display: grid;
150
+ place-items: center;
151
+ font-weight: 700;
152
+ font-size: 14px;
153
+ flex: none;
154
+ }
155
+ .v-chrome__logo {
156
+ display: block;
157
+ height: 34px;
158
+ width: auto;
159
+ max-width: 320px;
160
+ }
161
+ .v-chrome__name {
162
+ overflow: hidden;
163
+ text-overflow: ellipsis;
164
+ white-space: nowrap;
165
+ }
166
+ .v-chrome__spacer {
167
+ flex: 1;
168
+ }
169
+ .v-chrome__crumb {
170
+ display: inline-flex;
171
+ align-items: center;
172
+ gap: 8px;
173
+ font-size: 13.5px;
174
+ font-weight: 500;
175
+ color: var(--v-chrome-muted);
176
+ white-space: nowrap;
177
+ }
178
+ .v-chrome__sep {
179
+ color: #475569;
180
+ }
181
+ #vestry-root a:where(.v-chrome__crumb a) {
182
+ color: var(--v-chrome-text);
183
+ }
184
+ #vestry-root a:where(.v-chrome__crumb a):hover {
185
+ color: #fff;
186
+ text-decoration: none;
187
+ }
188
+ .v-status-dot {
189
+ width: 10px;
190
+ height: 10px;
191
+ border-radius: 50%;
192
+ flex: none;
193
+ background: var(--v-text-faint);
194
+ }
195
+ .v-status-dot--published {
196
+ background: var(--v-success);
197
+ }
198
+ .v-status-dot--draft {
199
+ background: var(--v-warning);
200
+ }
201
+ .v-chrome__chip {
202
+ display: inline-flex;
203
+ align-items: center;
204
+ gap: 6px;
205
+ height: 30px;
206
+ padding: 0 10px;
207
+ border-radius: 6px;
208
+ color: var(--v-chrome-muted);
209
+ font-size: 12.5px;
210
+ font-weight: 500;
211
+ text-decoration: none !important;
212
+ max-width: 220px;
213
+ }
214
+ .v-chrome__chip span {
215
+ overflow: hidden;
216
+ text-overflow: ellipsis;
217
+ white-space: nowrap;
218
+ }
219
+ .v-chrome__chip svg {
220
+ width: 14px;
221
+ height: 14px;
222
+ flex: none;
223
+ }
224
+ a.v-chrome__chip:hover {
225
+ background: rgba(255, 255, 255, 0.08);
226
+ color: #fff;
227
+ }
228
+ .v-chrome__avatar {
229
+ width: 32px;
230
+ height: 32px;
231
+ border-radius: 50%;
232
+ border: 2px solid rgba(255, 255, 255, 0.15);
233
+ background: #334155;
234
+ color: #fff;
235
+ padding: 0;
236
+ margin-left: 6px;
237
+ cursor: pointer;
238
+ display: grid;
239
+ place-items: center;
240
+ overflow: hidden;
241
+ }
242
+ .v-chrome__avatar img {
243
+ width: 100%;
244
+ height: 100%;
245
+ object-fit: cover;
246
+ }
247
+ .v-chrome__avatar:hover {
248
+ border-color: rgba(255, 255, 255, 0.4);
249
+ }
250
+ .v-chrome__avatar:focus-visible {
251
+ outline: none;
252
+ border-color: #fff;
253
+ }
254
+ #vestry-root a:where(.v-chrome__brand, .v-chrome__chip) {
255
+ color: inherit;
256
+ }
257
+ .v-menu__item--static {
258
+ cursor: default;
259
+ color: var(--v-text-muted);
260
+ }
261
+ .v-menu__item--static:hover {
262
+ background: none;
263
+ }
264
+
265
+ /* Outer body: light gray with the sidebar on it. The main content is a white
266
+ card with rounded top corners sitting on top of it. */
267
+ .v-shell {
268
+ flex: 1;
269
+ min-height: 0;
270
+ display: grid;
271
+ grid-template-columns: var(--v-sidebar-width) 1fr;
272
+ background: var(--v-bg);
273
+ border-radius: 12px 12px 0 0;
274
+ overflow: clip;
275
+ }
276
+ .v-main {
277
+ min-height: 0;
278
+ overflow-y: auto;
279
+ overscroll-behavior: contain;
280
+ background: var(--v-panel);
281
+ border-radius: 12px 12px 0 0;
282
+ margin: var(--v-frame-inset) var(--v-frame-inset) 0 0;
283
+ border: 1px solid var(--v-border);
284
+ border-bottom: 0;
285
+ box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06);
286
+ }
287
+
288
+ .v-sidebar {
289
+ background: transparent;
290
+ display: flex;
291
+ flex-direction: column;
292
+ min-height: 0;
293
+ overflow-y: auto;
294
+ }
295
+
296
+ .v-nav {
297
+ padding: 12px 10px;
298
+ flex: 1;
299
+ }
300
+ .v-nav__group {
301
+ margin-bottom: 18px;
302
+ }
303
+ .v-nav__heading {
304
+ font-size: 11px;
305
+ font-weight: 600;
306
+ letter-spacing: 0.06em;
307
+ text-transform: uppercase;
308
+ color: var(--v-text-faint);
309
+ padding: 0 10px;
310
+ margin: 0 0 6px;
311
+ }
312
+ .v-nav__link {
313
+ display: flex;
314
+ align-items: center;
315
+ gap: 10px;
316
+ padding: 7px 10px;
317
+ border-radius: var(--v-radius-sm);
318
+ color: var(--v-text);
319
+ text-decoration: none !important;
320
+ font-weight: 500;
321
+ font-size: 13.5px;
322
+ }
323
+ .v-nav__link svg {
324
+ width: 16px;
325
+ height: 16px;
326
+ color: var(--v-text-muted);
327
+ flex: none;
328
+ }
329
+ .v-nav__link:hover {
330
+ background: rgba(255, 255, 255, 0.55);
331
+ }
332
+ .v-nav__link.is-active {
333
+ background: var(--v-surface);
334
+ color: var(--v-primary);
335
+ box-shadow: var(--v-shadow-sm);
336
+ }
337
+ .v-nav__link.is-active svg {
338
+ color: var(--v-primary);
339
+ }
340
+
341
+ .v-main {
342
+ min-width: 0;
343
+ display: flex;
344
+ flex-direction: column;
345
+ }
346
+
347
+ /* Page heading row: sits directly on the panel, no fill or divider. */
348
+ .v-topbar {
349
+ min-height: var(--v-topbar-height);
350
+ display: flex;
351
+ align-items: center;
352
+ gap: 16px;
353
+ padding: 22px 28px 0;
354
+ }
355
+ .v-topbar__title {
356
+ font-size: 24px;
357
+ font-weight: 600;
358
+ margin: 0;
359
+ display: flex;
360
+ align-items: center;
361
+ gap: 8px;
362
+ min-width: 0;
363
+ }
364
+ .v-topbar__title .v-crumb {
365
+ color: var(--v-text-muted);
366
+ font-weight: 500;
367
+ }
368
+ .v-topbar__title .v-crumb-sep {
369
+ color: var(--v-text-faint);
370
+ }
371
+ .v-topbar__actions {
372
+ margin-left: auto;
373
+ display: flex;
374
+ align-items: center;
375
+ gap: 10px;
376
+ }
377
+
378
+ .v-page {
379
+ padding: 18px 28px 28px;
380
+ max-width: 1280px;
381
+ width: 100%;
382
+ }
383
+ .v-page--narrow {
384
+ max-width: 960px;
385
+ }
386
+
387
+ /* ------------------------------------------------------------ buttons */
388
+ .v-btn {
389
+ display: inline-flex;
390
+ align-items: center;
391
+ justify-content: center;
392
+ gap: 7px;
393
+ padding: 0 14px;
394
+ height: 36px;
395
+ border-radius: var(--v-radius-sm);
396
+ border: 1px solid var(--v-border-strong);
397
+ background: var(--v-surface);
398
+ color: var(--v-text);
399
+ font-weight: 500;
400
+ font-size: 13.5px;
401
+ cursor: pointer;
402
+ white-space: nowrap;
403
+ box-shadow: var(--v-shadow-sm);
404
+ transition:
405
+ background 0.12s,
406
+ border-color 0.12s,
407
+ box-shadow 0.12s;
408
+ text-decoration: none !important;
409
+ }
410
+ .v-btn svg {
411
+ width: 15px;
412
+ height: 15px;
413
+ }
414
+ .v-btn:hover {
415
+ background: var(--v-surface-muted);
416
+ }
417
+ .v-btn:focus-visible {
418
+ outline: none;
419
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
420
+ }
421
+ .v-btn:disabled {
422
+ opacity: 0.55;
423
+ cursor: not-allowed;
424
+ }
425
+ .v-btn--primary,
426
+ .v-btn--primary:hover,
427
+ .v-btn--primary:focus-visible {
428
+ background: var(--v-primary);
429
+ border-color: var(--v-primary);
430
+ color: #fff;
431
+ }
432
+ .v-btn--primary:hover {
433
+ background: var(--v-primary-hover);
434
+ }
435
+ .v-btn--danger {
436
+ color: var(--v-danger);
437
+ }
438
+ .v-btn--danger:hover {
439
+ background: var(--v-danger-soft);
440
+ border-color: #fca5a5;
441
+ }
442
+ .v-btn--ghost {
443
+ border-color: transparent;
444
+ background: transparent;
445
+ box-shadow: none;
446
+ }
447
+ .v-btn--ghost:hover {
448
+ background: #eef0f4;
449
+ }
450
+ .v-btn--sm {
451
+ height: 30px;
452
+ padding: 0 10px;
453
+ font-size: 12.5px;
454
+ }
455
+ .v-btn--xs {
456
+ height: 26px;
457
+ padding: 0 8px;
458
+ font-size: 12px;
459
+ }
460
+ .v-btn--icon {
461
+ width: 32px;
462
+ height: 32px;
463
+ padding: 0;
464
+ }
465
+ .v-btn--icon.v-btn--xs {
466
+ width: 26px;
467
+ height: 26px;
468
+ }
469
+ .v-kbd {
470
+ font-family: var(--v-mono);
471
+ font-size: 11px;
472
+ opacity: 0.7;
473
+ margin-left: 2px;
474
+ }
475
+
476
+ /* -------------------------------------------------------------- cards */
477
+ .v-card {
478
+ background: var(--v-surface);
479
+ border: 1px solid var(--v-border);
480
+ border-radius: var(--v-radius);
481
+ box-shadow: var(--v-shadow-sm);
482
+ }
483
+ .v-card__header {
484
+ padding: 14px 18px;
485
+ border-bottom: 1px solid var(--v-border);
486
+ display: flex;
487
+ align-items: center;
488
+ gap: 12px;
489
+ }
490
+ .v-card__title {
491
+ font-size: 13px;
492
+ font-weight: 600;
493
+ margin: 0;
494
+ color: var(--v-text);
495
+ }
496
+ .v-card__body {
497
+ padding: 18px;
498
+ }
499
+ .v-card__body--flush {
500
+ padding: 0;
501
+ }
502
+
503
+ /* -------------------------------------------------------------- forms */
504
+ .v-fields {
505
+ display: grid;
506
+ grid-template-columns: repeat(6, minmax(0, 1fr));
507
+ gap: 20px 18px;
508
+ }
509
+ .v-field {
510
+ grid-column: span 6;
511
+ min-width: 0;
512
+ }
513
+ .v-field--half {
514
+ grid-column: span 3;
515
+ }
516
+ .v-field--third {
517
+ grid-column: span 2;
518
+ }
519
+ .v-field__label {
520
+ display: flex;
521
+ align-items: baseline;
522
+ gap: 6px;
523
+ font-weight: 600;
524
+ font-size: 13px;
525
+ margin-bottom: 6px;
526
+ color: var(--v-text);
527
+ }
528
+ .v-field__required {
529
+ color: var(--v-danger);
530
+ }
531
+ .v-field__hint {
532
+ font-size: 12px;
533
+ color: var(--v-text-muted);
534
+ margin-top: 6px;
535
+ }
536
+ .v-field__error {
537
+ font-size: 12px;
538
+ color: var(--v-danger);
539
+ margin-top: 6px;
540
+ }
541
+ .v-field__key {
542
+ margin-left: auto;
543
+ font-family: var(--v-mono);
544
+ font-weight: 400;
545
+ font-size: 11px;
546
+ color: var(--v-text-faint);
547
+ }
548
+
549
+ .v-input,
550
+ .v-select,
551
+ .v-textarea {
552
+ width: 100%;
553
+ border: 1px solid var(--v-border-strong);
554
+ border-radius: var(--v-radius-sm);
555
+ background: var(--v-surface);
556
+ padding: 8px 11px;
557
+ font-size: 14px;
558
+ line-height: 1.4;
559
+ box-shadow: var(--v-shadow-sm);
560
+ transition:
561
+ border-color 0.12s,
562
+ box-shadow 0.12s;
563
+ }
564
+ .v-input:focus,
565
+ .v-select:focus,
566
+ .v-textarea:focus {
567
+ outline: none;
568
+ border-color: var(--v-primary);
569
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
570
+ }
571
+ .v-input[readonly],
572
+ .v-textarea[readonly] {
573
+ background: var(--v-surface-muted);
574
+ color: var(--v-text-muted);
575
+ }
576
+ .v-input--mono {
577
+ font-family: var(--v-mono);
578
+ font-size: 13px;
579
+ }
580
+ .v-textarea {
581
+ resize: vertical;
582
+ min-height: 80px;
583
+ }
584
+ .v-select {
585
+ appearance: none;
586
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
587
+ background-repeat: no-repeat;
588
+ background-position: right 10px center;
589
+ padding-right: 32px;
590
+ }
591
+ .v-input-group {
592
+ display: flex;
593
+ gap: 8px;
594
+ align-items: center;
595
+ }
596
+ .v-input-group .v-input {
597
+ flex: 1;
598
+ }
599
+
600
+ .v-toggle {
601
+ display: inline-flex;
602
+ align-items: center;
603
+ gap: 10px;
604
+ cursor: pointer;
605
+ user-select: none;
606
+ }
607
+ .v-toggle__track {
608
+ width: 38px;
609
+ height: 22px;
610
+ border-radius: 11px;
611
+ background: #cbd5e1;
612
+ position: relative;
613
+ transition: background 0.15s;
614
+ flex: none;
615
+ }
616
+ .v-toggle__track::after {
617
+ content: "";
618
+ position: absolute;
619
+ top: 2px;
620
+ left: 2px;
621
+ width: 18px;
622
+ height: 18px;
623
+ border-radius: 50%;
624
+ background: #fff;
625
+ box-shadow: var(--v-shadow);
626
+ transition: transform 0.15s;
627
+ }
628
+ .v-toggle.is-on .v-toggle__track {
629
+ background: var(--v-success);
630
+ }
631
+ .v-toggle.is-on .v-toggle__track::after {
632
+ transform: translateX(16px);
633
+ }
634
+ .v-toggle input {
635
+ position: absolute;
636
+ opacity: 0;
637
+ pointer-events: none;
638
+ }
639
+ .v-toggle__label {
640
+ font-weight: 500;
641
+ }
642
+
643
+ .v-swatch {
644
+ width: 14px;
645
+ height: 14px;
646
+ border-radius: 50%;
647
+ border: 1px solid rgba(0, 0, 0, 0.15);
648
+ display: inline-block;
649
+ flex: none;
650
+ }
651
+ .v-chips {
652
+ display: flex;
653
+ flex-wrap: wrap;
654
+ gap: 6px;
655
+ }
656
+ .v-chip {
657
+ display: inline-flex;
658
+ align-items: center;
659
+ gap: 6px;
660
+ padding: 5px 10px;
661
+ border-radius: 999px;
662
+ border: 1px solid var(--v-border-strong);
663
+ background: var(--v-surface);
664
+ font-size: 12.5px;
665
+ cursor: pointer;
666
+ font-weight: 500;
667
+ }
668
+ .v-chip.is-on {
669
+ background: var(--v-primary-soft);
670
+ border-color: var(--v-primary);
671
+ color: var(--v-primary);
672
+ }
673
+
674
+ /* object / array */
675
+ .v-group {
676
+ border: 1px solid var(--v-border);
677
+ border-radius: var(--v-radius-sm);
678
+ background: var(--v-surface-muted);
679
+ }
680
+ .v-group__body {
681
+ padding: 14px;
682
+ }
683
+ .v-group .v-fields {
684
+ gap: 14px 14px;
685
+ }
686
+ .v-list {
687
+ display: flex;
688
+ flex-direction: column;
689
+ gap: 10px;
690
+ }
691
+ .v-list__item {
692
+ border: 1px solid var(--v-border);
693
+ border-radius: var(--v-radius-sm);
694
+ background: var(--v-surface);
695
+ box-shadow: var(--v-shadow-sm);
696
+ }
697
+ .v-list__item.is-dragging,
698
+ .v-list__row.is-dragging {
699
+ opacity: 0.5;
700
+ }
701
+ /* Collapsed items that are switched off read as dimmed; expanded ones stay legible for editing. */
702
+ .v-list__item.is-disabled > .v-list__header {
703
+ opacity: 0.5;
704
+ }
705
+ .v-list__item.is-disabled > .v-list__header .v-list__title {
706
+ font-weight: 400;
707
+ }
708
+ /* Single-input items: an input group — handle addon, input, button addon. */
709
+ .v-list__row {
710
+ display: flex;
711
+ align-items: stretch;
712
+ border: 1px solid var(--v-border-strong);
713
+ border-radius: var(--v-radius-sm);
714
+ background: var(--v-surface);
715
+ box-shadow: var(--v-shadow-sm);
716
+ transition:
717
+ border-color 0.12s,
718
+ box-shadow 0.12s;
719
+ }
720
+ .v-list__row:focus-within {
721
+ border-color: var(--v-primary);
722
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
723
+ }
724
+ .v-list__row > .v-list__handle,
725
+ .v-list__row > .v-list__actions {
726
+ display: flex;
727
+ align-items: center;
728
+ background: var(--v-surface-muted);
729
+ color: var(--v-text-faint);
730
+ }
731
+ .v-list__row > .v-list__handle {
732
+ padding: 0 6px;
733
+ border-right: 1px solid var(--v-border);
734
+ border-radius: var(--v-radius-sm) 0 0 var(--v-radius-sm);
735
+ }
736
+ .v-list__row > .v-list__actions {
737
+ gap: 0;
738
+ padding: 0 4px;
739
+ border-left: 1px solid var(--v-border);
740
+ border-radius: 0 var(--v-radius-sm) var(--v-radius-sm) 0;
741
+ }
742
+ .v-list__row > .v-list__actions .v-btn {
743
+ color: var(--v-text-muted);
744
+ }
745
+ .v-list__row > .v-grow {
746
+ min-width: 0;
747
+ }
748
+ .v-list__row :where(.v-input, .v-select, .v-textarea) {
749
+ border: 0;
750
+ border-radius: 0;
751
+ box-shadow: none;
752
+ }
753
+ .v-list__row :where(.v-input, .v-select, .v-textarea):focus {
754
+ box-shadow: none;
755
+ }
756
+ .v-list__header {
757
+ display: flex;
758
+ align-items: center;
759
+ gap: 8px;
760
+ padding: 8px 10px;
761
+ cursor: pointer;
762
+ user-select: none;
763
+ }
764
+ .v-list__handle {
765
+ color: var(--v-text-faint);
766
+ cursor: grab;
767
+ display: inline-flex;
768
+ }
769
+ .v-list__handle svg {
770
+ width: 14px;
771
+ height: 14px;
772
+ }
773
+ .v-list__title {
774
+ font-weight: 500;
775
+ flex: 1;
776
+ min-width: 0;
777
+ white-space: nowrap;
778
+ overflow: hidden;
779
+ text-overflow: ellipsis;
780
+ font-size: 13px;
781
+ }
782
+ .v-list__title .v-muted {
783
+ font-weight: 400;
784
+ }
785
+ .v-list__actions {
786
+ display: flex;
787
+ gap: 2px;
788
+ }
789
+ .v-list__actions .v-btn {
790
+ color: var(--v-text-muted);
791
+ }
792
+ .v-list__body {
793
+ padding: 12px 14px 14px;
794
+ border-top: 1px solid var(--v-border);
795
+ background: var(--v-surface-muted);
796
+ }
797
+ .v-list__add {
798
+ margin-top: 10px;
799
+ }
800
+
801
+ /* -------------------------------------------------------------- misc */
802
+ .v-muted {
803
+ color: var(--v-text-muted);
804
+ }
805
+ .v-faint {
806
+ color: var(--v-text-faint);
807
+ }
808
+ .v-mono {
809
+ font-family: var(--v-mono);
810
+ font-size: 12.5px;
811
+ }
812
+ .v-stack {
813
+ display: flex;
814
+ flex-direction: column;
815
+ gap: 14px;
816
+ }
817
+ .v-row {
818
+ display: flex;
819
+ align-items: center;
820
+ gap: 10px;
821
+ }
822
+ .v-grow {
823
+ flex: 1;
824
+ min-width: 0;
825
+ }
826
+ .v-badge {
827
+ display: inline-flex;
828
+ align-items: center;
829
+ gap: 6px;
830
+ padding: 2px 9px;
831
+ border-radius: 999px;
832
+ font-size: 12px;
833
+ font-weight: 500;
834
+ background: #eef0f4;
835
+ color: var(--v-text-muted);
836
+ white-space: nowrap;
837
+ }
838
+ .v-badge::before {
839
+ content: "";
840
+ width: 7px;
841
+ height: 7px;
842
+ border-radius: 50%;
843
+ background: currentColor;
844
+ opacity: 0.8;
845
+ }
846
+ .v-badge--published {
847
+ background: var(--v-success-soft);
848
+ color: var(--v-success);
849
+ }
850
+ .v-badge--draft {
851
+ background: var(--v-warning-soft);
852
+ color: var(--v-warning);
853
+ }
854
+ .v-badge--plain::before {
855
+ display: none;
856
+ }
857
+ .v-dot {
858
+ width: 8px;
859
+ height: 8px;
860
+ border-radius: 50%;
861
+ display: inline-block;
862
+ }
863
+
864
+ .v-empty {
865
+ text-align: center;
866
+ padding: 56px 24px;
867
+ color: var(--v-text-muted);
868
+ }
869
+ .v-empty svg {
870
+ width: 36px;
871
+ height: 36px;
872
+ color: var(--v-text-faint);
873
+ margin-bottom: 10px;
874
+ }
875
+ .v-empty h3 {
876
+ margin: 0 0 4px;
877
+ color: var(--v-text);
878
+ font-size: 15px;
879
+ }
880
+ .v-empty p {
881
+ margin: 0 0 16px;
882
+ }
883
+
884
+ .v-spinner {
885
+ width: 18px;
886
+ height: 18px;
887
+ border: 2px solid var(--v-border-strong);
888
+ border-top-color: var(--v-primary);
889
+ border-radius: 50%;
890
+ animation: v-spin 0.8s linear infinite;
891
+ display: inline-block;
892
+ }
893
+ @keyframes v-spin {
894
+ to {
895
+ transform: rotate(360deg);
896
+ }
897
+ }
898
+ .v-loading {
899
+ display: flex;
900
+ align-items: center;
901
+ justify-content: center;
902
+ gap: 10px;
903
+ padding: 48px;
904
+ color: var(--v-text-muted);
905
+ }
906
+
907
+ .v-alert {
908
+ padding: 12px 14px;
909
+ border-radius: var(--v-radius-sm);
910
+ border: 1px solid;
911
+ font-size: 13.5px;
912
+ display: flex;
913
+ gap: 10px;
914
+ align-items: flex-start;
915
+ }
916
+ .v-alert svg {
917
+ width: 16px;
918
+ height: 16px;
919
+ flex: none;
920
+ margin-top: 2px;
921
+ }
922
+ .v-alert--error {
923
+ background: var(--v-danger-soft);
924
+ border-color: #fecaca;
925
+ color: #991b1b;
926
+ }
927
+ .v-alert--warning {
928
+ background: var(--v-warning-soft);
929
+ border-color: #fde68a;
930
+ color: #92400e;
931
+ }
932
+ .v-alert--info {
933
+ background: var(--v-primary-soft);
934
+ border-color: #bfdbfe;
935
+ color: #1e40af;
936
+ }
937
+
938
+ /* toasts */
939
+ .v-toasts {
940
+ position: fixed;
941
+ right: 20px;
942
+ bottom: 20px;
943
+ display: flex;
944
+ flex-direction: column;
945
+ gap: 8px;
946
+ z-index: 100;
947
+ }
948
+ .v-toast {
949
+ background: #111827;
950
+ color: #fff;
951
+ padding: 10px 14px;
952
+ border-radius: var(--v-radius-sm);
953
+ box-shadow: var(--v-shadow-lg);
954
+ font-size: 13.5px;
955
+ display: flex;
956
+ align-items: center;
957
+ gap: 10px;
958
+ min-width: 240px;
959
+ max-width: 420px;
960
+ animation: v-toast-in 0.2s ease-out;
961
+ }
962
+ .v-toast svg {
963
+ width: 16px;
964
+ height: 16px;
965
+ flex: none;
966
+ }
967
+ .v-toast--success svg {
968
+ color: #4ade80;
969
+ }
970
+ .v-toast--error {
971
+ background: #7f1d1d;
972
+ }
973
+ .v-toast a {
974
+ color: #93c5fd;
975
+ }
976
+ @keyframes v-toast-in {
977
+ from {
978
+ opacity: 0;
979
+ transform: translateY(6px);
980
+ }
981
+ }
982
+
983
+ /* modal */
984
+ .v-modal-backdrop {
985
+ position: fixed;
986
+ inset: 0;
987
+ background: rgba(17, 24, 39, 0.45);
988
+ display: grid;
989
+ place-items: center;
990
+ z-index: 90;
991
+ padding: 24px;
992
+ }
993
+ .v-modal {
994
+ background: var(--v-surface);
995
+ border-radius: 12px;
996
+ box-shadow: var(--v-shadow-lg);
997
+ width: 100%;
998
+ max-width: 560px;
999
+ max-height: calc(100vh - 48px);
1000
+ display: flex;
1001
+ flex-direction: column;
1002
+ animation: v-toast-in 0.15s ease-out;
1003
+ }
1004
+ .v-modal--wide {
1005
+ max-width: 960px;
1006
+ }
1007
+ .v-modal__header {
1008
+ padding: 16px 20px;
1009
+ border-bottom: 1px solid var(--v-border);
1010
+ display: flex;
1011
+ align-items: center;
1012
+ gap: 12px;
1013
+ }
1014
+ .v-modal__title {
1015
+ font-size: 15px;
1016
+ font-weight: 600;
1017
+ margin: 0;
1018
+ flex: 1;
1019
+ }
1020
+ .v-modal__body {
1021
+ padding: 20px;
1022
+ overflow: auto;
1023
+ }
1024
+ .v-modal__footer {
1025
+ padding: 14px 20px;
1026
+ border-top: 1px solid var(--v-border);
1027
+ display: flex;
1028
+ justify-content: flex-end;
1029
+ gap: 10px;
1030
+ }
1031
+
1032
+ /* dropdown menu */
1033
+ .v-menu {
1034
+ position: relative;
1035
+ display: inline-block;
1036
+ }
1037
+ .v-menu__panel {
1038
+ /* Portaled to <body> and positioned by Menu from the trigger's rect. */
1039
+ position: fixed;
1040
+ min-width: 220px;
1041
+ background: var(--v-surface);
1042
+ border: 1px solid var(--v-border);
1043
+ border-radius: var(--v-radius);
1044
+ box-shadow: var(--v-shadow-lg);
1045
+ padding: 6px;
1046
+ z-index: 80;
1047
+ max-height: 360px;
1048
+ overflow: auto;
1049
+ }
1050
+ .v-menu__item {
1051
+ display: flex;
1052
+ align-items: center;
1053
+ gap: 10px;
1054
+ width: 100%;
1055
+ padding: 8px 10px;
1056
+ border: 0;
1057
+ background: none;
1058
+ color: var(--v-text);
1059
+ font: inherit;
1060
+ border-radius: var(--v-radius-sm);
1061
+ text-align: left;
1062
+ cursor: pointer;
1063
+ font-size: 13.5px;
1064
+ }
1065
+ .v-menu__item:hover {
1066
+ background: #eef0f4;
1067
+ }
1068
+ .v-menu__item svg {
1069
+ width: 16px;
1070
+ height: 16px;
1071
+ color: var(--v-text-muted);
1072
+ flex: none;
1073
+ }
1074
+ .v-menu__item small {
1075
+ display: block;
1076
+ color: var(--v-text-muted);
1077
+ font-size: 12px;
1078
+ }
1079
+ .v-menu__item.is-danger {
1080
+ color: var(--v-danger);
1081
+ }
1082
+ .v-menu__heading {
1083
+ font-size: 11px;
1084
+ text-transform: uppercase;
1085
+ letter-spacing: 0.06em;
1086
+ color: var(--v-text-faint);
1087
+ padding: 8px 10px 4px;
1088
+ font-weight: 600;
1089
+ }
1090
+
1091
+ /* -------------------------------------------------------------- table */
1092
+ .v-toolbar {
1093
+ display: flex;
1094
+ align-items: center;
1095
+ gap: 10px;
1096
+ margin-bottom: 16px;
1097
+ flex-wrap: wrap;
1098
+ }
1099
+ .v-search {
1100
+ position: relative;
1101
+ flex: 1;
1102
+ max-width: 360px;
1103
+ }
1104
+ .v-search svg {
1105
+ position: absolute;
1106
+ left: 10px;
1107
+ top: 50%;
1108
+ transform: translateY(-50%);
1109
+ width: 15px;
1110
+ height: 15px;
1111
+ color: var(--v-text-faint);
1112
+ }
1113
+ .v-search .v-input {
1114
+ padding-left: 32px;
1115
+ }
1116
+ .v-segment {
1117
+ display: inline-flex;
1118
+ border: 1px solid var(--v-border-strong);
1119
+ border-radius: var(--v-radius-sm);
1120
+ overflow: hidden;
1121
+ background: var(--v-surface);
1122
+ }
1123
+ .v-segment button {
1124
+ border: 0;
1125
+ background: none;
1126
+ padding: 0 12px;
1127
+ height: 34px;
1128
+ cursor: pointer;
1129
+ font-weight: 500;
1130
+ font-size: 13px;
1131
+ color: var(--v-text-muted);
1132
+ }
1133
+ .v-segment button + button {
1134
+ border-left: 1px solid var(--v-border);
1135
+ }
1136
+ .v-segment button.is-active {
1137
+ background: #eef0f4;
1138
+ color: var(--v-text);
1139
+ }
1140
+
1141
+ .v-table-wrap {
1142
+ overflow-x: auto;
1143
+ /* Clip the header background to the card's rounded corners. */
1144
+ border-radius: inherit;
1145
+ }
1146
+ .v-table {
1147
+ width: 100%;
1148
+ border-collapse: collapse;
1149
+ font-size: 13.5px;
1150
+ }
1151
+ .v-table th {
1152
+ text-align: left;
1153
+ font-size: 12px;
1154
+ text-transform: uppercase;
1155
+ letter-spacing: 0.04em;
1156
+ color: var(--v-text-muted);
1157
+ font-weight: 600;
1158
+ padding: 10px 16px;
1159
+ border-bottom: 1px solid var(--v-border);
1160
+ background: var(--v-surface-muted);
1161
+ white-space: nowrap;
1162
+ }
1163
+ .v-table th button {
1164
+ background: none;
1165
+ border: 0;
1166
+ padding: 0;
1167
+ font: inherit;
1168
+ color: inherit;
1169
+ cursor: pointer;
1170
+ display: inline-flex;
1171
+ align-items: center;
1172
+ gap: 4px;
1173
+ text-transform: inherit;
1174
+ letter-spacing: inherit;
1175
+ }
1176
+ .v-table th button svg {
1177
+ width: 12px;
1178
+ height: 12px;
1179
+ }
1180
+ .v-table td {
1181
+ padding: 11px 16px;
1182
+ border-bottom: 1px solid var(--v-border);
1183
+ vertical-align: middle;
1184
+ }
1185
+ .v-table tr:last-child td {
1186
+ border-bottom: 0;
1187
+ }
1188
+ .v-table tbody tr:hover td {
1189
+ background: #fafbfc;
1190
+ }
1191
+ .v-table__title {
1192
+ font-weight: 600;
1193
+ color: var(--v-text);
1194
+ text-decoration: none !important;
1195
+ }
1196
+ .v-table__title:hover {
1197
+ color: var(--v-primary);
1198
+ }
1199
+ .v-table__sub {
1200
+ display: block;
1201
+ color: var(--v-text-muted);
1202
+ font-size: 12.5px;
1203
+ font-weight: 400;
1204
+ overflow: hidden;
1205
+ text-overflow: ellipsis;
1206
+ white-space: nowrap;
1207
+ }
1208
+ /* The title column takes whatever width the fixed columns leave and
1209
+ truncates, so the table never grows wider than its card. */
1210
+ .v-table td.v-table__main {
1211
+ width: 100%;
1212
+ max-width: 0;
1213
+ }
1214
+ .v-table td.v-table__actions {
1215
+ text-align: right;
1216
+ width: 1%;
1217
+ white-space: nowrap;
1218
+ }
1219
+ .v-table__footer {
1220
+ padding: 10px 16px;
1221
+ font-size: 12.5px;
1222
+ color: var(--v-text-muted);
1223
+ border-top: 1px solid var(--v-border);
1224
+ display: flex;
1225
+ align-items: center;
1226
+ gap: 12px;
1227
+ }
1228
+ /* Footer row inside a table: stays a real cell so colSpan spans the table. */
1229
+ .v-table td.v-table__more {
1230
+ padding: 10px 16px;
1231
+ font-size: 12.5px;
1232
+ border-top: 1px solid var(--v-border);
1233
+ border-bottom: 0;
1234
+ }
1235
+ .v-thumb {
1236
+ width: 44px;
1237
+ height: 32px;
1238
+ border-radius: 4px;
1239
+ object-fit: cover;
1240
+ background: #e5e7eb;
1241
+ display: block;
1242
+ }
1243
+
1244
+ /* ------------------------------------------------------------ editor */
1245
+ .v-editor-layout {
1246
+ display: grid;
1247
+ grid-template-columns: minmax(0, 1fr) 320px;
1248
+ gap: 24px;
1249
+ align-items: start;
1250
+ }
1251
+ @media (max-width: 1100px) {
1252
+ .v-editor-layout {
1253
+ grid-template-columns: 1fr;
1254
+ }
1255
+ }
1256
+ .v-editor-sidebar {
1257
+ /* Sticky, but capped at the viewport and scrollable on its own, so a rail
1258
+ taller than the screen can still be scrolled to its bottom. The padding
1259
+ and negative margin keep card shadows from being clipped. */
1260
+ position: sticky;
1261
+ top: 24px;
1262
+ max-height: calc(100vh - var(--v-chrome-height) - 56px);
1263
+ overflow-y: auto;
1264
+ overscroll-behavior: contain;
1265
+ min-width: 0;
1266
+ padding: 4px;
1267
+ margin: -4px;
1268
+ }
1269
+ .v-editor-sidebar .v-card__body {
1270
+ padding: 14px 16px;
1271
+ }
1272
+ .v-editor-sidebar .v-fields {
1273
+ gap: 14px;
1274
+ }
1275
+ .v-editor-sidebar .v-field {
1276
+ grid-column: span 6;
1277
+ }
1278
+ .v-meta {
1279
+ font-size: 12.5px;
1280
+ color: var(--v-text-muted);
1281
+ display: grid;
1282
+ grid-template-columns: auto 1fr;
1283
+ gap: 6px 12px;
1284
+ }
1285
+ .v-meta dt {
1286
+ font-weight: 500;
1287
+ }
1288
+ .v-meta dd {
1289
+ margin: 0;
1290
+ overflow: hidden;
1291
+ text-overflow: ellipsis;
1292
+ white-space: nowrap;
1293
+ }
1294
+
1295
+ .v-title-input {
1296
+ width: 100%;
1297
+ border: 0;
1298
+ background: transparent;
1299
+ font-size: 26px;
1300
+ font-weight: 700;
1301
+ padding: 2px 0;
1302
+ letter-spacing: -0.01em;
1303
+ }
1304
+ .v-title-input:focus {
1305
+ outline: none;
1306
+ }
1307
+ .v-title-input::placeholder {
1308
+ color: var(--v-text-faint);
1309
+ }
1310
+
1311
+ .v-unsaved {
1312
+ font-size: 12.5px;
1313
+ color: var(--v-warning);
1314
+ display: inline-flex;
1315
+ align-items: center;
1316
+ gap: 6px;
1317
+ }
1318
+
1319
+ /* --------------------------------------------------------- rich text */
1320
+ .v-rte {
1321
+ border: 1px solid var(--v-border-strong);
1322
+ border-radius: var(--v-radius-sm);
1323
+ background: var(--v-surface);
1324
+ box-shadow: var(--v-shadow-sm);
1325
+ }
1326
+ .v-rte:focus-within {
1327
+ border-color: var(--v-primary);
1328
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
1329
+ }
1330
+ /* Editor filling a card: the card supplies the border and the toolbar is its strip. */
1331
+ .v-card--editor {
1332
+ overflow: visible;
1333
+ }
1334
+ .v-card--editor .v-card__header {
1335
+ border-bottom: 0;
1336
+ }
1337
+ .v-rte--flush {
1338
+ border: 0;
1339
+ border-radius: 0 0 var(--v-radius) var(--v-radius);
1340
+ box-shadow: none;
1341
+ }
1342
+ .v-rte--flush:focus-within {
1343
+ box-shadow: none;
1344
+ }
1345
+ .v-rte--flush .v-rte__toolbar {
1346
+ border-top: 1px solid var(--v-border);
1347
+ border-radius: 0;
1348
+ }
1349
+ .v-rte--flush .v-prose {
1350
+ padding: 22px 28px 32px;
1351
+ }
1352
+ .v-rte--flush .v-source-editor .cm-editor {
1353
+ border: 0;
1354
+ border-radius: 0 0 var(--v-radius) var(--v-radius);
1355
+ }
1356
+ .v-rte__toolbar {
1357
+ display: flex;
1358
+ flex-wrap: wrap;
1359
+ align-items: center;
1360
+ gap: 2px;
1361
+ padding: 6px 8px;
1362
+ border-bottom: 1px solid var(--v-border);
1363
+ background: var(--v-surface-muted);
1364
+ border-radius: var(--v-radius-sm) var(--v-radius-sm) 0 0;
1365
+ position: sticky;
1366
+ top: 0;
1367
+ z-index: 10;
1368
+ }
1369
+ .v-rte__tool {
1370
+ width: 30px;
1371
+ height: 30px;
1372
+ border-radius: 5px;
1373
+ border: 0;
1374
+ background: none;
1375
+ display: inline-grid;
1376
+ place-items: center;
1377
+ cursor: pointer;
1378
+ color: var(--v-text-muted);
1379
+ }
1380
+ .v-rte__tool svg {
1381
+ width: 16px;
1382
+ height: 16px;
1383
+ }
1384
+ .v-rte__tool:hover {
1385
+ background: #e9ecf1;
1386
+ color: var(--v-text);
1387
+ }
1388
+ .v-rte__tool.is-active {
1389
+ background: #dbe4f8;
1390
+ color: var(--v-primary);
1391
+ }
1392
+ .v-rte__tool:disabled {
1393
+ opacity: 0.4;
1394
+ cursor: default;
1395
+ }
1396
+ .v-rte__sep {
1397
+ width: 1px;
1398
+ height: 20px;
1399
+ background: var(--v-border);
1400
+ margin: 0 4px;
1401
+ }
1402
+ .v-rte__spacer {
1403
+ flex: 1;
1404
+ }
1405
+ .v-rte__insert {
1406
+ display: inline-flex;
1407
+ align-items: center;
1408
+ gap: 6px;
1409
+ height: 30px;
1410
+ padding: 0 10px;
1411
+ border-radius: 5px;
1412
+ border: 1px solid var(--v-border-strong);
1413
+ background: var(--v-surface);
1414
+ cursor: pointer;
1415
+ font-weight: 500;
1416
+ font-size: 12.5px;
1417
+ }
1418
+ .v-rte__insert svg {
1419
+ width: 14px;
1420
+ height: 14px;
1421
+ }
1422
+
1423
+ .v-prose {
1424
+ padding: 18px 24px 28px;
1425
+ min-height: 320px;
1426
+ font-family: var(--v-serif);
1427
+ font-size: 16.5px;
1428
+ line-height: 1.7;
1429
+ color: #1f2937;
1430
+ }
1431
+ .v-prose:focus {
1432
+ outline: none;
1433
+ }
1434
+ .v-prose p {
1435
+ margin: 0.9em 0;
1436
+ }
1437
+ .v-prose > :first-child {
1438
+ margin-top: 0;
1439
+ }
1440
+ .v-prose > :last-child {
1441
+ margin-bottom: 0;
1442
+ }
1443
+ .v-prose h1,
1444
+ .v-prose h2,
1445
+ .v-prose h3,
1446
+ .v-prose h4 {
1447
+ font-family: var(--v-font);
1448
+ line-height: 1.25;
1449
+ margin: 1.4em 0 0.5em;
1450
+ font-weight: 700;
1451
+ letter-spacing: -0.01em;
1452
+ }
1453
+ .v-prose h1 {
1454
+ font-size: 1.9em;
1455
+ }
1456
+ .v-prose h2 {
1457
+ font-size: 1.5em;
1458
+ }
1459
+ .v-prose h3 {
1460
+ font-size: 1.25em;
1461
+ }
1462
+ .v-prose h4 {
1463
+ font-size: 1.05em;
1464
+ }
1465
+ .v-prose > :first-child {
1466
+ margin-top: 0;
1467
+ }
1468
+ .v-prose blockquote {
1469
+ border-left: 3px solid var(--v-border-strong);
1470
+ margin: 0;
1471
+ padding-left: 16px;
1472
+ color: #4b5563;
1473
+ }
1474
+ .v-prose ul,
1475
+ .v-prose ol {
1476
+ padding-left: 1.5em;
1477
+ }
1478
+ .v-prose li p {
1479
+ margin: 0;
1480
+ }
1481
+ .v-prose pre {
1482
+ font-family: var(--v-mono);
1483
+ font-size: 13px;
1484
+ background: #1f2937;
1485
+ color: #f9fafb;
1486
+ padding: 12px 14px;
1487
+ border-radius: 6px;
1488
+ overflow: auto;
1489
+ }
1490
+ .v-prose code {
1491
+ font-family: var(--v-mono);
1492
+ font-size: 0.85em;
1493
+ background: #f3f4f6;
1494
+ padding: 0.1em 0.35em;
1495
+ border-radius: 4px;
1496
+ }
1497
+ .v-prose pre code {
1498
+ background: none;
1499
+ padding: 0;
1500
+ font-size: inherit;
1501
+ }
1502
+ .v-prose hr {
1503
+ border: 0;
1504
+ border-top: 1px solid var(--v-border-strong);
1505
+ margin: 1.6em 0;
1506
+ }
1507
+ .v-prose a {
1508
+ color: var(--v-primary);
1509
+ text-decoration: underline;
1510
+ }
1511
+ .v-prose img {
1512
+ max-width: 100%;
1513
+ height: auto;
1514
+ border-radius: 4px;
1515
+ }
1516
+ .v-prose img.ProseMirror-selectednode {
1517
+ outline: 3px solid var(--v-primary-ring);
1518
+ }
1519
+ .v-prose table {
1520
+ border-collapse: collapse;
1521
+ width: 100%;
1522
+ font-size: 0.92em;
1523
+ table-layout: fixed;
1524
+ }
1525
+ .v-prose th,
1526
+ .v-prose td {
1527
+ border: 1px solid var(--v-border-strong);
1528
+ padding: 6px 10px;
1529
+ vertical-align: top;
1530
+ position: relative;
1531
+ }
1532
+ .v-prose th {
1533
+ background: var(--v-surface-muted);
1534
+ font-weight: 600;
1535
+ text-align: left;
1536
+ }
1537
+ .v-prose .selectedCell::after {
1538
+ content: "";
1539
+ position: absolute;
1540
+ inset: 0;
1541
+ background: rgba(37, 99, 235, 0.12);
1542
+ pointer-events: none;
1543
+ }
1544
+ .v-prose .column-resize-handle {
1545
+ position: absolute;
1546
+ right: -2px;
1547
+ top: 0;
1548
+ bottom: 0;
1549
+ width: 4px;
1550
+ background: var(--v-primary);
1551
+ pointer-events: none;
1552
+ }
1553
+ .v-prose p.is-editor-empty:first-child::before {
1554
+ content: attr(data-placeholder);
1555
+ color: var(--v-text-faint);
1556
+ float: left;
1557
+ height: 0;
1558
+ pointer-events: none;
1559
+ font-style: italic;
1560
+ }
1561
+ .v-prose sup,
1562
+ .v-prose sub {
1563
+ font-size: 0.7em;
1564
+ }
1565
+ .v-prose .ProseMirror-gapcursor:after {
1566
+ border-top: 1px solid var(--v-text);
1567
+ }
1568
+
1569
+ /* component (MDX) blocks inside the editor */
1570
+ .v-block {
1571
+ font-family: var(--v-font);
1572
+ font-size: 14px;
1573
+ line-height: 1.5;
1574
+ border: 1px solid var(--v-border-strong);
1575
+ border-radius: var(--v-radius);
1576
+ background: var(--v-surface);
1577
+ box-shadow: var(--v-shadow-sm);
1578
+ margin: 1em 0;
1579
+ }
1580
+ .v-block.is-selected {
1581
+ border-color: var(--v-primary);
1582
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
1583
+ }
1584
+ .v-block__header {
1585
+ display: flex;
1586
+ align-items: center;
1587
+ gap: 8px;
1588
+ padding: 7px 8px 7px 12px;
1589
+ background: var(--v-surface-muted);
1590
+ border-bottom: 1px solid var(--v-border);
1591
+ border-radius: var(--v-radius) var(--v-radius) 0 0;
1592
+ user-select: none;
1593
+ }
1594
+ .v-block.is-collapsed .v-block__header {
1595
+ border-bottom: 0;
1596
+ border-radius: var(--v-radius);
1597
+ }
1598
+ .v-block__icon {
1599
+ width: 22px;
1600
+ height: 22px;
1601
+ border-radius: 5px;
1602
+ background: #e0e7ff;
1603
+ color: #3730a3;
1604
+ display: grid;
1605
+ place-items: center;
1606
+ flex: none;
1607
+ }
1608
+ .v-block__icon svg {
1609
+ width: 13px;
1610
+ height: 13px;
1611
+ }
1612
+ .v-block__name {
1613
+ font-weight: 600;
1614
+ font-size: 13px;
1615
+ }
1616
+ .v-block__summary {
1617
+ color: var(--v-text-muted);
1618
+ font-size: 12.5px;
1619
+ flex: 1;
1620
+ min-width: 0;
1621
+ overflow: hidden;
1622
+ text-overflow: ellipsis;
1623
+ white-space: nowrap;
1624
+ }
1625
+ .v-block__actions {
1626
+ display: flex;
1627
+ gap: 2px;
1628
+ }
1629
+ .v-block__props {
1630
+ padding: 14px 14px 16px;
1631
+ border-bottom: 1px solid var(--v-border);
1632
+ }
1633
+ .v-block__props .v-fields {
1634
+ gap: 14px;
1635
+ }
1636
+ .v-block__children {
1637
+ padding: 4px 16px 12px;
1638
+ }
1639
+ .v-block__children .v-prose {
1640
+ padding: 8px 0;
1641
+ min-height: 40px;
1642
+ }
1643
+ .v-block__children .v-prose-nested {
1644
+ padding: 8px 0 4px;
1645
+ min-height: 1.5em;
1646
+ }
1647
+ .v-block__empty {
1648
+ padding: 10px 14px;
1649
+ color: var(--v-text-faint);
1650
+ font-size: 12.5px;
1651
+ font-style: italic;
1652
+ }
1653
+ .v-block--raw .v-block__icon {
1654
+ background: #fef3c7;
1655
+ color: #92400e;
1656
+ }
1657
+ .v-block__code {
1658
+ font-family: var(--v-mono);
1659
+ font-size: 12.5px;
1660
+ white-space: pre-wrap;
1661
+ padding: 10px 14px;
1662
+ margin: 0;
1663
+ color: #374151;
1664
+ background: #fffdf5;
1665
+ border-radius: 0 0 var(--v-radius) var(--v-radius);
1666
+ }
1667
+ .v-block__code textarea {
1668
+ width: 100%;
1669
+ font-family: var(--v-mono);
1670
+ font-size: 12.5px;
1671
+ border: 1px solid var(--v-border);
1672
+ border-radius: 4px;
1673
+ padding: 8px;
1674
+ min-height: 60px;
1675
+ }
1676
+ .v-inline-component {
1677
+ display: inline;
1678
+ background: #eef2ff;
1679
+ border-radius: 3px;
1680
+ padding: 0 3px;
1681
+ border-bottom: 1px solid #a5b4fc;
1682
+ }
1683
+ .v-inline-component::before {
1684
+ content: attr(data-name);
1685
+ font-family: var(--v-mono);
1686
+ font-size: 0.65em;
1687
+ color: #4338ca;
1688
+ vertical-align: super;
1689
+ margin-right: 2px;
1690
+ }
1691
+ .v-inline-expression {
1692
+ font-family: var(--v-mono);
1693
+ font-size: 0.8em;
1694
+ background: #fef3c7;
1695
+ color: #92400e;
1696
+ padding: 0 4px;
1697
+ border-radius: 3px;
1698
+ }
1699
+
1700
+ /* source editor */
1701
+ .v-source-editor .cm-editor {
1702
+ font-size: 13px;
1703
+ border-radius: 0 0 var(--v-radius-sm) var(--v-radius-sm);
1704
+ min-height: 320px;
1705
+ }
1706
+ .v-source-editor .cm-editor.cm-focused {
1707
+ outline: none;
1708
+ }
1709
+ .v-source-editor .cm-scroller {
1710
+ font-family: var(--v-mono);
1711
+ line-height: 1.55;
1712
+ }
1713
+
1714
+ /* -------------------------------------------------------------- assets */
1715
+ .v-asset-grid {
1716
+ display: grid;
1717
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1718
+ gap: 14px;
1719
+ }
1720
+ .v-asset {
1721
+ border: 1px solid var(--v-border);
1722
+ border-radius: var(--v-radius-sm);
1723
+ background: var(--v-surface);
1724
+ overflow: hidden;
1725
+ cursor: pointer;
1726
+ text-align: left;
1727
+ padding: 0;
1728
+ display: flex;
1729
+ flex-direction: column;
1730
+ transition:
1731
+ box-shadow 0.12s,
1732
+ border-color 0.12s;
1733
+ }
1734
+ .v-asset:hover {
1735
+ box-shadow: var(--v-shadow);
1736
+ border-color: var(--v-border-strong);
1737
+ }
1738
+ .v-asset.is-selected {
1739
+ border-color: var(--v-primary);
1740
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
1741
+ }
1742
+ .v-asset__preview {
1743
+ position: relative;
1744
+ aspect-ratio: 4 / 3;
1745
+ background: repeating-conic-gradient(#f3f4f6 0 25%, #fff 0 50%) 0 0 / 16px
1746
+ 16px;
1747
+ display: grid;
1748
+ place-items: center;
1749
+ overflow: hidden;
1750
+ }
1751
+ /* Absolutely positioned so the image always fills the box (a percentage
1752
+ height inside an aspect-ratio grid cell is unreliable). Contained rather
1753
+ than cropped: the grid is a picker, and seeing an image's real
1754
+ proportions is what decides whether it fits a slot. */
1755
+ .v-asset__preview img {
1756
+ position: absolute;
1757
+ inset: 0;
1758
+ width: 100%;
1759
+ height: 100%;
1760
+ object-fit: contain;
1761
+ object-position: center;
1762
+ }
1763
+ .v-asset__preview svg {
1764
+ width: 28px;
1765
+ height: 28px;
1766
+ color: var(--v-text-faint);
1767
+ }
1768
+ .v-asset__name {
1769
+ padding: 8px 10px;
1770
+ font-size: 12.5px;
1771
+ font-weight: 500;
1772
+ white-space: nowrap;
1773
+ overflow: hidden;
1774
+ text-overflow: ellipsis;
1775
+ }
1776
+ .v-asset__meta {
1777
+ padding: 0 10px 8px;
1778
+ font-size: 11.5px;
1779
+ color: var(--v-text-muted);
1780
+ }
1781
+ .v-folder {
1782
+ display: flex;
1783
+ align-items: center;
1784
+ gap: 8px;
1785
+ padding: 10px 12px;
1786
+ border: 1px solid var(--v-border);
1787
+ border-radius: var(--v-radius-sm);
1788
+ background: var(--v-surface);
1789
+ font-weight: 500;
1790
+ cursor: pointer;
1791
+ text-align: left;
1792
+ }
1793
+ .v-folder svg {
1794
+ width: 16px;
1795
+ height: 16px;
1796
+ color: var(--v-warning);
1797
+ }
1798
+ .v-dropzone {
1799
+ border: 2px dashed var(--v-border-strong);
1800
+ border-radius: var(--v-radius);
1801
+ padding: 24px;
1802
+ text-align: center;
1803
+ color: var(--v-text-muted);
1804
+ margin-bottom: 16px;
1805
+ transition:
1806
+ border-color 0.12s,
1807
+ background 0.12s;
1808
+ }
1809
+ .v-dropzone.is-over {
1810
+ border-color: var(--v-primary);
1811
+ background: var(--v-primary-soft);
1812
+ }
1813
+ .v-breadcrumbs {
1814
+ display: flex;
1815
+ align-items: center;
1816
+ gap: 6px;
1817
+ font-size: 13px;
1818
+ color: var(--v-text-muted);
1819
+ }
1820
+ .v-breadcrumbs button {
1821
+ background: none;
1822
+ border: 0;
1823
+ padding: 0;
1824
+ color: var(--v-primary);
1825
+ cursor: pointer;
1826
+ font: inherit;
1827
+ }
1828
+
1829
+ /* image field */
1830
+ .v-image-field {
1831
+ display: flex;
1832
+ gap: 14px;
1833
+ align-items: flex-start;
1834
+ }
1835
+ .v-image-field__preview {
1836
+ width: 140px;
1837
+ height: 100px;
1838
+ border-radius: var(--v-radius-sm);
1839
+ border: 1px solid var(--v-border);
1840
+ background: repeating-conic-gradient(#f3f4f6 0 25%, #fff 0 50%) 0 0 / 16px
1841
+ 16px;
1842
+ display: grid;
1843
+ place-items: center;
1844
+ overflow: hidden;
1845
+ flex: none;
1846
+ }
1847
+ .v-image-field__preview img {
1848
+ width: 100%;
1849
+ height: 100%;
1850
+ object-fit: contain;
1851
+ }
1852
+ .v-image-field__preview svg {
1853
+ width: 24px;
1854
+ height: 24px;
1855
+ color: var(--v-text-faint);
1856
+ }
1857
+ .v-image-field__info {
1858
+ flex: 1;
1859
+ min-width: 0;
1860
+ }
1861
+ .v-image-field__actions {
1862
+ display: flex;
1863
+ flex-wrap: wrap;
1864
+ gap: 6px 8px;
1865
+ }
1866
+ .v-editor-sidebar .v-image-field__preview {
1867
+ width: 96px;
1868
+ height: 72px;
1869
+ }
1870
+ .v-image-field__path {
1871
+ font-family: var(--v-mono);
1872
+ font-size: 12px;
1873
+ color: var(--v-text-muted);
1874
+ word-break: break-all;
1875
+ margin-bottom: 8px;
1876
+ }
1877
+
1878
+ /* ------------------------------------------------------------ dashboard */
1879
+ .v-stats {
1880
+ display: flex;
1881
+ flex-wrap: wrap;
1882
+ gap: 12px;
1883
+ }
1884
+ .v-stat {
1885
+ display: flex;
1886
+ align-items: center;
1887
+ gap: 12px;
1888
+ padding: 12px 16px 12px 12px;
1889
+ min-width: 200px;
1890
+ text-decoration: none !important;
1891
+ color: inherit;
1892
+ transition:
1893
+ box-shadow 0.12s,
1894
+ transform 0.12s;
1895
+ }
1896
+ .v-stat:hover {
1897
+ box-shadow: var(--v-shadow);
1898
+ transform: translateY(-1px);
1899
+ }
1900
+ .v-stat__icon {
1901
+ width: 34px;
1902
+ height: 34px;
1903
+ border-radius: 8px;
1904
+ background: var(--v-primary-soft);
1905
+ color: var(--v-primary);
1906
+ display: grid;
1907
+ place-items: center;
1908
+ flex: none;
1909
+ }
1910
+ .v-stat__icon svg {
1911
+ width: 17px;
1912
+ height: 17px;
1913
+ }
1914
+ .v-stat__body {
1915
+ display: flex;
1916
+ flex-direction: column;
1917
+ line-height: 1.3;
1918
+ }
1919
+ .v-stat__label {
1920
+ font-weight: 600;
1921
+ font-size: 14px;
1922
+ }
1923
+ .v-stat__meta {
1924
+ color: var(--v-text-muted);
1925
+ font-size: 12.5px;
1926
+ }
1927
+ #vestry-root a:where(.v-stat) {
1928
+ color: inherit;
1929
+ }
1930
+ .v-dash-grid {
1931
+ display: grid;
1932
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
1933
+ gap: 16px;
1934
+ }
1935
+ .v-dash-card {
1936
+ display: block;
1937
+ text-decoration: none !important;
1938
+ color: inherit;
1939
+ padding: 18px;
1940
+ transition:
1941
+ box-shadow 0.12s,
1942
+ transform 0.12s;
1943
+ }
1944
+ .v-dash-card:hover {
1945
+ box-shadow: var(--v-shadow);
1946
+ transform: translateY(-1px);
1947
+ }
1948
+ .v-dash-card__icon {
1949
+ width: 36px;
1950
+ height: 36px;
1951
+ border-radius: 8px;
1952
+ background: var(--v-primary-soft);
1953
+ color: var(--v-primary);
1954
+ display: grid;
1955
+ place-items: center;
1956
+ margin-bottom: 12px;
1957
+ }
1958
+ .v-dash-card__icon svg {
1959
+ width: 18px;
1960
+ height: 18px;
1961
+ }
1962
+ .v-dash-card h3 {
1963
+ margin: 0 0 2px;
1964
+ font-size: 15px;
1965
+ }
1966
+ .v-dash-card p {
1967
+ margin: 0;
1968
+ color: var(--v-text-muted);
1969
+ font-size: 13px;
1970
+ }
1971
+ .v-section {
1972
+ margin-top: 32px;
1973
+ }
1974
+ .v-section:first-child {
1975
+ margin-top: 0;
1976
+ }
1977
+ .v-section-title {
1978
+ font-size: 13px;
1979
+ text-transform: uppercase;
1980
+ letter-spacing: 0.06em;
1981
+ color: var(--v-text-muted);
1982
+ font-weight: 600;
1983
+ margin: 0 0 12px;
1984
+ }
1985
+
1986
+ /* ---------------------------------------------------------------- login */
1987
+ .v-login {
1988
+ min-height: 100vh;
1989
+ display: grid;
1990
+ place-items: center;
1991
+ padding: 24px;
1992
+ background:
1993
+ radial-gradient(1200px 600px at 20% -10%, #e0e7ff 0%, transparent 60%),
1994
+ radial-gradient(900px 500px at 100% 100%, #dbeafe 0%, transparent 55%),
1995
+ var(--v-bg);
1996
+ }
1997
+ .v-login__card {
1998
+ width: 100%;
1999
+ max-width: 400px;
2000
+ padding: 36px 32px;
2001
+ text-align: center;
2002
+ }
2003
+ .v-login__mark {
2004
+ width: 48px;
2005
+ height: 48px;
2006
+ border-radius: 12px;
2007
+ background: #1e293b;
2008
+ color: #fff;
2009
+ display: grid;
2010
+ place-items: center;
2011
+ font-weight: 700;
2012
+ font-size: 22px;
2013
+ margin: 0 auto 16px;
2014
+ }
2015
+ .v-login h1 {
2016
+ font-size: 20px;
2017
+ margin: 0 0 6px;
2018
+ }
2019
+ .v-login p {
2020
+ color: var(--v-text-muted);
2021
+ margin: 0 0 22px;
2022
+ }
2023
+ .v-login .v-btn {
2024
+ width: 100%;
2025
+ height: 40px;
2026
+ }
2027
+ .v-login__logo {
2028
+ display: inline-grid;
2029
+ place-items: center;
2030
+ background: #1e293b;
2031
+ border-radius: 12px;
2032
+ padding: 12px 18px;
2033
+ margin: 0 auto 16px;
2034
+ }
2035
+ .v-login__logo img {
2036
+ height: 36px;
2037
+ width: auto;
2038
+ display: block;
2039
+ }
2040
+ .v-login__form {
2041
+ display: flex;
2042
+ flex-direction: column;
2043
+ gap: 14px;
2044
+ text-align: left;
2045
+ }
2046
+ .v-login__form p {
2047
+ margin: 0;
2048
+ text-align: center;
2049
+ }
2050
+ .v-login__field {
2051
+ display: block;
2052
+ }
2053
+ .v-login__code {
2054
+ font-family: var(--v-mono);
2055
+ font-size: 22px;
2056
+ letter-spacing: 0.3em;
2057
+ text-align: center;
2058
+ padding: 10px 12px;
2059
+ }
2060
+ .v-login__links {
2061
+ display: flex;
2062
+ justify-content: space-between;
2063
+ gap: 12px;
2064
+ font-size: 13px;
2065
+ }
2066
+ .v-link {
2067
+ background: none;
2068
+ border: 0;
2069
+ padding: 0;
2070
+ font: inherit;
2071
+ font-size: 13px;
2072
+ color: var(--v-primary);
2073
+ cursor: pointer;
2074
+ display: inline-flex;
2075
+ align-items: center;
2076
+ gap: 4px;
2077
+ }
2078
+ .v-link:hover {
2079
+ text-decoration: underline;
2080
+ }
2081
+ .v-link:disabled {
2082
+ opacity: 0.5;
2083
+ cursor: default;
2084
+ }
2085
+
2086
+ /* ---------------------------------------------------------------- users */
2087
+ .v-menu__sub {
2088
+ display: block;
2089
+ text-transform: none;
2090
+ letter-spacing: 0;
2091
+ font-weight: 400;
2092
+ font-size: 12px;
2093
+ color: var(--v-text-muted);
2094
+ margin-top: 2px;
2095
+ }
2096
+ .v-menu__item--danger {
2097
+ color: var(--v-danger);
2098
+ }
2099
+ .v-user {
2100
+ display: flex;
2101
+ align-items: center;
2102
+ gap: 12px;
2103
+ min-width: 0;
2104
+ }
2105
+ .v-user__text {
2106
+ display: flex;
2107
+ flex-direction: column;
2108
+ min-width: 0;
2109
+ }
2110
+ .v-avatar {
2111
+ width: 32px;
2112
+ height: 32px;
2113
+ border-radius: 50%;
2114
+ background: var(--v-primary-soft);
2115
+ color: var(--v-primary);
2116
+ font-size: 12px;
2117
+ font-weight: 700;
2118
+ display: grid;
2119
+ place-items: center;
2120
+ flex: none;
2121
+ }
2122
+ .v-chrome__avatar {
2123
+ font-size: 12px;
2124
+ font-weight: 700;
2125
+ letter-spacing: 0.02em;
2126
+ }
2127
+ .v-radio-cards {
2128
+ display: grid;
2129
+ gap: 8px;
2130
+ }
2131
+ .v-radio-card {
2132
+ display: flex;
2133
+ gap: 10px;
2134
+ align-items: flex-start;
2135
+ padding: 10px 12px;
2136
+ border: 1px solid var(--v-border-strong);
2137
+ border-radius: var(--v-radius-sm);
2138
+ background: var(--v-surface);
2139
+ cursor: pointer;
2140
+ }
2141
+ .v-radio-card input {
2142
+ margin: 3px 0 0;
2143
+ accent-color: var(--v-primary);
2144
+ }
2145
+ .v-radio-card.is-selected {
2146
+ border-color: var(--v-primary);
2147
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
2148
+ }
2149
+ .v-radio-card.is-disabled {
2150
+ opacity: 0.6;
2151
+ cursor: default;
2152
+ }
2153
+ .v-radio-card__body {
2154
+ display: flex;
2155
+ flex-direction: column;
2156
+ gap: 2px;
2157
+ }
2158
+ .v-radio-card__label {
2159
+ font-weight: 600;
2160
+ font-size: 13.5px;
2161
+ }
2162
+ .v-radio-card__desc {
2163
+ font-size: 12.5px;
2164
+ color: var(--v-text-muted);
2165
+ }
2166
+
2167
+ @media (max-width: 860px) {
2168
+ .v-shell {
2169
+ grid-template-columns: 1fr;
2170
+ }
2171
+ .v-shell {
2172
+ display: flex;
2173
+ flex-direction: column;
2174
+ overflow-y: auto;
2175
+ }
2176
+ .v-sidebar {
2177
+ flex: none;
2178
+ overflow: visible;
2179
+ border-bottom: 1px solid var(--v-border);
2180
+ }
2181
+ .v-main {
2182
+ flex: none;
2183
+ overflow: visible;
2184
+ }
2185
+ .v-main {
2186
+ margin: 0;
2187
+ border-radius: 0;
2188
+ border: 0;
2189
+ }
2190
+ .v-chrome__chip span {
2191
+ display: none;
2192
+ }
2193
+ .v-page {
2194
+ padding: 18px 16px;
2195
+ }
2196
+ .v-topbar {
2197
+ padding: 0 16px;
2198
+ }
2199
+ }
2200
+
2201
+ /* ------------------------------------------------------ image tools */
2202
+ .v-image-tool {
2203
+ border: 1px solid var(--v-border-strong);
2204
+ border-radius: var(--v-radius-sm);
2205
+ background: var(--v-surface-muted);
2206
+ overflow: hidden;
2207
+ }
2208
+ .v-image-tool__stage {
2209
+ padding: 12px;
2210
+ display: flex;
2211
+ justify-content: center;
2212
+ background: repeating-conic-gradient(#eceef2 0 25%, #f9fafb 0 50%) 0 0 / 20px
2213
+ 20px;
2214
+ }
2215
+ .v-image-tool__loading,
2216
+ .v-image-tool__empty {
2217
+ padding: 24px;
2218
+ color: var(--v-text-muted);
2219
+ font-size: 13px;
2220
+ text-align: center;
2221
+ }
2222
+ .v-image-tool__empty {
2223
+ border: 1px dashed var(--v-border-strong);
2224
+ border-radius: var(--v-radius-sm);
2225
+ }
2226
+ .v-image-tool__bar {
2227
+ display: flex;
2228
+ align-items: center;
2229
+ gap: 12px;
2230
+ padding: 8px 12px;
2231
+ border-top: 1px solid var(--v-border);
2232
+ background: var(--v-surface);
2233
+ font-size: 12px;
2234
+ flex-wrap: wrap;
2235
+ }
2236
+ .v-crop,
2237
+ .v-focal {
2238
+ position: relative;
2239
+ user-select: none;
2240
+ touch-action: none;
2241
+ box-shadow: var(--v-shadow);
2242
+ }
2243
+ .v-crop img,
2244
+ .v-focal img {
2245
+ display: block;
2246
+ }
2247
+ .v-crop__shade {
2248
+ position: absolute;
2249
+ background: rgba(17, 24, 39, 0.55);
2250
+ pointer-events: none;
2251
+ }
2252
+ .v-crop__box {
2253
+ position: absolute;
2254
+ box-sizing: border-box;
2255
+ border: 1.5px solid #fff;
2256
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
2257
+ cursor: move;
2258
+ outline: none;
2259
+ }
2260
+ .v-crop__box:focus-visible {
2261
+ border-color: var(--v-primary);
2262
+ }
2263
+ .v-crop__grid {
2264
+ position: absolute;
2265
+ inset: 0;
2266
+ pointer-events: none;
2267
+ background-image:
2268
+ linear-gradient(rgba(255, 255, 255, 0.45) 1px, transparent 1px),
2269
+ linear-gradient(90deg, rgba(255, 255, 255, 0.45) 1px, transparent 1px);
2270
+ background-size: 33.333% 33.333%;
2271
+ background-position: -1px -1px;
2272
+ }
2273
+ .v-crop__handle {
2274
+ position: absolute;
2275
+ width: 12px;
2276
+ height: 12px;
2277
+ background: #fff;
2278
+ border: 1px solid rgba(0, 0, 0, 0.4);
2279
+ border-radius: 3px;
2280
+ transform: translate(-50%, -50%);
2281
+ }
2282
+ .v-crop__handle--n {
2283
+ left: 50%;
2284
+ top: 0;
2285
+ cursor: ns-resize;
2286
+ }
2287
+ .v-crop__handle--s {
2288
+ left: 50%;
2289
+ top: 100%;
2290
+ cursor: ns-resize;
2291
+ }
2292
+ .v-crop__handle--e {
2293
+ left: 100%;
2294
+ top: 50%;
2295
+ cursor: ew-resize;
2296
+ }
2297
+ .v-crop__handle--w {
2298
+ left: 0;
2299
+ top: 50%;
2300
+ cursor: ew-resize;
2301
+ }
2302
+ .v-crop__handle--nw {
2303
+ left: 0;
2304
+ top: 0;
2305
+ cursor: nwse-resize;
2306
+ }
2307
+ .v-crop__handle--se {
2308
+ left: 100%;
2309
+ top: 100%;
2310
+ cursor: nwse-resize;
2311
+ }
2312
+ .v-crop__handle--ne {
2313
+ left: 100%;
2314
+ top: 0;
2315
+ cursor: nesw-resize;
2316
+ }
2317
+ .v-crop__handle--sw {
2318
+ left: 0;
2319
+ top: 100%;
2320
+ cursor: nesw-resize;
2321
+ }
2322
+ .v-focal {
2323
+ cursor: crosshair;
2324
+ }
2325
+ .v-focal__line {
2326
+ position: absolute;
2327
+ background: rgba(255, 255, 255, 0.6);
2328
+ pointer-events: none;
2329
+ }
2330
+ .v-focal__line--v {
2331
+ top: 0;
2332
+ bottom: 0;
2333
+ width: 1px;
2334
+ }
2335
+ .v-focal__line--h {
2336
+ left: 0;
2337
+ right: 0;
2338
+ height: 1px;
2339
+ }
2340
+ .v-focal__marker {
2341
+ position: absolute;
2342
+ width: 28px;
2343
+ height: 28px;
2344
+ margin: -14px 0 0 -14px;
2345
+ border-radius: 50%;
2346
+ background: rgba(37, 99, 235, 0.85);
2347
+ border: 2px solid #fff;
2348
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
2349
+ display: grid;
2350
+ place-items: center;
2351
+ color: #fff;
2352
+ pointer-events: none;
2353
+ }
2354
+ .v-focal__marker svg {
2355
+ width: 14px;
2356
+ height: 14px;
2357
+ }
2358
+ .v-focal__previews {
2359
+ display: flex;
2360
+ gap: 6px;
2361
+ }
2362
+ .v-focal__preview {
2363
+ height: 40px;
2364
+ border-radius: 3px;
2365
+ overflow: hidden;
2366
+ border: 1px solid var(--v-border);
2367
+ background: #e5e7eb;
2368
+ }
2369
+ .v-focal__preview img {
2370
+ width: 100%;
2371
+ height: 100%;
2372
+ object-fit: cover;
2373
+ display: block;
2374
+ }
2375
+
2376
+ /* Sortable list rows (dnd-kit): the dragged row stays as a dimmed placeholder
2377
+ while a clone floats under the cursor. */
2378
+ .v-list__item.is-placeholder,
2379
+ .v-list__row.is-placeholder {
2380
+ /* Keep the row's space (the gap) but show nothing in it. */
2381
+ visibility: hidden;
2382
+ }
2383
+ .v-list__ghost {
2384
+ /* The floating preview carries the focus ring the real row would show. */
2385
+ border-color: var(--v-primary);
2386
+ box-shadow:
2387
+ 0 0 0 3px var(--v-primary-ring),
2388
+ var(--v-shadow-lg);
2389
+ cursor: grabbing;
2390
+ pointer-events: none;
2391
+ }
2392
+ .v-list__ghost.v-list__item > .v-list__header {
2393
+ border-radius: var(--v-radius-sm);
2394
+ }
2395
+ .v-list__handle {
2396
+ cursor: grab;
2397
+ touch-action: none;
2398
+ }
2399
+ .v-list__handle:focus-visible {
2400
+ outline: 2px solid var(--v-primary);
2401
+ outline-offset: 2px;
2402
+ border-radius: 3px;
2403
+ }
2404
+
2405
+ /* ------------------------------------------------------------ tree field */
2406
+ .v-tree {
2407
+ display: flex;
2408
+ flex-direction: column;
2409
+ gap: 6px;
2410
+ }
2411
+ .v-tree > .v-list__add {
2412
+ align-self: flex-start;
2413
+ }
2414
+ .v-tree__empty {
2415
+ padding: 14px;
2416
+ color: var(--v-text-muted);
2417
+ font-size: 13px;
2418
+ border: 1px dashed var(--v-border-strong);
2419
+ border-radius: var(--v-radius-sm);
2420
+ }
2421
+ .v-tree__row {
2422
+ box-sizing: border-box;
2423
+ }
2424
+ .v-tree__item {
2425
+ display: flex;
2426
+ align-items: center;
2427
+ gap: 6px;
2428
+ min-height: 40px;
2429
+ padding: 4px 6px 4px 8px;
2430
+ border: 1px solid var(--v-border-strong);
2431
+ border-radius: var(--v-radius-sm);
2432
+ background: var(--v-surface);
2433
+ box-shadow: var(--v-shadow-sm);
2434
+ }
2435
+ .v-tree__row.is-editing > .v-tree__item {
2436
+ border-bottom-left-radius: 0;
2437
+ border-bottom-right-radius: 0;
2438
+ border-color: var(--v-primary);
2439
+ }
2440
+ .v-tree__row.is-placeholder > .v-tree__item {
2441
+ visibility: hidden;
2442
+ }
2443
+ .v-tree__row.is-placeholder {
2444
+ position: relative;
2445
+ }
2446
+ .v-tree__row.is-placeholder::after {
2447
+ /* Dashed slot showing where (and how deep) the node will land. */
2448
+ content: "";
2449
+ position: absolute;
2450
+ inset: 0 0 0 var(--v-tree-indent, 0px);
2451
+ border: 2px dashed var(--v-primary);
2452
+ border-radius: var(--v-radius-sm);
2453
+ background: var(--v-primary-soft);
2454
+ opacity: 0.8;
2455
+ }
2456
+ .v-tree__toggle {
2457
+ width: 22px;
2458
+ height: 22px;
2459
+ border: 0;
2460
+ background: none;
2461
+ border-radius: 4px;
2462
+ display: grid;
2463
+ place-items: center;
2464
+ color: var(--v-text-muted);
2465
+ cursor: pointer;
2466
+ flex: none;
2467
+ padding: 0;
2468
+ }
2469
+ .v-tree__toggle svg {
2470
+ width: 15px;
2471
+ height: 15px;
2472
+ }
2473
+ button.v-tree__toggle:hover {
2474
+ background: #eef0f4;
2475
+ }
2476
+ .v-tree__toggle--leaf {
2477
+ /* Spacer that keeps leaf labels aligned with expandable rows. */
2478
+ cursor: default;
2479
+ pointer-events: none;
2480
+ }
2481
+ .v-tree__text {
2482
+ flex: 1;
2483
+ min-width: 0;
2484
+ display: flex;
2485
+ align-items: baseline;
2486
+ gap: 10px;
2487
+ border: 0;
2488
+ background: none;
2489
+ padding: 4px 2px;
2490
+ text-align: left;
2491
+ cursor: pointer;
2492
+ border-radius: 4px;
2493
+ }
2494
+ .v-tree__text:hover .v-tree__label {
2495
+ color: var(--v-primary);
2496
+ }
2497
+ .v-tree__label {
2498
+ font-weight: 600;
2499
+ font-size: 13.5px;
2500
+ white-space: nowrap;
2501
+ overflow: hidden;
2502
+ text-overflow: ellipsis;
2503
+ }
2504
+ .v-tree__detail {
2505
+ color: var(--v-text-muted);
2506
+ font-size: 12.5px;
2507
+ font-family: var(--v-mono);
2508
+ white-space: nowrap;
2509
+ overflow: hidden;
2510
+ text-overflow: ellipsis;
2511
+ }
2512
+ .v-tree__count {
2513
+ font-size: 11px;
2514
+ font-weight: 600;
2515
+ color: var(--v-text-muted);
2516
+ background: #eef0f4;
2517
+ border-radius: 999px;
2518
+ padding: 1px 7px;
2519
+ }
2520
+ .v-tree__form {
2521
+ padding: 14px;
2522
+ border: 1px solid var(--v-primary);
2523
+ border-top: 0;
2524
+ border-radius: 0 0 var(--v-radius-sm) var(--v-radius-sm);
2525
+ background: var(--v-surface-muted);
2526
+ }
2527
+ .v-tree__ghost {
2528
+ box-shadow:
2529
+ 0 0 0 3px var(--v-primary-ring),
2530
+ var(--v-shadow-lg);
2531
+ border-color: var(--v-primary);
2532
+ pointer-events: none;
2533
+ width: max-content;
2534
+ max-width: 420px;
2535
+ cursor: grabbing;
2536
+ }