stkit 17.0.0__py3-none-any.whl

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 (128) hide show
  1. stk/__init__.py +19 -0
  2. stk/__main__.py +6 -0
  3. stk/agent_login.py +78 -0
  4. stk/app.py +323 -0
  5. stk/checks.py +634 -0
  6. stk/cli/__init__.py +49 -0
  7. stk/cli/agent.py +267 -0
  8. stk/cli/base.py +79 -0
  9. stk/cli/database.py +199 -0
  10. stk/cli/develop.py +63 -0
  11. stk/cli/doctor.py +262 -0
  12. stk/cli/main.py +101 -0
  13. stk/cli/reports.py +296 -0
  14. stk/cli/smoke.py +399 -0
  15. stk/cli/users.py +259 -0
  16. stk/cli/watch.py +68 -0
  17. stk/extensions.py +29 -0
  18. stk/migrations/__init__.py +130 -0
  19. stk/migrations/env.py +58 -0
  20. stk/migrations/script.py.mako +17 -0
  21. stk/migrations/versions/stk_0001_initial_schema.py +213 -0
  22. stk/portal/views.py +34 -0
  23. stk/public/__init__.py +0 -0
  24. stk/public/models.py +0 -0
  25. stk/public/views.py +344 -0
  26. stk/scaffold/__init__.py +1 -0
  27. stk/scaffold/app/CLAUDE.md +65 -0
  28. stk/scaffold/app/Dockerfile +41 -0
  29. stk/scaffold/app/README.md +16 -0
  30. stk/scaffold/app/__app__/__init__.py +1 -0
  31. stk/scaffold/app/__app__/app.py +8 -0
  32. stk/scaffold/app/__app__/settings.py +7 -0
  33. stk/scaffold/app/claude/skills/stk-api/SKILL.md +52 -0
  34. stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +48 -0
  35. stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +61 -0
  36. stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +58 -0
  37. stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +96 -0
  38. stk/scaffold/app/docker-compose.yml +88 -0
  39. stk/scaffold/app/dockerignore +13 -0
  40. stk/scaffold/app/env-sample +78 -0
  41. stk/scaffold/app/gitignore +8 -0
  42. stk/scaffold/app/nginx/nginx.conf +46 -0
  43. stk/scaffold/app/nginx/stk.conf +36 -0
  44. stk/scaffold/app/postgres/init-db.sh +8 -0
  45. stk/scaffold/app/pyproject.toml +23 -0
  46. stk/scaffold/app/tests/__init__.py +0 -0
  47. stk/scaffold/app/tests/test_app.py +23 -0
  48. stk/scaffold/generator.py +209 -0
  49. stk/scaffold/templates.py +437 -0
  50. stk/security.py +145 -0
  51. stk/settings.py +147 -0
  52. stk/shell.py +284 -0
  53. stk/static/VERSIONS.txt +4 -0
  54. stk/static/css/app.css +229 -0
  55. stk/static/css/layout.css +330 -0
  56. stk/static/css/vuetify.min.css +6 -0
  57. stk/static/icons/fonts/tabler-icons.woff2 +0 -0
  58. stk/static/icons/tabler-icons.min.css +4 -0
  59. stk/static/img/auth-bg.webp +0 -0
  60. stk/static/img/favicon.ico +0 -0
  61. stk/static/img/stk.svg +1664 -0
  62. stk/static/js/axios.min.js +5 -0
  63. stk/static/js/components/NotificationDropdown.js +156 -0
  64. stk/static/js/components/ThemeSwitcher.js +68 -0
  65. stk/static/js/components/TransitionExpand.js +64 -0
  66. stk/static/js/components/VerticalNavGroup.js +119 -0
  67. stk/static/js/components/VerticalNavLink.js +36 -0
  68. stk/static/js/components/VerticalNavSectionTitle.js +21 -0
  69. stk/static/js/components/index.js +47 -0
  70. stk/static/js/config.js +150 -0
  71. stk/static/js/vue.min.js +13 -0
  72. stk/static/js/vuetify.min.js +3148 -0
  73. stk/static/robots.txt +1 -0
  74. stk/tasks/__init__.py +78 -0
  75. stk/templates/401.html +14 -0
  76. stk/templates/404.html +12 -0
  77. stk/templates/500.html +13 -0
  78. stk/templates/_brand.html +1 -0
  79. stk/templates/auth_layout.html +26 -0
  80. stk/templates/cms/activities.html +135 -0
  81. stk/templates/cms/roles.html +215 -0
  82. stk/templates/cms/users.html +312 -0
  83. stk/templates/dashboard.html +132 -0
  84. stk/templates/index.html +52 -0
  85. stk/templates/layout.html +328 -0
  86. stk/templates/login-layout.html +54 -0
  87. stk/templates/security/_macros.html +39 -0
  88. stk/templates/security/_messages.html +12 -0
  89. stk/templates/security/change_password.html +100 -0
  90. stk/templates/security/confirm_email.html +44 -0
  91. stk/templates/security/email/change_notice.html +4 -0
  92. stk/templates/security/email/change_notice.txt +5 -0
  93. stk/templates/security/email/confirmation_instructions.html +3 -0
  94. stk/templates/security/email/confirmation_instructions.txt +3 -0
  95. stk/templates/security/email/login_instructions.html +5 -0
  96. stk/templates/security/email/login_instructions.txt +5 -0
  97. stk/templates/security/email/reset_instructions.html +1 -0
  98. stk/templates/security/email/reset_instructions.txt +3 -0
  99. stk/templates/security/email/reset_notice.html +1 -0
  100. stk/templates/security/email/reset_notice.txt +1 -0
  101. stk/templates/security/email/welcome.html +7 -0
  102. stk/templates/security/email/welcome.txt +7 -0
  103. stk/templates/security/forgot_password.html +63 -0
  104. stk/templates/security/login_user.html +206 -0
  105. stk/templates/security/mf_recovery.html +67 -0
  106. stk/templates/security/mf_recovery_codes.html +119 -0
  107. stk/templates/security/register_user.html +263 -0
  108. stk/templates/security/reset_password.html +53 -0
  109. stk/templates/security/send_confirmation.html +48 -0
  110. stk/templates/security/send_login.html +39 -0
  111. stk/templates/security/two_factor_select.html +78 -0
  112. stk/templates/security/two_factor_setup.html +166 -0
  113. stk/templates/security/two_factor_verify_code.html +71 -0
  114. stk/templates/security/wan_register.html +179 -0
  115. stk/templates/security/wan_signin.html +123 -0
  116. stk/templates/security/wan_verify.html +109 -0
  117. stk/user/__init__.py +0 -0
  118. stk/user/forms.py +10 -0
  119. stk/user/models.py +308 -0
  120. stk/user/views.py +339 -0
  121. stk/utils/email.py +32 -0
  122. stk/utils/ratelimit.py +67 -0
  123. stk/websocket.py +133 -0
  124. stkit-17.0.0.dist-info/METADATA +135 -0
  125. stkit-17.0.0.dist-info/RECORD +128 -0
  126. stkit-17.0.0.dist-info/WHEEL +4 -0
  127. stkit-17.0.0.dist-info/entry_points.txt +2 -0
  128. stkit-17.0.0.dist-info/licenses/LICENSE +21 -0
