@telepath-computer/television 0.1.216 → 1.3.2

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 (81) hide show
  1. package/dist/THIRD-PARTY-NOTICES.txt +336 -0
  2. package/dist/canonical/v1/frozen.json +6 -0
  3. package/dist/canonical/v1/styles.css +7 -2
  4. package/dist/canonical/v2/components.js +1 -0
  5. package/dist/canonical/v2/fonts/Hind-Variable.933e9900.woff2 +0 -0
  6. package/dist/canonical/v2/styles.css +1153 -0
  7. package/dist/cli.cjs +1763 -761
  8. package/dist/onboarding/README.md +16 -14
  9. package/dist/onboarding/business-ops/about.html +4 -3
  10. package/dist/onboarding/business-ops/retention-vs-releases.html +25 -32
  11. package/dist/onboarding/business-ops/revenue-vs-goals.html +26 -31
  12. package/dist/onboarding/productivity/about.html +4 -3
  13. package/dist/onboarding/productivity/company-todos/THIRD-PARTY-NOTICES.txt +29 -0
  14. package/dist/onboarding/productivity/company-todos/index.html +20 -20
  15. package/dist/onboarding/productivity/company-todos/task.css +4 -5
  16. package/dist/onboarding/productivity/company-todos/task.js +1 -1
  17. package/dist/onboarding/productivity/meeting-prep.html +12 -12
  18. package/dist/onboarding/productivity/priorities-today.html +10 -10
  19. package/dist/onboarding/productivity/todays-calendar/calendar.css +1 -1
  20. package/dist/onboarding/productivity/todays-calendar/index.html +12 -14
  21. package/dist/onboarding/research/about.html +4 -3
  22. package/dist/onboarding/research/open-model-research.html +35 -35
  23. package/dist/onboarding/research/todays-news.html +72 -72
  24. package/dist/onboarding/tv-guide/welcome.html +178 -0
  25. package/dist/skills/television/SKILL.md +122 -67
  26. package/dist/skills/television/theming.md +1087 -0
  27. package/dist/skills/tv-calendar/SKILL.md +3 -1
  28. package/dist/skills/tv-calendar/calendar.css +1 -1
  29. package/dist/skills/tv-table/SKILL.md +5 -2
  30. package/dist/skills/tv-tasks/SKILL.md +19 -15
  31. package/dist/skills/tv-tasks/task.css +4 -26
  32. package/dist/skills/tv-tasks/task.js +1 -1
  33. package/dist/themes/README.md +5 -0
  34. package/dist/themes/aquarium/README.md +48 -0
  35. package/dist/themes/aquarium/iframe-background.js +137 -0
  36. package/dist/themes/aquarium/manifest.json +7 -0
  37. package/dist/themes/aquarium/theme.css +46 -0
  38. package/dist/themes/blueprint/README.md +31 -0
  39. package/dist/themes/blueprint/manifest.json +6 -0
  40. package/dist/themes/blueprint/theme.css +115 -0
  41. package/dist/themes/clouds/README.md +15 -0
  42. package/dist/themes/clouds/THIRD-PARTY-NOTICES.txt +81 -0
  43. package/dist/themes/clouds/manifest.json +6 -0
  44. package/dist/themes/clouds/theme.css +62 -0
  45. package/dist/themes/clouds/wallpaper-dark.webp +0 -0
  46. package/dist/themes/clouds/wallpaper.webp +0 -0
  47. package/dist/themes/crt-phosphor/README.md +31 -0
  48. package/dist/themes/crt-phosphor/manifest.json +6 -0
  49. package/dist/themes/crt-phosphor/theme.css +105 -0
  50. package/dist/themes/nord/README.md +9 -0
  51. package/dist/themes/nord/THIRD-PARTY-NOTICES.txt +29 -0
  52. package/dist/themes/nord/manifest.json +5 -0
  53. package/dist/themes/nord/theme.css +28 -0
  54. package/dist/themes/swiss/README.md +5 -0
  55. package/dist/themes/swiss/manifest.json +5 -0
  56. package/dist/themes/swiss/theme.css +34 -0
  57. package/dist/themes/tokyo-night/README.md +17 -0
  58. package/dist/themes/tokyo-night/THIRD-PARTY-NOTICES.txt +235 -0
  59. package/dist/themes/tokyo-night/manifest.json +5 -0
  60. package/dist/themes/tokyo-night/theme.css +31 -0
  61. package/dist/themes/tokyo-night/wallpaper.webp +0 -0
  62. package/dist/views/artifact-missing/index.html +40 -68
  63. package/dist/views/markdown/THIRD-PARTY-NOTICES.txt +26 -0
  64. package/dist/views/markdown/index.html +49 -23
  65. package/dist/web/THIRD-PARTY-NOTICES.txt +26 -0
  66. package/dist/web/assets/artifact-bridge-BEUWXKq7.js +1 -0
  67. package/dist/web/assets/{artifactMissing-BiK_l3Ts.js → artifactMissing-CUs1ZwDx.js} +1 -1
  68. package/dist/web/assets/main-3UimTRLK.css +1 -0
  69. package/dist/web/assets/main-CSSzbG7q.js +704 -0
  70. package/dist/web/assets/{urlUnsupported-RGIrhMDe.js → urlUnsupported-powR79PT.js} +1 -1
  71. package/dist/web/index.html +40 -5
  72. package/dist/web/views/artifact-missing/index.html +40 -68
  73. package/dist/web/views/url-unsupported/index.html +40 -68
  74. package/package.json +1 -1
  75. package/dist/onboarding/tv-guide/welcome/assets/television.svg +0 -71
  76. package/dist/onboarding/tv-guide/welcome/index.html +0 -203
  77. package/dist/skills/tv-theme/SKILL.md +0 -449
  78. package/dist/web/assets/artifact-bridge-3Trk-B2L.js +0 -1
  79. package/dist/web/assets/clouds-CAYIArXj.jpg +0 -0
  80. package/dist/web/assets/main-Blg8fuaV.css +0 -1
  81. package/dist/web/assets/main-V14r9Psb.js +0 -596
