@usequark/quark-create-app 1.23.11

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 (237) hide show
  1. package/README.md +210 -0
  2. package/package.json +56 -0
  3. package/src/dependabot-config.test.js +128 -0
  4. package/src/deploy/adapters/iac.js +203 -0
  5. package/src/deploy/adapters/index.js +27 -0
  6. package/src/deploy/adapters/railway.js +508 -0
  7. package/src/deploy/adapters/railway.test.js +318 -0
  8. package/src/deploy/contract.js +59 -0
  9. package/src/deploy/deploy.integration.test.js +183 -0
  10. package/src/deploy/deploy.js +430 -0
  11. package/src/deploy/deploy.test.js +105 -0
  12. package/src/deploy/discovery.js +115 -0
  13. package/src/deploy/discovery.test.js +141 -0
  14. package/src/deploy/index.js +38 -0
  15. package/src/deploy/inspect.js +130 -0
  16. package/src/index.js +2920 -0
  17. package/src/mobile-template.test.js +232 -0
  18. package/src/prepare-hooks.test.js +535 -0
  19. package/src/scaffold-guards.test.js +140 -0
  20. package/src/scaffold-output.test.js +228 -0
  21. package/src/template-scripts.test.js +160 -0
  22. package/src/utils.js +77 -0
  23. package/src/utils.test.js +95 -0
  24. package/src/worker-dockerfile.test.js +85 -0
  25. package/templates/base-project/.cursor/rules/quark.mdc +172 -0
  26. package/templates/base-project/.dockerignore +14 -0
  27. package/templates/base-project/.env.railway.example +87 -0
  28. package/templates/base-project/.github/copilot-instructions.md +65 -0
  29. package/templates/base-project/.github/dependabot.yml +31 -0
  30. package/templates/base-project/.github/skills/project-context/SKILL.md +109 -0
  31. package/templates/base-project/.github/workflows/ci.yml +116 -0
  32. package/templates/base-project/.github/workflows/dependabot-auto-merge.yml +22 -0
  33. package/templates/base-project/.github/workflows/install-windows.yml +27 -0
  34. package/templates/base-project/.github/workflows/release.yml +67 -0
  35. package/templates/base-project/.railway/railway.ts +40 -0
  36. package/templates/base-project/CLAUDE.md +163 -0
  37. package/templates/base-project/MAIN.md +21 -0
  38. package/templates/base-project/README.md +164 -0
  39. package/templates/base-project/apps/web/.env.example +99 -0
  40. package/templates/base-project/apps/web/Dockerfile +38 -0
  41. package/templates/base-project/apps/web/biome.json +8 -0
  42. package/templates/base-project/apps/web/jsconfig.json +9 -0
  43. package/templates/base-project/apps/web/next.config.js +154 -0
  44. package/templates/base-project/apps/web/package.json +42 -0
  45. package/templates/base-project/apps/web/postcss.config.mjs +7 -0
  46. package/templates/base-project/apps/web/public/.well-known/security.txt +3 -0
  47. package/templates/base-project/apps/web/public/quark.svg +47 -0
  48. package/templates/base-project/apps/web/scripts/prepare-standalone.mjs +71 -0
  49. package/templates/base-project/apps/web/src/app/_components/AsciiPanel.js +103 -0
  50. package/templates/base-project/apps/web/src/app/_components/HealthIndicator.js +85 -0
  51. package/templates/base-project/apps/web/src/app/_components/HomeThemeToggle.js +63 -0
  52. package/templates/base-project/apps/web/src/app/_components/QuarkAnimation.js +168 -0
  53. package/templates/base-project/apps/web/src/app/_components/SyncedPanels.js +212 -0
  54. package/templates/base-project/apps/web/src/app/_components/UmamiReplayRecorder.js +198 -0
  55. package/templates/base-project/apps/web/src/app/_components/UmamiWebVitals.js +39 -0
  56. package/templates/base-project/apps/web/src/app/api/auth/[...nextauth]/route.js +7 -0
  57. package/templates/base-project/apps/web/src/app/api/auth/apple/route.js +82 -0
  58. package/templates/base-project/apps/web/src/app/api/auth/google/route.js +59 -0
  59. package/templates/base-project/apps/web/src/app/api/auth/refresh/route.js +48 -0
  60. package/templates/base-project/apps/web/src/app/api/auth/register/route.js +63 -0
  61. package/templates/base-project/apps/web/src/app/api/auth/token/route.js +44 -0
  62. package/templates/base-project/apps/web/src/app/api/csrf/route.js +40 -0
  63. package/templates/base-project/apps/web/src/app/api/device/register/route.js +57 -0
  64. package/templates/base-project/apps/web/src/app/api/error-handler.js +23 -0
  65. package/templates/base-project/apps/web/src/app/api/files/[id]/route.js +89 -0
  66. package/templates/base-project/apps/web/src/app/api/files/route.js +120 -0
  67. package/templates/base-project/apps/web/src/app/api/health/route.js +160 -0
  68. package/templates/base-project/apps/web/src/app/api/users/[id]/route.js +54 -0
  69. package/templates/base-project/apps/web/src/app/api/users/me/route.js +67 -0
  70. package/templates/base-project/apps/web/src/app/api/users/route.js +45 -0
  71. package/templates/base-project/apps/web/src/app/auth/error/page.js +83 -0
  72. package/templates/base-project/apps/web/src/app/auth/forgot-password/page.js +58 -0
  73. package/templates/base-project/apps/web/src/app/auth/provider-loading.js +25 -0
  74. package/templates/base-project/apps/web/src/app/auth/register/RegisterPageClient.js +340 -0
  75. package/templates/base-project/apps/web/src/app/auth/register/page.js +17 -0
  76. package/templates/base-project/apps/web/src/app/auth/signin/SignInPageClient.js +230 -0
  77. package/templates/base-project/apps/web/src/app/auth/signin/page.js +10 -0
  78. package/templates/base-project/apps/web/src/app/favicon.ico +0 -0
  79. package/templates/base-project/apps/web/src/app/global-error.js +58 -0
  80. package/templates/base-project/apps/web/src/app/globals.css +1166 -0
  81. package/templates/base-project/apps/web/src/app/icon.svg +47 -0
  82. package/templates/base-project/apps/web/src/app/layout.js +57 -0
  83. package/templates/base-project/apps/web/src/app/manifest.js +12 -0
  84. package/templates/base-project/apps/web/src/app/not-found.js +39 -0
  85. package/templates/base-project/apps/web/src/app/page.js +262 -0
  86. package/templates/base-project/apps/web/src/app/robots.js +23 -0
  87. package/templates/base-project/apps/web/src/app/sitemap.js +14 -0
  88. package/templates/base-project/apps/web/src/lib/analytics/umami-before-send.js +29 -0
  89. package/templates/base-project/apps/web/src/lib/analytics/umami-config.js +95 -0
  90. package/templates/base-project/apps/web/src/lib/analytics/umami-csp.js +24 -0
  91. package/templates/base-project/apps/web/src/lib/analytics/umami-marketing.js +131 -0
  92. package/templates/base-project/apps/web/src/lib/analytics/umami-replay.js +204 -0
  93. package/templates/base-project/apps/web/src/lib/analytics/umami-web-vitals.js +237 -0
  94. package/templates/base-project/apps/web/src/lib/analytics/umami.js +57 -0
  95. package/templates/base-project/apps/web/src/lib/auth-middleware.js +82 -0
  96. package/templates/base-project/apps/web/src/lib/auth-signup.js +5 -0
  97. package/templates/base-project/apps/web/src/lib/auth.js +191 -0
  98. package/templates/base-project/apps/web/src/lib/csrf-cookie.js +11 -0
  99. package/templates/base-project/apps/web/src/lib/jwt.js +84 -0
  100. package/templates/base-project/apps/web/src/lib/load-crm-config.js +35 -0
  101. package/templates/base-project/apps/web/src/lib/normalize-auth-request.js +33 -0
  102. package/templates/base-project/apps/web/src/lib/proxy-auth.js +80 -0
  103. package/templates/base-project/apps/web/src/lib/seo/indexing.js +25 -0
  104. package/templates/base-project/apps/web/src/lib/seo/site-metadata.js +74 -0
  105. package/templates/base-project/apps/web/src/lib/sitemap-entries.js +22 -0
  106. package/templates/base-project/apps/web/src/preflight.js +55 -0
  107. package/templates/base-project/apps/web/src/proxy.js +328 -0
  108. package/templates/base-project/biome.json +52 -0
  109. package/templates/base-project/docker-compose.override.yml +17 -0
  110. package/templates/base-project/docker-compose.yml +37 -0
  111. package/templates/base-project/docs/FIRST_FEATURE.md +74 -0
  112. package/templates/base-project/docs/START_HERE.md +70 -0
  113. package/templates/base-project/docs/adr/README.md +42 -0
  114. package/templates/base-project/package.json +58 -0
  115. package/templates/base-project/packages/db/package.json +38 -0
  116. package/templates/base-project/packages/db/prisma/migrations/20260202061128_initial/migration.sql +202 -0
  117. package/templates/base-project/packages/db/prisma/migrations/migration_lock.toml +3 -0
  118. package/templates/base-project/packages/db/prisma/schema.prisma +161 -0
  119. package/templates/base-project/packages/db/prisma/seed.js +152 -0
  120. package/templates/base-project/packages/db/prisma.config.js +27 -0
  121. package/templates/base-project/packages/db/src/client.js +75 -0
  122. package/templates/base-project/packages/db/src/connection.js +44 -0
  123. package/templates/base-project/packages/db/src/context.js +275 -0
  124. package/templates/base-project/packages/db/src/index.js +5 -0
  125. package/templates/base-project/packages/db/src/ping.js +66 -0
  126. package/templates/base-project/packages/db/src/queries.js +292 -0
  127. package/templates/base-project/packages/db/src/schemas.js +30 -0
  128. package/templates/base-project/packages/db/src/umami.js +299 -0
  129. package/templates/base-project/pnpm-workspace.yaml +20 -0
  130. package/templates/base-project/scripts/check-loading.mjs +49 -0
  131. package/templates/base-project/scripts/check-standards.mjs +186 -0
  132. package/templates/base-project/scripts/clean-workspace.mjs +479 -0
  133. package/templates/base-project/scripts/dev-preflight.mjs +48 -0
  134. package/templates/base-project/scripts/doctor.js +408 -0
  135. package/templates/base-project/scripts/prepare.js +490 -0
  136. package/templates/base-project/scripts/run-tests.mjs +71 -0
  137. package/templates/base-project/skills/add-dashboard/SKILL.md +28 -0
  138. package/templates/base-project/skills/add-endpoint/SKILL.md +29 -0
  139. package/templates/base-project/skills/add-model/SKILL.md +28 -0
  140. package/templates/base-project/skills/ecommerce/SKILL.md +224 -0
  141. package/templates/base-project/skills/ecommerce-cart/SKILL.md +193 -0
  142. package/templates/base-project/skills/ecommerce-catalog/SKILL.md +179 -0
  143. package/templates/base-project/skills/ecommerce-checkout/SKILL.md +195 -0
  144. package/templates/base-project/skills/i18n/SKILL.md +211 -0
  145. package/templates/base-project/skills/payment/SKILL.md +280 -0
  146. package/templates/base-project/skills/quark-skills/SKILL.md +37 -0
  147. package/templates/base-project/turbo.json +143 -0
  148. package/templates/config/package.json +20 -0
  149. package/templates/config/src/app-url.js +134 -0
  150. package/templates/config/src/environment.js +284 -0
  151. package/templates/config/src/index.js +16 -0
  152. package/templates/config/src/load-config.js +157 -0
  153. package/templates/config/src/rate-limit.js +136 -0
  154. package/templates/config/src/redis.js +43 -0
  155. package/templates/config/src/validate-env.js +532 -0
  156. package/templates/jobs/README.md +52 -0
  157. package/templates/jobs/package.json +9 -0
  158. package/templates/jobs/src/definitions.js +13 -0
  159. package/templates/jobs/src/index.js +1 -0
  160. package/templates/mobile/.env.example +16 -0
  161. package/templates/mobile/CHECKLIST.md +40 -0
  162. package/templates/mobile/README.md +130 -0
  163. package/templates/mobile/app/(app)/_layout.tsx +83 -0
  164. package/templates/mobile/app/(app)/index.tsx +147 -0
  165. package/templates/mobile/app/(app)/profile.tsx +204 -0
  166. package/templates/mobile/app/(app)/settings.tsx +80 -0
  167. package/templates/mobile/app/(auth)/_layout.tsx +10 -0
  168. package/templates/mobile/app/(auth)/sign-in.tsx +140 -0
  169. package/templates/mobile/app/(auth)/sign-up.tsx +178 -0
  170. package/templates/mobile/app/+not-found.tsx +32 -0
  171. package/templates/mobile/app/_layout.tsx +18 -0
  172. package/templates/mobile/app.json +47 -0
  173. package/templates/mobile/assets/adaptive-icon.png +0 -0
  174. package/templates/mobile/assets/icon.png +0 -0
  175. package/templates/mobile/assets/notification-icon.png +0 -0
  176. package/templates/mobile/assets/splash.png +0 -0
  177. package/templates/mobile/biome.json +27 -0
  178. package/templates/mobile/components/AuthGate.tsx +19 -0
  179. package/templates/mobile/components/ThemeProvider.tsx +23 -0
  180. package/templates/mobile/eas.json +29 -0
  181. package/templates/mobile/hooks/use-auth.ts +127 -0
  182. package/templates/mobile/lib/api-client.ts +138 -0
  183. package/templates/mobile/lib/api.ts +83 -0
  184. package/templates/mobile/lib/auth-providers-google.ts +74 -0
  185. package/templates/mobile/lib/auth-providers.ts +74 -0
  186. package/templates/mobile/lib/auth.ts +100 -0
  187. package/templates/mobile/lib/config.ts +14 -0
  188. package/templates/mobile/lib/errors.ts +46 -0
  189. package/templates/mobile/lib/notifications.ts +77 -0
  190. package/templates/mobile/lib/storage.ts +25 -0
  191. package/templates/mobile/lib/theme-tokens.ts +16 -0
  192. package/templates/mobile/package.json +37 -0
  193. package/templates/mobile/tsconfig.json +10 -0
  194. package/templates/pwa/app/_components/PWARegister.js +15 -0
  195. package/templates/pwa/app/manifest.js +32 -0
  196. package/templates/pwa/public/sw.js +72 -0
  197. package/templates/ui/README.md +44 -0
  198. package/templates/ui/package.json +21 -0
  199. package/templates/ui/src/badge.js +23 -0
  200. package/templates/ui/src/button.js +111 -0
  201. package/templates/ui/src/card.js +128 -0
  202. package/templates/ui/src/checkbox.js +20 -0
  203. package/templates/ui/src/container.js +13 -0
  204. package/templates/ui/src/dialog.js +137 -0
  205. package/templates/ui/src/error-banner.js +15 -0
  206. package/templates/ui/src/footer.js +239 -0
  207. package/templates/ui/src/form-field.integration-test.js +142 -0
  208. package/templates/ui/src/form-field.js +50 -0
  209. package/templates/ui/src/index.js +24 -0
  210. package/templates/ui/src/input.js +11 -0
  211. package/templates/ui/src/label.js +14 -0
  212. package/templates/ui/src/lightbox.integration-test.js +167 -0
  213. package/templates/ui/src/lightbox.js +243 -0
  214. package/templates/ui/src/logo.js +52 -0
  215. package/templates/ui/src/navbar.js +602 -0
  216. package/templates/ui/src/password-input.js +31 -0
  217. package/templates/ui/src/rich-text.js +514 -0
  218. package/templates/ui/src/select.js +317 -0
  219. package/templates/ui/src/skeleton.js +10 -0
  220. package/templates/ui/src/spinner.js +31 -0
  221. package/templates/ui/src/table.js +169 -0
  222. package/templates/ui/src/textarea.js +11 -0
  223. package/templates/ui/src/theme-constants.js +24 -0
  224. package/templates/ui/src/theme.js +196 -0
  225. package/templates/ui/src/toast.js +206 -0
  226. package/templates/worker/Dockerfile +64 -0
  227. package/templates/worker/README.md +56 -0
  228. package/templates/worker/package.json +36 -0
  229. package/templates/worker/src/config/pricing.js +46 -0
  230. package/templates/worker/src/handlers/email-job-validation.js +36 -0
  231. package/templates/worker/src/handlers/email.js +98 -0
  232. package/templates/worker/src/handlers/files.js +59 -0
  233. package/templates/worker/src/handlers/index.js +20 -0
  234. package/templates/worker/src/handlers/push-notification.js +234 -0
  235. package/templates/worker/src/index.js +505 -0
  236. package/templates/worker/src/lib/railway.js +438 -0
  237. package/templates/worker/src/lib/tokens.js +81 -0
