@blurrow/ui 0.2.0 → 0.2.1

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 (271) hide show
  1. package/README.md +48 -36
  2. package/dist/Accordion.d.ts +17 -0
  3. package/dist/Accordion.js +138 -0
  4. package/dist/Accordion.js.map +1 -0
  5. package/dist/Alert.d.ts +13 -0
  6. package/dist/Alert.js +69 -0
  7. package/dist/Alert.js.map +1 -0
  8. package/dist/AuthorCard.d.ts +12 -0
  9. package/dist/AuthorCard.js +57 -0
  10. package/dist/AuthorCard.js.map +1 -0
  11. package/dist/Avatar.d.ts +21 -0
  12. package/dist/Avatar.js +96 -0
  13. package/dist/Avatar.js.map +1 -0
  14. package/dist/Badge.d.ts +12 -0
  15. package/dist/Badge.js +46 -0
  16. package/dist/Badge.js.map +1 -0
  17. package/dist/Banner.d.ts +15 -0
  18. package/dist/Banner.js +68 -0
  19. package/dist/Banner.js.map +1 -0
  20. package/dist/Breadcrumb.d.ts +13 -0
  21. package/dist/Breadcrumb.js +45 -0
  22. package/dist/Breadcrumb.js.map +1 -0
  23. package/dist/Button.d.ts +19 -0
  24. package/dist/Button.js +282 -0
  25. package/dist/Button.js.map +1 -0
  26. package/dist/ButtonGroup.d.ts +19 -0
  27. package/dist/ButtonGroup.js +62 -0
  28. package/dist/ButtonGroup.js.map +1 -0
  29. package/dist/Card.d.ts +40 -0
  30. package/dist/Card.js +175 -0
  31. package/dist/Card.js.map +1 -0
  32. package/dist/CardShell.d.ts +18 -0
  33. package/dist/CardShell.js +58 -0
  34. package/dist/CardShell.js.map +1 -0
  35. package/dist/Carousel.d.ts +22 -0
  36. package/dist/Carousel.js +247 -0
  37. package/dist/Carousel.js.map +1 -0
  38. package/dist/Checkbox.d.ts +15 -0
  39. package/dist/Checkbox.js +84 -0
  40. package/dist/Checkbox.js.map +1 -0
  41. package/dist/CloseButton.d.ts +12 -0
  42. package/dist/CloseButton.js +63 -0
  43. package/dist/CloseButton.js.map +1 -0
  44. package/dist/CodeBlock.d.ts +12 -0
  45. package/dist/CodeBlock.js +221 -0
  46. package/dist/CodeBlock.js.map +1 -0
  47. package/dist/ColorPicker.d.ts +24 -0
  48. package/dist/ColorPicker.js +366 -0
  49. package/dist/ColorPicker.js.map +1 -0
  50. package/dist/Combobox.d.ts +34 -0
  51. package/dist/Combobox.js +255 -0
  52. package/dist/Combobox.js.map +1 -0
  53. package/dist/CommandPalette.d.ts +25 -0
  54. package/dist/CommandPalette.js +304 -0
  55. package/dist/CommandPalette.js.map +1 -0
  56. package/dist/ContextMenu.d.ts +16 -0
  57. package/dist/ContextMenu.js +209 -0
  58. package/dist/ContextMenu.js.map +1 -0
  59. package/dist/DataList.d.ts +15 -0
  60. package/dist/DataList.js +42 -0
  61. package/dist/DataList.js.map +1 -0
  62. package/dist/DatePicker.d.ts +65 -0
  63. package/dist/DatePicker.js +391 -0
  64. package/dist/DatePicker.js.map +1 -0
  65. package/dist/DateRangePicker.d.ts +34 -0
  66. package/dist/DateRangePicker.js +180 -0
  67. package/dist/DateRangePicker.js.map +1 -0
  68. package/dist/Drawer.d.ts +17 -0
  69. package/dist/Drawer.js +132 -0
  70. package/dist/Drawer.js.map +1 -0
  71. package/dist/DropdownMenu.d.ts +23 -0
  72. package/dist/DropdownMenu.js +185 -0
  73. package/dist/DropdownMenu.js.map +1 -0
  74. package/dist/EmptyState.d.ts +13 -0
  75. package/dist/EmptyState.js +55 -0
  76. package/dist/EmptyState.js.map +1 -0
  77. package/dist/FieldMessage.d.ts +26 -0
  78. package/dist/FieldMessage.js +95 -0
  79. package/dist/FieldMessage.js.map +1 -0
  80. package/dist/FileUpload.d.ts +32 -0
  81. package/dist/FileUpload.js +253 -0
  82. package/dist/FileUpload.js.map +1 -0
  83. package/dist/Floating.d.ts +32 -0
  84. package/dist/Floating.js +95 -0
  85. package/dist/Floating.js.map +1 -0
  86. package/dist/HoverCard.d.ts +18 -0
  87. package/dist/HoverCard.js +90 -0
  88. package/dist/HoverCard.js.map +1 -0
  89. package/dist/Input.d.ts +17 -0
  90. package/dist/Input.js +96 -0
  91. package/dist/Input.js.map +1 -0
  92. package/dist/Kbd.d.ts +9 -0
  93. package/dist/Kbd.js +24 -0
  94. package/dist/Kbd.js.map +1 -0
  95. package/dist/Modal.d.ts +15 -0
  96. package/dist/Modal.js +109 -0
  97. package/dist/Modal.js.map +1 -0
  98. package/dist/NumberInput.d.ts +23 -0
  99. package/dist/NumberInput.js +162 -0
  100. package/dist/NumberInput.js.map +1 -0
  101. package/dist/OtpInput.d.ts +26 -0
  102. package/dist/OtpInput.js +173 -0
  103. package/dist/OtpInput.js.map +1 -0
  104. package/dist/Pagination.d.ts +11 -0
  105. package/dist/Pagination.js +148 -0
  106. package/dist/Pagination.js.map +1 -0
  107. package/dist/Popover.d.ts +13 -0
  108. package/dist/Popover.js +98 -0
  109. package/dist/Popover.js.map +1 -0
  110. package/dist/Progress.d.ts +12 -0
  111. package/dist/Progress.js +61 -0
  112. package/dist/Progress.js.map +1 -0
  113. package/dist/QuoteCard.d.ts +13 -0
  114. package/dist/QuoteCard.js +92 -0
  115. package/dist/QuoteCard.js.map +1 -0
  116. package/dist/Radio.d.ts +19 -0
  117. package/dist/Radio.js +161 -0
  118. package/dist/Radio.js.map +1 -0
  119. package/dist/RadioCards.d.ts +27 -0
  120. package/dist/RadioCards.js +205 -0
  121. package/dist/RadioCards.js.map +1 -0
  122. package/dist/ReactionBar.d.ts +15 -0
  123. package/dist/ReactionBar.js +92 -0
  124. package/dist/ReactionBar.js.map +1 -0
  125. package/dist/ReadingProgress.d.ts +8 -0
  126. package/dist/ReadingProgress.js +51 -0
  127. package/dist/ReadingProgress.js.map +1 -0
  128. package/dist/ReadingTime.d.ts +9 -0
  129. package/dist/ReadingTime.js +39 -0
  130. package/dist/ReadingTime.js.map +1 -0
  131. package/dist/Select.d.ts +20 -0
  132. package/dist/Select.js +244 -0
  133. package/dist/Select.js.map +1 -0
  134. package/dist/Separator.d.ts +14 -0
  135. package/dist/Separator.js +33 -0
  136. package/dist/Separator.js.map +1 -0
  137. package/dist/ShareButtons.d.ts +10 -0
  138. package/dist/ShareButtons.js +107 -0
  139. package/dist/ShareButtons.js.map +1 -0
  140. package/dist/Sidebar.d.ts +30 -0
  141. package/dist/Sidebar.js +140 -0
  142. package/dist/Sidebar.js.map +1 -0
  143. package/dist/Skeleton.d.ts +15 -0
  144. package/dist/Skeleton.js +67 -0
  145. package/dist/Skeleton.js.map +1 -0
  146. package/dist/Slider.d.ts +27 -0
  147. package/dist/Slider.js +182 -0
  148. package/dist/Slider.js.map +1 -0
  149. package/dist/Spinner.d.ts +12 -0
  150. package/dist/Spinner.js +24 -0
  151. package/dist/Spinner.js.map +1 -0
  152. package/dist/SpotlightCard.d.ts +17 -0
  153. package/dist/SpotlightCard.js +87 -0
  154. package/dist/SpotlightCard.js.map +1 -0
  155. package/dist/Stat.d.ts +19 -0
  156. package/dist/Stat.js +71 -0
  157. package/dist/Stat.js.map +1 -0
  158. package/dist/StatCard.d.ts +24 -0
  159. package/dist/StatCard.js +212 -0
  160. package/dist/StatCard.js.map +1 -0
  161. package/dist/Steps.d.ts +14 -0
  162. package/dist/Steps.js +94 -0
  163. package/dist/Steps.js.map +1 -0
  164. package/dist/Table.d.ts +21 -0
  165. package/dist/Table.js +93 -0
  166. package/dist/Table.js.map +1 -0
  167. package/dist/TableOfContents.d.ts +17 -0
  168. package/dist/TableOfContents.js +92 -0
  169. package/dist/TableOfContents.js.map +1 -0
  170. package/dist/Tabs.d.ts +17 -0
  171. package/dist/Tabs.js +123 -0
  172. package/dist/Tabs.js.map +1 -0
  173. package/dist/TagInput.d.ts +16 -0
  174. package/dist/TagInput.js +169 -0
  175. package/dist/TagInput.js.map +1 -0
  176. package/dist/Textarea.d.ts +13 -0
  177. package/dist/Textarea.js +74 -0
  178. package/dist/Textarea.js.map +1 -0
  179. package/dist/TimePicker.d.ts +28 -0
  180. package/dist/TimePicker.js +264 -0
  181. package/dist/TimePicker.js.map +1 -0
  182. package/dist/Timeline.d.ts +19 -0
  183. package/dist/Timeline.js +76 -0
  184. package/dist/Timeline.js.map +1 -0
  185. package/dist/Toast.d.ts +18 -0
  186. package/dist/Toast.js +318 -0
  187. package/dist/Toast.js.map +1 -0
  188. package/dist/Toggle.d.ts +13 -0
  189. package/dist/Toggle.js +84 -0
  190. package/dist/Toggle.js.map +1 -0
  191. package/dist/Tooltip.d.ts +13 -0
  192. package/dist/Tooltip.js +138 -0
  193. package/dist/Tooltip.js.map +1 -0
  194. package/dist/cli.js +172 -5
  195. package/dist/index.d.ts +68 -631
  196. package/dist/index.js +67 -5558
  197. package/dist/index.js.map +1 -1
  198. package/dist/registry/Accordion.tsx +6 -6
  199. package/dist/registry/Alert.tsx +13 -10
  200. package/dist/registry/AuthorCard.tsx +7 -7
  201. package/dist/registry/Avatar.tsx +5 -5
  202. package/dist/registry/Badge.tsx +12 -15
  203. package/dist/registry/Banner.tsx +15 -18
  204. package/dist/registry/Breadcrumb.tsx +3 -3
  205. package/dist/registry/Button.tsx +58 -69
  206. package/dist/registry/ButtonGroup.tsx +9 -9
  207. package/dist/registry/Card.tsx +15 -15
  208. package/dist/registry/CardShell.tsx +6 -6
  209. package/dist/registry/Carousel.tsx +19 -15
  210. package/dist/registry/Checkbox.tsx +14 -9
  211. package/dist/registry/CloseButton.tsx +2 -2
  212. package/dist/registry/CodeBlock.tsx +15 -15
  213. package/dist/registry/ColorPicker.tsx +404 -0
  214. package/dist/registry/Combobox.tsx +318 -0
  215. package/dist/registry/CommandPalette.tsx +31 -24
  216. package/dist/registry/ContextMenu.tsx +241 -0
  217. package/dist/registry/DataList.tsx +4 -4
  218. package/dist/registry/DatePicker.tsx +494 -0
  219. package/dist/registry/DateRangePicker.tsx +232 -0
  220. package/dist/registry/Drawer.tsx +17 -15
  221. package/dist/registry/DropdownMenu.tsx +39 -77
  222. package/dist/registry/EmptyState.tsx +8 -8
  223. package/dist/registry/FieldMessage.tsx +123 -0
  224. package/dist/registry/FileUpload.tsx +318 -0
  225. package/dist/registry/Floating.tsx +134 -0
  226. package/dist/registry/HoverCard.tsx +113 -0
  227. package/dist/registry/Input.tsx +54 -43
  228. package/dist/registry/Kbd.tsx +4 -5
  229. package/dist/registry/Modal.tsx +17 -15
  230. package/dist/registry/NumberInput.tsx +183 -0
  231. package/dist/registry/OtpInput.tsx +219 -0
  232. package/dist/registry/Pagination.tsx +15 -18
  233. package/dist/registry/Popover.tsx +40 -45
  234. package/dist/registry/Progress.tsx +4 -4
  235. package/dist/registry/QuoteCard.tsx +7 -7
  236. package/dist/registry/Radio.tsx +24 -21
  237. package/dist/registry/RadioCards.tsx +247 -0
  238. package/dist/registry/ReactionBar.tsx +5 -5
  239. package/dist/registry/ReadingProgress.tsx +2 -2
  240. package/dist/registry/ReadingTime.tsx +2 -2
  241. package/dist/registry/Select.tsx +28 -53
  242. package/dist/registry/Separator.tsx +52 -0
  243. package/dist/registry/ShareButtons.tsx +2 -2
  244. package/dist/registry/Sidebar.tsx +18 -18
  245. package/dist/registry/Skeleton.tsx +1 -1
  246. package/dist/registry/Slider.tsx +229 -0
  247. package/dist/registry/Spinner.tsx +32 -0
  248. package/dist/registry/SpotlightCard.tsx +6 -6
  249. package/dist/registry/Stat.tsx +4 -4
  250. package/dist/registry/StatCard.tsx +19 -19
  251. package/dist/registry/Steps.tsx +7 -7
  252. package/dist/registry/Table.tsx +12 -10
  253. package/dist/registry/TableOfContents.tsx +8 -8
  254. package/dist/registry/Tabs.tsx +6 -6
  255. package/dist/registry/TagInput.tsx +9 -9
  256. package/dist/registry/Textarea.tsx +5 -5
  257. package/dist/registry/TimePicker.tsx +315 -0
  258. package/dist/registry/Timeline.tsx +14 -14
  259. package/dist/registry/Toast.tsx +15 -12
  260. package/dist/registry/Toggle.tsx +7 -10
  261. package/dist/registry/Tooltip.tsx +21 -73
  262. package/dist/registry/styles.css +868 -62
  263. package/dist/registry/useFloatingPanel.ts +112 -0
  264. package/dist/styles.css +868 -62
  265. package/dist/useFloatingPanel.d.ts +24 -0
  266. package/dist/useFloatingPanel.js +89 -0
  267. package/dist/useFloatingPanel.js.map +1 -0
  268. package/dist/useModalBehavior.d.ts +15 -0
  269. package/dist/useModalBehavior.js +57 -0
  270. package/dist/useModalBehavior.js.map +1 -0
  271. package/package.json +1 -1
