@telepath-computer/television 0.1.216 → 1.3.3

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