@@ -0,0 +1,1153 @@
1
+ /* Foundation styling carries no specificity: every selector that can be is
2
+ wrapped in `:where()`, so later sheets — the element sheets, surfaces,
3
+ themes — override any of this with a plain rule. The universal box-sizing
4
+ rule stays bare only because pseudo-elements cannot appear inside
5
+ `:where()`; its selectors carry no specificity anyway. */
6
+ *,
7
+ *::before,
8
+ *::after {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ :where(html, body) {
13
+ margin: 0;
14
+ padding: 0;
15
+ }
16
+
17
+ /* The document's base typography, stated once at the root: surfaces inherit
18
+ rather than restate, and the form controls below inherit it in turn. */
19
+ :where(body) {
20
+ font-family: var(--font-sans);
21
+ font-size: var(--text-md);
22
+ font-weight: var(--font-weight-normal);
23
+ line-height: normal;
24
+ color: var(--color-text);
25
+ }
26
+
27
+ :where(h1, h2, h3, h4, h5, h6, p, ul, ol, blockquote, pre, hr) {
28
+ margin: 0;
29
+ }
30
+
31
+ :where(button, input, select, textarea) {
32
+ font: inherit;
33
+ color: inherit;
34
+ }
35
+
36
+ /* Floating elements arrive with UA chrome — a solid border and padding on
37
+ dialog and on popover hosts. Zeroed here so the panel styling states only
38
+ what a panel looks like. The UA margin stays: it centers a modal dialog. */
39
+ :where(dialog, [popover]) {
40
+ border: 0;
41
+ padding: 0;
42
+ }
43
+
44
+ /* Hind — the committed variable cut, weights 300–700. */
45
+ @font-face {
46
+ font-family: "Hind";
47
+ src: url(/canonical/v2/fonts/Hind-Variable.933e9900.woff2) format("woff2-variations");
48
+ font-weight: 300 700;
49
+ font-display: block;
50
+ }
51
+
52
+ :where(:root) { /* The faces above, named; weights as material plus the body role. */
53
+ --font-sans: "Hind", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
54
+ --font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
55
+ --font-weight-normal: 400;
56
+ --font-weight-medium: 500;
57
+ --font-weight-semibold: 600;
58
+ }
59
+
60
+ /* Palette seeds and derived scales adapted from Tailwind CSS.
61
+ https://github.com/tailwindlabs/tailwindcss
62
+ Copyright (c) Tailwind Labs, Inc.
63
+ MIT License; see THIRD-PARTY-NOTICES.txt. */
64
+
65
+ /* Both tables sit at zero specificity — ambient styling is the weakest
66
+ thing in the cascade, tokens included. A theme states a token with a
67
+ plain `:root` and out-cascades both tables at once — one statement, both
68
+ modes — or writes its own `[data-theme="dark"]` block to retune dark
69
+ alone. Between the tables themselves, source order decides: the dark
70
+ table follows the light one, so it wins where the attribute matches. */
71
+ :where(:root) {
72
+ /* A surface's inner edge moves toward its light pole; its outer line moves
73
+ toward its dark pole. Both follow the surface rather than appearance. */
74
+ --surface-border-color: color-mix(in oklch, var(--color-surface), oklch(from var(--color-surface) 1 0 h) var(--alpha-15));
75
+ --surface-outline-color: color-mix(in oklch, var(--color-surface), oklch(from var(--color-surface) 0 0 h) var(--alpha-10));
76
+ --surface-outline-width: 1px;
77
+ --surface-outline: var(--surface-outline-width) solid var(--surface-outline-color);
78
+
79
+ /* Panels: popovers, menus, selects and dialogs. */
80
+ --panel-background: var(--color-surface);
81
+ --panel-text-color: var(--color-text);
82
+ --panel-border-color: var(--surface-border-color);
83
+ --panel-border-width: 1px;
84
+ --panel-border: var(--panel-border-width) solid var(--panel-border-color);
85
+ --panel-outline: var(--surface-outline);
86
+
87
+ /* Options: menu items and select options. */
88
+ --option-background-highlighted: color-mix(in oklch, transparent, var(--option-background-active) var(--hover-mix));
89
+ --option-background-active: var(--tint-active);
90
+
91
+ /* Checked checklist marker fill and border. */
92
+ --checkbox-color: var(--color-primary);
93
+
94
+ /* Controls share these defaults in app and artifact documents. */
95
+ --control-text-color: var(--color-text);
96
+ --control-border-color: var(--color-border);
97
+ --control-border-width: 1px;
98
+ --control-border: var(--control-border-width) solid var(--control-border-color);
99
+ --input-placeholder-text-color: var(--color-text-muted);
100
+
101
+ /* Slots — the theme's raw material, each stated at its own 500 step: the
102
+ hue's mid, the brand weight. A theme restates any of them and the scale
103
+ below it regenerates. The neutral is genuinely neutral; give it a chroma
104
+ and hue and the whole grey world takes the undertone. */
105
+ --neutral: oklch(0.556 0 0);
106
+ --red: oklch(0.637 0.237 25.331);
107
+ --orange: oklch(0.705 0.213 47.604);
108
+ --yellow: oklch(0.852 0.199 91.936);
109
+ --green: oklch(0.723 0.219 149.579);
110
+ --cyan: oklch(0.715 0.143 215.221);
111
+ --blue: oklch(0.623 0.214 259.815);
112
+ --purple: oklch(0.627 0.265 303.9);
113
+ --pink: oklch(0.656 0.241 354.308);
114
+ /* The one tunable identity — any color a theme or picker supplies; used
115
+ raw as the accent fill, and everything accent-flavored derives from it. */
116
+ --accent: var(--blue);
117
+
118
+ /* Scales. Every step is generated around its slot: pale steps climb a
119
+ fraction of the headroom from the slot's lightness to white, deep steps
120
+ climb toward black, and every step keeps a fraction of the slot's chroma.
121
+ The numbers sit inline, per scale, per step — tuning is editing the line.
122
+ Cluster scales carry the curve measured off the hand-tuned red ramp;
123
+ yellow carries its own numbers, including chroma fractions above 1 and
124
+ per-step hue rotation (dark yellow at constant hue is olive).
125
+
126
+ The neutral scale is a lightness ladder with chroma carried unchanged:
127
+ dose arithmetic cannot hold an undertone at the pale end, and the
128
+ interface's contrast skeleton wants stated lightnesses. */
129
+ --neutral-50: oklch(from var(--neutral) 0.985 c h);
130
+ --neutral-100: oklch(from var(--neutral) 0.97 c h);
131
+ --neutral-200: oklch(from var(--neutral) 0.922 c h);
132
+ --neutral-300: oklch(from var(--neutral) 0.87 c h);
133
+ --neutral-400: oklch(from var(--neutral) 0.708 c h);
134
+ --neutral-500: var(--neutral);
135
+ --neutral-600: oklch(from var(--neutral) 0.439 c h);
136
+ --neutral-700: oklch(from var(--neutral) 0.371 c h);
137
+ --neutral-800: oklch(from var(--neutral) 0.269 c h);
138
+ --neutral-900: oklch(from var(--neutral) 0.205 c h);
139
+ --neutral-950: oklch(from var(--neutral) 0.145 c h);
140
+
141
+ --red-50: oklch(from var(--red) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
142
+ --red-100: oklch(from var(--red) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
143
+ --red-200: oklch(from var(--red) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
144
+ --red-300: oklch(from var(--red) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
145
+ --red-400: oklch(from var(--red) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
146
+ --red-500: var(--red);
147
+ --red-600: oklch(from var(--red) calc(l * (1 - 0.094)) calc(c * 1.034) h);
148
+ --red-700: oklch(from var(--red) calc(l * (1 - 0.207)) calc(c * 0.899) h);
149
+ --red-800: oklch(from var(--red) calc(l * (1 - 0.303)) calc(c * 0.747) h);
150
+ --red-900: oklch(from var(--red) calc(l * (1 - 0.378)) calc(c * 0.595) h);
151
+ --red-950: oklch(from var(--red) calc(l * (1 - 0.595)) calc(c * 0.435) h);
152
+
153
+ --orange-50: oklch(from var(--orange) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
154
+ --orange-100: oklch(from var(--orange) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
155
+ --orange-200: oklch(from var(--orange) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
156
+ --orange-300: oklch(from var(--orange) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
157
+ --orange-400: oklch(from var(--orange) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
158
+ --orange-500: var(--orange);
159
+ --orange-600: oklch(from var(--orange) calc(l * (1 - 0.094)) calc(c * 1.034) h);
160
+ --orange-700: oklch(from var(--orange) calc(l * (1 - 0.207)) calc(c * 0.899) h);
161
+ --orange-800: oklch(from var(--orange) calc(l * (1 - 0.303)) calc(c * 0.747) h);
162
+ --orange-900: oklch(from var(--orange) calc(l * (1 - 0.378)) calc(c * 0.595) h);
163
+ --orange-950: oklch(from var(--orange) calc(l * (1 - 0.595)) calc(c * 0.435) h);
164
+
165
+ --yellow-50: oklch(from var(--yellow) calc(l + (1 - l) * 0.937) calc(c * 0.141) calc(h + 10));
166
+ --yellow-100: oklch(from var(--yellow) calc(l + (1 - l) * 0.868) calc(c * 0.386) calc(h + 11));
167
+ --yellow-200: oklch(from var(--yellow) calc(l + (1 - l) * 0.732) calc(c * 0.701) calc(h + 10));
168
+ --yellow-300: oklch(from var(--yellow) calc(l + (1 - l) * 0.537) calc(c * 0.989) calc(h + 6));
169
+ --yellow-400: oklch(from var(--yellow) calc(l + (1 - l) * 0.278) calc(c * 1.082) calc(h + 3));
170
+ --yellow-500: var(--yellow);
171
+ --yellow-600: oklch(from var(--yellow) calc(l * (1 - 0.08)) calc(c * 0.95) calc(h - 6));
172
+ --yellow-700: oklch(from var(--yellow) calc(l * (1 - 0.19)) calc(c * 0.8) calc(h - 14));
173
+ --yellow-800: oklch(from var(--yellow) calc(l * (1 - 0.33)) calc(c * 0.65) calc(h - 26));
174
+ --yellow-900: oklch(from var(--yellow) calc(l * (1 - 0.46)) calc(c * 0.52) calc(h - 32));
175
+ --yellow-950: oklch(from var(--yellow) calc(l * (1 - 0.64)) calc(c * 0.359) calc(h - 38));
176
+
177
+ --green-50: oklch(from var(--green) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
178
+ --green-100: oklch(from var(--green) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
179
+ --green-200: oklch(from var(--green) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
180
+ --green-300: oklch(from var(--green) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
181
+ --green-400: oklch(from var(--green) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
182
+ --green-500: var(--green);
183
+ --green-600: oklch(from var(--green) calc(l * (1 - 0.094)) calc(c * 1.034) h);
184
+ --green-700: oklch(from var(--green) calc(l * (1 - 0.207)) calc(c * 0.899) h);
185
+ --green-800: oklch(from var(--green) calc(l * (1 - 0.303)) calc(c * 0.747) h);
186
+ --green-900: oklch(from var(--green) calc(l * (1 - 0.378)) calc(c * 0.595) h);
187
+ --green-950: oklch(from var(--green) calc(l * (1 - 0.595)) calc(c * 0.435) h);
188
+
189
+ --cyan-50: oklch(from var(--cyan) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
190
+ --cyan-100: oklch(from var(--cyan) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
191
+ --cyan-200: oklch(from var(--cyan) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
192
+ --cyan-300: oklch(from var(--cyan) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
193
+ --cyan-400: oklch(from var(--cyan) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
194
+ --cyan-500: var(--cyan);
195
+ --cyan-600: oklch(from var(--cyan) calc(l * (1 - 0.094)) calc(c * 1.034) h);
196
+ --cyan-700: oklch(from var(--cyan) calc(l * (1 - 0.207)) calc(c * 0.899) h);
197
+ --cyan-800: oklch(from var(--cyan) calc(l * (1 - 0.303)) calc(c * 0.747) h);
198
+ --cyan-900: oklch(from var(--cyan) calc(l * (1 - 0.378)) calc(c * 0.595) h);
199
+ --cyan-950: oklch(from var(--cyan) calc(l * (1 - 0.595)) calc(c * 0.435) h);
200
+
201
+ --blue-50: oklch(from var(--blue) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
202
+ --blue-100: oklch(from var(--blue) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
203
+ --blue-200: oklch(from var(--blue) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
204
+ --blue-300: oklch(from var(--blue) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
205
+ --blue-400: oklch(from var(--blue) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
206
+ --blue-500: var(--blue);
207
+ --blue-600: oklch(from var(--blue) calc(l * (1 - 0.094)) calc(c * 1.034) h);
208
+ --blue-700: oklch(from var(--blue) calc(l * (1 - 0.207)) calc(c * 0.899) h);
209
+ --blue-800: oklch(from var(--blue) calc(l * (1 - 0.303)) calc(c * 0.747) h);
210
+ --blue-900: oklch(from var(--blue) calc(l * (1 - 0.378)) calc(c * 0.595) h);
211
+ --blue-950: oklch(from var(--blue) calc(l * (1 - 0.595)) calc(c * 0.435) h);
212
+
213
+ --purple-50: oklch(from var(--purple) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
214
+ --purple-100: oklch(from var(--purple) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
215
+ --purple-200: oklch(from var(--purple) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
216
+ --purple-300: oklch(from var(--purple) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
217
+ --purple-400: oklch(from var(--purple) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
218
+ --purple-500: var(--purple);
219
+ --purple-600: oklch(from var(--purple) calc(l * (1 - 0.094)) calc(c * 1.034) h);
220
+ --purple-700: oklch(from var(--purple) calc(l * (1 - 0.207)) calc(c * 0.899) h);
221
+ --purple-800: oklch(from var(--purple) calc(l * (1 - 0.303)) calc(c * 0.747) h);
222
+ --purple-900: oklch(from var(--purple) calc(l * (1 - 0.378)) calc(c * 0.595) h);
223
+ --purple-950: oklch(from var(--purple) calc(l * (1 - 0.595)) calc(c * 0.435) h);
224
+
225
+ --pink-50: oklch(from var(--pink) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
226
+ --pink-100: oklch(from var(--pink) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
227
+ --pink-200: oklch(from var(--pink) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
228
+ --pink-300: oklch(from var(--pink) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
229
+ --pink-400: oklch(from var(--pink) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
230
+ --pink-500: var(--pink);
231
+ --pink-600: oklch(from var(--pink) calc(l * (1 - 0.094)) calc(c * 1.034) h);
232
+ --pink-700: oklch(from var(--pink) calc(l * (1 - 0.207)) calc(c * 0.899) h);
233
+ --pink-800: oklch(from var(--pink) calc(l * (1 - 0.303)) calc(c * 0.747) h);
234
+ --pink-900: oklch(from var(--pink) calc(l * (1 - 0.378)) calc(c * 0.595) h);
235
+ --pink-950: oklch(from var(--pink) calc(l * (1 - 0.595)) calc(c * 0.435) h);
236
+
237
+ --accent-50: oklch(from var(--accent) calc(l + (1 - l) * 0.92) calc(c * 0.055) h);
238
+ --accent-100: oklch(from var(--accent) calc(l + (1 - l) * 0.824) calc(c * 0.135) h);
239
+ --accent-200: oklch(from var(--accent) calc(l + (1 - l) * 0.683) calc(c * 0.262) h);
240
+ --accent-300: oklch(from var(--accent) calc(l + (1 - l) * 0.471) calc(c * 0.481) h);
241
+ --accent-400: oklch(from var(--accent) calc(l + (1 - l) * 0.185) calc(c * 0.806) h);
242
+ --accent-500: var(--accent);
243
+ --accent-600: oklch(from var(--accent) calc(l * (1 - 0.094)) calc(c * 1.034) h);
244
+ --accent-700: oklch(from var(--accent) calc(l * (1 - 0.207)) calc(c * 0.899) h);
245
+ --accent-800: oklch(from var(--accent) calc(l * (1 - 0.303)) calc(c * 0.747) h);
246
+ --accent-900: oklch(from var(--accent) calc(l * (1 - 0.378)) calc(c * 0.595) h);
247
+ --accent-950: oklch(from var(--accent) calc(l * (1 - 0.595)) calc(c * 0.435) h);
248
+
249
+ /* Alpha scales — each color slot at a ladder of opacities, compositing on
250
+ whatever is beneath: restate the slot and its alphas follow. Used for
251
+ tinted backgrounds and translucent content. */
252
+ --neutral-alpha-5: oklch(from var(--neutral) l c h / var(--alpha-5)); --neutral-alpha-10: oklch(from var(--neutral) l c h / var(--alpha-10)); --neutral-alpha-15: oklch(from var(--neutral) l c h / var(--alpha-15));
253
+ --neutral-alpha-25: oklch(from var(--neutral) l c h / var(--alpha-25)); --neutral-alpha-50: oklch(from var(--neutral) l c h / var(--alpha-50)); --neutral-alpha-75: oklch(from var(--neutral) l c h / var(--alpha-75));
254
+ --red-alpha-5: oklch(from var(--red) l c h / var(--alpha-5)); --red-alpha-10: oklch(from var(--red) l c h / var(--alpha-10)); --red-alpha-15: oklch(from var(--red) l c h / var(--alpha-15));
255
+ --red-alpha-25: oklch(from var(--red) l c h / var(--alpha-25)); --red-alpha-50: oklch(from var(--red) l c h / var(--alpha-50)); --red-alpha-75: oklch(from var(--red) l c h / var(--alpha-75));
256
+ --orange-alpha-5: oklch(from var(--orange) l c h / var(--alpha-5)); --orange-alpha-10: oklch(from var(--orange) l c h / var(--alpha-10)); --orange-alpha-15: oklch(from var(--orange) l c h / var(--alpha-15));
257
+ --orange-alpha-25: oklch(from var(--orange) l c h / var(--alpha-25)); --orange-alpha-50: oklch(from var(--orange) l c h / var(--alpha-50)); --orange-alpha-75: oklch(from var(--orange) l c h / var(--alpha-75));
258
+ --yellow-alpha-5: oklch(from var(--yellow) l c h / var(--alpha-5)); --yellow-alpha-10: oklch(from var(--yellow) l c h / var(--alpha-10)); --yellow-alpha-15: oklch(from var(--yellow) l c h / var(--alpha-15));
259
+ --yellow-alpha-25: oklch(from var(--yellow) l c h / var(--alpha-25)); --yellow-alpha-50: oklch(from var(--yellow) l c h / var(--alpha-50)); --yellow-alpha-75: oklch(from var(--yellow) l c h / var(--alpha-75));
260
+ --green-alpha-5: oklch(from var(--green) l c h / var(--alpha-5)); --green-alpha-10: oklch(from var(--green) l c h / var(--alpha-10)); --green-alpha-15: oklch(from var(--green) l c h / var(--alpha-15));
261
+ --green-alpha-25: oklch(from var(--green) l c h / var(--alpha-25)); --green-alpha-50: oklch(from var(--green) l c h / var(--alpha-50)); --green-alpha-75: oklch(from var(--green) l c h / var(--alpha-75));
262
+ --cyan-alpha-5: oklch(from var(--cyan) l c h / var(--alpha-5)); --cyan-alpha-10: oklch(from var(--cyan) l c h / var(--alpha-10)); --cyan-alpha-15: oklch(from var(--cyan) l c h / var(--alpha-15));
263
+ --cyan-alpha-25: oklch(from var(--cyan) l c h / var(--alpha-25)); --cyan-alpha-50: oklch(from var(--cyan) l c h / var(--alpha-50)); --cyan-alpha-75: oklch(from var(--cyan) l c h / var(--alpha-75));
264
+ --blue-alpha-5: oklch(from var(--blue) l c h / var(--alpha-5)); --blue-alpha-10: oklch(from var(--blue) l c h / var(--alpha-10)); --blue-alpha-15: oklch(from var(--blue) l c h / var(--alpha-15));
265
+ --blue-alpha-25: oklch(from var(--blue) l c h / var(--alpha-25)); --blue-alpha-50: oklch(from var(--blue) l c h / var(--alpha-50)); --blue-alpha-75: oklch(from var(--blue) l c h / var(--alpha-75));
266
+ --purple-alpha-5: oklch(from var(--purple) l c h / var(--alpha-5)); --purple-alpha-10: oklch(from var(--purple) l c h / var(--alpha-10)); --purple-alpha-15: oklch(from var(--purple) l c h / var(--alpha-15));
267
+ --purple-alpha-25: oklch(from var(--purple) l c h / var(--alpha-25)); --purple-alpha-50: oklch(from var(--purple) l c h / var(--alpha-50)); --purple-alpha-75: oklch(from var(--purple) l c h / var(--alpha-75));
268
+ --pink-alpha-5: oklch(from var(--pink) l c h / var(--alpha-5)); --pink-alpha-10: oklch(from var(--pink) l c h / var(--alpha-10)); --pink-alpha-15: oklch(from var(--pink) l c h / var(--alpha-15));
269
+ --pink-alpha-25: oklch(from var(--pink) l c h / var(--alpha-25)); --pink-alpha-50: oklch(from var(--pink) l c h / var(--alpha-50)); --pink-alpha-75: oklch(from var(--pink) l c h / var(--alpha-75));
270
+ --accent-alpha-5: oklch(from var(--accent) l c h / var(--alpha-5)); --accent-alpha-10: oklch(from var(--accent) l c h / var(--alpha-10)); --accent-alpha-15: oklch(from var(--accent) l c h / var(--alpha-15));
271
+ --accent-alpha-25: oklch(from var(--accent) l c h / var(--alpha-25)); --accent-alpha-50: oklch(from var(--accent) l c h / var(--alpha-50)); --accent-alpha-75: oklch(from var(--accent) l c h / var(--alpha-75));
272
+
273
+ /* The alpha ladder the alpha scales read — nominal percent, values tunable
274
+ within a few points of their names. */
275
+ --alpha-3: 3%; --alpha-5: 5%; --alpha-10: 10%; --alpha-15: 15%; --alpha-20: 20%;
276
+ --alpha-25: 25%; --alpha-40: 40%; --alpha-50: 50%; --alpha-75: 75%;
277
+
278
+ /* Active strength and hover position are independent. Hover moves this
279
+ fraction of the way from resting to the resolved active color. The
280
+ default is halfway in both appearances. */
281
+ --hover-mix: 50%;
282
+ --alpha-active: 8%;
283
+
284
+ /* Ghost controls use their text pigment at active strength. Hover blends
285
+ transparent with that resolved endpoint, including theme overrides. */
286
+ --tint-hover: color-mix(in oklch, transparent, var(--tint-active) var(--hover-mix));
287
+ --tint-active: oklch(from currentColor l c h / var(--alpha-active));
288
+
289
+ /* Stencil icons: a drawing from the glyph vocabulary, packaged as a mask
290
+ for the places a rule must draw it — a pseudo-element can hold no
291
+ element, so the checklist's done mark and the select's selected mark
292
+ paint through this stencil instead of composing tv-icon. */
293
+ --icon-check: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path d="M10.2 3.1 5.1 8.2 2.4 5.5" fill="none" stroke="black" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>');
294
+
295
+ /* Neutral roles — the interface's jobs, pointed at the material. Sheets
296
+ read roles and scales, never a value of their own. */
297
+ /* Pure white, stated: the surface is a frame for user content, and
298
+ white-backgrounded artifacts and images must composite seamlessly. */
299
+ --color-surface: white;
300
+ --color-surface-muted: var(--neutral-100);
301
+ --color-text: var(--neutral-900);
302
+ --color-text-muted: var(--neutral-500);
303
+ /* Reversed type — the text knocked out of a solid fill. One token: fills
304
+ share the readable band by construction (meanings sit at fill weight),
305
+ and a variant whose fill is too light for reversed type wears
306
+ --color-text instead. */
307
+ --color-text-reversed: white;
308
+ /* Borders are tints of the text role, not stated steps: a translucent
309
+ edge always separates from whatever ground it sits on — an opaque
310
+ step vanishes the day a region paints that same step. Dark appearance
311
+ uses stronger tints to keep the edges visible. */
312
+ --color-border: oklch(from var(--color-text) l c h / 12%);
313
+ /* Translucent surface fills; backdrop blur is applied by the component. */
314
+ --tint-surface: oklch(from var(--color-surface) l c h / var(--alpha-50));
315
+ --tint-surface-muted: oklch(from var(--color-surface-muted) l c h / var(--alpha-50));
316
+ /* A control is a small surface, lifted by its border. */
317
+ --control-background: var(--color-surface);
318
+ /* Situations — danger, alert, success, primary. Each is one pointer at
319
+ the hue's 500; its tints and state fills derive from it, so repointing
320
+ the situation moves everything that wears it. */
321
+ --color-danger: var(--red-500);
322
+ --tint-danger: oklch(from var(--color-danger) l c h / var(--alpha-10));
323
+ --tint-danger-hover: color-mix(in oklch, transparent, var(--tint-danger-active) var(--hover-mix));
324
+ --tint-danger-active: oklch(from var(--color-danger) l c h / var(--alpha-active));
325
+
326
+ --color-alert: var(--yellow-500);
327
+ --tint-alert: oklch(from var(--color-alert) l c h / var(--alpha-10));
328
+ --tint-alert-hover: color-mix(in oklch, transparent, var(--tint-alert-active) var(--hover-mix));
329
+ --tint-alert-active: oklch(from var(--color-alert) l c h / var(--alpha-active));
330
+
331
+ --color-success: var(--green-500);
332
+ --tint-success: oklch(from var(--color-success) l c h / var(--alpha-10));
333
+
334
+ /* The primary action — the accent worn as a situation: the default
335
+ button, the current sidebar row. Selection wears it too; a selected
336
+ role returns the day selection and primary want different colors. */
337
+ --color-primary: var(--accent-500);
338
+ --tint-primary: oklch(from var(--color-primary) l c h / var(--alpha-10));
339
+ --tint-primary-hover: color-mix(in oklch, transparent, var(--tint-primary-active) var(--hover-mix));
340
+ --tint-primary-active: oklch(from var(--color-primary) l c h / var(--alpha-active));
341
+ /* The text on the primary fill, computed against it: dark ink above the
342
+ contrast flip, near-white below — so any accent, light gold included,
343
+ keeps a legible label wherever the fill appears. */
344
+ --color-primary-text: oklch(from var(--color-primary) clamp(0.2, (var(--contrast-flip) - l) * 1e6, 0.98) 0 h);
345
+ /* The keyboard's ring: one stroke wherever focus lands — width and
346
+ pigment here, the offset each control's own. */
347
+ --outline-focus: 2px solid var(--color-primary);
348
+
349
+ /* Filled active colors move toward black or white according to state-flip.
350
+ Hover follows the resolved active endpoint. Ordinary buttons consume
351
+ only resting and active fills; their hover appearance stays unchanged.
352
+ Derived states accept colors. Supply explicit states for gradients. */
353
+ --control-background-hover: color-mix(in oklch, var(--control-background), var(--control-background-active) var(--hover-mix));
354
+ --control-background-active: color-mix(in oklch, var(--control-background), oklch(from var(--control-background) clamp(0, (var(--state-flip) - l) * 1e6, 1) 0 h) var(--alpha-active));
355
+ --color-primary-hover: color-mix(in oklch, var(--color-primary), var(--color-primary-active) var(--hover-mix));
356
+ --color-primary-active: color-mix(in oklch, var(--color-primary), oklch(from var(--color-primary) clamp(0, (var(--state-flip) - l) * 1e6, 1) 0 h) var(--alpha-active));
357
+ --color-danger-hover: color-mix(in oklch, var(--color-danger), var(--color-danger-active) var(--hover-mix));
358
+ --color-danger-active: color-mix(in oklch, var(--color-danger), oklch(from var(--color-danger) clamp(0, (var(--state-flip) - l) * 1e6, 1) 0 h) var(--alpha-active));
359
+ --color-alert-hover: color-mix(in oklch, var(--color-alert), var(--color-alert-active) var(--hover-mix));
360
+ --color-alert-active: color-mix(in oklch, var(--color-alert), oklch(from var(--color-alert) clamp(0, (var(--state-flip) - l) * 1e6, 1) 0 h) var(--alpha-active));
361
+
362
+ /* A link follows its surrounding text unless a theme gives it an accent.
363
+ currentColor, not inherit: it means the ambient ink in any property it
364
+ lands in, and CSS-wide keywords substitute brittly through var(). */
365
+ --color-link: currentColor;
366
+ /* The dialog backdrop: near-black at the overlay opacity, dimming the
367
+ page; deriving from the neutral keeps the theme's undertone. */
368
+ --color-overlay: oklch(from var(--neutral-950) l c h / var(--alpha-40));
369
+
370
+ /* Two thresholds read a fill's own lightness and pick between two
371
+ outcomes. --state-flip steers the fill's motion in its states: a fill
372
+ lighter than it darkens on hover and press, a darker one lightens —
373
+ a genuinely dark fill has nowhere darker to go. --contrast-flip
374
+ steers the ink on the fill: a fill lighter than it wears dark text,
375
+ a darker one wears reversed type. Different questions flip at
376
+ different points — a red of 0.6 lightness wants reversed text yet
377
+ still darkens on press. */
378
+ --state-flip: 0.35;
379
+ --contrast-flip: 0.66;
380
+
381
+ color-scheme: light;
382
+ }
383
+
384
+ /* The dark table changes the role pointers. Scales are not restated as
385
+ scales; the hue slots take slightly lighter values (saturated mids vibrate
386
+ on near-black), and every derived step follows. The neutral slot does not
387
+ move — the roles flip which steps they take. */
388
+ :where([data-theme="dark"]) {
389
+ --alpha-active: 12%;
390
+ --red: oklch(0.704 0.191 22.216);
391
+ --orange: oklch(0.75 0.183 55.934);
392
+ --yellow: oklch(0.879 0.169 91.5);
393
+ --green: oklch(0.792 0.209 151.711);
394
+ --cyan: oklch(0.789 0.154 211.53);
395
+ --blue: oklch(0.707 0.165 254.624);
396
+ --purple: oklch(0.714 0.203 305.504);
397
+ --pink: oklch(0.718 0.202 349.761);
398
+
399
+ --color-surface: var(--neutral-900);
400
+ --color-surface-muted: var(--neutral-800);
401
+ --color-text: var(--neutral-100);
402
+ --color-text-muted: var(--neutral-400);
403
+ --color-border: oklch(from var(--color-text) l c h / 18%);
404
+
405
+ color-scheme: dark;
406
+ }
407
+
408
+ :where(:root) {
409
+ /* Controls follow the standard UI text size by default. */
410
+ --control-font-size: var(--text-md);
411
+
412
+ /* Type — one seed; each size a stated multiple, written as its tuned pixel
413
+ over the default base: retune --text-base and every size follows (at the
414
+ root — the var chain resolves there, not per subtree); read the fraction
415
+ and you see the pixels it lands on at 14. */
416
+ --text-base: 14px;
417
+ --text-sm: round(calc(var(--text-base) * 12 / 14), 1px);
418
+ --text-md: var(--text-base);
419
+ --text-lg: round(calc(var(--text-base) * 16 / 14), 1px);
420
+ --text-xl: round(calc(var(--text-base) * 18 / 14), 1px);
421
+ --text-2xl: round(calc(var(--text-base) * 20 / 14), 1px);
422
+ --text-3xl: round(calc(var(--text-base) * 24 / 14), 1px);
423
+ --text-4xl: round(calc(var(--text-base) * 30 / 14), 1px);
424
+
425
+ /* The control lines: the line-height a control's label uses, and the one
426
+ input its height derives from — the line, plus the space-2 padding step
427
+ top and bottom, plus a 1px border each side, gives every control the
428
+ same height: 18 + 4 + 2 = 24px (14 + 4 + 2 = 20px at the small step).
429
+ Stated in pixels, not a ratio: a control's cell is tighter than prose,
430
+ immune to the leading around it, and lands on even pixels in any font. */
431
+ --line-control: 18px;
432
+ --line-control-sm: 14px;
433
+ }
434
+
435
+ :where(:root) {
436
+ /* Ordinary buttons and text fields share control padding. */
437
+ --control-padding: var(--space-2) var(--space-16);
438
+
439
+ /* Spacing — the thirteen legal distances. The ladder is a constraint, not a
440
+ theming surface: names state their fixed pixel values, and an off-ladder
441
+ length in a sheet is a visible smell. A density mode, if ever real,
442
+ swaps the ladder wholesale rather than lying under these names. */
443
+ --space-2: 2px;
444
+ --space-3: 3px;
445
+ --space-4: 4px;
446
+ --space-6: 6px;
447
+ --space-8: 8px;
448
+ --space-10: 10px;
449
+ --space-12: 12px;
450
+ --space-16: 16px;
451
+ --space-20: 20px;
452
+ --space-24: 24px;
453
+ --space-32: 32px;
454
+ --space-48: 48px;
455
+ --space-64: 64px;
456
+
457
+ /* Radius roles — jobs, themable. A numbered ladder would be value-names a
458
+ theme cannot restate without lying. */
459
+ --control-radius: 6px; /* buttons, inputs, menu items, small insets */
460
+ --radius-pill: 9999px; /* fully rounded ends at any element size */
461
+
462
+ /* Shared panel corners. */
463
+ --panel-radius: 8px;
464
+
465
+ /* The clearance a placed panel keeps on every side — off the trigger and
466
+ off every window edge ([[ui/foundation/popover/index.md]], Placement).
467
+ The element reads it from computed style, so a theme override reaches
468
+ the placement algorithm. Foundation, not app chrome: the popover family
469
+ ships to artifact documents, so its clearance must travel with it. */
470
+ --popover-distance: var(--space-4);
471
+ }
472
+
473
+ :where(:root) {
474
+ /* Reusable elevations, from a close contact to a broad floating shadow. */
475
+ --shadow-sm: 0 1px 4px oklch(from var(--neutral-950) l c h / var(--alpha-15));
476
+ --shadow-md: 0 4px 12px oklch(from var(--neutral-950) l c h / var(--alpha-15));
477
+ --shadow-lg:
478
+ 0 8px 24px oklch(from var(--neutral-950) l c h / var(--alpha-10)),
479
+ 0 2px 8px oklch(from var(--neutral-950) l c h / var(--alpha-5));
480
+
481
+ /* Broad window elevation for artifact frames. */
482
+ --shadow-xl:
483
+ 0 24px 48px oklch(from var(--neutral-950) l c h / var(--alpha-15)),
484
+ 0 4px 12px oklch(from var(--neutral-950) l c h / 7.5%);
485
+
486
+ /* Popovers, including menus and selects, sit closer than dialogs. */
487
+ --popover-shadow: var(--shadow-md);
488
+ --dialog-shadow: var(--shadow-lg);
489
+ }
490
+
491
+ /* Dark surfaces need stronger shadows with the same neutral pigment. */
492
+ :where([data-theme="dark"]) {
493
+ --shadow-sm: 0 1px 4px oklch(from var(--neutral-950) l c h / var(--alpha-40));
494
+ --shadow-md: 0 4px 12px oklch(from var(--neutral-950) l c h / var(--alpha-40));
495
+ --shadow-lg:
496
+ 0 8px 24px oklch(from var(--neutral-950) l c h / var(--alpha-40)),
497
+ 0 2px 8px oklch(from var(--neutral-950) l c h / var(--alpha-25));
498
+ --shadow-xl:
499
+ 0 24px 48px oklch(from var(--neutral-950) l c h / 30%),
500
+ 0 4px 12px oklch(from var(--neutral-950) l c h / 18.75%);
501
+ }
502
+
503
+ /* The app-level paint order ([[ui/foundation/index.md]], Layers): every
504
+ z-index either places an element on one of these or lifts above siblings
505
+ with a calculation on one. Zero specificity like every token table — a
506
+ plain `:root` rule from a theme beats it. */
507
+ :where(:root) {
508
+ /* The base plane the document lays out. Nothing is placed on it; lifts on
509
+ the base plane calculate from it. */
510
+ --layer-ground: 0;
511
+ /* Floating panels opened from a surface: menus, the top-bar popovers. */
512
+ --layer-panel: 100;
513
+ /* The interruption: the dialog overlay and its backdrop. */
514
+ --layer-overlay: 200;
515
+ }
516
+
517
+ /* The prose treatment ([[ui/foundation/index.md]], Prose): document
518
+ typography for plain semantic HTML, applying inside any region marked
519
+ text-display="prose". Zero specificity like every foundation rule; a
520
+ document's own styling outranks all of it. */
521
+
522
+ :where([text-display="prose"]) {
523
+ font-size: var(--text-md);
524
+ font-weight: var(--font-weight-medium);
525
+ line-height: 1.5;
526
+ -webkit-font-smoothing: antialiased;
527
+ }
528
+
529
+ /* The heading scale. */
530
+ :where([text-display="prose"] h1) {
531
+ font-size: var(--text-3xl);
532
+ font-weight: var(--font-weight-semibold);
533
+ line-height: 1.25;
534
+ margin: 0 0 var(--space-12);
535
+ }
536
+
537
+ :where([text-display="prose"] h2) {
538
+ font-size: var(--text-xl);
539
+ font-weight: var(--font-weight-semibold);
540
+ line-height: 1.3;
541
+ margin: var(--space-24) 0 var(--space-8);
542
+ }
543
+
544
+ :where([text-display="prose"] h3, [text-display="prose"] h4, [text-display="prose"] h5, [text-display="prose"] h6) {
545
+ font-size: var(--text-lg);
546
+ font-weight: var(--font-weight-semibold);
547
+ margin: var(--space-16) 0 var(--space-4);
548
+ }
549
+
550
+ /* Block rhythm. */
551
+ :where([text-display="prose"] p, [text-display="prose"] ul, [text-display="prose"] ol, [text-display="prose"] blockquote, [text-display="prose"] pre, [text-display="prose"] hr, [text-display="prose"] table, [text-display="prose"] figure) {
552
+ margin: 0 0 var(--space-12);
553
+ }
554
+
555
+ :where([text-display="prose"] > :last-child) {
556
+ margin-bottom: 0;
557
+ }
558
+
559
+ :where([text-display="prose"] a) {
560
+ color: var(--color-link);
561
+ text-decoration: underline;
562
+ text-underline-offset: 2px;
563
+ }
564
+
565
+ :where([text-display="prose"] ul, [text-display="prose"] ol) {
566
+ padding-left: var(--space-24);
567
+ }
568
+
569
+ :where([text-display="prose"] li + li) {
570
+ margin-top: var(--space-4);
571
+ }
572
+
573
+ :where([text-display="prose"] blockquote) {
574
+ border-left: 2px solid var(--color-border);
575
+ padding-left: var(--space-12);
576
+ color: var(--color-text-muted);
577
+ }
578
+
579
+ :where([text-display="prose"] code, [text-display="prose"] kbd, [text-display="prose"] samp) {
580
+ font-family: var(--font-mono);
581
+ font-size: var(--text-sm);
582
+ background: var(--color-surface-muted);
583
+ border-radius: var(--control-radius);
584
+ padding: 1px var(--space-4);
585
+ }
586
+
587
+ :where([text-display="prose"] pre) {
588
+ font-family: var(--font-mono);
589
+ font-size: var(--text-sm);
590
+ background: var(--color-surface-muted);
591
+ border: 1px solid var(--color-border);
592
+ border-radius: var(--control-radius);
593
+ padding: var(--space-12);
594
+ overflow-x: auto;
595
+ }
596
+
597
+ /* A block's code cell. */
598
+ :where([text-display="prose"] pre code) {
599
+ background: none;
600
+ border-radius: 0;
601
+ padding: 0;
602
+ }
603
+
604
+ :where([text-display="prose"] hr) {
605
+ border: 0;
606
+ border-top: 1px solid var(--color-border);
607
+ }
608
+
609
+ :where([text-display="prose"] table) {
610
+ border-collapse: collapse;
611
+ width: 100%;
612
+ }
613
+
614
+ :where([text-display="prose"] th) {
615
+ text-align: left;
616
+ font-weight: var(--font-weight-semibold);
617
+ background: var(--color-surface-muted);
618
+ }
619
+
620
+ :where([text-display="prose"] th, [text-display="prose"] td) {
621
+ padding: var(--space-4) var(--space-12) var(--space-4) 0;
622
+ border-bottom: 1px solid var(--color-border);
623
+ }
624
+
625
+ :where([text-display="prose"] img) {
626
+ max-width: 100%;
627
+ height: auto;
628
+ }
629
+
630
+ /* The header idiom. */
631
+ :where([text-display="prose"] header p) {
632
+ color: var(--color-text-muted);
633
+ }
634
+
635
+ /* The heading's own bottom margin would collapse with the subtitle's; the
636
+ pair states its gap once. */
637
+ :where(header :is(h1, h2, h3, h4, h5, h6):has(+ p)) {
638
+ margin-bottom: 0;
639
+ }
640
+
641
+ :where(header :is(h1, h2, h3, h4, h5, h6) + p) {
642
+ margin-top: var(--space-4);
643
+ }
644
+
645
+ /* Every rule in this sheet is wrapped in `:where()`, which contributes
646
+ nothing to specificity — foundation styling is the weakest thing in the
647
+ cascade, and a component placing a button overrides any of it with a plain
648
+ selector instead of contriving one that outranks it. Without this,
649
+ `button:hover:not(:disabled)` beats a component's own `:hover` rule, and
650
+ the component has to mirror the `:not()` to win — a workaround that reads
651
+ like an accident and gets tidied away. Within the sheet, source order does
652
+ the layering: intents beat the bare element, sizes beat the intents. */
653
+ :where(button) {
654
+ display: inline-flex;
655
+ align-items: center;
656
+ justify-content: center;
657
+ gap: var(--space-6);
658
+ padding: var(--control-padding);
659
+ border: var(--control-border);
660
+ border-radius: var(--control-radius);
661
+ /* A button is a surface of its own, lifted off whatever it sits on, rather
662
+ than a bordered hole in it. The fill is the kind's situation role, and
663
+ hover and press are that role's derived state fills in the token table
664
+ ([[ui/foundation/tokens/colors.css]]) — the state rules below point at
665
+ them per kind, so a theme that restates a fill gets its states for
666
+ free, and one with an exact opinion overrides a state token. */
667
+ background: var(--control-background);
668
+ color: var(--control-text-color);
669
+ /* A button is a target, not text to select through. */
670
+ cursor: default;
671
+ }
672
+
673
+ /* A colored intent is a fill declaration; its states follow below. The
674
+ label is reversed type with the contrast clamp layered over it per kind,
675
+ so a theme's repointed situation keeps a legible label — the clamp
676
+ answers dark text on the alert gold (a clamp line drops at parse time in
677
+ engines without relative color, leaving the reversed role). */
678
+ :where(button[intent]) {
679
+ border-color: transparent;
680
+ color: var(--color-text-reversed);
681
+ }
682
+
683
+ /* The primary intent is the default action — the one Return fires; the
684
+ accent worn as a fill, macOS-style. */
685
+ :where(button[intent="primary"]) {
686
+ background: var(--color-primary);
687
+ color: var(--color-primary-text);
688
+ }
689
+
690
+ :where(button[intent="danger"]) {
691
+ background: var(--color-danger);
692
+ color: oklch(from var(--color-danger) clamp(0.2, (var(--contrast-flip) - l) * 1e6, 0.98) 0 h);
693
+ }
694
+
695
+ :where(button[intent="alert"]) {
696
+ background: var(--color-alert);
697
+ color: oklch(from var(--color-alert) clamp(0.2, (var(--contrast-flip) - l) * 1e6, 0.98) 0 h);
698
+ }
699
+
700
+ /* Buttons retain their resting appearance on hover. Pressed fills follow
701
+ the base fills so source order lets the state win. */
702
+ :where(button:active:not(:disabled)) {
703
+ background: var(--control-background-active);
704
+ }
705
+
706
+ :where(button[intent="primary"]:active:not(:disabled)) {
707
+ background: var(--color-primary-active);
708
+ }
709
+
710
+ :where(button[intent="danger"]:active:not(:disabled)) {
711
+ background: var(--color-danger-active);
712
+ }
713
+
714
+ :where(button[intent="alert"]:active:not(:disabled)) {
715
+ background: var(--color-alert-active);
716
+ }
717
+
718
+ /* Keyboard focus only — a ring on every click would be noise. */
719
+ :where(button:focus-visible) {
720
+ outline: var(--outline-focus);
721
+ outline-offset: 1px;
722
+ }
723
+
724
+ /* Faded rather than restyled, so a disabled button keeps its shape and stays
725
+ recognisable as the control it is. */
726
+ :where(button:disabled) {
727
+ opacity: 0.4;
728
+ }
729
+
730
+ /* A button whose whole content is an icon: a square line-sized cell, the
731
+ glyph centred. Geometry only — the quiet treatment is `ghost`, stated
732
+ separately. */
733
+ :where(button[icon]) {
734
+ padding: var(--space-2);
735
+ display: inline-grid;
736
+ place-items: center;
737
+ }
738
+
739
+ /* The expanded state: the button holds the panel it opened (the element
740
+ maintains `aria-expanded`).
741
+ Source order puts it after the ghost and pointer-state rules. A filled
742
+ button holds the press-step fill; the ghost's tint treatment below is
743
+ guarded out of every kind. */
744
+ :where(button[aria-expanded="true"]:not(:disabled):not([variant="ghost"])) {
745
+ background: var(--control-background-active);
746
+ }
747
+
748
+ :where(button[intent="primary"][aria-expanded="true"]:not(:disabled):not([variant="ghost"])) {
749
+ background: var(--color-primary-active);
750
+ }
751
+
752
+ :where(button[intent="danger"][aria-expanded="true"]:not(:disabled):not([variant="ghost"])) {
753
+ background: var(--color-danger-active);
754
+ }
755
+
756
+ :where(button[intent="alert"][aria-expanded="true"]:not(:disabled):not([variant="ghost"])) {
757
+ background: var(--color-alert-active);
758
+ }
759
+
760
+ /* Ghost buttons inherit their containing text color. Intent colors
761
+ remain explicit; hover gives way to pressed and expanded states. */
762
+ :where(button[variant="ghost"]) {
763
+ border-color: transparent;
764
+ background: transparent;
765
+ color: inherit;
766
+ }
767
+
768
+ :where(button[variant="ghost"][intent="primary"]) { color: var(--color-primary); }
769
+ :where(button[variant="ghost"][intent="danger"]) { color: var(--color-danger); }
770
+ :where(button[variant="ghost"][intent="alert"]) { color: var(--color-alert); }
771
+
772
+ :where(button[variant="ghost"]:hover:not(:disabled)) {
773
+ background: var(--tint-hover);
774
+ }
775
+
776
+ :where(button[variant="ghost"]:is(:active, [aria-expanded="true"]):not(:disabled)) {
777
+ background: var(--tint-active);
778
+ background-clip: border-box;
779
+ }
780
+
781
+ :where(button[variant="ghost"][intent="primary"]:hover:not(:disabled)) {
782
+ background: var(--tint-primary-hover);
783
+ }
784
+
785
+ :where(button[variant="ghost"][intent="primary"]:is(:active, [aria-expanded="true"]):not(:disabled)) {
786
+ background: var(--tint-primary-active);
787
+ }
788
+
789
+ :where(button[variant="ghost"][intent="danger"]:hover:not(:disabled)) {
790
+ background: var(--tint-danger-hover);
791
+ }
792
+
793
+ :where(button[variant="ghost"][intent="danger"]:is(:active, [aria-expanded="true"]):not(:disabled)) {
794
+ background: var(--tint-danger-active);
795
+ }
796
+
797
+ :where(button[variant="ghost"][intent="alert"]:hover:not(:disabled)) {
798
+ background: var(--tint-alert-hover);
799
+ }
800
+
801
+ :where(button[variant="ghost"][intent="alert"]:is(:active, [aria-expanded="true"]):not(:disabled)) {
802
+ background: var(--tint-alert-active);
803
+ }
804
+
805
+ :where(button[icon])::before {
806
+ content: "";
807
+ inline-size: 1lh;
808
+ block-size: 1lh;
809
+ grid-area: 1 / 1;
810
+ }
811
+
812
+ :where(button[icon] > *) {
813
+ grid-area: 1 / 1;
814
+ }
815
+
816
+ /* Sizes. A size pairs a text step — the line a control's cell follows —
817
+ with a padding step, so a size means the same thing wherever it appears
818
+ and mixed rows line up. The control lines are shared vocabulary
819
+ ([[ui/foundation/tokens/text.css]]): non-button controls — the tab, the
820
+ sidebar channel — state the same line from their own sheets.
821
+
822
+ Placed after the attribute rules above: at equal (zero) specificity,
823
+ source order lets a size outrank them (`size="sm"` padding beats
824
+ `icon`). */
825
+
826
+ /* The default size, stated: a control's line is the control's, not its
827
+ context's — without this, height follows whatever leading the surroundings
828
+ happen to have. Stated in pixels, not the prose leading: a control's cell
829
+ is tighter than a paragraph's. A control's height is derived, never
830
+ declared: the control line, plus the space-2 padding step top and
831
+ bottom, plus a 1px border each side (transparent where no border
832
+ shows) — 24px, with the line as the only input
833
+ ([[ui/foundation/tokens/text.css]]). A control that visually contains
834
+ another holds it as an overlaid sibling. */
835
+ :where(button) {
836
+ font-size: var(--control-font-size);
837
+ line-height: var(--line-control);
838
+ }
839
+
840
+ :where(button[size="sm"]) {
841
+ font-size: var(--text-sm);
842
+ /* Stated in pixels, not a ratio: a compact control's cell hugs its content,
843
+ and the default's prose leading would leave it mostly air. */
844
+ line-height: var(--line-control-sm);
845
+ padding: var(--space-2) var(--space-8);
846
+ }
847
+
848
+ :where(button[size="sm"][icon]) {
849
+ padding: var(--space-2);
850
+ }
851
+
852
+ /* A sized control sizes the icon it holds: whatever size the composer put on
853
+ the icon, the control's own wins — `tv-icon[size]` names a fixed pixel
854
+ size, so left alone the icon would ignore the control shrinking around it.
855
+ 12px is the even-pixel step below the sm line, the same seat the default
856
+ button gives its glyph. */
857
+ :where(button[size="sm"] tv-icon) {
858
+ font-size: 12px;
859
+ }
860
+
861
+ :where(:is(input:not([type]), input[type=""], input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="number"], textarea)) {
862
+ box-sizing: border-box;
863
+ font-family: var(--font-sans);
864
+ font-size: var(--control-font-size);
865
+ line-height: var(--line-control);
866
+ color: var(--control-text-color);
867
+ background: var(--control-background);
868
+ border: var(--control-border);
869
+ border-radius: var(--control-radius);
870
+ padding: var(--control-padding);
871
+ }
872
+
873
+ :where(:is(input:not([type]), input[type=""], input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="number"], textarea))::placeholder {
874
+ color: var(--input-placeholder-text-color);
875
+ opacity: 1;
876
+ }
877
+
878
+ :where(:is(input:not([type]), input[type=""], input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="number"], textarea):focus-visible) {
879
+ outline: var(--outline-focus);
880
+ outline-offset: 0;
881
+ }
882
+
883
+ :where(:is(input:not([type]), input[type=""], input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="number"], textarea)[aria-invalid="true"]) {
884
+ border-color: var(--color-danger);
885
+ }
886
+
887
+ :where(:is(input:not([type]), input[type=""], input[type="text"], input[type="email"], input[type="url"], input[type="tel"], input[type="password"], input[type="number"], textarea):disabled) {
888
+ background: var(--color-surface-muted);
889
+ opacity: 0.4;
890
+ }
891
+
892
+ :where(textarea) {
893
+ line-height: 1.5;
894
+ resize: vertical;
895
+ }
896
+
897
+ /* Shared error-message styling; validation and announcement belong to the surface. */
898
+ :where(.tv-error) {
899
+ margin: 0;
900
+ color: var(--color-danger);
901
+ font-size: var(--text-sm);
902
+ line-height: 1.5;
903
+ overflow-wrap: anywhere;
904
+ }
905
+
906
+ /* The popover: the panel that floats over the page while the page stays
907
+ live, anchored to the trigger that opened it
908
+ ([[ui/foundation/popover/index.md]]). This sheet states the box and the
909
+ open state; the contents belong to the composing surface, and where the
910
+ panel sits is stated by the placement algorithm in the spec. */
911
+
912
+ /* The box: the panel and the background, nothing inward. Sized to the
913
+ contents, not the page. tv-menu ([[ui/foundation/menu/index.md]]) and
914
+ tv-select ([[ui/foundation/select/index.md]]) are popover
915
+ specializations and wear the same box. */
916
+ :where(tv-popover, tv-menu, tv-select) {
917
+ width: max-content;
918
+ background: var(--panel-background);
919
+ background-clip: border-box;
920
+ /* Its own surface, its own ink and face: a panel floats over arbitrary
921
+ context and never inherits the color or font treatment around it —
922
+ the way a native menu renders as a system panel wherever it opens.
923
+ Weight and leading are the document base's and inherit unrepeated. */
924
+ color: var(--panel-text-color);
925
+ font-family: var(--font-sans);
926
+ /* The shared panel border keeps the existing hairline geometry. */
927
+ border: var(--panel-border);
928
+ outline: var(--panel-outline);
929
+ outline-offset: 0;
930
+ border-radius: var(--panel-radius);
931
+ box-shadow: var(--popover-shadow);
932
+ }
933
+
934
+ /* The `open` attribute is the open state ([[ui/foundation/popover/index.md]],
935
+ Markup). The closed rule is deliberately not `:where()`-wrapped: an
936
+ interior sheet stating its own open display (a flex panel) must not
937
+ accidentally out-cascade closedness. */
938
+ tv-popover:not([open]),
939
+ tv-menu:not([open]),
940
+ tv-select:not([open]) {
941
+ display: none;
942
+ }
943
+
944
+ :where(tv-popover[open], tv-menu[open], tv-select[open]) {
945
+ display: block;
946
+ }
947
+
948
+ /* The expanded trigger's treatment is the button's
949
+ ([[ui/foundation/button/index.md]]): the element maintains
950
+ `aria-expanded`, and the button sheet paints the state. */
951
+
952
+ /* The menu interior ([[ui/foundation/menu/index.md]]): the panel is the
953
+ popover's box ([[ui/foundation/popover/styles.css]]); these rules state
954
+ what a menu holds inside it. */
955
+ :where(tv-menu) {
956
+ min-width: 180px;
957
+ padding: var(--space-4);
958
+ }
959
+
960
+ :where(tv-menu-item) {
961
+ display: flex;
962
+ align-items: center;
963
+ /* The control line, stated as the button states it — a menu row is a
964
+ control, not prose, and inherits prose leading without this. */
965
+ font-size: var(--text-md);
966
+ line-height: var(--line-control);
967
+ /* A deeper inset than a control's row padding, and no border at all —
968
+ menu rows read as one list, and sit denser than controls standing
969
+ alone. */
970
+ padding: var(--space-3) var(--space-10);
971
+ /* The panel's radius less its padding, so the two curves sit concentric. */
972
+ border-radius: calc(var(--panel-radius) - var(--space-4));
973
+ cursor: default;
974
+ }
975
+
976
+ /* One highlight: the hover tint stands down while the keyboard holds the
977
+ highlight (a script focus from pointing shows no :focus-visible, so the
978
+ pointer path keeps its tint); the mouse moving again takes it back. */
979
+ :where(tv-menu:not(:has(:focus-visible)) tv-menu-item:hover),
980
+ :where(tv-menu-item:focus-visible) {
981
+ background: var(--option-background-highlighted);
982
+ outline: none;
983
+ }
984
+
985
+ :where(tv-menu-item:active) {
986
+ background: var(--option-background-active);
987
+ }
988
+
989
+ /* The destructive treatment: the danger situation as text, and the
990
+ situation's own tints, stated explicitly so a theme
991
+ overrides the pigment through the role. */
992
+ :where(tv-menu-item[intent="danger"]) {
993
+ color: var(--color-danger);
994
+ }
995
+
996
+ :where(tv-menu:not(:has(:focus-visible)) tv-menu-item[intent="danger"]:hover),
997
+ :where(tv-menu-item[intent="danger"]:focus-visible) {
998
+ background: var(--tint-danger-hover);
999
+ }
1000
+
1001
+ :where(tv-menu-item[intent="danger"]:active) {
1002
+ background: var(--tint-danger-active);
1003
+ }
1004
+
1005
+ :where(tv-menu hr) {
1006
+ border: 0;
1007
+ border-top: 1px solid var(--color-border);
1008
+ margin: var(--space-4) calc(-1 * var(--space-4));
1009
+ }
1010
+
1011
+ /* The select interior ([[ui/foundation/select/index.md]]): the panel is
1012
+ the popover's box ([[ui/foundation/popover/styles.css]]); these rules state
1013
+ what a select holds inside it. The row sits as the menu's does
1014
+ ([[ui/foundation/menu/styles.css]]), with a leading gutter carrying the
1015
+ selected mark. */
1016
+ /* The trigger: the choice reads from the leading edge and the caret sits
1017
+ pinned at the trailing edge — the native pop-up arrangement, visible
1018
+ whenever the widest-option width leaves room. The hook is the combobox
1019
+ semantics the element writes on the trigger ([[arch/ui/elements.md]]);
1020
+ the button's own inline-flex row ([[ui/foundation/button/button.css]])
1021
+ supplies the rest. */
1022
+ :where(button[aria-haspopup="listbox"]) {
1023
+ justify-content: space-between;
1024
+ text-align: left;
1025
+ /* Tighter than the standalone button's inset: the panel opens over this
1026
+ control at its width, so the trigger sits closer to its list rows than
1027
+ to freestanding buttons. Tighter still on the trailing side — the caret
1028
+ hugs the right edge, since the label holds the left. */
1029
+ padding-inline: var(--space-8) var(--space-4);
1030
+ }
1031
+
1032
+ /* No minimum of its own: the panel is at least as wide as its trigger
1033
+ ([[ui/foundation/select/index.md]], Placement), and no wider than its
1034
+ options ask. */
1035
+ :where(tv-select) {
1036
+ padding: var(--space-4);
1037
+ }
1038
+
1039
+ :where(tv-option) {
1040
+ position: relative;
1041
+ display: flex;
1042
+ align-items: center;
1043
+ /* The control line, stated as the button states it — an option row is a
1044
+ control, not prose, and inherits prose leading without this. */
1045
+ font-size: var(--text-md);
1046
+ line-height: var(--line-control);
1047
+ /* A near-even inset: the leading edge holds the mark's gutter, and the
1048
+ trailing edge carries enough of it back that the row reads balanced. */
1049
+ padding: var(--space-3) var(--space-12) var(--space-3) var(--space-20);
1050
+ /* The panel's radius less its padding, so the two curves sit concentric. */
1051
+ border-radius: calc(var(--panel-radius) - var(--space-4));
1052
+ cursor: default;
1053
+ }
1054
+
1055
+ /* One highlight, element-managed ([[ui/foundation/select/index.md]],
1056
+ Keyboard) — pointer and keyboard share the tint by construction, so no
1057
+ hover rule exists to fight it. */
1058
+ :where(tv-option[highlighted]) {
1059
+ background: var(--option-background-highlighted);
1060
+ }
1061
+
1062
+ :where(tv-option:active) {
1063
+ background: var(--option-background-active);
1064
+ }
1065
+
1066
+ /* The selected mark: a check in the leading gutter, drawn as a mask (the
1067
+ checklist's technique, [[ui/foundation/checkbox-list/styles.css]]) so the
1068
+ authored option carries no icon markup. currentColor keeps it true on the
1069
+ highlight tint. */
1070
+ :where(tv-option[selected])::before {
1071
+ content: "";
1072
+ position: absolute;
1073
+ left: var(--space-4);
1074
+ top: 50%;
1075
+ translate: 0 -50%;
1076
+ width: 1em;
1077
+ height: 1em;
1078
+ background: currentColor;
1079
+ mask: var(--icon-check) center / 80% no-repeat;
1080
+ }
1081
+
1082
+ /* The checklist's looks ([[ui/foundation/checkbox-list/index.md]]): the list's
1083
+ stack, the row's marker-and-content grid, and the done treatment.
1084
+ The checked marker consumes the shared `--checkbox-color` token. */
1085
+ checkbox-list {
1086
+ display: grid;
1087
+ gap: var(--space-6);
1088
+ margin: var(--space-12) 0;
1089
+ }
1090
+
1091
+ checkbox-item {
1092
+ position: relative;
1093
+ display: grid;
1094
+ grid-template-columns: 1em minmax(0, 1fr);
1095
+ gap: var(--space-8);
1096
+ align-items: baseline;
1097
+ }
1098
+
1099
+ /* The marker box, seated on the first line's text. */
1100
+ checkbox-item::before {
1101
+ content: "";
1102
+ width: 1em;
1103
+ height: 1em;
1104
+ align-self: start;
1105
+ translate: 0 0.12em;
1106
+ background: transparent;
1107
+ border: var(--control-border);
1108
+ border-radius: var(--control-radius);
1109
+ box-sizing: border-box;
1110
+ cursor: not-allowed;
1111
+ }
1112
+
1113
+ checkbox-item[checked]::before {
1114
+ background: var(--checkbox-color);
1115
+ border-color: var(--checkbox-color);
1116
+ }
1117
+
1118
+ /* The check, drawn as a mask over the marker's cell. */
1119
+ checkbox-item[checked]::after {
1120
+ content: "";
1121
+ position: absolute;
1122
+ left: 0;
1123
+ top: 0.12em;
1124
+ width: 1em;
1125
+ height: 1em;
1126
+ background: var(--color-surface);
1127
+ mask: var(--icon-check) center / 80% no-repeat;
1128
+ }
1129
+
1130
+ checkbox-item[checked] {
1131
+ color: var(--color-text-muted);
1132
+ text-decoration: line-through;
1133
+ text-decoration-thickness: 0.08em;
1134
+ }
1135
+
1136
+ tv-icon:not(:defined) {
1137
+ display: inline-flex;
1138
+ flex: none;
1139
+ width: 1em;
1140
+ height: 1em;
1141
+ /* Glyphs are drawn on a square canvas whose visual weight sits below the
1142
+ cap height of the text beside them; nudging down aligns their centre with
1143
+ the text's rather than with its baseline box. */
1144
+ vertical-align: -0.125em;
1145
+ }
1146
+
1147
+ tv-icon:not(:defined)[size="sm"] { font-size: 16px; }
1148
+
1149
+ tv-icon:not(:defined)[size="md"] { font-size: 20px; }
1150
+
1151
+ tv-icon:not(:defined)[size="lg"] { font-size: 24px; }
1152
+
1153
+ tv-icon:not(:defined)[size="xl"] { font-size: 32px; }