package/dist/styles.css CHANGED
@@ -1,14 +1,61 @@
1
1
  /* blurrow/ui: design tokens, materials and keyframes.
2
2
  Import after tailwindcss: @import "tailwindcss"; @import "@blurrow/ui/styles.css";
3
- The kit is set in Archivo; load it as the --font-archivo variable. */
3
+ The kit is set in Archivo; load it as the --font-archivo variable.
4
+
5
+ Light is the default. For dark, set data-theme="dark" on <html> (the
6
+ class "dark" works too), or on any element to darken just that part.
7
+ data-theme="system" follows the visitor's OS setting, and
8
+ data-theme="light" keeps a part of the page light inside a dark one. */
4
9
 
5
10
  @theme {
6
11
  --font-sans: var(--font-archivo), ui-sans-serif, system-ui, sans-serif;
7
12
  }
8
13
 
9
- /* ── Material tokens ─────────────────────────────────────────────── */
14
+ /* The theme's colours as Tailwind utilities: text-ink, bg-panel-face,
15
+ border-line… `inline` keeps the var() in every class, so a colour is
16
+ read where it's used and follows whichever theme that element is in. */
17
+ @theme inline {
18
+ --color-page: var(--page);
19
+ --color-panel-face: var(--panel-face);
20
+ --color-surface-hover: var(--surface-hover);
21
+ --color-surface-active: var(--surface-active);
22
+ --color-line: var(--line);
23
+ --color-line-strong: var(--line-strong);
24
+ --color-groove: var(--groove);
25
+ --color-track: var(--track);
26
+ --color-track-on: var(--track-on);
27
+
28
+ --color-ink-strong: var(--ink-strong);
29
+ --color-ink: var(--ink);
30
+ --color-ink-soft: var(--ink-soft);
31
+ --color-ink-label: var(--ink-label);
32
+ --color-ink-body: var(--ink-body);
33
+ --color-ink-muted: var(--ink-muted);
34
+ --color-ink-faint: var(--ink-faint);
35
+ --color-ink-ghost: var(--ink-ghost);
36
+ --color-ink-inverse: var(--ink-inverse);
37
+
38
+ --color-danger: var(--danger);
39
+ --color-danger-soft: var(--danger-soft);
40
+ --color-danger-wash: var(--danger-wash);
41
+ --color-success: var(--success);
42
+ --color-success-soft: var(--success-soft);
43
+ --color-warning: var(--warning);
44
+ --color-warning-ink: var(--warning-ink);
45
+ --color-accent: var(--accent);
46
+ --color-accent-soft: var(--accent-soft);
47
+
48
+ --color-code-keyword: var(--code-keyword);
49
+ --color-code-string: var(--code-string);
50
+ --color-code-tag: var(--code-tag);
51
+ --color-code-number: var(--code-number);
52
+ }
53
+
54
+ /* ── Materials ───────────────────────────────────────────────────── */
10
55
  /* One light source from above: highlights live on top edges, shade on
11
- bottom edges, and every raised face casts onto whatever holds it. */
56
+ bottom edges, and every raised face casts onto whatever holds it.
57
+ Materials are fixed: light is always light, dark is always dark.
58
+ Components read the theme roles further down, which point at these. */
12
59
  :root {
13
60
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
14
61
  --ease-out-soft: cubic-bezier(0.16, 1, 0.3, 1);
@@ -24,6 +71,14 @@
24
71
  0 0 3px rgba(17, 17, 17, 0.119),
25
72
  0 1px 4px -1px rgba(17, 17, 17, 0.1), inset 0 2px 3px -2px rgba(255, 255, 255, 1),
26
73
  inset 0 -2px 3px -2px rgba(17, 17, 17, 0.035);
74
+ --face-light-shadow-hover:
75
+ 0 0 3px rgba(17, 17, 17, 0.136), 0 2px 3px rgba(17, 17, 17, 0.1),
76
+ 0 4px 8px -2px rgba(17, 17, 17, 0.08), inset 0 2px 3px -2px rgba(255, 255, 255, 1),
77
+ inset 0 -2px 3px -2px rgba(17, 17, 17, 0.035);
78
+ /* A face with no bezel casts further when it lifts */
79
+ --face-light-float-shadow:
80
+ 0 0 3px rgba(17, 17, 17, 0.136), 0 2px 4px rgba(17, 17, 17, 0.08),
81
+ 0 6px 14px -4px rgba(17, 17, 17, 0.1), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
27
82
 
28
83
  /* Dark material */
29
84
  --bezel-dark: linear-gradient(180deg, #2c2c2c 0%, #0c0c0c 100%);
@@ -35,6 +90,31 @@
35
90
  0 0 3px rgba(0, 0, 0, 0.35),
36
91
  0 1px 4px -1px rgba(0, 0, 0, 0.3), inset 0 2px 3px -2px rgba(255, 255, 255, 0.105),
37
92
  inset 0 -2px 3px -2px rgba(0, 0, 0, 0.25);
93
+ --face-dark-shadow-hover:
94
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 2px 3px rgba(0, 0, 0, 0.45),
95
+ 0 4px 8px -2px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.135),
96
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.25);
97
+ --face-dark-float-shadow:
98
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 2px 4px rgba(0, 0, 0, 0.4),
99
+ 0 6px 14px -4px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.135);
100
+
101
+ /* Mid material: a raised face a few steps lighter than a dark surface —
102
+ present, but in the same family of tones (secondary on dark) */
103
+ --bezel-mid: linear-gradient(180deg, #2a2a2a 0%, #161616 100%);
104
+ --bezel-mid-shadow:
105
+ 0 0 0 0.5px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.35),
106
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.053);
107
+ --face-mid: linear-gradient(180deg, #353535 0%, #2a2a2a 55%, #242424 100%);
108
+ --face-mid-shadow:
109
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 1px 1.5px rgba(0, 0, 0, 0.4),
110
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09), inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
111
+ --face-mid-shadow-hover:
112
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 2px 3px rgba(0, 0, 0, 0.45),
113
+ 0 4px 8px -2px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.12),
114
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
115
+ --face-mid-float-shadow:
116
+ 0 0 3px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.4),
117
+ 0 6px 14px -4px rgba(0, 0, 0, 0.4), inset 0 2px 3px -2px rgba(255, 255, 255, 0.12);
38
118
 
