vestrycms 0.0.0-stage → 0.2.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 (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +383 -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 +412 -0
  8. package/dist/config/types.js +10 -0
  9. package/dist/core/api.d.ts +67 -0
  10. package/dist/core/api.js +543 -0
  11. package/dist/core/auth.d.ts +1597 -0
  12. package/dist/core/auth.js +264 -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 +401 -0
  56. package/dist/mdx/serialize.d.ts +9 -0
  57. package/dist/mdx/serialize.js +485 -0
  58. package/dist/mdx/types.d.ts +42 -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 +230 -0
  81. package/dist/ui/components/editor/extensions.d.ts +30 -0
  82. package/dist/ui/components/editor/extensions.js +248 -0
  83. package/dist/ui/components/editor/schema.d.ts +12 -0
  84. package/dist/ui/components/editor/schema.js +37 -0
  85. package/dist/ui/components/fields/CodeInput.d.ts +10 -0
  86. package/dist/ui/components/fields/CodeInput.js +8 -0
  87. package/dist/ui/components/fields/CodeMirrorInput.d.ts +2 -0
  88. package/dist/ui/components/fields/CodeMirrorInput.js +44 -0
  89. package/dist/ui/components/fields/FieldRenderer.d.ts +41 -0
  90. package/dist/ui/components/fields/FieldRenderer.js +299 -0
  91. package/dist/ui/components/fields/ImageTools.d.ts +31 -0
  92. package/dist/ui/components/fields/ImageTools.js +296 -0
  93. package/dist/ui/components/fields/MarkdownField.d.ts +7 -0
  94. package/dist/ui/components/fields/MarkdownField.js +16 -0
  95. package/dist/ui/components/fields/TreeInput.d.ts +13 -0
  96. package/dist/ui/components/fields/TreeInput.js +221 -0
  97. package/dist/ui/components/ui.d.ts +63 -0
  98. package/dist/ui/components/ui.js +113 -0
  99. package/dist/ui/context.d.ts +17 -0
  100. package/dist/ui/context.js +13 -0
  101. package/dist/ui/lib/format.d.ts +5 -0
  102. package/dist/ui/lib/format.js +11 -0
  103. package/dist/ui/lib/utils.d.ts +24 -0
  104. package/dist/ui/lib/utils.js +255 -0
  105. package/dist/ui/main.d.ts +1 -0
  106. package/dist/ui/main.js +11 -0
  107. package/dist/ui/pages/Assets.d.ts +1 -0
  108. package/dist/ui/pages/Assets.js +69 -0
  109. package/dist/ui/pages/CollectionList.d.ts +6 -0
  110. package/dist/ui/pages/CollectionList.js +115 -0
  111. package/dist/ui/pages/Dashboard.d.ts +1 -0
  112. package/dist/ui/pages/Dashboard.js +57 -0
  113. package/dist/ui/pages/EntryEditor.d.ts +3 -0
  114. package/dist/ui/pages/EntryEditor.js +313 -0
  115. package/dist/ui/pages/Login.d.ts +6 -0
  116. package/dist/ui/pages/Login.js +88 -0
  117. package/dist/ui/pages/Users.d.ts +1 -0
  118. package/dist/ui/pages/Users.js +102 -0
  119. package/dist/ui/styles.css +2554 -0
  120. package/package.json +123 -3
@@ -0,0 +1,2554 @@
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
+ .v-footnote-reference {
1700
+ font-family: var(--v-mono);
1701
+ font-size: 0.7em;
1702
+ color: #4338ca;
1703
+ }
1704
+ .v-footnote-definition {
1705
+ position: relative;
1706
+ margin: 0.75em 0;
1707
+ padding-left: 1em;
1708
+ border-left: 2px solid #c7d2fe;
1709
+ }
1710
+ .v-footnote-definition::before {
1711
+ content: attr(data-label);
1712
+ display: block;
1713
+ font-family: var(--v-mono);
1714
+ font-size: 0.75em;
1715
+ color: #4338ca;
1716
+ }
1717
+
1718
+ /* source editor */
1719
+ .v-source-editor .cm-editor {
1720
+ font-size: 13px;
1721
+ border-radius: 0 0 var(--v-radius-sm) var(--v-radius-sm);
1722
+ min-height: 320px;
1723
+ }
1724
+ .v-source-editor .cm-editor.cm-focused {
1725
+ outline: none;
1726
+ }
1727
+ .v-source-editor .cm-scroller {
1728
+ font-family: var(--v-mono);
1729
+ line-height: 1.55;
1730
+ }
1731
+
1732
+ /* -------------------------------------------------------------- assets */
1733
+ .v-asset-grid {
1734
+ display: grid;
1735
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1736
+ gap: 14px;
1737
+ }
1738
+ .v-asset {
1739
+ border: 1px solid var(--v-border);
1740
+ border-radius: var(--v-radius-sm);
1741
+ background: var(--v-surface);
1742
+ overflow: hidden;
1743
+ cursor: pointer;
1744
+ text-align: left;
1745
+ padding: 0;
1746
+ display: flex;
1747
+ flex-direction: column;
1748
+ transition:
1749
+ box-shadow 0.12s,
1750
+ border-color 0.12s;
1751
+ }
1752
+ .v-asset:hover {
1753
+ box-shadow: var(--v-shadow);
1754
+ border-color: var(--v-border-strong);
1755
+ }
1756
+ .v-asset.is-selected {
1757
+ border-color: var(--v-primary);
1758
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
1759
+ }
1760
+ .v-asset__preview {
1761
+ position: relative;
1762
+ aspect-ratio: 4 / 3;
1763
+ background: repeating-conic-gradient(#f3f4f6 0 25%, #fff 0 50%) 0 0 / 16px
1764
+ 16px;
1765
+ display: grid;
1766
+ place-items: center;
1767
+ overflow: hidden;
1768
+ }
1769
+ /* Absolutely positioned so the image always fills the box (a percentage
1770
+ height inside an aspect-ratio grid cell is unreliable). Contained rather
1771
+ than cropped: the grid is a picker, and seeing an image's real
1772
+ proportions is what decides whether it fits a slot. */
1773
+ .v-asset__preview img {
1774
+ position: absolute;
1775
+ inset: 0;
1776
+ width: 100%;
1777
+ height: 100%;
1778
+ object-fit: contain;
1779
+ object-position: center;
1780
+ }
1781
+ .v-asset__preview svg {
1782
+ width: 28px;
1783
+ height: 28px;
1784
+ color: var(--v-text-faint);
1785
+ }
1786
+ .v-asset__name {
1787
+ padding: 8px 10px;
1788
+ font-size: 12.5px;
1789
+ font-weight: 500;
1790
+ white-space: nowrap;
1791
+ overflow: hidden;
1792
+ text-overflow: ellipsis;
1793
+ }
1794
+ .v-asset__meta {
1795
+ padding: 0 10px 8px;
1796
+ font-size: 11.5px;
1797
+ color: var(--v-text-muted);
1798
+ }
1799
+ .v-folder {
1800
+ display: flex;
1801
+ align-items: center;
1802
+ gap: 8px;
1803
+ padding: 10px 12px;
1804
+ border: 1px solid var(--v-border);
1805
+ border-radius: var(--v-radius-sm);
1806
+ background: var(--v-surface);
1807
+ font-weight: 500;
1808
+ cursor: pointer;
1809
+ text-align: left;
1810
+ }
1811
+ .v-folder svg {
1812
+ width: 16px;
1813
+ height: 16px;
1814
+ color: var(--v-warning);
1815
+ }
1816
+ .v-dropzone {
1817
+ border: 2px dashed var(--v-border-strong);
1818
+ border-radius: var(--v-radius);
1819
+ padding: 24px;
1820
+ text-align: center;
1821
+ color: var(--v-text-muted);
1822
+ margin-bottom: 16px;
1823
+ transition:
1824
+ border-color 0.12s,
1825
+ background 0.12s;
1826
+ }
1827
+ .v-dropzone.is-over {
1828
+ border-color: var(--v-primary);
1829
+ background: var(--v-primary-soft);
1830
+ }
1831
+ .v-breadcrumbs {
1832
+ display: flex;
1833
+ align-items: center;
1834
+ gap: 6px;
1835
+ font-size: 13px;
1836
+ color: var(--v-text-muted);
1837
+ }
1838
+ .v-breadcrumbs button {
1839
+ background: none;
1840
+ border: 0;
1841
+ padding: 0;
1842
+ color: var(--v-primary);
1843
+ cursor: pointer;
1844
+ font: inherit;
1845
+ }
1846
+
1847
+ /* image field */
1848
+ .v-image-field {
1849
+ display: flex;
1850
+ gap: 14px;
1851
+ align-items: flex-start;
1852
+ }
1853
+ .v-image-field__preview {
1854
+ width: 140px;
1855
+ height: 100px;
1856
+ border-radius: var(--v-radius-sm);
1857
+ border: 1px solid var(--v-border);
1858
+ background: repeating-conic-gradient(#f3f4f6 0 25%, #fff 0 50%) 0 0 / 16px
1859
+ 16px;
1860
+ display: grid;
1861
+ place-items: center;
1862
+ overflow: hidden;
1863
+ flex: none;
1864
+ }
1865
+ .v-image-field__preview img {
1866
+ width: 100%;
1867
+ height: 100%;
1868
+ object-fit: contain;
1869
+ }
1870
+ .v-image-field__preview svg {
1871
+ width: 24px;
1872
+ height: 24px;
1873
+ color: var(--v-text-faint);
1874
+ }
1875
+ .v-image-field__info {
1876
+ flex: 1;
1877
+ min-width: 0;
1878
+ }
1879
+ .v-image-field__actions {
1880
+ display: flex;
1881
+ flex-wrap: wrap;
1882
+ gap: 6px 8px;
1883
+ }
1884
+ .v-editor-sidebar .v-image-field__preview {
1885
+ width: 96px;
1886
+ height: 72px;
1887
+ }
1888
+ .v-image-field__path {
1889
+ font-family: var(--v-mono);
1890
+ font-size: 12px;
1891
+ color: var(--v-text-muted);
1892
+ word-break: break-all;
1893
+ margin-bottom: 8px;
1894
+ }
1895
+
1896
+ /* ------------------------------------------------------------ dashboard */
1897
+ .v-stats {
1898
+ display: flex;
1899
+ flex-wrap: wrap;
1900
+ gap: 12px;
1901
+ }
1902
+ .v-stat {
1903
+ display: flex;
1904
+ align-items: center;
1905
+ gap: 12px;
1906
+ padding: 12px 16px 12px 12px;
1907
+ min-width: 200px;
1908
+ text-decoration: none !important;
1909
+ color: inherit;
1910
+ transition:
1911
+ box-shadow 0.12s,
1912
+ transform 0.12s;
1913
+ }
1914
+ .v-stat:hover {
1915
+ box-shadow: var(--v-shadow);
1916
+ transform: translateY(-1px);
1917
+ }
1918
+ .v-stat__icon {
1919
+ width: 34px;
1920
+ height: 34px;
1921
+ border-radius: 8px;
1922
+ background: var(--v-primary-soft);
1923
+ color: var(--v-primary);
1924
+ display: grid;
1925
+ place-items: center;
1926
+ flex: none;
1927
+ }
1928
+ .v-stat__icon svg {
1929
+ width: 17px;
1930
+ height: 17px;
1931
+ }
1932
+ .v-stat__body {
1933
+ display: flex;
1934
+ flex-direction: column;
1935
+ line-height: 1.3;
1936
+ }
1937
+ .v-stat__label {
1938
+ font-weight: 600;
1939
+ font-size: 14px;
1940
+ }
1941
+ .v-stat__meta {
1942
+ color: var(--v-text-muted);
1943
+ font-size: 12.5px;
1944
+ }
1945
+ #vestry-root a:where(.v-stat) {
1946
+ color: inherit;
1947
+ }
1948
+ .v-dash-grid {
1949
+ display: grid;
1950
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
1951
+ gap: 16px;
1952
+ }
1953
+ .v-dash-card {
1954
+ display: block;
1955
+ text-decoration: none !important;
1956
+ color: inherit;
1957
+ padding: 18px;
1958
+ transition:
1959
+ box-shadow 0.12s,
1960
+ transform 0.12s;
1961
+ }
1962
+ .v-dash-card:hover {
1963
+ box-shadow: var(--v-shadow);
1964
+ transform: translateY(-1px);
1965
+ }
1966
+ .v-dash-card__icon {
1967
+ width: 36px;
1968
+ height: 36px;
1969
+ border-radius: 8px;
1970
+ background: var(--v-primary-soft);
1971
+ color: var(--v-primary);
1972
+ display: grid;
1973
+ place-items: center;
1974
+ margin-bottom: 12px;
1975
+ }
1976
+ .v-dash-card__icon svg {
1977
+ width: 18px;
1978
+ height: 18px;
1979
+ }
1980
+ .v-dash-card h3 {
1981
+ margin: 0 0 2px;
1982
+ font-size: 15px;
1983
+ }
1984
+ .v-dash-card p {
1985
+ margin: 0;
1986
+ color: var(--v-text-muted);
1987
+ font-size: 13px;
1988
+ }
1989
+ .v-section {
1990
+ margin-top: 32px;
1991
+ }
1992
+ .v-section:first-child {
1993
+ margin-top: 0;
1994
+ }
1995
+ .v-section-title {
1996
+ font-size: 13px;
1997
+ text-transform: uppercase;
1998
+ letter-spacing: 0.06em;
1999
+ color: var(--v-text-muted);
2000
+ font-weight: 600;
2001
+ margin: 0 0 12px;
2002
+ }
2003
+
2004
+ /* ---------------------------------------------------------------- login */
2005
+ .v-login {
2006
+ min-height: 100vh;
2007
+ display: grid;
2008
+ place-items: center;
2009
+ padding: 24px;
2010
+ background:
2011
+ radial-gradient(1200px 600px at 20% -10%, #e0e7ff 0%, transparent 60%),
2012
+ radial-gradient(900px 500px at 100% 100%, #dbeafe 0%, transparent 55%),
2013
+ var(--v-bg);
2014
+ }
2015
+ .v-login__card {
2016
+ width: 100%;
2017
+ max-width: 400px;
2018
+ padding: 36px 32px;
2019
+ text-align: center;
2020
+ }
2021
+ .v-login__mark {
2022
+ width: 48px;
2023
+ height: 48px;
2024
+ border-radius: 12px;
2025
+ background: #1e293b;
2026
+ color: #fff;
2027
+ display: grid;
2028
+ place-items: center;
2029
+ font-weight: 700;
2030
+ font-size: 22px;
2031
+ margin: 0 auto 16px;
2032
+ }
2033
+ .v-login h1 {
2034
+ font-size: 20px;
2035
+ margin: 0 0 6px;
2036
+ }
2037
+ .v-login p {
2038
+ color: var(--v-text-muted);
2039
+ margin: 0 0 22px;
2040
+ }
2041
+ .v-login .v-btn {
2042
+ width: 100%;
2043
+ height: 40px;
2044
+ }
2045
+ .v-login__logo {
2046
+ display: inline-grid;
2047
+ place-items: center;
2048
+ background: #1e293b;
2049
+ border-radius: 12px;
2050
+ padding: 12px 18px;
2051
+ margin: 0 auto 16px;
2052
+ }
2053
+ .v-login__logo img {
2054
+ height: 36px;
2055
+ width: auto;
2056
+ display: block;
2057
+ }
2058
+ .v-login__form {
2059
+ display: flex;
2060
+ flex-direction: column;
2061
+ gap: 14px;
2062
+ text-align: left;
2063
+ }
2064
+ .v-login__form p {
2065
+ margin: 0;
2066
+ text-align: center;
2067
+ }
2068
+ .v-login__field {
2069
+ display: block;
2070
+ }
2071
+ .v-login__code {
2072
+ font-family: var(--v-mono);
2073
+ font-size: 22px;
2074
+ letter-spacing: 0.3em;
2075
+ text-align: center;
2076
+ padding: 10px 12px;
2077
+ }
2078
+ .v-login__links {
2079
+ display: flex;
2080
+ justify-content: space-between;
2081
+ gap: 12px;
2082
+ font-size: 13px;
2083
+ }
2084
+ .v-link {
2085
+ background: none;
2086
+ border: 0;
2087
+ padding: 0;
2088
+ font: inherit;
2089
+ font-size: 13px;
2090
+ color: var(--v-primary);
2091
+ cursor: pointer;
2092
+ display: inline-flex;
2093
+ align-items: center;
2094
+ gap: 4px;
2095
+ }
2096
+ .v-link:hover {
2097
+ text-decoration: underline;
2098
+ }
2099
+ .v-link:disabled {
2100
+ opacity: 0.5;
2101
+ cursor: default;
2102
+ }
2103
+
2104
+ /* ---------------------------------------------------------------- users */
2105
+ .v-menu__sub {
2106
+ display: block;
2107
+ text-transform: none;
2108
+ letter-spacing: 0;
2109
+ font-weight: 400;
2110
+ font-size: 12px;
2111
+ color: var(--v-text-muted);
2112
+ margin-top: 2px;
2113
+ }
2114
+ .v-menu__item--danger {
2115
+ color: var(--v-danger);
2116
+ }
2117
+ .v-user {
2118
+ display: flex;
2119
+ align-items: center;
2120
+ gap: 12px;
2121
+ min-width: 0;
2122
+ }
2123
+ .v-user__text {
2124
+ display: flex;
2125
+ flex-direction: column;
2126
+ min-width: 0;
2127
+ }
2128
+ .v-avatar {
2129
+ width: 32px;
2130
+ height: 32px;
2131
+ border-radius: 50%;
2132
+ background: var(--v-primary-soft);
2133
+ color: var(--v-primary);
2134
+ font-size: 12px;
2135
+ font-weight: 700;
2136
+ display: grid;
2137
+ place-items: center;
2138
+ flex: none;
2139
+ }
2140
+ .v-chrome__avatar {
2141
+ font-size: 12px;
2142
+ font-weight: 700;
2143
+ letter-spacing: 0.02em;
2144
+ }
2145
+ .v-radio-cards {
2146
+ display: grid;
2147
+ gap: 8px;
2148
+ }
2149
+ .v-radio-card {
2150
+ display: flex;
2151
+ gap: 10px;
2152
+ align-items: flex-start;
2153
+ padding: 10px 12px;
2154
+ border: 1px solid var(--v-border-strong);
2155
+ border-radius: var(--v-radius-sm);
2156
+ background: var(--v-surface);
2157
+ cursor: pointer;
2158
+ }
2159
+ .v-radio-card input {
2160
+ margin: 3px 0 0;
2161
+ accent-color: var(--v-primary);
2162
+ }
2163
+ .v-radio-card.is-selected {
2164
+ border-color: var(--v-primary);
2165
+ box-shadow: 0 0 0 3px var(--v-primary-ring);
2166
+ }
2167
+ .v-radio-card.is-disabled {
2168
+ opacity: 0.6;
2169
+ cursor: default;
2170
+ }
2171
+ .v-radio-card__body {
2172
+ display: flex;
2173
+ flex-direction: column;
2174
+ gap: 2px;
2175
+ }
2176
+ .v-radio-card__label {
2177
+ font-weight: 600;
2178
+ font-size: 13.5px;
2179
+ }
2180
+ .v-radio-card__desc {
2181
+ font-size: 12.5px;
2182
+ color: var(--v-text-muted);
2183
+ }
2184
+
2185
+ @media (max-width: 860px) {
2186
+ .v-shell {
2187
+ grid-template-columns: 1fr;
2188
+ }
2189
+ .v-shell {
2190
+ display: flex;
2191
+ flex-direction: column;
2192
+ overflow-y: auto;
2193
+ }
2194
+ .v-sidebar {
2195
+ flex: none;
2196
+ overflow: visible;
2197
+ border-bottom: 1px solid var(--v-border);
2198
+ }
2199
+ .v-main {
2200
+ flex: none;
2201
+ overflow: visible;
2202
+ }
2203
+ .v-main {
2204
+ margin: 0;
2205
+ border-radius: 0;
2206
+ border: 0;
2207
+ }
2208
+ .v-chrome__chip span {
2209
+ display: none;
2210
+ }
2211
+ .v-page {
2212
+ padding: 18px 16px;
2213
+ }
2214
+ .v-topbar {
2215
+ padding: 0 16px;
2216
+ }
2217
+ }
2218
+
2219
+ /* ------------------------------------------------------ image tools */
2220
+ .v-image-tool {
2221
+ border: 1px solid var(--v-border-strong);
2222
+ border-radius: var(--v-radius-sm);
2223
+ background: var(--v-surface-muted);
2224
+ overflow: hidden;
2225
+ }
2226
+ .v-image-tool__stage {
2227
+ padding: 12px;
2228
+ display: flex;
2229
+ justify-content: center;
2230
+ background: repeating-conic-gradient(#eceef2 0 25%, #f9fafb 0 50%) 0 0 / 20px
2231
+ 20px;
2232
+ }
2233
+ .v-image-tool__loading,
2234
+ .v-image-tool__empty {
2235
+ padding: 24px;
2236
+ color: var(--v-text-muted);
2237
+ font-size: 13px;
2238
+ text-align: center;
2239
+ }
2240
+ .v-image-tool__empty {
2241
+ border: 1px dashed var(--v-border-strong);
2242
+ border-radius: var(--v-radius-sm);
2243
+ }
2244
+ .v-image-tool__bar {
2245
+ display: flex;
2246
+ align-items: center;
2247
+ gap: 12px;
2248
+ padding: 8px 12px;
2249
+ border-top: 1px solid var(--v-border);
2250
+ background: var(--v-surface);
2251
+ font-size: 12px;
2252
+ flex-wrap: wrap;
2253
+ }
2254
+ .v-crop,
2255
+ .v-focal {
2256
+ position: relative;
2257
+ user-select: none;
2258
+ touch-action: none;
2259
+ box-shadow: var(--v-shadow);
2260
+ }
2261
+ .v-crop img,
2262
+ .v-focal img {
2263
+ display: block;
2264
+ }
2265
+ .v-crop__shade {
2266
+ position: absolute;
2267
+ background: rgba(17, 24, 39, 0.55);
2268
+ pointer-events: none;
2269
+ }
2270
+ .v-crop__box {
2271
+ position: absolute;
2272
+ box-sizing: border-box;
2273
+ border: 1.5px solid #fff;
2274
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
2275
+ cursor: move;
2276
+ outline: none;
2277
+ }
2278
+ .v-crop__box:focus-visible {
2279
+ border-color: var(--v-primary);
2280
+ }
2281
+ .v-crop__grid {
2282
+ position: absolute;
2283
+ inset: 0;
2284
+ pointer-events: none;
2285
+ background-image:
2286
+ linear-gradient(rgba(255, 255, 255, 0.45) 1px, transparent 1px),
2287
+ linear-gradient(90deg, rgba(255, 255, 255, 0.45) 1px, transparent 1px);
2288
+ background-size: 33.333% 33.333%;
2289
+ background-position: -1px -1px;
2290
+ }
2291
+ .v-crop__handle {
2292
+ position: absolute;
2293
+ width: 12px;
2294
+ height: 12px;
2295
+ background: #fff;
2296
+ border: 1px solid rgba(0, 0, 0, 0.4);
2297
+ border-radius: 3px;
2298
+ transform: translate(-50%, -50%);
2299
+ }
2300
+ .v-crop__handle--n {
2301
+ left: 50%;
2302
+ top: 0;
2303
+ cursor: ns-resize;
2304
+ }
2305
+ .v-crop__handle--s {
2306
+ left: 50%;
2307
+ top: 100%;
2308
+ cursor: ns-resize;
2309
+ }
2310
+ .v-crop__handle--e {
2311
+ left: 100%;
2312
+ top: 50%;
2313
+ cursor: ew-resize;
2314
+ }
2315
+ .v-crop__handle--w {
2316
+ left: 0;
2317
+ top: 50%;
2318
+ cursor: ew-resize;
2319
+ }
2320
+ .v-crop__handle--nw {
2321
+ left: 0;
2322
+ top: 0;
2323
+ cursor: nwse-resize;
2324
+ }
2325
+ .v-crop__handle--se {
2326
+ left: 100%;
2327
+ top: 100%;
2328
+ cursor: nwse-resize;
2329
+ }
2330
+ .v-crop__handle--ne {
2331
+ left: 100%;
2332
+ top: 0;
2333
+ cursor: nesw-resize;
2334
+ }
2335
+ .v-crop__handle--sw {
2336
+ left: 0;
2337
+ top: 100%;
2338
+ cursor: nesw-resize;
2339
+ }
2340
+ .v-focal {
2341
+ cursor: crosshair;
2342
+ }
2343
+ .v-focal__line {
2344
+ position: absolute;
2345
+ background: rgba(255, 255, 255, 0.6);
2346
+ pointer-events: none;
2347
+ }
2348
+ .v-focal__line--v {
2349
+ top: 0;
2350
+ bottom: 0;
2351
+ width: 1px;
2352
+ }
2353
+ .v-focal__line--h {
2354
+ left: 0;
2355
+ right: 0;
2356
+ height: 1px;
2357
+ }
2358
+ .v-focal__marker {
2359
+ position: absolute;
2360
+ width: 28px;
2361
+ height: 28px;
2362
+ margin: -14px 0 0 -14px;
2363
+ border-radius: 50%;
2364
+ background: rgba(37, 99, 235, 0.85);
2365
+ border: 2px solid #fff;
2366
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
2367
+ display: grid;
2368
+ place-items: center;
2369
+ color: #fff;
2370
+ pointer-events: none;
2371
+ }
2372
+ .v-focal__marker svg {
2373
+ width: 14px;
2374
+ height: 14px;
2375
+ }
2376
+ .v-focal__previews {
2377
+ display: flex;
2378
+ gap: 6px;
2379
+ }
2380
+ .v-focal__preview {
2381
+ height: 40px;
2382
+ border-radius: 3px;
2383
+ overflow: hidden;
2384
+ border: 1px solid var(--v-border);
2385
+ background: #e5e7eb;
2386
+ }
2387
+ .v-focal__preview img {
2388
+ width: 100%;
2389
+ height: 100%;
2390
+ object-fit: cover;
2391
+ display: block;
2392
+ }
2393
+
2394
+ /* Sortable list rows (dnd-kit): the dragged row stays as a dimmed placeholder
2395
+ while a clone floats under the cursor. */
2396
+ .v-list__item.is-placeholder,
2397
+ .v-list__row.is-placeholder {
2398
+ /* Keep the row's space (the gap) but show nothing in it. */
2399
+ visibility: hidden;
2400
+ }
2401
+ .v-list__ghost {
2402
+ /* The floating preview carries the focus ring the real row would show. */
2403
+ border-color: var(--v-primary);
2404
+ box-shadow:
2405
+ 0 0 0 3px var(--v-primary-ring),
2406
+ var(--v-shadow-lg);
2407
+ cursor: grabbing;
2408
+ pointer-events: none;
2409
+ }
2410
+ .v-list__ghost.v-list__item > .v-list__header {
2411
+ border-radius: var(--v-radius-sm);
2412
+ }
2413
+ .v-list__handle {
2414
+ cursor: grab;
2415
+ touch-action: none;
2416
+ }
2417
+ .v-list__handle:focus-visible {
2418
+ outline: 2px solid var(--v-primary);
2419
+ outline-offset: 2px;
2420
+ border-radius: 3px;
2421
+ }
2422
+
2423
+ /* ------------------------------------------------------------ tree field */
2424
+ .v-tree {
2425
+ display: flex;
2426
+ flex-direction: column;
2427
+ gap: 6px;
2428
+ }
2429
+ .v-tree > .v-list__add {
2430
+ align-self: flex-start;
2431
+ }
2432
+ .v-tree__empty {
2433
+ padding: 14px;
2434
+ color: var(--v-text-muted);
2435
+ font-size: 13px;
2436
+ border: 1px dashed var(--v-border-strong);
2437
+ border-radius: var(--v-radius-sm);
2438
+ }
2439
+ .v-tree__row {
2440
+ box-sizing: border-box;
2441
+ }
2442
+ .v-tree__item {
2443
+ display: flex;
2444
+ align-items: center;
2445
+ gap: 6px;
2446
+ min-height: 40px;
2447
+ padding: 4px 6px 4px 8px;
2448
+ border: 1px solid var(--v-border-strong);
2449
+ border-radius: var(--v-radius-sm);
2450
+ background: var(--v-surface);
2451
+ box-shadow: var(--v-shadow-sm);
2452
+ }
2453
+ .v-tree__row.is-editing > .v-tree__item {
2454
+ border-bottom-left-radius: 0;
2455
+ border-bottom-right-radius: 0;
2456
+ border-color: var(--v-primary);
2457
+ }
2458
+ .v-tree__row.is-placeholder > .v-tree__item {
2459
+ visibility: hidden;
2460
+ }
2461
+ .v-tree__row.is-placeholder {
2462
+ position: relative;
2463
+ }
2464
+ .v-tree__row.is-placeholder::after {
2465
+ /* Dashed slot showing where (and how deep) the node will land. */
2466
+ content: "";
2467
+ position: absolute;
2468
+ inset: 0 0 0 var(--v-tree-indent, 0px);
2469
+ border: 2px dashed var(--v-primary);
2470
+ border-radius: var(--v-radius-sm);
2471
+ background: var(--v-primary-soft);
2472
+ opacity: 0.8;
2473
+ }
2474
+ .v-tree__toggle {
2475
+ width: 22px;
2476
+ height: 22px;
2477
+ border: 0;
2478
+ background: none;
2479
+ border-radius: 4px;
2480
+ display: grid;
2481
+ place-items: center;
2482
+ color: var(--v-text-muted);
2483
+ cursor: pointer;
2484
+ flex: none;
2485
+ padding: 0;
2486
+ }
2487
+ .v-tree__toggle svg {
2488
+ width: 15px;
2489
+ height: 15px;
2490
+ }
2491
+ button.v-tree__toggle:hover {
2492
+ background: #eef0f4;
2493
+ }
2494
+ .v-tree__toggle--leaf {
2495
+ /* Spacer that keeps leaf labels aligned with expandable rows. */
2496
+ cursor: default;
2497
+ pointer-events: none;
2498
+ }
2499
+ .v-tree__text {
2500
+ flex: 1;
2501
+ min-width: 0;
2502
+ display: flex;
2503
+ align-items: baseline;
2504
+ gap: 10px;
2505
+ border: 0;
2506
+ background: none;
2507
+ padding: 4px 2px;
2508
+ text-align: left;
2509
+ cursor: pointer;
2510
+ border-radius: 4px;
2511
+ }
2512
+ .v-tree__text:hover .v-tree__label {
2513
+ color: var(--v-primary);
2514
+ }
2515
+ .v-tree__label {
2516
+ font-weight: 600;
2517
+ font-size: 13.5px;
2518
+ white-space: nowrap;
2519
+ overflow: hidden;
2520
+ text-overflow: ellipsis;
2521
+ }
2522
+ .v-tree__detail {
2523
+ color: var(--v-text-muted);
2524
+ font-size: 12.5px;
2525
+ font-family: var(--v-mono);
2526
+ white-space: nowrap;
2527
+ overflow: hidden;
2528
+ text-overflow: ellipsis;
2529
+ }
2530
+ .v-tree__count {
2531
+ font-size: 11px;
2532
+ font-weight: 600;
2533
+ color: var(--v-text-muted);
2534
+ background: #eef0f4;
2535
+ border-radius: 999px;
2536
+ padding: 1px 7px;
2537
+ }
2538
+ .v-tree__form {
2539
+ padding: 14px;
2540
+ border: 1px solid var(--v-primary);
2541
+ border-top: 0;
2542
+ border-radius: 0 0 var(--v-radius-sm) var(--v-radius-sm);
2543
+ background: var(--v-surface-muted);
2544
+ }
2545
+ .v-tree__ghost {
2546
+ box-shadow:
2547
+ 0 0 0 3px var(--v-primary-ring),
2548
+ var(--v-shadow-lg);
2549
+ border-color: var(--v-primary);
2550
+ pointer-events: none;
2551
+ width: max-content;
2552
+ max-width: 420px;
2553
+ cursor: grabbing;
2554
+ }