@@ -0,0 +1,1166 @@
1
+ @import "tailwindcss";
2
+ @source "../../../../packages/ui/src/**/*.{js,jsx}";
3
+
4
+ /* Tailwind dark variant - activates when data-theme="dark" is set on <html> */
5
+ @custom-variant dark (&:is([data-theme="dark"] *));
6
+
7
+ /*
8
+ * ─── DESIGN TOKEN ARCHITECTURE ───────────────────────────────────────────────
9
+ *
10
+ * This file defines two layers:
11
+ *
12
+ * 1. NEUTRAL LAYER (:root base)
13
+ * - Structural tokens that every component needs (spacing, sizing, radii)
14
+ * - Minimal color defaults that work in any context
15
+ * - Override these to retheme without touching component source
16
+ *
17
+ * 2. OPINIONATED LAYER (Quark defaults)
18
+ * - The specific blue/gray palette that ships with Quark
19
+ * - Delete this block to start with a blank canvas
20
+ * - Or replace with your own brand tokens
21
+ *
22
+ * Components consume CSS variables, never hardcoded values.
23
+ * To fully retheme: override tokens in the neutral layer.
24
+ */
25
+ :root {
26
+ /*
27
+ * ─── NEUTRAL TOKENS ────────────────────────────────────────────────────────
28
+ *
29
+ * Structural tokens that every component needs. These are theme-independent
30
+ * and provide the baseline for any design system.
31
+ */
32
+
33
+ /* Shape */
34
+ --radius-default: 0px;
35
+
36
+ /* Badge */
37
+ --badge-min-width: auto;
38
+ --badge-padding-x: 0.625rem;
39
+ --badge-padding-y: 0.125rem;
40
+ --badge-font-size: 0.75rem;
41
+
42
+ /* Button */
43
+ --btn-sm-height: 2rem;
44
+ --btn-sm-padding-x: 0.75rem;
45
+ --btn-sm-font-size: 0.875rem;
46
+ --btn-md-height: 2.5rem;
47
+ --btn-md-padding-x: 1rem;
48
+ --btn-md-font-size: 0.875rem;
49
+ --btn-lg-height: 2.75rem;
50
+ --btn-lg-padding-x: 1.5rem;
51
+ --btn-lg-font-size: 1rem;
52
+ --btn-icon-sm: 1rem;
53
+ --btn-icon-md: 1rem;
54
+ --btn-icon-lg: 1.25rem;
55
+
56
+ /* Input */
57
+ --input-height: 2.5rem;
58
+ --input-padding-x: 0.75rem;
59
+ --input-font-size: 0.875rem;
60
+
61
+ /* Textarea */
62
+ --textarea-padding-x: 0.75rem;
63
+ --textarea-padding-y: 0.5rem;
64
+ --textarea-font-size: 0.875rem;
65
+
66
+ /* Select */
67
+ --select-height: 2.5rem;
68
+ --select-padding-x: 0.75rem;
69
+ --select-font-size: 0.875rem;
70
+ --select-option-padding-x: 0.625rem;
71
+ --select-option-padding-y: 0.5rem;
72
+ --select-option-font-size: 0.875rem;
73
+ --select-empty-padding-x: 0.625rem;
74
+ --select-empty-padding-y: 0.5rem;
75
+ --select-empty-font-size: 0.875rem;
76
+
77
+ /* Dialog */
78
+ --dialog-padding-x: 1.25rem;
79
+ --dialog-padding-y: 1rem;
80
+
81
+ /* Card */
82
+ --card-header-padding: 1.5rem;
83
+ --card-header-gap: 0.375rem;
84
+ --card-content-padding: 1.5rem;
85
+ --card-content-padding-top: 0;
86
+ --card-footer-padding: 1.5rem;
87
+ --card-footer-padding-top: 0;
88
+
89
+ /* Form Field */
90
+ --form-field-gap: 0.375rem;
91
+
92
+ /* Table */
93
+ --table-head-height: 2.75rem;
94
+ --table-head-padding-x: 0.75rem;
95
+ --table-cell-padding: 0.75rem;
96
+
97
+ /* Navbar */
98
+ --navbar-desktop-height: 4.5rem;
99
+ --navbar-container-padding-x: 1rem;
100
+ --navbar-mobile-height: 3.5rem;
101
+ --navbar-mark-height: 2rem;
102
+ --navbar-mark-width: 2rem;
103
+ --navbar-mark-font-size: 11px;
104
+ --navbar-link-padding-x: 0.875rem;
105
+ --navbar-link-height: 2.5rem;
106
+ --navbar-link-font-size: 1rem;
107
+ --navbar-action-height: 2.75rem;
108
+ --navbar-action-padding-x: 1.25rem;
109
+ --navbar-action-font-size: 1rem;
110
+ --navbar-dropdown-width: 12rem;
111
+ --navbar-dropdown-item-padding-x: 0.875rem;
112
+ --navbar-dropdown-item-padding-y: 0.625rem;
113
+ --navbar-dropdown-item-font-size: 1rem;
114
+ --navbar-mobile-toggle-height: 2.5rem;
115
+ --navbar-mobile-toggle-width: 2.5rem;
116
+ --navbar-mobile-link-min-height: 3rem;
117
+ --navbar-mobile-link-padding-x: 1rem;
118
+ --navbar-mobile-link-font-size: 1rem;
119
+ --navbar-mobile-sublink-padding-x: 1.5rem;
120
+ --navbar-mobile-sublink-padding-y: 0.5rem;
121
+ --navbar-mobile-sublink-font-size: 0.875rem;
122
+ --navbar-mobile-action-padding-x: 1rem;
123
+ --navbar-mobile-action-height: 2.5rem;
124
+ --navbar-mobile-action-font-size: 0.875rem;
125
+
126
+ /* Footer */
127
+ --footer-container-max-width: 80rem;
128
+ --footer-container-padding-x: 1rem;
129
+ --footer-container-padding-y: 2rem;
130
+ --footer-mark-height: 2.75rem;
131
+ --footer-mark-width: 2.75rem;
132
+ --footer-cta-padding-x: 1.5rem;
133
+ --footer-cta-padding-y: 0.625rem;
134
+ --footer-cta-font-size: 0.875rem;
135
+ --footer-column-title-font-size: 0.75rem;
136
+ --footer-column-gap: 0.625rem;
137
+ --footer-bottom-gap: 0.75rem;
138
+
139
+ /* Lightbox */
140
+ --lightbox-padding-x: 0.75rem;
141
+ --lightbox-padding-y: 1rem;
142
+ --lightbox-dialog-padding: 0.75rem;
143
+ --lightbox-btn-padding-x: 0.75rem;
144
+ --lightbox-btn-padding-y: 0.375rem;
145
+ --lightbox-btn-font-size: 0.875rem;
146
+ --lightbox-close-padding-x: 0.5rem;
147
+ --lightbox-close-padding-y: 0.25rem;
148
+ --lightbox-close-font-size: 0.875rem;
149
+ --lightbox-counter-min-width: 4rem;
150
+ --lightbox-counter-font-size: 0.75rem;
151
+ --lightbox-nav-gap: 0.5rem;
152
+
153
+ /* RichText */
154
+ --richtext-padding-x: 0.75rem;
155
+ --richtext-padding-y: 0.5rem;
156
+ --richtext-toolbar-padding-x: 0.5rem;
157
+ --richtext-toolbar-padding-y: 0.375rem;
158
+ --richtext-toolbar-gap: 0.125rem;
159
+ --richtext-btn-size: 1.75rem;
160
+ --richtext-separator-height: 1.25rem;
161
+ --richtext-separator-width: 1px;
162
+ --richtext-separator-margin-x: 0.25rem;
163
+
164
+ /* Error Banner */
165
+ --error-banner-margin-bottom: 1rem;
166
+ --error-banner-padding-x: 0.75rem;
167
+ --error-banner-padding-y: 0.5rem;
168
+ --error-banner-font-size: 0.875rem;
169
+
170
+ /* Toast */
171
+ --toast-bottom: 1rem;
172
+ --toast-right: 1rem;
173
+ --toast-padding-x: 1rem;
174
+ --toast-padding-y: 0.75rem;
175
+ --toast-font-size: 0.875rem;
176
+ --toast-icon-size: 1.5rem;
177
+ --toast-dismiss-size: 1.5rem;
178
+
179
+ /* Skeleton */
180
+ --skeleton-radius: 0.5rem;
181
+
182
+ /* Spinner */
183
+ --spinner-size: 1rem;
184
+
185
+ /* Theme Toggle */
186
+ --toggle-height: 28px;
187
+ --toggle-padding-x: 10px;
188
+ --toggle-padding-x-right: 12px;
189
+ --toggle-radius: 14px;
190
+ --toggle-gap: 6px;
191
+ --toggle-font-size: 12px;
192
+
193
+ /*
194
+ * ─── OPINIONATED TOKENS (Quark Defaults) ───────────────────────────────────
195
+ *
196
+ * Delete or replace this entire block to start with a blank canvas.
197
+ * These values define the specific Quark blue/gray aesthetic.
198
+ */
199
+
200
+ /* Primary brand colour */
201
+ --color-primary: #377dff;
202
+ --color-primary-hover: #5594ff;
203
+ --color-primary-muted: rgba(55, 125, 255, 0.1);
204
+
205
+ /* Surfaces */
206
+ --color-bg: #09101a;
207
+ --color-surface: #101827;
208
+ --color-surface-hover: #1c2642;
209
+
210
+ /* Borders */
211
+ --color-border: #263044;
212
+ --color-border-hover: rgba(55, 125, 255, 0.4);
213
+
214
+ /* Text */
215
+ --color-text: #e8eaf0;
216
+ --color-text-muted: #8d9ec0;
217
+ --color-text-faint: #7886a8;
218
+
219
+ /* Semantic: danger */
220
+ --color-danger: #ff4757;
221
+ --color-danger-hover: #ff6b78;
222
+ --color-danger-muted: rgba(255, 71, 87, 0.1);
223
+
224
+ /* Semantic: success */
225
+ --color-success: #34d399;
226
+ --color-success-muted: rgba(52, 211, 153, 0.1);
227
+
228
+ /* Semantic: warning */
229
+ --color-warning: #fbbf24;
230
+ --color-warning-muted: rgba(251, 191, 36, 0.1);
231
+
232
+ /* Semantic: info */
233
+ --color-info: #22d3ee;
234
+ --color-info-muted: rgba(34, 211, 238, 0.1);
235
+
236
+ /*
237
+ * ─── QUARK TYPOGRAPHIC OPINIONS ─────────────────────────────────────────────
238
+ *
239
+ * The specific typographic identity that was previously hardcoded in components.
240
+ * Delete these to revert to neutral defaults from the structural layer.
241
+ */
242
+ --label-text-transform: uppercase;
243
+ --label-text-tracking: 0.1em;
244
+ --badge-text-transform: uppercase;
245
+ --badge-text-tracking: 0.05em;
246
+ --table-head-text-transform: uppercase;
247
+ --table-head-text-tracking: 0.1em;
248
+ --table-head-font-family: monospace;
249
+ --table-head-font-size: 0.75rem;
250
+ --table-cell-font-family: monospace;
251
+ --table-cell-font-size: 0.875rem;
252
+ --navbar-mark-text-transform: uppercase;
253
+ --navbar-mark-text-tracking: 0.1em;
254
+ --footer-column-title-text-transform: uppercase;
255
+ --footer-column-title-text-tracking: 0.2em;
256
+ --footer-column-title-font-family: monospace;
257
+ --lightbox-counter-text-transform: uppercase;
258
+ --lightbox-counter-text-tracking: 0.16em;
259
+ --lightbox-counter-font-family: monospace;
260
+
261
+ /* App shell */
262
+ --quark-page-bg: var(--color-bg);
263
+ --quark-grid-line: rgba(255, 255, 255, 0.07);
264
+ --quark-home-muted: #4e5870;
265
+ --quark-home-sep: #2c3450;
266
+ --quark-home-link-idle: #7886a8;
267
+ --quark-home-link-hover: var(--color-primary);
268
+ --quark-health-text: #7886a8;
269
+ --quark-health-sep: #2c3450;
270
+ --quark-health-dot-idle: #2c3450;
271
+ /* Logo dark arc - visible on lifted dark bg */
272
+ --quark-logo-dark-arc: #4e5e7a;
273
+ /* Toast SVG stroke colours */
274
+ --quark-toast-track: rgba(255, 255, 255, 0.2);
275
+ --quark-toast-progress: rgba(255, 255, 255, 0.85);
276
+
277
+ /*
278
+ * ─── COMPONENT CSS VARIABLES ──────────────────────────────────────────────
279
+ *
280
+ * Each component reads its visual properties from these CSS variables.
281
+ * Override any variable to retheme without editing component source files.
282
+ * Variables using var(--color-xxx) adapt automatically to the active theme.
283
+ */
284
+
285
+ /* ── Button ─────────────────────────────────────────────────────────────── */
286
+ --btn-radius: var(--radius-default);
287
+ --btn-bg: var(--color-primary-hover);
288
+ --btn-border: var(--color-primary-hover);
289
+ --btn-text: #ffffff;
290
+ --btn-ring: color-mix(in srgb, var(--color-primary) 60%, transparent);
291
+ --btn-hover-brightness: 0.95;
292
+ --btn-hover-shadow:
293
+ 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
294
+ --btn-hover-bg: transparent;
295
+ --btn-hover-text: inherit;
296
+ --btn-hover-opacity: 1;
297
+
298
+ /* ── Badge ──────────────────────────────────────────────────────────────── */
299
+ --badge-bg: var(--color-surface-hover);
300
+ --badge-border: var(--color-border);
301
+ --badge-text: var(--color-text-muted);
302
+
303
+ /* ── Toast ──────────────────────────────────────────────────────────────── */
304
+ --toast-bg: var(--color-primary-muted);
305
+ --toast-border: color-mix(in srgb, var(--color-primary) 40%, transparent);
306
+ --toast-text: var(--color-primary);
307
+ --toast-shadow:
308
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
309
+
310
+ /* ── Card ───────────────────────────────────────────────────────────────── */
311
+ --card-bg: var(--color-surface);
312
+ --card-border: var(--color-border);
313
+ --card-title-text: var(--color-text);
314
+ --card-text-muted: var(--color-text-muted);
315
+ --card-trigger-hover-bg: var(--color-surface-hover);
316
+ --card-trigger-ring: color-mix(
317
+ in srgb,
318
+ var(--color-primary) 40%,
319
+ transparent
320
+ );
321
+
322
+ /* ── Dialog ─────────────────────────────────────────────────────────────── */
323
+ --dialog-bg: var(--color-surface);
324
+ --dialog-border: var(--color-border);
325
+ --dialog-title-text: var(--color-text);
326
+ --dialog-text-muted: var(--color-text-muted);
327
+ --dialog-close-hover-bg: var(--color-surface-hover);
328
+ --dialog-close-ring: var(--color-border-hover);
329
+ --dialog-backdrop: rgba(0, 0, 0, 0.6);
330
+
331
+ /* ── Table ──────────────────────────────────────────────────────────────── */
332
+ --table-bg: var(--color-surface);
333
+ --table-border: var(--color-border);
334
+ --table-header-bg: var(--color-surface-hover);
335
+ --table-row-hover-bg: color-mix(
336
+ in srgb,
337
+ var(--color-surface-hover) 60%,
338
+ transparent
339
+ );
340
+ --table-row-active-bg: color-mix(
341
+ in srgb,
342
+ var(--color-surface-hover) 40%,
343
+ transparent
344
+ );
345
+ --table-head-text: var(--color-text-faint);
346
+ --table-cell-text: var(--color-text);
347
+ --table-sort-icon: var(--color-text-faint);
348
+ --table-sort-icon-hover: var(--color-text);
349
+ --table-sort-active: var(--color-primary);
350
+ --table-ring: color-mix(in srgb, var(--color-primary) 40%, transparent);
351
+
352
+ /* ── Input / Textarea ───────────────────────────────────────────────────── */
353
+ --input-bg: var(--color-surface-hover);
354
+ --input-border: var(--color-border);
355
+ --input-text: var(--color-text);
356
+ --input-placeholder: var(--color-text-faint);
357
+ --input-border-hover: var(--color-border-hover);
358
+ --input-border-focus: var(--color-primary);
359
+ --input-ring-focus: color-mix(in srgb, var(--color-primary) 20%, transparent);
360
+ --input-disabled-opacity: 0.3;
361
+
362
+ /* ── Select ─────────────────────────────────────────────────────────────── */
363
+ --select-bg: var(--color-surface-hover);
364
+ --select-border: var(--color-border);
365
+ --select-text: var(--color-text);
366
+ --select-text-faint: var(--color-text-faint);
367
+ --select-border-hover: var(--color-border-hover);
368
+ --select-ring: color-mix(in srgb, var(--color-primary) 40%, transparent);
369
+ --select-panel-bg: var(--color-surface);
370
+ --select-panel-border: var(--color-border);
371
+ --select-option-hover-bg: var(--color-surface-hover);
372
+ --select-option-hover-text: var(--color-text);
373
+ --select-option-active-bg: var(--color-primary-muted);
374
+ --select-option-active-text: var(--color-primary);
375
+
376
+ /* ── Checkbox ───────────────────────────────────────────────────────────── */
377
+ --checkbox-bg: #090d14;
378
+ --checkbox-border: #1e2535;
379
+ --checkbox-accent: #377dff;
380
+ --checkbox-ring: color-mix(in srgb, #377dff 40%, transparent);
381
+ --checkbox-disabled-opacity: 0.4;
382
+ --checkbox-label-text: #6b7a99;
383
+
384
+ /* ── Label ──────────────────────────────────────────────────────────────── */
385
+ --label-text: var(--color-text-muted);
386
+
387
+ /* ── Skeleton ───────────────────────────────────────────────────────────── */
388
+ --skeleton-bg: rgba(30, 37, 53, 0.7);
389
+
390
+ /* ── Container ──────────────────────────────────────────────────────────── */
391
+ --container-bg: var(--color-surface);
392
+ --container-border: var(--color-border);
393
+
394
+ /* ── Error Banner ───────────────────────────────────────────────────────── */
395
+ --error-bg: var(--color-danger-muted);
396
+ --error-border: color-mix(in srgb, var(--color-danger) 40%, transparent);
397
+ --error-text: var(--color-danger);
398
+
399
+ /* ── Password Input ─────────────────────────────────────────────────────── */
400
+ --input-icon: var(--color-text-muted);
401
+ --input-icon-hover: var(--color-text);
402
+
403
+ /* ── Lightbox ───────────────────────────────────────────────────────────── */
404
+ --lightbox-backdrop: rgba(0, 0, 0, 0.8);
405
+ --lightbox-btn-border: rgba(255, 255, 255, 0.25);
406
+ --lightbox-btn-text: #ffffff;
407
+ --lightbox-btn-hover-bg: rgba(255, 255, 255, 0.15);
408
+ --lightbox-btn-ring: rgba(255, 255, 255, 0.6);
409
+ --lightbox-caption-text: rgba(255, 255, 255, 0.85);
410
+ --lightbox-counter-text: rgba(255, 255, 255, 0.8);
411
+
412
+ /* ── RichText ───────────────────────────────────────────────────────────── */
413
+ --richtext-bg: var(--color-surface-hover);
414
+ --richtext-border: var(--color-border);
415
+ --richtext-text: var(--color-text);
416
+ --richtext-border-focus: var(--color-primary);
417
+ --richtext-ring: color-mix(in srgb, var(--color-primary) 20%, transparent);
418
+ --richtext-toolbar-bg: var(--color-surface);
419
+ --richtext-toolbar-border: var(--color-border);
420
+ --richtext-btn-text: var(--color-text-muted);
421
+ --richtext-btn-hover-bg: var(--color-surface-hover);
422
+ --richtext-btn-hover-text: var(--color-text);
423
+ --richtext-btn-active-bg: var(--color-surface-hover);
424
+ --richtext-btn-active-text: var(--color-text);
425
+ --richtext-separator: var(--color-border);
426
+ --richtext-placeholder: var(--color-text-faint);
427
+ --richtext-link: var(--color-primary);
428
+ --richtext-blockquote-border: var(--color-border);
429
+ --richtext-blockquote-text: var(--color-text-muted);
430
+ --richtext-code-bg: var(--color-surface-hover);
431
+
432
+ /* ── Navbar ─────────────────────────────────────────────────────────────── */
433
+ --navbar-bg: color-mix(in srgb, var(--color-surface) 95%, transparent);
434
+ --navbar-mobile-bg: var(--color-surface);
435
+ --navbar-border: var(--color-border);
436
+ --navbar-inset-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02);
437
+ --navbar-text: var(--color-text);
438
+ --navbar-text-muted: var(--color-text-muted);
439
+ --navbar-hover-bg: var(--color-surface);
440
+ --navbar-hover-text: var(--color-text);
441
+ --navbar-logo-text: var(--color-text);
442
+ --navbar-logo-hover: var(--color-primary);
443
+ --navbar-mark-bg: var(--color-primary-muted);
444
+ --navbar-mark-border: color-mix(
445
+ in srgb,
446
+ var(--color-primary) 40%,
447
+ transparent
448
+ );
449
+ --navbar-mark-text: var(--color-primary);
450
+ --navbar-action-border: color-mix(
451
+ in srgb,
452
+ var(--color-primary) 55%,
453
+ transparent
454
+ );
455
+ --navbar-action-bg: var(--color-primary-muted);
456
+ --navbar-action-text: var(--color-primary);
457
+ --navbar-action-hover-border: var(--color-primary);
458
+ --navbar-action-hover-bg: color-mix(
459
+ in srgb,
460
+ var(--color-primary-muted) 70%,
461
+ transparent
462
+ );
463
+ --navbar-ring: color-mix(in srgb, var(--color-primary) 40%, transparent);
464
+ --navbar-dropdown-bg: var(--color-surface);
465
+ --navbar-dropdown-border: var(--color-border);
466
+ --navbar-dropdown-shadow:
467
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
468
+ --navbar-mobile-panel-bg: var(--color-surface);
469
+ --navbar-mobile-panel-border: var(--color-border);
470
+ --navbar-divide-border: var(--color-border);
471
+
472
+ /* ── Footer ─────────────────────────────────────────────────────────────── */
473
+ --footer-bg: var(--color-surface);
474
+ --footer-border: var(--color-border);
475
+ --footer-text: var(--color-text);
476
+ --footer-text-muted: var(--color-text-muted);
477
+ --footer-text-faint: var(--color-text-faint);
478
+ --footer-mark-bg: var(--color-primary-muted);
479
+ --footer-mark-text: var(--color-primary);
480
+ --footer-cta-border: var(--color-primary);
481
+ --footer-cta-text: var(--color-primary);
482
+ --footer-cta-hover-bg: var(--color-primary-muted);
483
+ --footer-link-hover: var(--color-text);
484
+ --footer-sep: color-mix(in srgb, var(--color-text-faint) 70%, transparent);
485
+
486
+ /* ── Logo ───────────────────────────────────────────────────────────────── */
487
+ --logo-blue-arc: #377dff;
488
+ --logo-red-arc: #ff4757;
489
+ --logo-red-dash: #ff4757;
490
+ }
491
+
492
+ @media (prefers-color-scheme: light) {
493
+ /* OS says light AND the user hasn't explicitly pinned dark via the toggle */
494
+ :root:not([data-theme="dark"]) {
495
+ --color-primary: #1d4ed8;
496
+ --color-primary-hover: #1a56db;
497
+ --color-primary-muted: rgba(29, 78, 216, 0.08);
498
+
499
+ --color-bg: #f7f8fa;
500
+ --color-surface: #ffffff;
501
+ --color-surface-hover: #f3f4f6;
502
+
503
+ --color-border: #e5e7eb;
504
+ --color-border-hover: #d1d5db;
505
+
506
+ --color-text: #111827;
507
+ --color-text-muted: #6b7280;
508
+ --color-text-faint: #9ca3af;
509
+
510
+ --color-danger: #dc2626;
511
+ --color-danger-hover: #b91c1c;
512
+ --color-danger-muted: rgba(220, 38, 38, 0.08);
513
+
514
+ --color-success: #059669;
515
+ --color-success-muted: rgba(5, 150, 105, 0.08);
516
+
517
+ --color-warning: #d97706;
518
+ --color-warning-muted: rgba(217, 119, 6, 0.08);
519
+
520
+ --color-info: #0891b2;
521
+ --color-info-muted: rgba(8, 145, 178, 0.08);
522
+
523
+ --quark-page-bg: var(--color-bg);
524
+ --quark-grid-line: rgba(0, 0, 0, 0.07);
525
+ --quark-home-muted: #9ca3af;
526
+ --quark-home-sep: #d1d5db;
527
+ --quark-home-link-idle: #6b7280;
528
+ --quark-health-text: #9ca3af;
529
+ --quark-health-sep: #d1d5db;
530
+ --quark-health-dot-idle: #d1d5db;
531
+ --quark-logo-dark-arc: #2d3436;
532
+
533
+ /* Component variable light overrides */
534
+ --checkbox-bg: transparent;
535
+ --checkbox-border: #d1d5db;
536
+ --checkbox-accent: #2563eb;
537
+ --checkbox-ring: color-mix(in srgb, #3b82f6 40%, transparent);
538
+ --checkbox-disabled-opacity: 0.6;
539
+ --checkbox-label-text: #374151;
540
+ --skeleton-bg: rgba(229, 231, 235, 0.8);
541
+ --navbar-inset-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.02);
542
+ }
543
+ }
544
+
545
+ /* User explicitly toggled to light (overrides OS dark preference) */
546
+ :root[data-theme="light"] {
547
+ --color-primary: #1d4ed8;
548
+ --color-primary-hover: #1a56db;
549
+ --color-primary-muted: rgba(29, 78, 216, 0.08);
550
+
551
+ --color-bg: #f7f8fa;
552
+ --color-surface: #ffffff;
553
+ --color-surface-hover: #f3f4f6;
554
+
555
+ --color-border: #e5e7eb;
556
+ --color-border-hover: #d1d5db;
557
+
558
+ --color-text: #111827;
559
+ --color-text-muted: #6b7280;
560
+ --color-text-faint: #9ca3af;
561
+
562
+ --color-danger: #dc2626;
563
+ --color-danger-hover: #b91c1c;
564
+ --color-danger-muted: rgba(220, 38, 38, 0.08);
565
+
566
+ --color-success: #059669;
567
+ --color-success-muted: rgba(5, 150, 105, 0.08);
568
+
569
+ --color-warning: #d97706;
570
+ --color-warning-muted: rgba(217, 119, 6, 0.08);
571
+
572
+ --color-info: #0891b2;
573
+ --color-info-muted: rgba(8, 145, 178, 0.08);
574
+
575
+ --quark-page-bg: var(--color-bg);
576
+ --quark-grid-line: rgba(0, 0, 0, 0.07);
577
+ --quark-home-muted: #9ca3af;
578
+ --quark-home-sep: #d1d5db;
579
+ --quark-home-link-idle: #6b7280;
580
+ --quark-health-text: #9ca3af;
581
+ --quark-health-sep: #d1d5db;
582
+ --quark-health-dot-idle: #d1d5db;
583
+ --quark-logo-dark-arc: #2d3436;
584
+
585
+ /* Component variable light overrides */
586
+ --checkbox-bg: transparent;
587
+ --checkbox-border: #d1d5db;
588
+ --checkbox-accent: #2563eb;
589
+ --checkbox-ring: color-mix(in srgb, #3b82f6 40%, transparent);
590
+ --checkbox-disabled-opacity: 0.6;
591
+ --checkbox-label-text: #374151;
592
+ --skeleton-bg: rgba(229, 231, 235, 0.8);
593
+ --navbar-inset-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.02);
594
+ }
595
+
596
+ /*
597
+ * ─── TAILWIND THEME BRIDGE ───────────────────────────────────────────────────
598
+ *
599
+ * Register CSS vars as first-class Tailwind v4 utilities.
600
+ * Components use bg-primary, text-text-muted, border-border, etc.
601
+ * No arbitrary [#hex] values needed inside component source files.
602
+ */
603
+ @theme inline {
604
+ --color-primary: var(--color-primary);
605
+ --color-primary-hover: var(--color-primary-hover);
606
+ --color-primary-muted: var(--color-primary-muted);
607
+
608
+ --color-bg: var(--color-bg);
609
+ --color-surface: var(--color-surface);
610
+ --color-surface-hover: var(--color-surface-hover);
611
+
612
+ --color-border: var(--color-border);
613
+ --color-border-hover: var(--color-border-hover);
614
+
615
+ --color-text: var(--color-text);
616
+ --color-text-muted: var(--color-text-muted);
617
+ --color-text-faint: var(--color-text-faint);
618
+
619
+ --color-danger: var(--color-danger);
620
+ --color-danger-hover: var(--color-danger-hover);
621
+ --color-danger-muted: var(--color-danger-muted);
622
+
623
+ --color-success: var(--color-success);
624
+ --color-success-muted: var(--color-success-muted);
625
+
626
+ --color-warning: var(--color-warning);
627
+ --color-warning-muted: var(--color-warning-muted);
628
+
629
+ --color-info: var(--color-info);
630
+ --color-info-muted: var(--color-info-muted);
631
+
632
+ --radius-default: var(--radius-default);
633
+ }
634
+
635
+ /* Theme toggle pill - CSS-driven so the toggle reflects data-theme
636
+ * immediately from the blocking script, with no React-state flash. */
637
+ :root,
638
+ :root[data-theme="light"] {
639
+ --toggle-track-bg: #e5e7eb;
640
+ --toggle-track-border: #d1d5db;
641
+ --toggle-knob-bg: #9ca3af;
642
+ --toggle-knob-x: translateX(0);
643
+ }
644
+ :root[data-theme="dark"] {
645
+ --toggle-track-bg: #131d30;
646
+ --toggle-track-border: #253650;
647
+ --toggle-knob-bg: #377dff;
648
+ --toggle-knob-x: translateX(14px);
649
+ }
650
+
651
+ /* Toast SVG strokes - per-variant dark overrides.
652
+ /*
653
+ * ─── BUTTON VARIANTS ─────────────────────────────────────────────────────────
654
+ * Each variant overrides the generic --btn-* variables.
655
+ */
656
+ [data-btn-variant="default"] {
657
+ --btn-bg: var(--color-surface);
658
+ --btn-border: var(--color-border);
659
+ --btn-text: var(--color-text);
660
+ --btn-hover-brightness: 1;
661
+ --btn-hover-shadow: none;
662
+ --btn-hover-bg: var(--color-surface-hover);
663
+ --btn-hover-text: var(--color-text);
664
+ --btn-ring: var(--color-border-hover);
665
+ }
666
+ [data-btn-variant="primary"] {
667
+ --btn-bg: var(--color-primary-hover);
668
+ --btn-border: var(--color-primary-hover);
669
+ --btn-text: #ffffff;
670
+ --btn-hover-brightness: 0.95;
671
+ --btn-hover-shadow:
672
+ 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
673
+ --btn-ring: color-mix(in srgb, var(--color-primary) 60%, transparent);
674
+ }
675
+ [data-btn-variant="secondary"] {
676
+ --btn-bg: var(--color-surface);
677
+ --btn-border: var(--color-border-hover);
678
+ --btn-text: var(--color-text);
679
+ --btn-hover-brightness: 1;
680
+ --btn-hover-shadow: none;
681
+ --btn-hover-bg: var(--color-surface-hover);
682
+ --btn-ring: var(--color-border-hover);
683
+ }
684
+ [data-btn-variant="danger"] {
685
+ --btn-bg: var(--color-danger-hover);
686
+ --btn-border: var(--color-danger-hover);
687
+ --btn-text: #ffffff;
688
+ --btn-hover-brightness: 0.95;
689
+ --btn-hover-shadow:
690
+ 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
691
+ --btn-ring: color-mix(in srgb, var(--color-danger) 60%, transparent);
692
+ }
693
+ [data-btn-variant="ghost"] {
694
+ --btn-bg: transparent;
695
+ --btn-border: transparent;
696
+ --btn-text: var(--color-text-faint);
697
+ --btn-hover-brightness: 1;
698
+ --btn-hover-shadow: none;
699
+ --btn-hover-bg: var(--color-surface-hover);
700
+ --btn-hover-text: var(--color-text);
701
+ --btn-ring: var(--color-border-hover);
702
+ }
703
+ [data-btn-variant="success"] {
704
+ --btn-bg: var(--color-success-muted);
705
+ --btn-border: var(--color-success);
706
+ --btn-text: var(--color-success);
707
+ --btn-hover-brightness: 1;
708
+ --btn-hover-shadow: none;
709
+ --btn-hover-bg: color-mix(
710
+ in srgb,
711
+ var(--color-success-muted) 80%,
712
+ transparent
713
+ );
714
+ --btn-ring: color-mix(in srgb, var(--color-success) 60%, transparent);
715
+ }
716
+ [data-btn-variant="warning"] {
717
+ --btn-bg: var(--color-warning-muted);
718
+ --btn-border: var(--color-warning);
719
+ --btn-text: var(--color-warning);
720
+ --btn-hover-brightness: 1;
721
+ --btn-hover-shadow: none;
722
+ --btn-hover-bg: color-mix(
723
+ in srgb,
724
+ var(--color-warning-muted) 80%,
725
+ transparent
726
+ );
727
+ --btn-ring: color-mix(in srgb, var(--color-warning) 60%, transparent);
728
+ }
729
+ [data-btn-variant="info"] {
730
+ --btn-bg: var(--color-info-muted);
731
+ --btn-border: var(--color-info);
732
+ --btn-text: var(--color-info);
733
+ --btn-hover-brightness: 1;
734
+ --btn-hover-shadow: none;
735
+ --btn-hover-bg: color-mix(in srgb, var(--color-info-muted) 80%, transparent);
736
+ --btn-ring: color-mix(in srgb, var(--color-info) 60%, transparent);
737
+ }
738
+ [data-btn-variant="outline"] {
739
+ --btn-bg: transparent;
740
+ --btn-border: var(--color-primary);
741
+ --btn-text: var(--color-primary);
742
+ --btn-hover-brightness: 1;
743
+ --btn-hover-shadow: none;
744
+ --btn-hover-bg: var(--color-primary-muted);
745
+ --btn-ring: color-mix(in srgb, var(--color-primary) 40%, transparent);
746
+ }
747
+ [data-btn-variant="solid"] {
748
+ --btn-bg: var(--color-primary);
749
+ --btn-border: var(--color-primary);
750
+ --btn-text: #ffffff;
751
+ --btn-hover-brightness: 1;
752
+ --btn-hover-shadow: none;
753
+ --btn-hover-opacity: 0.9;
754
+ --btn-ring: color-mix(in srgb, var(--color-primary) 60%, transparent);
755
+ }
756
+
757
+ /*
758
+ * ─── BADGE VARIANTS ──────────────────────────────────────────────────────────
759
+ */
760
+ [data-badge-variant="default"] {
761
+ --badge-bg: var(--color-surface-hover);
762
+ --badge-border: var(--color-border);
763
+ --badge-text: var(--color-text-muted);
764
+ }
765
+ [data-badge-variant="primary"] {
766
+ --badge-bg: var(--color-primary-muted);
767
+ --badge-border: color-mix(in srgb, var(--color-primary) 40%, transparent);
768
+ --badge-text: var(--color-primary);
769
+ }
770
+ [data-badge-variant="success"] {
771
+ --badge-bg: var(--color-success-muted);
772
+ --badge-border: color-mix(in srgb, var(--color-success) 40%, transparent);
773
+ --badge-text: var(--color-success);
774
+ }
775
+ [data-badge-variant="warning"] {
776
+ --badge-bg: var(--color-warning-muted);
777
+ --badge-border: color-mix(in srgb, var(--color-warning) 40%, transparent);
778
+ --badge-text: var(--color-warning);
779
+ }
780
+ [data-badge-variant="danger"] {
781
+ --badge-bg: var(--color-danger-muted);
782
+ --badge-border: color-mix(in srgb, var(--color-danger) 40%, transparent);
783
+ --badge-text: var(--color-danger);
784
+ }
785
+ [data-badge-variant="info"] {
786
+ --badge-bg: var(--color-info-muted);
787
+ --badge-border: color-mix(in srgb, var(--color-info) 40%, transparent);
788
+ --badge-text: var(--color-info);
789
+ }
790
+
791
+ /*
792
+ * ─── TOAST VARIANTS ──────────────────────────────────────────────────────────
793
+ */
794
+ [data-toast-variant="default"] {
795
+ --toast-bg: var(--color-primary-muted);
796
+ --toast-border: color-mix(in srgb, var(--color-primary) 40%, transparent);
797
+ --toast-text: var(--color-primary);
798
+ --toast-shadow:
799
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
800
+ }
801
+ [data-toast-variant="success"] {
802
+ --toast-bg: var(--color-success-muted);
803
+ --toast-border: color-mix(in srgb, var(--color-success) 40%, transparent);
804
+ --toast-text: var(--color-success);
805
+ --toast-shadow:
806
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
807
+ }
808
+ [data-toast-variant="error"] {
809
+ --toast-bg: var(--color-danger-muted);
810
+ --toast-border: color-mix(in srgb, var(--color-danger) 40%, transparent);
811
+ --toast-text: var(--color-danger);
812
+ --toast-shadow:
813
+ 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
814
+ }
815
+
816
+ /* Toast SVG strokes — per-variant dark overrides.
817
+ * Light uses :root defaults (white strokes on dark toast bg).
818
+ * The blocking script in layout.js ensures data-theme is always set. */
819
+ [data-theme="dark"] [data-toast-variant="default"] {
820
+ --quark-toast-track: rgba(55, 125, 255, 0.12);
821
+ --quark-toast-progress: rgba(55, 125, 255, 0.7);
822
+ }
823
+ [data-theme="dark"] [data-toast-variant="success"] {
824
+ --quark-toast-track: rgba(52, 211, 153, 0.12);
825
+ --quark-toast-progress: rgba(52, 211, 153, 0.7);
826
+ }
827
+ [data-theme="dark"] [data-toast-variant="error"] {
828
+ --quark-toast-track: rgba(255, 71, 87, 0.12);
829
+ --quark-toast-progress: rgba(255, 71, 87, 0.7);
830
+ }
831
+
832
+ /* Pin the document background to match the app shell - prevents overscroll flash */
833
+ html,
834
+ body {
835
+ background-color: var(--quark-page-bg);
836
+ }
837
+
838
+ .quark-home-main,
839
+ .quark-page-grid {
840
+ background-color: var(--quark-page-bg);
841
+ background-image:
842
+ linear-gradient(var(--quark-grid-line) 1px, transparent 1px),
843
+ linear-gradient(90deg, var(--quark-grid-line) 1px, transparent 1px);
844
+ background-size: 40px 40px;
845
+ }
846
+
847
+ /* Native dialog - browser default positions it at the top; force viewport-center */
848
+ dialog {
849
+ position: fixed;
850
+ inset: 0;
851
+ margin: auto;
852
+ }
853
+
854
+ /* Home page - footer fades in after animation takes the stage */
855
+ @keyframes quark-home-fade-in {
856
+ from {
857
+ opacity: 0;
858
+ }
859
+ to {
860
+ opacity: 1;
861
+ }
862
+ }
863
+
864
+ .quark-home-footer {
865
+ animation: quark-home-fade-in 0.6s ease-out 1.2s both;
866
+ }
867
+
868
+ .quark-home-label {
869
+ color: var(--quark-home-muted);
870
+ font-size: 11px;
871
+ letter-spacing: 0.15em;
872
+ }
873
+
874
+ .quark-home-sep {
875
+ color: var(--quark-home-sep);
876
+ font-family: monospace;
877
+ }
878
+
879
+ .quark-home-link {
880
+ color: var(--quark-home-link-idle);
881
+ text-decoration: none;
882
+ font-family: monospace;
883
+ transition: color 0.15s ease;
884
+ }
885
+
886
+ .quark-home-link:hover {
887
+ color: var(--quark-home-link-hover);
888
+ }
889
+
890
+ .quark-home-link:focus-visible {
891
+ outline: 1px solid var(--quark-home-link-hover);
892
+ outline-offset: 2px;
893
+ border-radius: 2px;
894
+ }
895
+
896
+ .quark-home-external-icon {
897
+ display: inline-block;
898
+ width: 0.65em;
899
+ height: 0.65em;
900
+ margin-left: 3px;
901
+ vertical-align: 0.05em;
902
+ opacity: 0.6;
903
+ transition: opacity 0.15s ease;
904
+ }
905
+
906
+ .quark-home-link:hover .quark-home-external-icon {
907
+ opacity: 1;
908
+ }
909
+
910
+ /* Home page - ASCII panels grid */
911
+ .quark-home-panels {
912
+ display: grid;
913
+ grid-template-columns: repeat(3, 1fr);
914
+ gap: 1rem;
915
+ max-width: 820px;
916
+ width: 100%;
917
+ padding: 0 1rem;
918
+ margin: 0 auto;
919
+ }
920
+
921
+ @media (max-width: 820px) {
922
+ .quark-home-panels {
923
+ grid-template-columns: 1fr;
924
+ max-width: 400px;
925
+ }
926
+ }
927
+
928
+ .quark-ascii-panel {
929
+ position: relative;
930
+ }
931
+
932
+ .quark-ascii-box {
933
+ margin: 0;
934
+ padding: 0;
935
+ font-family: monospace;
936
+ font-size: 11px;
937
+ line-height: 1.4;
938
+ color: var(--quark-home-link-idle);
939
+ white-space: pre;
940
+ }
941
+
942
+ .quark-ascii-char {
943
+ color: var(--quark-home-link-idle);
944
+ }
945
+
946
+ .quark-ascii-line {
947
+ display: block;
948
+ }
949
+
950
+ .quark-ascii-copy {
951
+ position: absolute;
952
+ bottom: 0.8rem;
953
+ right: 0.6rem;
954
+ background: none;
955
+ border: none;
956
+ color: #6b7a99;
957
+ cursor: pointer;
958
+ font-family: monospace;
959
+ font-size: 10px;
960
+ text-decoration: underline;
961
+ text-underline-offset: 2px;
962
+ padding: 0;
963
+ line-height: 1;
964
+ transition: color 0.15s ease;
965
+ }
966
+
967
+ .quark-ascii-copy:hover {
968
+ color: var(--color-primary);
969
+ }
970
+
971
+ .quark-ascii-copy--copied {
972
+ color: var(--color-success);
973
+ text-decoration: none;
974
+ }
975
+
976
+ /* Floating theme toggle - top-right corner, always visible */
977
+ .floating-theme-toggle {
978
+ position: fixed;
979
+ top: 1.5rem;
980
+ right: 1.5rem;
981
+ z-index: 50;
982
+ display: flex;
983
+ align-items: center;
984
+ justify-content: center;
985
+ width: 2.5rem;
986
+ height: 2.5rem;
987
+ background-color: var(--color-surface);
988
+ border: 1px solid var(--color-border);
989
+ border-radius: 50%;
990
+ cursor: pointer;
991
+ color: var(--color-text-muted);
992
+ transition:
993
+ background-color 0.2s ease,
994
+ border-color 0.2s ease,
995
+ transform 0.15s ease;
996
+ font-size: 1.25rem;
997
+ line-height: 1;
998
+ padding: 0;
999
+ }
1000
+
1001
+ .floating-theme-toggle:hover {
1002
+ background-color: var(--color-surface-hover);
1003
+ border-color: var(--color-border-hover);
1004
+ color: var(--color-text);
1005
+ transform: scale(1.08);
1006
+ }
1007
+
1008
+ .floating-theme-toggle:active {
1009
+ transform: scale(0.95);
1010
+ }
1011
+
1012
+ .floating-theme-toggle:focus-visible {
1013
+ outline: 2px solid var(--color-primary);
1014
+ outline-offset: 2px;
1015
+ }
1016
+
1017
+ .floating-theme-toggle-icon {
1018
+ display: block;
1019
+ width: 1rem;
1020
+ height: 1rem;
1021
+ transition: transform 0.2s ease;
1022
+ }
1023
+
1024
+ /* Subtle rotation on toggle */
1025
+ .floating-theme-toggle:active .floating-theme-toggle-icon {
1026
+ transform: rotate(180deg);
1027
+ }
1028
+
1029
+ @keyframes quark-dialog-in {
1030
+ from {
1031
+ opacity: 0;
1032
+ transform: scale(0.95) translateY(-8px);
1033
+ }
1034
+ to {
1035
+ opacity: 1;
1036
+ transform: scale(1) translateY(0);
1037
+ }
1038
+ }
1039
+
1040
+ @keyframes quark-dialog-out {
1041
+ from {
1042
+ opacity: 1;
1043
+ transform: scale(1) translateY(0);
1044
+ }
1045
+ to {
1046
+ opacity: 0;
1047
+ transform: scale(0.95) translateY(-8px);
1048
+ }
1049
+ }
1050
+
1051
+ /*
1052
+ * ─── AUTH LAYOUT ─────────────────────────────────────────────────────────────
1053
+ *
1054
+ * Split-panel auth layout: authority branding panel (left) + form panel (right).
1055
+ * On mobile the authority panel collapses - only the logo bar is shown.
1056
+ */
1057
+ @keyframes quark-auth-enter {
1058
+ from {
1059
+ opacity: 0;
1060
+ }
1061
+ to {
1062
+ opacity: 1;
1063
+ }
1064
+ }
1065
+
1066
+ .quark-auth-layout {
1067
+ display: grid;
1068
+ grid-template-columns: 1fr;
1069
+ min-height: 100dvh;
1070
+ }
1071
+
1072
+ @media (min-width: 768px) {
1073
+ .quark-auth-layout {
1074
+ grid-template-columns: 2fr 3fr;
1075
+ }
1076
+ }
1077
+
1078
+ .quark-auth-panel {
1079
+ animation: quark-auth-enter 300ms linear both;
1080
+ }
1081
+
1082
+ .quark-auth-brand {
1083
+ display: none;
1084
+ background-color: var(--color-bg);
1085
+ background-image:
1086
+ linear-gradient(var(--quark-grid-line) 1px, transparent 1px),
1087
+ linear-gradient(90deg, var(--quark-grid-line) 1px, transparent 1px);
1088
+ background-size: 40px 40px;
1089
+ border-right: 1px solid var(--color-border);
1090
+ }
1091
+
1092
+ @media (min-width: 768px) {
1093
+ .quark-auth-brand {
1094
+ display: flex;
1095
+ flex-direction: column;
1096
+ justify-content: center;
1097
+ padding: 3rem;
1098
+ }
1099
+ }
1100
+
1101
+ .quark-auth-form {
1102
+ display: flex;
1103
+ flex-direction: column;
1104
+ justify-content: center;
1105
+ align-items: center;
1106
+ padding: 2rem 1.5rem;
1107
+ background-color: var(--color-surface);
1108
+ }
1109
+
1110
+ @media (min-width: 768px) {
1111
+ .quark-auth-form {
1112
+ padding: 3rem;
1113
+ }
1114
+ }
1115
+
1116
+ @keyframes quark-toast-in {
1117
+ from {
1118
+ opacity: 0;
1119
+ transform: translateY(16px);
1120
+ }
1121
+ to {
1122
+ opacity: 1;
1123
+ transform: translateY(0);
1124
+ }
1125
+ }
1126
+
1127
+ @keyframes quark-toast-out {
1128
+ from {
1129
+ opacity: 1;
1130
+ transform: translateY(0);
1131
+ }
1132
+ to {
1133
+ opacity: 0;
1134
+ transform: translateY(16px);
1135
+ }
1136
+ }
1137
+
1138
+ @keyframes slide-in-from-right {
1139
+ from {
1140
+ opacity: 0;
1141
+ transform: translateX(24px);
1142
+ }
1143
+ to {
1144
+ opacity: 1;
1145
+ transform: translateX(0);
1146
+ }
1147
+ }
1148
+
1149
+ @keyframes slide-in-from-left {
1150
+ from {
1151
+ opacity: 0;
1152
+ transform: translateX(-24px);
1153
+ }
1154
+ to {
1155
+ opacity: 1;
1156
+ transform: translateX(0);
1157
+ }
1158
+ }
1159
+
1160
+ .animate-slide-in {
1161
+ animation: slide-in-from-right 300ms ease-out both;
1162
+ }
1163
+
1164
+ .animate-slide-in-back {
1165
+ animation: slide-in-from-left 300ms ease-out both;
1166
+ }