39
119
  /* Sunken face (pressed) — the face drops flush with its bezel */
40
120
  --face-light-pressed:
@@ -42,12 +122,240 @@
42
122
  --face-dark-pressed:
43
123
  0 0 3px rgba(0, 0, 0, 0.35), inset 0 1px 2px rgba(0, 0, 0, 0.45);
44
124
 
125
+ /* Cards — the largest surfaces; pair with .rim */
126
+ --card-light: linear-gradient(180deg, #ffffff 0%, #fefefe 40%, #fafafa 100%);
127
+ --card-light-shadow:
128
+ 0 1px 3px rgba(17, 17, 17, 0.02), 0 4px 12px -2px rgba(17, 17, 17, 0.05),
129
+ 0 8px 24px -4px rgba(17, 17, 17, 0.04), 0 0 0 0.5px rgba(17, 17, 17, 0.036),
130
+ inset 0 2.5px 3px -2px rgba(255, 255, 255, 0.712);
131
+ --card-light-shadow-hover:
132
+ 0 2px 6px rgba(17, 17, 17, 0.03), 0 8px 24px -4px rgba(17, 17, 17, 0.08),
133
+ 0 16px 48px -8px rgba(17, 17, 17, 0.1), 0 32px 96px -16px rgba(17, 17, 17, 0.08),
134
+ 0 0 0 0.5px rgba(17, 17, 17, 0.048), inset 0 3px 3px -2px rgba(255, 255, 255, 1);
135
+ --card-dark:
136
+ radial-gradient(90% 60% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 75%),
137
+ radial-gradient(140% 100% at 30% 0%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 65%),
138
+ linear-gradient(180deg, #121212 0%, #0a0a0a 100%);
139
+ /* Same lift as the light card — only the material changes, not the height */
140
+ --card-dark-shadow:
141
+ 0 0 0 0.5px rgba(0, 0, 0, 0.33), 0 1px 3px rgba(17, 17, 17, 0.12),
142
+ 0 4px 12px -2px rgba(17, 17, 17, 0.14), 0 12px 28px -8px rgba(17, 17, 17, 0.2),
143
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.075);
144
+ --card-dark-shadow-hover:
145
+ 0 0 0 0.5px rgba(0, 0, 0, 0.54), 0 2px 6px rgba(17, 17, 17, 0.14),
146
+ 0 10px 28px -6px rgba(17, 17, 17, 0.22), 0 28px 64px -16px rgba(17, 17, 17, 0.22),
147
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
148
+
149
+ /* Card chassis is nearly the face's own tone — the hairline separator
150
+ between them does the dividing, not a change of colour. */
151
+ --card-bezel-light: linear-gradient(180deg, #fdfdfd 0%, #f7f7f7 100%);
152
+ --card-face-light-shadow:
153
+ 0 0 0 0.5px rgba(17, 17, 17, 0.042), 0 1px 2px rgba(17, 17, 17, 0.04),
154
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
155
+ --card-bezel-dark: linear-gradient(180deg, #151515 0%, #0e0e0e 100%);
156
+ --card-face-dark-shadow:
157
+ 0 0 0 0.5px rgba(255, 255, 255, 0.042), 0 1px 2px rgba(0, 0, 0, 0.4),
158
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.045);
159
+
160
+ /* Empty media slots: a whisper darker than the face they sit in */
161
+ --media-well-light: linear-gradient(180deg, rgba(17, 17, 17, 0.025) 0%, rgba(17, 17, 17, 0.04) 100%);
162
+ --media-well-dark: linear-gradient(180deg, rgba(0, 0, 0, 0.18) 0%, rgba(0, 0, 0, 0.26) 100%);
163
+
164
+ /* Icon tiles on cards — small raised faces */
165
+ --tile-light-shadow:
166
+ 0 0 3px rgba(17, 17, 17, 0.102), 0 1px 2px rgba(17, 17, 17, 0.08),
167
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
168
+ --tile-dark: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.04) 100%);
169
+ --tile-dark-shadow:
170
+ 0 0 0 0.5px rgba(255, 255, 255, 0.048), 0 2px 6px rgba(0, 0, 0, 0.35),
171
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
172
+
173
+ /* The dark card's own details (what theme="dark" wears on any page) */
174
+ --card-dark-rim: rgba(255, 255, 255, 0.1);
175
+ --divider-dark: rgba(255, 255, 255, 0.07);
176
+ --media-dots-dark: rgba(255, 255, 255, 0.09);
177
+ --media-dots-on-colour: rgba(255, 255, 255, 0.22);
178
+ --spark-dark: #ffffff;
179
+ --spark-dark-halo: rgba(255, 255, 255, 0.14);
180
+ --photo-edge-dark: rgba(255, 255, 255, 0.084);
181
+
182
+ /* Status tiles: coloured faces, lit like every other face */
183
+ --tile-success: linear-gradient(180deg, #10b981 0%, #059669 100%);
184
+ --tile-warning: linear-gradient(180deg, #fbbf24 0%, #f59e0b 100%);
185
+ --tile-error: linear-gradient(180deg, #f87171 0%, #dc2626 100%);
186
+ --tile-status-shadow:
187
+ 0 0 3px rgba(0, 0, 0, 0.212), 0 1px 2px rgba(0, 0, 0, 0.15),
188
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.188);
189
+ --lamp-success: #10b981;
190
+ --lamp-warning: #f59e0b;
191
+ --lamp-accent: #8b5cf6;
192
+ --lamp-lip: inset 0 1px 1px rgba(255, 255, 255, 0.35);
193
+
194
+ /* Violet: the accent banner, in the same build as the primary key */
195
+ --bezel-violet: linear-gradient(180deg, #4c2a99 0%, #2e1065 100%);
196
+ --bezel-violet-shadow:
197
+ 0 0 0 0.5px rgba(46, 16, 101, 0.36), 0 4px 12px -4px rgba(76, 29, 149, 0.45),
198
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
199
+ --face-violet: linear-gradient(180deg, #7c3aed 0%, #6d28d9 100%);
200
+ --face-violet-shadow:
201
+ 0 0 3px rgba(46, 16, 101, 0.35), 0 1px 2px rgba(46, 16, 101, 0.4),
202
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.165);
203
+ }
204
+
205
+ /* ── Theme roles ─────────────────────────────────────────────────── */
206
+ /* What the components read. Light points each role at a material above;
207
+ dark (below) gives each its own value. To re-brand, override these on
208
+ :root (and in the dark block); the materials can stay as they are. */
209
+ :root,
210
+ .light,
211
+ [data-theme='light'] {
212
+ /* Surfaces */
213
+ --page: #ebebeb;
214
+ --panel-face: #ffffff;
215
+ --panel-tray: linear-gradient(180deg, #fafafa 0%, #f6f6f6 100%);
216
+ --palette-tray: linear-gradient(180deg, #fcfcfc 0%, #f8f8f8 100%);
217
+ --surface-hover: oklch(98.5% 0 0);
218
+ --surface-active: oklch(97% 0 0);
219
+ --line: oklch(97% 0 0);
220
+ --line-strong: oklch(92.2% 0 0);
221
+ --hairline: rgba(17, 17, 17, 0.06);
222
+ --divider: rgba(17, 17, 17, 0.07);
223
+ --groove: color-mix(in oklab, oklch(20.5% 0 0) 8%, transparent);
224
+ --groove-lip: 1px 0 0 rgba(255, 255, 255, 0.9);
225
+ --groove-lip-below: 0 1px 0 rgba(255, 255, 255, 0.9);
226
+ --scrollbar: rgba(17, 17, 17, 0.15);
227
+ --dots: rgba(17, 17, 17, 0.07);
228
+ --shimmer: rgba(255, 255, 255, 0.55);
229
+ --sheen: rgba(255, 255, 255, 0.75);
230
+ --halo: 0 0 0 4px rgba(17, 17, 17, 0.06), 0 0 0 5px rgba(17, 17, 17, 0.03);
231
+ --focus-ring-inset: inset 0 0 0 2px rgba(17, 17, 17, 0.85);
232
+
233
+ /* Ink: text and icons, strongest to faintest */
234
+ --ink-strong: oklch(14.5% 0 0);
235
+ --ink: oklch(20.5% 0 0);
236
+ --ink-soft: oklch(26.9% 0 0);
237
+ --ink-label: oklch(37.1% 0 0);
238
+ --ink-body: oklch(43.9% 0 0);
239
+ --ink-muted: oklch(55.6% 0 0);
240
+ --ink-faint: oklch(70.8% 0 0);
241
+ --ink-ghost: oklch(87% 0 0);
242
+ /* Ink on the inverse face: primary keys, checked boxes, tooltips */
243
+ --ink-inverse: #ffffff;
244
+ /* Letterpress: text pressed into a raised face, lit from above */
245
+ --emboss: 0 1px 0 rgba(255, 255, 255, 0.9);
246
+ --emboss-soft: 0 1px 0 rgba(255, 255, 255, 0.7);
247
+ --emboss-inverse: 0 -1px 0 rgba(0, 0, 0, 0.4);
248
+ --emboss-inverse-deep: 0 -1px 0 rgba(0, 0, 0, 0.45);
249
+
250
+ /* Status */
251
+ --danger: oklch(57.7% 0.245 27.325);
252
+ --danger-soft: oklch(63.7% 0.237 25.331);
253
+ --danger-wash: oklch(97.1% 0.013 17.38);
254
+ --success: oklch(50.8% 0.118 165.612);
255
+ --success-soft: oklch(59.6% 0.145 163.225);
256
+ --success-strong: #047857;
257
+ --warning: oklch(66.6% 0.179 58.318);
258
+ --warning-ink: oklch(41.4% 0.112 45.904);
259
+ --accent: oklch(49.1% 0.27 292.581);
260
+ --accent-soft: oklch(54.1% 0.281 293.009);
261
+ --code-keyword: oklch(54.1% 0.281 293.009);
262
+ --code-string: oklch(50.8% 0.118 165.612);
263
+ --code-tag: oklch(50% 0.134 242.749);
264
+ --code-number: oklch(55.5% 0.163 48.998);
265
+ --lamp-default: #262626;
266
+
267
+ /* Raised: the everyday key (secondary buttons, thumbs, knobs, tiles) */
268
+ --bezel: var(--bezel-light);
269
+ --bezel-shadow: var(--bezel-light-shadow);
270
+ --face: var(--face-light);
271
+ --face-shadow: var(--face-light-shadow);
272
+ --face-shadow-hover: var(--face-light-shadow-hover);
273
+ --face-pressed: var(--face-light-pressed);
274
+ --face-float-shadow: var(--face-light-float-shadow);
275
+ --face-drop: 0 6px 16px -6px rgba(17, 17, 17, 0.15);
276
+ --tile: var(--face-light);
277
+ --tile-shadow: var(--tile-light-shadow);
278
+ --thumb-shadow:
279
+ 0 0 3px rgba(17, 17, 17, 0.102), 0 1px 2px rgba(17, 17, 17, 0.1),
280
+ 0 2px 6px -2px rgba(17, 17, 17, 0.08), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
281
+ --key-shadow:
282
+ 0 0 3px rgba(17, 17, 17, 0.136), 0 1.5px 1px rgba(17, 17, 17, 0.12),
283
+ 0 2px 3px -1px rgba(17, 17, 17, 0.1), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
284
+ --knob-shadow:
285
+ 0 0 3px rgba(17, 17, 17, 0.119), 0 1px 2px rgba(0, 0, 0, 0.14),
286
+ 0 3px 6px -1px rgba(0, 0, 0, 0.12), inset 0 2px 3px -2px rgba(255, 255, 255, 1),
287
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.04);
288
+
289
+ /* Inverse: the opposite of the page (primary, checked, selected) */
290
+ --bezel-inverse: var(--bezel-dark);
291
+ --bezel-inverse-shadow: var(--bezel-dark-shadow);
292
+ --face-inverse: var(--face-dark);
293
+ --face-inverse-shadow: var(--face-dark-shadow);
294
+ --face-inverse-shadow-hover: var(--face-dark-shadow-hover);
295
+ --face-inverse-pressed: var(--face-dark-pressed);
296
+ --face-inverse-float-shadow: var(--face-dark-float-shadow);
297
+ --inverse-lip: rgba(255, 255, 255, 0.12);
298
+ --dot-shadow: 0 1px 1px rgba(0, 0, 0, 0.3);
299
+ --fill-shadow: inset 0 2px 3px -2px rgba(255, 255, 255, 0.135), 0 0 2px rgba(0, 0, 0, 0.2);
300
+ --tooltip-shadow:
301
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.22),
302
+ 0 8px 24px -4px rgba(0, 0, 0, 0.3), inset 0 2px 3px -2px rgba(255, 255, 255, 0.105);
303
+ --badge-inverse-shadow:
304
+ 0 0 3px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.2),
305
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.105);
306
+ --progress-bar: linear-gradient(90deg, #0a0a0a 0%, #262626 100%);
307
+ --banner-bezel: var(--bezel-dark);
308
+ --banner-bezel-shadow: var(--bezel-dark-shadow);
309
+ --banner-face: var(--face-dark);
310
+ --banner-face-shadow: var(--face-dark-shadow);
311
+ --progress-bar-glow: 0 0 8px rgba(17, 17, 17, 0.3);
312
+
313
+ /* Outline and ghost keys */
314
+ --outline-bezel: linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(17, 17, 17, 0.04) 100%);
315
+ --outline-bezel-shadow: 0 0 0 0.5px rgba(17, 17, 17, 0.048), inset 0 2px 3px -2px rgba(255, 255, 255, 0.45);
316
+ --outline-face-shadow:
317
+ 0 0 3px rgba(17, 17, 17, 0.102), 0 1px 1px rgba(17, 17, 17, 0.06),
318
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
319
+ --outline-face-shadow-hover:
320
+ 0 0 3px rgba(17, 17, 17, 0.136), 0 1px 2px rgba(17, 17, 17, 0.08),
321
+ 0 3px 6px -2px rgba(17, 17, 17, 0.06), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
322
+ --outline-shadow: inset 0 0 2px rgba(17, 17, 17, 0.15);
323
+ --outline-shadow-hover: inset 0 0 2px rgba(17, 17, 17, 0.24), 0 1px 2px rgba(17, 17, 17, 0.05);
324
+ --outline-shadow-pressed: inset 0 0 2px rgba(17, 17, 17, 0.24), inset 0 1px 2px rgba(17, 17, 17, 0.06);
325
+ --ghost-shadow-hover: inset 0 0 2px rgba(17, 17, 17, 0.09);
326
+ --ghost-shadow-pressed: inset 0 1px 2px rgba(17, 17, 17, 0.08);
327
+
328
+ /* Badges */
329
+ --badge: linear-gradient(180deg, #ffffff 0%, #f1f1f1 100%);
330
+ --badge-shadow:
331
+ 0 0 3px rgba(17, 17, 17, 0.119), 0 1px 1.5px rgba(17, 17, 17, 0.07),
332
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
333
+ --badge-accent: linear-gradient(180deg, #f5f0ff 0%, #ebe1ff 100%);
334
+ --badge-accent-shadow:
335
+ 0 0 3px rgba(124, 58, 237, 0.255), 0 1px 1.5px rgba(124, 58, 237, 0.12),
336
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.637);
337
+
338
+ /* The warning banner: an amber bezel holding a pale amber face */
339
+ --bezel-warning: linear-gradient(180deg, #fde68a 0%, #f5c84b 100%);
340
+ --bezel-warning-shadow:
341
+ 0 0 0 0.5px rgba(180, 83, 9, 0.15), 0 4px 12px -4px rgba(217, 119, 6, 0.3),
342
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.45);
343
+ --face-warning: linear-gradient(180deg, #fffbeb 0%, #fef3c7 100%);
344
+ --face-warning-shadow:
345
+ 0 0 3px rgba(180, 83, 9, 0.255), 0 1px 2px rgba(180, 83, 9, 0.12),
346
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
347
+
45
348
  /* Fields — sunken: darker at the top, shade inside, a lit lip below.
46
349
  Raised means "press me", sunken means "fill me".
47
350
 
48
351
  Every state has the same five layers (edge, shade, lip, border, halo)
49
352
  so box-shadow interpolates between them instead of snapping: hover
50
353
  hints at the halo, focus lifts the well flush and draws the border. */
354
+ --field-from: #f2f2f2;
355
+ --field-to: #f8f8f8;
356
+ --field-hover-from: #eeeeee;
357
+ --field-hover-to: #f5f5f5;
358
+ --field-focus: #ffffff;
51
359
  --field: linear-gradient(180deg, #f2f2f2 0%, #f8f8f8 100%);
52
360
  --field-hover: linear-gradient(180deg, #eeeeee 0%, #f5f5f5 100%);
53
361
  --field-shadow:
@@ -74,15 +382,21 @@
74
382
  inset 0 0 2px rgba(220, 38, 38, 0.1), inset 0 1px 2px rgba(220, 38, 38, 0.02),
75
383
  0 1px 2px -1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(220, 38, 38, 0.7),
76
384
  0 0 0 4px rgba(220, 38, 38, 0.1);
385
+ /* A switch's channel: recessed when off, filled with the inverse when on */
386
+ --track: color-mix(in oklab, oklch(92.2% 0 0) 70%, transparent);
387
+ --track-shadow:
388
+ inset 0 1px 2px rgba(17, 17, 17, 0.12), inset 0 0 2px rgba(17, 17, 17, 0.12),
389
+ 0 1px 2px -1px rgba(255, 255, 255, 0.64);
390
+ --track-on: oklch(20.5% 0 0);
391
+ --track-on-shadow:
392
+ inset 0 1px 3px rgba(0, 0, 0, 0.5), inset 0 0 2px rgba(0, 0, 0, 0.3),
393
+ 0 1px 2px -1px rgba(255, 255, 255, 0.48);
77
394
 
78
395
  /* Wells hold a sliding thumb (tabs, pagination, segmented controls) */
79
396
  --well: linear-gradient(180deg, #ebebeb 0%, #f1f1f1 100%);
80
397
  --well-shadow:
81
398
  inset 0 0 2px rgba(17, 17, 17, 0.09), inset 0 1px 2px rgba(17, 17, 17, 0.08),
82
399
  0 1px 2px -1px rgba(255, 255, 255, 0.72);
83
- --thumb-shadow:
84
- 0 0 3px rgba(17, 17, 17, 0.102), 0 1px 2px rgba(17, 17, 17, 0.1),
85
- 0 2px 6px -2px rgba(17, 17, 17, 0.08), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
86
400
 
87
401
  /* Panels — a soft bezel holding a white face (lists, menus, alerts) */
88
402
  --panel-bezel: linear-gradient(180deg, #fbfbfb 0%, #f0f0f0 100%);
@@ -101,59 +415,538 @@
101
415
  0 32px 80px -16px rgba(17, 17, 17, 0.32), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
102
416
  --scrim: rgba(10, 10, 10, 0.4);
103
417
 
104
- /* Cards — the largest surfaces; pair with .rim */
105
- --card-light: linear-gradient(180deg, #ffffff 0%, #fefefe 40%, #fafafa 100%);
106
- --card-light-shadow:
107
- 0 1px 3px rgba(17, 17, 17, 0.02), 0 4px 12px -2px rgba(17, 17, 17, 0.05),
108
- 0 8px 24px -4px rgba(17, 17, 17, 0.04), 0 0 0 0.5px rgba(17, 17, 17, 0.036),
109
- inset 0 2.5px 3px -2px rgba(255, 255, 255, 0.712);
110
- --card-light-shadow-hover:
111
- 0 2px 6px rgba(17, 17, 17, 0.03), 0 8px 24px -4px rgba(17, 17, 17, 0.08),
112
- 0 16px 48px -8px rgba(17, 17, 17, 0.1), 0 32px 96px -16px rgba(17, 17, 17, 0.08),
113
- 0 0 0 0.5px rgba(17, 17, 17, 0.048), inset 0 3px 3px -2px rgba(255, 255, 255, 1);
114
- --card-dark:
115
- radial-gradient(90% 60% at 50% 0%, rgba(255, 255, 255, 0.06), transparent 75%),
116
- radial-gradient(140% 100% at 30% 0%, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0) 65%),
117
- linear-gradient(180deg, #121212 0%, #0a0a0a 100%);
118
- /* Same lift as the light card — only the material changes, not the height */
119
- --card-dark-shadow:
120
- 0 0 0 0.5px rgba(0, 0, 0, 0.33), 0 1px 3px rgba(17, 17, 17, 0.12),
121
- 0 4px 12px -2px rgba(17, 17, 17, 0.14), 0 12px 28px -8px rgba(17, 17, 17, 0.2),
122
- inset 0 2px 3px -2px rgba(255, 255, 255, 0.075);
123
- --card-dark-shadow-hover:
124
- 0 0 0 0.5px rgba(0, 0, 0, 0.54), 0 2px 6px rgba(17, 17, 17, 0.14),
125
- 0 10px 28px -6px rgba(17, 17, 17, 0.22), 0 28px 64px -16px rgba(17, 17, 17, 0.22),
126
- inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
418
+ /* Cards */
419
+ --card: var(--card-light);
420
+ --card-bezel: var(--card-bezel-light);
421
+ --card-shadow: var(--card-light-shadow);
422
+ --card-shadow-hover: var(--card-light-shadow-hover);
423
+ --card-face-shadow: var(--card-face-light-shadow);
424
+ --card-rim: rgba(255, 255, 255, 1);
425
+ --rim-default: rgba(255, 255, 255, 0.9);
426
+ --media-well: var(--media-well-light);
427
+ --media-dots: rgba(17, 17, 17, 0.09);
428
+ --slide-dots: rgba(17, 17, 17, 0.08);
429
+ --spark: #171717;
430
+ --spark-halo: rgba(17, 17, 17, 0.08);
431
+ --photo-edge: rgba(17, 17, 17, 0.048);
432
+ --cover:
433
+ radial-gradient(130% 90% at 25% 0%, rgba(255, 255, 255, 0.7), transparent 60%),
434
+ linear-gradient(180deg, #ececeb 0%, #f4f4f3 100%);
435
+ --cover-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.06), inset 0 -2px 3px -2px rgba(17, 17, 17, 0.05);
436
+ --avatar-lift: 0 3px 12px -2px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(0, 0, 0, 0.05);
437
+ --avatar-rim:
438
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.45), inset 0 0 2px rgba(17, 17, 17, 0.12),
439
+ inset 0 -2px 3px -2px rgba(17, 17, 17, 0.04);
440
+ }
127
441
 
128
- /* Card chassis is nearly the face's own tone — the hairline separator
129
- between them does the dividing, not a change of colour. */
130
- --card-bezel-light: linear-gradient(180deg, #fdfdfd 0%, #f7f7f7 100%);
131
- --card-face-light-shadow:
132
- 0 0 0 0.5px rgba(17, 17, 17, 0.042), 0 1px 2px rgba(17, 17, 17, 0.04),
133
- inset 0 2px 3px -2px rgba(255, 255, 255, 1);
134
- --card-bezel-dark: linear-gradient(180deg, #151515 0%, #0e0e0e 100%);
135
- --card-face-dark-shadow:
136
- 0 0 0 0.5px rgba(255, 255, 255, 0.042), 0 1px 2px rgba(0, 0, 0, 0.4),
137
- inset 0 2px 3px -2px rgba(255, 255, 255, 0.045);
442
+ /* Dark: the same objects under the same lamp, in darker materials.
443
+ Primary: data-theme="dark". The class "dark" does the same, for setups
444
+ (next-themes, Tailwind's class strategy) that toggle a class.
445
+ Keep this block and the system one below in step. */
446
+ .dark,
447
+ [data-theme='dark'] {
448
+ color-scheme: dark;
449
+ /* Surfaces: the page is the deepest layer (never pure black); each
450
+ thing raised off it is a step lighter, each well cut into it a step
451
+ darker. The lamp hasn't moved: highlights stay on top edges. */
452
+ --page: #111111;
453
+ --panel-face: #202020;
454
+ --panel-tray: linear-gradient(180deg, #1b1b1b 0%, #191919 100%);
455
+ --palette-tray: linear-gradient(180deg, #1d1d1d 0%, #1b1b1b 100%);
456
+ --surface-hover: #262626;
457
+ --surface-active: #2b2b2b;
458
+ --line: rgba(255, 255, 255, 0.06);
459
+ --line-strong: rgba(255, 255, 255, 0.1);
460
+ --hairline: rgba(255, 255, 255, 0.06);
461
+ --divider: rgba(255, 255, 255, 0.07);
462
+ --groove: rgba(0, 0, 0, 0.45);
463
+ --groove-lip: 1px 0 0 rgba(255, 255, 255, 0.06);
464
+ --groove-lip-below: 0 1px 0 rgba(255, 255, 255, 0.06);
465
+ --scrollbar: rgba(255, 255, 255, 0.16);
466
+ --dots: rgba(255, 255, 255, 0.07);
467
+ --shimmer: rgba(255, 255, 255, 0.07);
468
+ --sheen: rgba(255, 255, 255, 0.06);
469
+ --halo: 0 0 0 4px rgba(255, 255, 255, 0.07), 0 0 0 5px rgba(255, 255, 255, 0.035);
470
+ --focus-ring-inset: inset 0 0 0 2px rgba(245, 245, 245, 0.8);
138
471
 
139
- /* Empty media slots: a whisper darker than the face they sit in */
140
- --media-well-light: linear-gradient(180deg, rgba(17, 17, 17, 0.025) 0%, rgba(17, 17, 17, 0.04) 100%);
141
- --media-well-dark: linear-gradient(180deg, rgba(0, 0, 0, 0.18) 0%, rgba(0, 0, 0, 0.26) 100%);
472
+ /* Ink */
473
+ --ink-strong: #ffffff;
474
+ --ink: oklch(97% 0 0);
475
+ --ink-soft: oklch(92.2% 0 0);
476
+ --ink-label: oklch(87% 0 0);
477
+ --ink-body: oklch(80% 0 0);
478
+ --ink-muted: oklch(70.8% 0 0);
479
+ --ink-faint: oklch(58% 0 0);
480
+ --ink-ghost: oklch(42% 0 0);
481
+ --ink-inverse: oklch(20.5% 0 0);
482
+ --emboss: 0 -1px 0 rgba(0, 0, 0, 0.4);
483
+ --emboss-soft: 0 -1px 0 rgba(0, 0, 0, 0.3);
484
+ --emboss-inverse: 0 1px 0 rgba(255, 255, 255, 0.55);
485
+ --emboss-inverse-deep: 0 1px 0 rgba(255, 255, 255, 0.6);
142
486
 
143
- /* Icon tiles on cards — small raised faces */
144
- --tile-light-shadow:
145
- 0 0 3px rgba(17, 17, 17, 0.102), 0 1px 2px rgba(17, 17, 17, 0.08),
487
+ /* Status: the same hues, a few steps lighter to read on dark */
488
+ --danger: oklch(70.4% 0.191 22.216);
489
+ --danger-soft: oklch(70.4% 0.191 22.216);
490
+ --danger-wash: rgba(239, 68, 68, 0.12);
491
+ --success: oklch(76.5% 0.177 163.223);
492
+ --success-soft: oklch(76.5% 0.177 163.223);
493
+ --success-strong: oklch(76.5% 0.177 163.223);
494
+ --warning: oklch(82.8% 0.189 84.429);
495
+ --warning-ink: oklch(92.4% 0.12 95.746);
496
+ --accent: oklch(81.1% 0.111 293.571);
497
+ --accent-soft: oklch(70.2% 0.183 293.541);
498
+ --code-keyword: oklch(76% 0.14 293.5);
499
+ --code-string: oklch(78% 0.15 163);
500
+ --code-tag: oklch(78% 0.12 232.7);
501
+ --code-number: oklch(80% 0.15 70);
502
+ --lamp-default: #e5e5e5;
503
+
504
+ /* Raised: the mid material — the secondary key he drew for dark cards */
505
+ --bezel: var(--bezel-mid);
506
+ --bezel-shadow: var(--bezel-mid-shadow);
507
+ --face: var(--face-mid);
508
+ --face-shadow: var(--face-mid-shadow);
509
+ --face-shadow-hover: var(--face-mid-shadow-hover);
510
+ --face-pressed: var(--face-dark-pressed);
511
+ --face-float-shadow: var(--face-mid-float-shadow);
512
+ --face-drop: 0 6px 16px -6px rgba(0, 0, 0, 0.6);
513
+ --tile: var(--tile-dark);
514
+ --tile-shadow: var(--tile-dark-shadow);
515
+ --thumb-shadow:
516
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.45),
517
+ 0 2px 6px -2px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
518
+ --key-shadow:
519
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1.5px 1px rgba(0, 0, 0, 0.55),
520
+ 0 2px 3px -1px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
521
+ --knob-shadow:
522
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.45),
523
+ 0 3px 6px -1px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.14),
524
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
525
+
526
+ /* Inverse: a pale key on the dark page, toned down so it doesn't glare */
527
+ --bezel-inverse: linear-gradient(180deg, #e4e4e4 0%, #a9a9a9 100%);
528
+ --bezel-inverse-shadow:
529
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.4),
530
+ 0 4px 12px -4px rgba(0, 0, 0, 0.6), inset 0 2px 3px -2px rgba(255, 255, 255, 0.9),
531
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.12);
532
+ --face-inverse: linear-gradient(180deg, #f2f2f2 0%, #e6e6e6 55%, #d9d9d9 100%);
533
+ --face-inverse-shadow:
534
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 4px -1px rgba(0, 0, 0, 0.4),
535
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1), inset 0 -2px 3px -2px rgba(0, 0, 0, 0.1);
536
+ --face-inverse-shadow-hover:
537
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 3px rgba(0, 0, 0, 0.45),
538
+ 0 4px 8px -2px rgba(0, 0, 0, 0.4), inset 0 2px 3px -2px rgba(255, 255, 255, 1),
539
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.1);
540
+ --face-inverse-pressed: 0 0 3px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(0, 0, 0, 0.22);
541
+ --face-inverse-float-shadow:
542
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.45),
543
+ 0 6px 14px -4px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
544
+ --inverse-lip: rgba(255, 255, 255, 0.6);
545
+ --dot-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
546
+ --fill-shadow: inset 0 2px 3px -2px rgba(255, 255, 255, 0.9), 0 0 2px rgba(0, 0, 0, 0.5);
547
+ --tooltip-shadow:
548
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.45),
549
+ 0 8px 24px -4px rgba(0, 0, 0, 0.55), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
550
+ --badge-inverse-shadow:
551
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4),
146
552
  inset 0 2px 3px -2px rgba(255, 255, 255, 1);
147
- --tile-dark: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.04) 100%);
148
- --tile-dark-shadow:
149
- 0 0 0 0.5px rgba(255, 255, 255, 0.048), 0 2px 6px rgba(0, 0, 0, 0.35),
553
+ --progress-bar: linear-gradient(90deg, #8a8a8a 0%, #ededed 100%);
554
+ --banner-bezel: var(--bezel-mid);
555
+ --banner-bezel-shadow: var(--bezel-mid-shadow);
556
+ --banner-face: var(--face-mid);
557
+ --banner-face-shadow: var(--face-mid-shadow);
558
+ --progress-bar-glow: 0 0 8px rgba(255, 255, 255, 0.18);
559
+
560
+ /* Outline and ghost keys */
561
+ --outline-bezel: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(0, 0, 0, 0.3) 100%);
562
+ --outline-bezel-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.5), inset 0 2px 3px -2px rgba(255, 255, 255, 0.06);
563
+ --outline-face-shadow:
564
+ 0 0 3px rgba(0, 0, 0, 0.4), 0 1px 1px rgba(0, 0, 0, 0.35),
150
565
  inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