stk/static/css/app.css ADDED
@@ -0,0 +1,229 @@
1
+ /* Native cross-document view transitions for MPA navigation.
2
+ Chrome 126+/Safari 18.2+ animate; Firefox ignores and snaps (graceful). */
3
+ @view-transition {
4
+ navigation: auto;
5
+ }
6
+ @media (prefers-reduced-motion: reduce) {
7
+ @view-transition {
8
+ navigation: none;
9
+ }
10
+ }
11
+
12
+ html, body {
13
+ height: 100%;
14
+ }
15
+
16
+ /* ===== Typography ===== */
17
+ body,
18
+ .v-application {
19
+ font-family: 'Be Vietnam Pro', sans-serif !important;
20
+ }
21
+
22
+ .v-toolbar-title,
23
+ .v-card-title,
24
+ .text-display-large, .text-display-medium, .text-display-small,
25
+ .text-headline-large, .text-headline-medium, .text-headline-small,
26
+ .text-title-large, .text-title-medium, .text-title-small,
27
+ .font-weight-bold,
28
+ .v-list-item-title {
29
+ font-family: 'Plus Jakarta Sans', sans-serif !important;
30
+ letter-spacing: -0.02em !important;
31
+ }
32
+
33
+ .v-toolbar-title {
34
+ text-transform: uppercase;
35
+ font-weight: 800 !important;
36
+ }
37
+
38
+ .v-btn {
39
+ font-family: 'Plus Jakarta Sans', sans-serif !important;
40
+ text-transform: uppercase !important;
41
+ font-weight: 700 !important;
42
+ letter-spacing: 0 !important;
43
+ }
44
+
45
+ .v-data-table th,
46
+ .v-chip,
47
+ .v-label,
48
+ .text-body-small, .text-label-small,
49
+ .v-field__input,
50
+ .v-pagination {
51
+ font-family: 'Inter', sans-serif !important;
52
+ }
53
+
54
+ /* ===== Brutalist Cards ===== */
55
+ .v-card {
56
+ border: 2px solid #1a1a1a !important;
57
+ transition: transform 0.15s ease-out, box-shadow 0.15s ease-out;
58
+ }
59
+
60
+ .v-theme--dark .v-card {
61
+ border-color: rgba(255, 255, 255, 0.15) !important;
62
+ }
63
+
64
+ .v-card > .v-toolbar {
65
+ border-bottom: 2px solid #1a1a1a;
66
+ }
67
+
68
+ .v-theme--dark .v-card > .v-toolbar {
69
+ border-bottom-color: rgba(255, 255, 255, 0.15);
70
+ }
71
+
72
+ /* ===== Brutalist Buttons ===== */
73
+ .v-btn--variant-elevated.v-btn--color-primary,
74
+ .v-btn--variant-elevated.bg-primary {
75
+ border: 2px solid #1a1a1a;
76
+ box-shadow: 3px 3px 0 0 rgba(0,0,0,1) !important;
77
+ }
78
+
79
+ .v-btn--variant-elevated.v-btn--color-primary:hover,
80
+ .v-btn--variant-elevated.bg-primary:hover {
81
+ transform: translate(-1px, -1px);
82
+ box-shadow: 4px 4px 0 0 rgba(0,0,0,1) !important;
83
+ }
84
+
85
+ .v-btn--variant-elevated.v-btn--color-primary:active,
86
+ .v-btn--variant-elevated.bg-primary:active {
87
+ transform: translate(1px, 1px);
88
+ box-shadow: 1px 1px 0 0 rgba(0,0,0,1) !important;
89
+ }
90
+
91
+ .v-theme--dark .v-btn--variant-elevated.v-btn--color-primary,
92
+ .v-theme--dark .v-btn--variant-elevated.bg-primary {
93
+ border-color: rgba(255, 255, 255, 0.3);
94
+ box-shadow: 3px 3px 0 0 rgba(255,255,255,0.15) !important;
95
+ }
96
+
97
+ .v-btn--variant-tonal,
98
+ .v-btn--variant-outlined {
99
+ border: 2px solid #1a1a1a !important;
100
+ box-shadow: 2px 2px 0 0 rgba(0,0,0,1);
101
+ }
102
+
103
+ .v-theme--dark .v-btn--variant-tonal,
104
+ .v-theme--dark .v-btn--variant-outlined {
105
+ border-color: rgba(255, 255, 255, 0.2) !important;
106
+ box-shadow: 2px 2px 0 0 rgba(255,255,255,0.1);
107
+ }
108
+
109
+ /* ===== Inputs ===== */
110
+ .v-field--variant-outlined .v-field__outline {
111
+ --v-field-border-width: 2px;
112
+ }
113
+
114
+ .v-field--focused {
115
+ box-shadow: 3px 3px 0 0 rgba(0, 0, 0, 0.2) !important;
116
+ }
117
+
118
+ .v-field--focused .v-field__outline {
119
+ --v-field-border-opacity: 1;
120
+ }
121
+
122
+ /* ===== Data Table ===== */
123
+ .v-data-table th {
124
+ font-weight: 700 !important;
125
+ text-transform: uppercase;
126
+ font-size: 0.7rem !important;
127
+ letter-spacing: 0.08em;
128
+ border-bottom: 2px solid #1a1a1a !important;
129
+ }
130
+
131
+ .v-theme--dark .v-data-table th {
132
+ border-bottom-color: rgba(255, 255, 255, 0.15) !important;
133
+ }
134
+
135
+ .v-data-table tbody tr:hover {
136
+ background-color: rgba(0, 0, 0, 0.04) !important;
137
+ }
138
+
139
+ /* ===== Chips ===== */
140
+ .v-chip {
141
+ font-weight: 600 !important;
142
+ text-transform: uppercase;
143
+ font-size: 0.7rem !important;
144
+ letter-spacing: 0.04em;
145
+ }
146
+
147
+ /* ===== Dialogs ===== */
148
+ .v-overlay__content > .v-card {
149
+ border: 3px solid #1a1a1a !important;
150
+ box-shadow: 8px 8px 0 0 rgba(0,0,0,1) !important;
151
+ }
152
+
153
+ .v-theme--dark .v-overlay__content > .v-card {
154
+ border-color: rgba(255, 255, 255, 0.2) !important;
155
+ box-shadow: 8px 8px 0 0 rgba(255,255,255,0.08) !important;
156
+ }
157
+
158
+ /* ===== Snackbar ===== */
159
+ .v-snackbar__wrapper {
160
+ border: 2px solid #1a1a1a !important;
161
+ border-radius: 0 !important;
162
+ box-shadow: 4px 4px 0 0 rgba(0,0,0,1) !important;
163
+ }
164
+
165
+ /* ===== App Bar ===== */
166
+ .v-app-bar {
167
+ border-bottom: 3px solid #1a1a1a !important;
168
+ }
169
+
170
+ .v-theme--dark .v-app-bar {
171
+ border-bottom-color: rgba(255, 255, 255, 0.12) !important;
172
+ }
173
+
174
+ /* ===== Navigation Drawer ===== */
175
+ .v-navigation-drawer {
176
+ border-right: 2px solid #1a1a1a !important;
177
+ }
178
+
179
+ .v-theme--dark .v-navigation-drawer {
180
+ border-right-color: rgba(255, 255, 255, 0.1) !important;
181
+ }
182
+
183
+ .nav-header .app-title {
184
+ font-family: 'Plus Jakarta Sans', sans-serif;
185
+ font-weight: 800;
186
+ text-transform: uppercase;
187
+ letter-spacing: -0.02em;
188
+ }
189
+
190
+ .v-navigation-drawer .nav-items .nav-section-title {
191
+ font-family: 'Inter', sans-serif;
192
+ font-size: 0.65rem;
193
+ font-weight: 600;
194
+ text-transform: uppercase;
195
+ letter-spacing: 0.1em;
196
+ }
197
+
198
+ /* ===== Scrollbars ===== */
199
+ ::-webkit-scrollbar {
200
+ width: 6px;
201
+ height: 6px;
202
+ }
203
+
204
+ ::-webkit-scrollbar-track {
205
+ background: transparent;
206
+ }
207
+
208
+ ::-webkit-scrollbar-thumb {
209
+ background-color: rgba(0, 0, 0, 0.15);
210
+ border-radius: 0;
211
+ }
212
+
213
+ ::-webkit-scrollbar-thumb:hover {
214
+ background-color: rgba(0, 0, 0, 0.25);
215
+ }
216
+
217
+ /* ===== Links ===== */
218
+ /* Vuetify 4 ships its styles in cascade layers, so these unlayered rules win
219
+ over any Vuetify rule regardless of specificity. Exclude components that
220
+ render as an anchor (`v-btn` with href, list items) or their own text colour
221
+ is replaced by this one. */
222
+ a:not(.v-btn, .v-list-item, .v-tab, .v-chip) {
223
+ color: rgb(var(--v-theme-primary));
224
+ transition: opacity 0.15s;
225
+ }
226
+
227
+ a:not(.v-btn, .v-list-item, .v-tab, .v-chip):hover {
228
+ opacity: 0.8;
229
+ }
@@ -0,0 +1,330 @@
1
+ /**
2
+ * Layout Styles
3
+ * Vuexy-inspired navigation and layout system
4
+ */
5
+
6
+ :root {
7
+ --nav-width: 260px;
8
+ --nav-collapsed-width: 84px;
9
+ --navbar-height: 64px;
10
+ --transition-duration: 0.25s;
11
+ --nav-item-padding: 10px 16px;
12
+ }
13
+
14
+ /* ===== Transition Expand Animation ===== */
15
+ .expand-enter-active,
16
+ .expand-leave-active {
17
+ overflow: hidden;
18
+ transition: height var(--transition-duration) ease;
19
+ }
20
+
21
+ .expand-enter-from,
22
+ .expand-leave-to {
23
+ height: 0 !important;
24
+ }
25
+
26
+ /* ===== Navigation Header ===== */
27
+ .nav-header {
28
+ display: flex;
29
+ align-items: center;
30
+ padding: 12px 16px;
31
+ height: 56px;
32
+ gap: 12px;
33
+ }
34
+
35
+ .nav-header .app-title {
36
+ font-size: 0.875rem;
37
+ font-weight: 600;
38
+ text-transform: uppercase;
39
+ letter-spacing: 0.5px;
40
+ opacity: 0.7;
41
+ }
42
+
43
+ /* ===== Navigation Items Container ===== */
44
+ .nav-items {
45
+ list-style: none;
46
+ padding: 8px 0;
47
+ margin: 0;
48
+ }
49
+
50
+ /* ===== Navigation Link ===== */
51
+ .nav-link a {
52
+ display: flex;
53
+ align-items: center;
54
+ padding: var(--nav-item-padding);
55
+ text-decoration: none;
56
+ color: inherit;
57
+ transition: background-color 0.2s, color 0.2s;
58
+ margin: 0 8px;
59
+ border-radius: 6px;
60
+ }
61
+
62
+ .nav-link a:hover {
63
+ background-color: rgba(var(--v-theme-primary), 0.08);
64
+ }
65
+
66
+ .nav-link.active a {
67
+ background-color: rgba(var(--v-theme-primary), 0.16);
68
+ color: rgb(var(--v-theme-primary));
69
+ }
70
+
71
+ .nav-link.active .nav-item-icon {
72
+ color: rgb(var(--v-theme-primary));
73
+ }
74
+
75
+ /* ===== Navigation Group ===== */
76
+ .nav-group-label {
77
+ display: flex;
78
+ align-items: center;
79
+ padding: var(--nav-item-padding);
80
+ cursor: pointer;
81
+ transition: background-color 0.2s;
82
+ margin: 0 8px;
83
+ border-radius: 6px;
84
+ user-select: none;
85
+ }
86
+
87
+ .nav-group-label:hover {
88
+ background-color: rgba(var(--v-theme-primary), 0.08);
89
+ }
90
+
91
+ .nav-group.active > .nav-group-label {
92
+ color: rgb(var(--v-theme-primary));
93
+ }
94
+
95
+ .nav-group.active > .nav-group-label .nav-item-icon {
96
+ color: rgb(var(--v-theme-primary));
97
+ }
98
+
99
+ .nav-group-children {
100
+ list-style: none;
101
+ padding: 0;
102
+ margin: 0;
103
+ padding-left: 16px;
104
+ }
105
+
106
+ /* Nested levels get more indentation */
107
+ .nav-group-children .nav-group-children {
108
+ padding-left: 12px;
109
+ }
110
+
111
+ /* ===== Navigation Icons ===== */
112
+ .nav-item-icon {
113
+ font-size: 22px;
114
+ margin-right: 12px;
115
+ opacity: 0.78;
116
+ flex-shrink: 0;
117
+ width: 22px;
118
+ text-align: center;
119
+ }
120
+
121
+ .nav-item-title {
122
+ flex: 1;
123
+ overflow: hidden;
124
+ text-overflow: ellipsis;
125
+ white-space: nowrap;
126
+ font-size: 0.9375rem;
127
+ }
128
+
129
+ /* ===== Navigation Arrow ===== */
130
+ .nav-group-arrow {
131
+ font-size: 18px;
132
+ opacity: 0.7;
133
+ transition: transform 0.2s ease;
134
+ margin-left: auto;
135
+ }
136
+
137
+ .nav-group-arrow.rotate-90 {
138
+ transform: rotate(90deg);
139
+ }
140
+
141
+ /* ===== Section Title ===== */
142
+ .nav-section-title {
143
+ padding: 24px 16px 8px;
144
+ font-size: 0.6875rem;
145
+ font-weight: 600;
146
+ text-transform: uppercase;
147
+ letter-spacing: 0.4px;
148
+ opacity: 0.6;
149
+ margin: 0 8px;
150
+ }
151
+
152
+ .nav-section-title:first-child {
153
+ padding-top: 8px;
154
+ }
155
+
156
+ /* ===== Navigation Badge ===== */
157
+ .nav-item-badge {
158
+ padding: 2px 8px;
159
+ border-radius: 10px;
160
+ font-size: 0.75rem;
161
+ font-weight: 500;
162
+ margin-left: 8px;
163
+ background-color: rgb(var(--v-theme-primary));
164
+ color: white;
165
+ }
166
+
167
+ .nav-item-badge.bg-error {
168
+ background-color: rgb(var(--v-theme-error));
169
+ }
170
+
171
+ .nav-item-badge.bg-success {
172
+ background-color: rgb(var(--v-theme-success));
173
+ }
174
+
175
+ .nav-item-badge.bg-warning {
176
+ background-color: rgb(var(--v-theme-warning));
177
+ }
178
+
179
+ /* ===== Rail/Collapsed Mode ===== */
180
+ .v-navigation-drawer--rail .nav-item-title,
181
+ .v-navigation-drawer--rail .nav-group-arrow,
182
+ .v-navigation-drawer--rail .nav-item-badge,
183
+ .v-navigation-drawer--rail .app-title {
184
+ display: none;
185
+ }
186
+
187
+ .v-navigation-drawer--rail .nav-section-title {
188
+ display: none;
189
+ }
190
+
191
+ .v-navigation-drawer--rail .nav-item-icon {
192
+ margin-right: 0;
193
+ }
194
+
195
+ .v-navigation-drawer--rail .nav-link a,
196
+ .v-navigation-drawer--rail .nav-group-label {
197
+ display: flex;
198
+ justify-content: center;
199
+ align-items: center;
200
+ padding: 12px 0;
201
+ margin: 0;
202
+ width: 100%;
203
+ }
204
+
205
+ .v-navigation-drawer--rail .nav-group-children {
206
+ display: none;
207
+ }
208
+
209
+ .v-navigation-drawer--rail .nav-header {
210
+ display: flex;
211
+ justify-content: center;
212
+ align-items: center;
213
+ padding: 12px 0;
214
+ width: 100%;
215
+ }
216
+
217
+ .v-navigation-drawer--rail .nav-header .v-spacer {
218
+ display: none;
219
+ }
220
+
221
+ .v-navigation-drawer--rail .nav-items {
222
+ padding: 8px 0;
223
+ }
224
+
225
+ .v-navigation-drawer--rail .nav-link,
226
+ .v-navigation-drawer--rail .nav-group {
227
+ display: flex;
228
+ justify-content: center;
229
+ }
230
+
231
+ /* ===== Menu Dropdown Cards ===== */
232
+ .v-menu > .v-overlay__content > .v-card {
233
+ box-shadow: 0 4px 25px 0 rgba(0, 0, 0, 0.1) !important;
234
+ }
235
+
236
+ /* ===== Notification Styles ===== */
237
+ .notification-list {
238
+ max-height: 300px;
239
+ overflow-y: auto;
240
+ }
241
+
242
+ .notification-item {
243
+ cursor: pointer;
244
+ transition: background-color 0.2s;
245
+ }
246
+
247
+ .notification-item:hover {
248
+ background-color: rgba(var(--v-theme-primary), 0.04);
249
+ }
250
+
251
+ .notification-item .visible-on-hover {
252
+ opacity: 0;
253
+ transition: opacity 0.2s;
254
+ }
255
+
256
+ .notification-item:hover .visible-on-hover {
257
+ opacity: 1;
258
+ }
259
+
260
+ /* ===== Scrollbar Styling ===== */
261
+ .nav-items::-webkit-scrollbar {
262
+ width: 6px;
263
+ }
264
+
265
+ .nav-items::-webkit-scrollbar-track {
266
+ background: transparent;
267
+ }
268
+
269
+ .nav-items::-webkit-scrollbar-thumb {
270
+ background-color: rgba(0, 0, 0, 0.2);
271
+ border-radius: 3px;
272
+ }
273
+
274
+ .v-theme--dark .nav-items::-webkit-scrollbar-thumb {
275
+ background-color: rgba(255, 255, 255, 0.2);
276
+ }
277
+
278
+ /* ===== Adaptive Logo (inverts in dark mode) ===== */
279
+ .logo-adaptive {
280
+ transition: filter 0.2s ease;
281
+ }
282
+
283
+ body.dark-mode .logo-adaptive {
284
+ filter: invert(1) brightness(2);
285
+ }
286
+
287
+ /* ===== QR Code (inverts in dark mode) ===== */
288
+ body.dark-mode .qr-adaptive {
289
+ filter: invert(1);
290
+ }
291
+
292
+ /* ===== Flash Messages ===== */
293
+ .flash-container {
294
+ position: fixed;
295
+ bottom: 20px;
296
+ right: 20px;
297
+ z-index: 2000;
298
+ }
299
+
300
+ .flash-message {
301
+ color: white;
302
+ padding: 12px 16px;
303
+ border-radius: 4px;
304
+ margin-bottom: 8px;
305
+ min-width: 250px;
306
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: space-between;
310
+ }
311
+
312
+ .flash-message.success { background: #16A34A; }
313
+ .flash-message.error { background: #DC2626; }
314
+ .flash-message.warning { background: #EAB308; }
315
+ .flash-message.info { background: #0EA5E9; }
316
+ .flash-message.primary { background: #EA580C; }
317
+
318
+ .flash-close {
319
+ background: none;
320
+ border: none;
321
+ color: white;
322
+ cursor: pointer;
323
+ font-size: 18px;
324
+ margin-left: 12px;
325
+ opacity: 0.8;
326
+ }
327
+
328
+ .flash-close:hover {
329
+ opacity: 1;
330
+ }