566
+ --outline-face-shadow-hover:
567
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.4),
568
+ 0 3px 6px -2px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.12);
569
+ --outline-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 0 2px rgba(0, 0, 0, 0.5);
570
+ --outline-shadow-hover:
571
+ inset 0 0 0 1px rgba(255, 255, 255, 0.16), inset 0 0 2px rgba(0, 0, 0, 0.5),
572
+ 0 1px 2px rgba(0, 0, 0, 0.3);
573
+ --outline-shadow-pressed:
574
+ inset 0 0 0 1px rgba(255, 255, 255, 0.12), inset 0 1px 2px rgba(0, 0, 0, 0.5);
575
+ --ghost-shadow-hover: inset 0 0 2px rgba(0, 0, 0, 0.5);
576
+ --ghost-shadow-pressed: inset 0 1px 2px rgba(0, 0, 0, 0.55);
577
+
578
+ /* Badges */
579
+ --badge: linear-gradient(180deg, #333333 0%, #2a2a2a 100%);
580
+ --badge-shadow:
581
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 1.5px rgba(0, 0, 0, 0.4),
582
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
583
+ --badge-accent: linear-gradient(180deg, #33245c 0%, #2a1e4c 100%);
584
+ --badge-accent-shadow:
585
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 0 0 0.5px rgba(167, 139, 250, 0.22),
586
+ 0 1px 1.5px rgba(0, 0, 0, 0.4), inset 0 2px 3px -2px rgba(196, 181, 253, 0.25);
587
+
588
+ /* The warning banner, as a dark amber face */
589
+ --bezel-warning: linear-gradient(180deg, #5c4310 0%, #33240a 100%);
590
+ --bezel-warning-shadow:
591
+ 0 0 0 0.5px rgba(0, 0, 0, 0.5), 0 4px 12px -4px rgba(0, 0, 0, 0.55),
592
+ inset 0 2px 3px -2px rgba(253, 230, 138, 0.18);
593
+ --face-warning: linear-gradient(180deg, #3d2e0f 0%, #33260b 100%);
594
+ --face-warning-shadow:
595
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.4),
596
+ inset 0 2px 3px -2px rgba(253, 230, 138, 0.16);
597
+
598
+ /* Fields: a step darker than whatever holds them */
599
+ --field-from: #090909;
600
+ --field-to: #0d0d0d;
601
+ --field-hover-from: #070707;
602
+ --field-hover-to: #0b0b0b;
603
+ --field-focus: #151515;
604
+ --field: linear-gradient(180deg, #090909 0%, #0d0d0d 100%);
605
+ --field-hover: linear-gradient(180deg, #070707 0%, #0b0b0b 100%);
606
+ --field-shadow:
607
+ inset 0 0 2px rgba(0, 0, 0, 0.7), inset 0 1px 2px rgba(0, 0, 0, 0.5),
608
+ 0 1px 2px -1px rgba(255, 255, 255, 0.1), inset 0 0 0 1px rgba(255, 255, 255, 0.09),
609
+ 0 0 0 0 rgba(255, 255, 255, 0);
610
+ --field-shadow-hover:
611
+ inset 0 0 2px rgba(0, 0, 0, 0.8), inset 0 1px 2px rgba(0, 0, 0, 0.55),
612
+ 0 1px 2px -1px rgba(255, 255, 255, 0.1), inset 0 0 0 1px rgba(255, 255, 255, 0.12),
613
+ 0 0 0 3px rgba(255, 255, 255, 0.025);
614
+ --field-shadow-focus:
615
+ inset 0 0 2px rgba(0, 0, 0, 0.3), inset 0 1px 2px rgba(0, 0, 0, 0.2),
616
+ 0 1px 2px -1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(255, 255, 255, 0.42),
617
+ 0 0 0 4px rgba(255, 255, 255, 0.07);
618
+ --field-shadow-error:
619
+ inset 0 0 2px rgba(248, 113, 113, 0.45), inset 0 1px 2px rgba(0, 0, 0, 0.5),
620
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(248, 113, 113, 0),
621
+ 0 0 0 0 rgba(248, 113, 113, 0);
622
+ --field-shadow-error-hover:
623
+ inset 0 0 2px rgba(248, 113, 113, 0.6), inset 0 1px 2px rgba(0, 0, 0, 0.55),
624
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(248, 113, 113, 0.12),
625
+ 0 0 0 3px rgba(248, 113, 113, 0.05);
626
+ --field-shadow-error-focus:
627
+ inset 0 0 2px rgba(248, 113, 113, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.2),
628
+ 0 1px 2px -1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(248, 113, 113, 0.7),
629
+ 0 0 0 4px rgba(248, 113, 113, 0.12);
630
+ --track: rgba(0, 0, 0, 0.4);
631
+ --track-shadow:
632
+ inset 0 1px 2px rgba(0, 0, 0, 0.6), inset 0 0 2px rgba(0, 0, 0, 0.6),
633
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09);
634
+ --track-on: #e6e6e6;
635
+ --track-on-shadow:
636
+ inset 0 1px 3px rgba(0, 0, 0, 0.35), inset 0 0 2px rgba(0, 0, 0, 0.25),
637
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09);
638
+
639
+ /* Wells */
640
+ --well: linear-gradient(180deg, #0a0a0a 0%, #0e0e0e 100%);
641
+ --well-shadow:
642
+ inset 0 0 2px rgba(0, 0, 0, 0.7), inset 0 1px 2px rgba(0, 0, 0, 0.6),
643
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(255, 255, 255, 0.03);
644
+
645
+ /* Panels */
646
+ --panel-bezel: linear-gradient(180deg, #1c1c1c 0%, #161616 100%);
647
+ --panel-shadow:
648
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.4),
649
+ 0 4px 12px -4px rgba(0, 0, 0, 0.5), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
650
+ --panel-face-shadow:
651
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4),
652
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.08);
653
+ --overlay-shadow:
654
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 4px 12px -4px rgba(0, 0, 0, 0.55),
655
+ 0 16px 40px -12px rgba(0, 0, 0, 0.7), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
656
+ --modal-shadow:
657
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 8px 24px -8px rgba(0, 0, 0, 0.6),
658
+ 0 32px 80px -16px rgba(0, 0, 0, 0.8), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
659
+ --scrim: rgba(0, 0, 0, 0.6);
660
+
661
+ /* Cards: his dark card, lifted a few steps so it rises off the page */
662
+ --card:
663
+ radial-gradient(90% 60% at 50% 0%, rgba(255, 255, 255, 0.05), transparent 75%),
664
+ radial-gradient(140% 100% at 30% 0%, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 65%),
665
+ linear-gradient(180deg, #1a1a1a 0%, #141414 100%);
666
+ --card-bezel: linear-gradient(180deg, #1d1d1d 0%, #161616 100%);
667
+ --card-shadow:
668
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.3),
669
+ 0 4px 12px -2px rgba(0, 0, 0, 0.35), 0 12px 28px -8px rgba(0, 0, 0, 0.45),
670
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.075);
671
+ --card-shadow-hover:
672
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 2px 6px rgba(0, 0, 0, 0.35),
673
+ 0 10px 28px -6px rgba(0, 0, 0, 0.45), 0 28px 64px -16px rgba(0, 0, 0, 0.5),
674
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
675
+ --card-face-shadow: var(--card-face-dark-shadow);
676
+ --card-rim: rgba(255, 255, 255, 0.1);
677
+ --rim-default: rgba(255, 255, 255, 0.1);
678
+ --media-well: var(--media-well-dark);
679
+ --media-dots: rgba(255, 255, 255, 0.09);
680
+ --slide-dots: rgba(255, 255, 255, 0.08);
681
+ --spark: #f5f5f5;
682
+ --spark-halo: rgba(255, 255, 255, 0.14);
683
+ --photo-edge: rgba(255, 255, 255, 0.084);
684
+ --cover:
685
+ radial-gradient(130% 90% at 25% 0%, rgba(255, 255, 255, 0.06), transparent 60%),
686
+ linear-gradient(180deg, #181818 0%, #1c1c1c 100%);
687
+ --cover-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.45), inset 0 -2px 3px -2px rgba(0, 0, 0, 0.3);
688
+ --avatar-lift: 0 3px 12px -2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(0, 0, 0, 0.3);
689
+ --avatar-rim:
690
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.5),
691
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
692
+ }
693
+
694
+ /* Opt-in system preference: data-theme="system" follows the OS. An
695
+ explicit data-theme="light" (or "dark") on the page wins, so a toggle
696
+ can always override it. A copy of the dark block above. */
697
+ @media (prefers-color-scheme: dark) {
698
+ [data-theme='system'] {
699
+ color-scheme: dark;
700
+ /* Surfaces: the page is the deepest layer (never pure black); each
701
+ thing raised off it is a step lighter, each well cut into it a step
702
+ darker. The lamp hasn't moved: highlights stay on top edges. */
703
+ --page: #111111;
704
+ --panel-face: #202020;
705
+ --panel-tray: linear-gradient(180deg, #1b1b1b 0%, #191919 100%);
706
+ --palette-tray: linear-gradient(180deg, #1d1d1d 0%, #1b1b1b 100%);
707
+ --surface-hover: #262626;
708
+ --surface-active: #2b2b2b;
709
+ --line: rgba(255, 255, 255, 0.06);
710
+ --line-strong: rgba(255, 255, 255, 0.1);
711
+ --hairline: rgba(255, 255, 255, 0.06);
712
+ --divider: rgba(255, 255, 255, 0.07);
713
+ --groove: rgba(0, 0, 0, 0.45);
714
+ --groove-lip: 1px 0 0 rgba(255, 255, 255, 0.06);
715
+ --groove-lip-below: 0 1px 0 rgba(255, 255, 255, 0.06);
716
+ --scrollbar: rgba(255, 255, 255, 0.16);
717
+ --dots: rgba(255, 255, 255, 0.07);
718
+ --shimmer: rgba(255, 255, 255, 0.07);
719
+ --sheen: rgba(255, 255, 255, 0.06);
720
+ --halo: 0 0 0 4px rgba(255, 255, 255, 0.07), 0 0 0 5px rgba(255, 255, 255, 0.035);
721
+ --focus-ring-inset: inset 0 0 0 2px rgba(245, 245, 245, 0.8);
722
+
723
+ /* Ink */
724
+ --ink-strong: #ffffff;
725
+ --ink: oklch(97% 0 0);
726
+ --ink-soft: oklch(92.2% 0 0);
727
+ --ink-label: oklch(87% 0 0);
728
+ --ink-body: oklch(80% 0 0);
729
+ --ink-muted: oklch(70.8% 0 0);
730
+ --ink-faint: oklch(58% 0 0);
731
+ --ink-ghost: oklch(42% 0 0);
732
+ --ink-inverse: oklch(20.5% 0 0);
733
+ --emboss: 0 -1px 0 rgba(0, 0, 0, 0.4);
734
+ --emboss-soft: 0 -1px 0 rgba(0, 0, 0, 0.3);
735
+ --emboss-inverse: 0 1px 0 rgba(255, 255, 255, 0.55);
736
+ --emboss-inverse-deep: 0 1px 0 rgba(255, 255, 255, 0.6);
737
+
738
+ /* Status: the same hues, a few steps lighter to read on dark */
739
+ --danger: oklch(70.4% 0.191 22.216);
740
+ --danger-soft: oklch(70.4% 0.191 22.216);
741
+ --danger-wash: rgba(239, 68, 68, 0.12);
742
+ --success: oklch(76.5% 0.177 163.223);
743
+ --success-soft: oklch(76.5% 0.177 163.223);
744
+ --success-strong: oklch(76.5% 0.177 163.223);
745
+ --warning: oklch(82.8% 0.189 84.429);
746
+ --warning-ink: oklch(92.4% 0.12 95.746);
747
+ --accent: oklch(81.1% 0.111 293.571);
748
+ --accent-soft: oklch(70.2% 0.183 293.541);
749
+ --code-keyword: oklch(76% 0.14 293.5);
750
+ --code-string: oklch(78% 0.15 163);
751
+ --code-tag: oklch(78% 0.12 232.7);
752
+ --code-number: oklch(80% 0.15 70);
753
+ --lamp-default: #e5e5e5;
754
+
755
+ /* Raised: the mid material — the secondary key he drew for dark cards */
756
+ --bezel: var(--bezel-mid);
757
+ --bezel-shadow: var(--bezel-mid-shadow);
758
+ --face: var(--face-mid);
759
+ --face-shadow: var(--face-mid-shadow);
760
+ --face-shadow-hover: var(--face-mid-shadow-hover);
761
+ --face-pressed: var(--face-dark-pressed);
762
+ --face-float-shadow: var(--face-mid-float-shadow);
763
+ --face-drop: 0 6px 16px -6px rgba(0, 0, 0, 0.6);
764
+ --tile: var(--tile-dark);
765
+ --tile-shadow: var(--tile-dark-shadow);
766
+ --thumb-shadow:
767
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.45),
768
+ 0 2px 6px -2px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
769
+ --key-shadow:
770
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1.5px 1px rgba(0, 0, 0, 0.55),
771
+ 0 2px 3px -1px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
772
+ --knob-shadow:
773
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.45),
774
+ 0 3px 6px -1px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.14),
775
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
776
+
777
+ /* Inverse: a pale key on the dark page, toned down so it doesn't glare */
778
+ --bezel-inverse: linear-gradient(180deg, #e4e4e4 0%, #a9a9a9 100%);
779
+ --bezel-inverse-shadow:
780
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(0, 0, 0, 0.4),
781
+ 0 4px 12px -4px rgba(0, 0, 0, 0.6), inset 0 2px 3px -2px rgba(255, 255, 255, 0.9),
782
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.12);
783
+ --face-inverse: linear-gradient(180deg, #f2f2f2 0%, #e6e6e6 55%, #d9d9d9 100%);
784
+ --face-inverse-shadow:
785
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 4px -1px rgba(0, 0, 0, 0.4),
786
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1), inset 0 -2px 3px -2px rgba(0, 0, 0, 0.1);
787
+ --face-inverse-shadow-hover:
788
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 3px rgba(0, 0, 0, 0.45),
789
+ 0 4px 8px -2px rgba(0, 0, 0, 0.4), inset 0 2px 3px -2px rgba(255, 255, 255, 1),
790
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.1);
791
+ --face-inverse-pressed: 0 0 3px rgba(0, 0, 0, 0.5), inset 0 1px 2px rgba(0, 0, 0, 0.22);
792
+ --face-inverse-float-shadow:
793
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 4px rgba(0, 0, 0, 0.45),
794
+ 0 6px 14px -4px rgba(0, 0, 0, 0.45), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
795
+ --inverse-lip: rgba(255, 255, 255, 0.6);
796
+ --dot-shadow: 0 1px 0 rgba(255, 255, 255, 0.45);
797
+ --fill-shadow: inset 0 2px 3px -2px rgba(255, 255, 255, 0.9), 0 0 2px rgba(0, 0, 0, 0.5);
798
+ --tooltip-shadow:
799
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.45),
800
+ 0 8px 24px -4px rgba(0, 0, 0, 0.55), inset 0 2px 3px -2px rgba(255, 255, 255, 1);
801
+ --badge-inverse-shadow:
802
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4),
803
+ inset 0 2px 3px -2px rgba(255, 255, 255, 1);
804
+ --progress-bar: linear-gradient(90deg, #8a8a8a 0%, #ededed 100%);
805
+ --banner-bezel: var(--bezel-mid);
806
+ --banner-bezel-shadow: var(--bezel-mid-shadow);
807
+ --banner-face: var(--face-mid);
808
+ --banner-face-shadow: var(--face-mid-shadow);
809
+ --progress-bar-glow: 0 0 8px rgba(255, 255, 255, 0.18);
810
+
811
+ /* Outline and ghost keys */
812
+ --outline-bezel: linear-gradient(180deg, rgba(255, 255, 255, 0.06) 0%, rgba(0, 0, 0, 0.3) 100%);
813
+ --outline-bezel-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.5), inset 0 2px 3px -2px rgba(255, 255, 255, 0.06);
814
+ --outline-face-shadow:
815
+ 0 0 3px rgba(0, 0, 0, 0.4), 0 1px 1px rgba(0, 0, 0, 0.35),
816
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
817
+ --outline-face-shadow-hover:
818
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.4),
819
+ 0 3px 6px -2px rgba(0, 0, 0, 0.35), inset 0 2px 3px -2px rgba(255, 255, 255, 0.12);
820
+ --outline-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1), inset 0 0 2px rgba(0, 0, 0, 0.5);
821
+ --outline-shadow-hover:
822
+ inset 0 0 0 1px rgba(255, 255, 255, 0.16), inset 0 0 2px rgba(0, 0, 0, 0.5),
823
+ 0 1px 2px rgba(0, 0, 0, 0.3);
824
+ --outline-shadow-pressed:
825
+ inset 0 0 0 1px rgba(255, 255, 255, 0.12), inset 0 1px 2px rgba(0, 0, 0, 0.5);
826
+ --ghost-shadow-hover: inset 0 0 2px rgba(0, 0, 0, 0.5);
827
+ --ghost-shadow-pressed: inset 0 1px 2px rgba(0, 0, 0, 0.55);
828
+
829
+ /* Badges */
830
+ --badge: linear-gradient(180deg, #333333 0%, #2a2a2a 100%);
831
+ --badge-shadow:
832
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 1.5px rgba(0, 0, 0, 0.4),
833
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.1);
834
+ --badge-accent: linear-gradient(180deg, #33245c 0%, #2a1e4c 100%);
835
+ --badge-accent-shadow:
836
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 0 0 0.5px rgba(167, 139, 250, 0.22),
837
+ 0 1px 1.5px rgba(0, 0, 0, 0.4), inset 0 2px 3px -2px rgba(196, 181, 253, 0.25);
838
+
839
+ /* The warning banner, as a dark amber face */
840
+ --bezel-warning: linear-gradient(180deg, #5c4310 0%, #33240a 100%);
841
+ --bezel-warning-shadow:
842
+ 0 0 0 0.5px rgba(0, 0, 0, 0.5), 0 4px 12px -4px rgba(0, 0, 0, 0.55),
843
+ inset 0 2px 3px -2px rgba(253, 230, 138, 0.18);
844
+ --face-warning: linear-gradient(180deg, #3d2e0f 0%, #33260b 100%);
845
+ --face-warning-shadow:
846
+ 0 0 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.4),
847
+ inset 0 2px 3px -2px rgba(253, 230, 138, 0.16);
848
+
849
+ /* Fields: a step darker than whatever holds them */
850
+ --field-from: #090909;
851
+ --field-to: #0d0d0d;
852
+ --field-hover-from: #070707;
853
+ --field-hover-to: #0b0b0b;
854
+ --field-focus: #151515;
855
+ --field: linear-gradient(180deg, #090909 0%, #0d0d0d 100%);
856
+ --field-hover: linear-gradient(180deg, #070707 0%, #0b0b0b 100%);
857
+ --field-shadow:
858
+ inset 0 0 2px rgba(0, 0, 0, 0.7), inset 0 1px 2px rgba(0, 0, 0, 0.5),
859
+ 0 1px 2px -1px rgba(255, 255, 255, 0.1), inset 0 0 0 1px rgba(255, 255, 255, 0.09),
860
+ 0 0 0 0 rgba(255, 255, 255, 0);
861
+ --field-shadow-hover:
862
+ inset 0 0 2px rgba(0, 0, 0, 0.8), inset 0 1px 2px rgba(0, 0, 0, 0.55),
863
+ 0 1px 2px -1px rgba(255, 255, 255, 0.1), inset 0 0 0 1px rgba(255, 255, 255, 0.12),
864
+ 0 0 0 3px rgba(255, 255, 255, 0.025);
865
+ --field-shadow-focus:
866
+ inset 0 0 2px rgba(0, 0, 0, 0.3), inset 0 1px 2px rgba(0, 0, 0, 0.2),
867
+ 0 1px 2px -1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(255, 255, 255, 0.42),
868
+ 0 0 0 4px rgba(255, 255, 255, 0.07);
869
+ --field-shadow-error:
870
+ inset 0 0 2px rgba(248, 113, 113, 0.45), inset 0 1px 2px rgba(0, 0, 0, 0.5),
871
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(248, 113, 113, 0),
872
+ 0 0 0 0 rgba(248, 113, 113, 0);
873
+ --field-shadow-error-hover:
874
+ inset 0 0 2px rgba(248, 113, 113, 0.6), inset 0 1px 2px rgba(0, 0, 0, 0.55),
875
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(248, 113, 113, 0.12),
876
+ 0 0 0 3px rgba(248, 113, 113, 0.05);
877
+ --field-shadow-error-focus:
878
+ inset 0 0 2px rgba(248, 113, 113, 0.15), inset 0 1px 2px rgba(0, 0, 0, 0.2),
879
+ 0 1px 2px -1px rgba(255, 255, 255, 0), inset 0 0 0 1px rgba(248, 113, 113, 0.7),
880
+ 0 0 0 4px rgba(248, 113, 113, 0.12);
881
+ --track: rgba(0, 0, 0, 0.4);
882
+ --track-shadow:
883
+ inset 0 1px 2px rgba(0, 0, 0, 0.6), inset 0 0 2px rgba(0, 0, 0, 0.6),
884
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09);
885
+ --track-on: #e6e6e6;
886
+ --track-on-shadow:
887
+ inset 0 1px 3px rgba(0, 0, 0, 0.35), inset 0 0 2px rgba(0, 0, 0, 0.25),
888
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09);
889
+
890
+ /* Wells */
891
+ --well: linear-gradient(180deg, #0a0a0a 0%, #0e0e0e 100%);
892
+ --well-shadow:
893
+ inset 0 0 2px rgba(0, 0, 0, 0.7), inset 0 1px 2px rgba(0, 0, 0, 0.6),
894
+ 0 1px 2px -1px rgba(255, 255, 255, 0.09), inset 0 0 0 1px rgba(255, 255, 255, 0.03);
895
+
896
+ /* Panels */
897
+ --panel-bezel: linear-gradient(180deg, #1c1c1c 0%, #161616 100%);
898
+ --panel-shadow:
899
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 2px rgba(0, 0, 0, 0.4),
900
+ 0 4px 12px -4px rgba(0, 0, 0, 0.5), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
901
+ --panel-face-shadow:
902
+ 0 0 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4),
903
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.08);
904
+ --overlay-shadow:
905
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 4px 12px -4px rgba(0, 0, 0, 0.55),
906
+ 0 16px 40px -12px rgba(0, 0, 0, 0.7), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
907
+ --modal-shadow:
908
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 8px 24px -8px rgba(0, 0, 0, 0.6),
909
+ 0 32px 80px -16px rgba(0, 0, 0, 0.8), inset 0 2px 3px -2px rgba(255, 255, 255, 0.07);
910
+ --scrim: rgba(0, 0, 0, 0.6);
911
+
912
+ /* Cards: his dark card, lifted a few steps so it rises off the page */
913
+ --card:
914
+ radial-gradient(90% 60% at 50% 0%, rgba(255, 255, 255, 0.05), transparent 75%),
915
+ radial-gradient(140% 100% at 30% 0%, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0) 65%),
916
+ linear-gradient(180deg, #1a1a1a 0%, #141414 100%);
917
+ --card-bezel: linear-gradient(180deg, #1d1d1d 0%, #161616 100%);
918
+ --card-shadow:
919
+ 0 0 0 0.5px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.3),
920
+ 0 4px 12px -2px rgba(0, 0, 0, 0.35), 0 12px 28px -8px rgba(0, 0, 0, 0.45),
921
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.075);
922
+ --card-shadow-hover:
923
+ 0 0 0 0.5px rgba(0, 0, 0, 0.7), 0 2px 6px rgba(0, 0, 0, 0.35),
924
+ 0 10px 28px -6px rgba(0, 0, 0, 0.45), 0 28px 64px -16px rgba(0, 0, 0, 0.5),
925
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.09);
926
+ --card-face-shadow: var(--card-face-dark-shadow);
927
+ --card-rim: rgba(255, 255, 255, 0.1);
928
+ --rim-default: rgba(255, 255, 255, 0.1);
929
+ --media-well: var(--media-well-dark);
930
+ --media-dots: rgba(255, 255, 255, 0.09);
931
+ --slide-dots: rgba(255, 255, 255, 0.08);
932
+ --spark: #f5f5f5;
933
+ --spark-halo: rgba(255, 255, 255, 0.14);
934
+ --photo-edge: rgba(255, 255, 255, 0.084);
935
+ --cover:
936
+ radial-gradient(130% 90% at 25% 0%, rgba(255, 255, 255, 0.06), transparent 60%),
937
+ linear-gradient(180deg, #181818 0%, #1c1c1c 100%);
938
+ --cover-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.45), inset 0 -2px 3px -2px rgba(0, 0, 0, 0.3);
939
+ --avatar-lift: 0 3px 12px -2px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(0, 0, 0, 0.3);
940
+ --avatar-rim:
941
+ inset 0 2px 3px -2px rgba(255, 255, 255, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.5),
942
+ inset 0 -2px 3px -2px rgba(0, 0, 0, 0.2);
943
+ }
151
944
  }
152
945
 
153
946
  /* ── Scrollbars inside panels ────────────────────────────────────── */
154
947
  .scroll-thin {
155
948
  scrollbar-width: thin;
156
- scrollbar-color: rgba(17, 17, 17, 0.15) transparent;
949
+ scrollbar-color: var(--scrollbar) transparent;
157
950
  }
158
951
 
159
952
  /* ── Rim light ───────────────────────────────────────────────────── */
@@ -168,7 +961,7 @@
168
961
  inset: 0;
169
962
  border-radius: inherit;
170
963
  padding: 1px;
171
- background: linear-gradient(180deg, var(--rim, rgba(255, 255, 255, 0.9)) 0%, transparent 55%);
964
+ background: linear-gradient(180deg, var(--rim, var(--rim-default)) 0%, transparent 55%);
172
965
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
173
966
  -webkit-mask-composite: xor;
174
967
  mask-composite: exclude;
@@ -178,8 +971,9 @@
178
971
 
179
972
  /* ── Field ───────────────────────────────────────────────────────── */
180
973
  /* Gradients can't transition, but registered colours can — so the well's
181
- two stops are animatable properties and focus fades it up to white
182
- rather than swapping it in a single frame. */
974
+ two stops are animatable properties and focus fades it up to its lit
975
+ colour rather than swapping it in a single frame. The stops come from
976
+ the theme (--field-from/-to, --field-hover-from/-to, --field-focus). */
183
977
  @property --field-top {
184
978
  syntax: '<color>';
185
979
  inherits: false;
@@ -192,8 +986,8 @@
192
986
  }
193
987
  @layer components {
194
988
  .field {
195
- --field-top: #f2f2f2;
196
- --field-bottom: #f8f8f8;
989
+ --field-top: var(--field-from);
990
+ --field-bottom: var(--field-to);
197
991
  background: linear-gradient(180deg, var(--field-top) 0%, var(--field-bottom) 100%);
198
992
  box-shadow: var(--field-shadow);
199
993
  transition:
@@ -202,25 +996,28 @@
202
996
  box-shadow 260ms var(--ease-out-soft);
203
997
  }
204
998
  .field:hover {
205
- --field-top: #eeeeee;
206
- --field-bottom: #f5f5f5;
999
+ --field-top: var(--field-hover-from);
1000
+ --field-bottom: var(--field-hover-to);
207
1001
  box-shadow: var(--field-shadow-hover);
208
1002
  }
209
1003
  .field:focus-visible,
210
1004
  .field:is(input, textarea):focus,
211
1005
  .field[data-open] {
212
- --field-top: #ffffff;
213
- --field-bottom: #ffffff;
1006
+ --field-top: var(--field-focus);
1007
+ --field-bottom: var(--field-focus);
214
1008
  outline: none;
215
1009
  box-shadow: var(--field-shadow-focus);
216
1010
  }
217
- .field[aria-invalid='true'] {
1011
+ .field[aria-invalid='true'],
1012
+ .field[data-invalid] {
218
1013
  box-shadow: var(--field-shadow-error);
219
1014
  }
220
- .field[aria-invalid='true']:hover {
1015
+ .field[aria-invalid='true']:hover,
1016
+ .field[data-invalid]:hover {
221
1017
  box-shadow: var(--field-shadow-error-hover);
222
1018
  }
223
- .field[aria-invalid='true']:focus {
1019
+ .field[aria-invalid='true']:focus,
1020
+ .field[data-invalid][data-open] {
224
1021
  box-shadow: var(--field-shadow-error-focus);
225
1022
  }
226
1023
  }
@@ -322,6 +1119,15 @@
322
1119
  animation: input-shimmer 850ms cubic-bezier(0.4, 0, 0.2, 1) both;
323
1120
  }
324
1121
 
1122
+ /* ── OTP slot caret ──────────────────────────────────────────────── */
1123
+ @keyframes otp-caret {
1124
+ 0%, 100% { opacity: 1; }
1125
+ 50% { opacity: 0; }
1126
+ }
1127
+ .animate-otp-caret {
1128
+ animation: otp-caret 1s steps(1, end) infinite;
1129
+ }
1130
+
325
1131
  @keyframes select-in {
326
1132
  from { opacity: 0; transform: scale(0.97) translateY(-4px); }
327
1133
  to { opacity: 1; transform: scale(1) translateY(0); }
@@ -388,7 +1194,7 @@
388
1194
  background-image: linear-gradient(
389
1195
  90deg,
390
1196
  transparent 0%,
391
- rgba(255, 255, 255, 0.75) 50%,
1197
+ var(--sheen) 50%,
392
1198
  transparent 100%
393
1199
  );
394
1200
  background-size: 40vw 100%;