clampography 2.0.0-beta.2 → 2.0.0-beta.21

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.
package/src/convert.js ADDED
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * JS to CSS Converter for Bun.sh
5
+ * Converts multiple .js files to .css and .css.min
6
+ */
7
+
8
+ import { existsSync, mkdirSync, statSync, writeFileSync } from "fs";
9
+ import { basename, resolve } from "path";
10
+
11
+ // Configuration
12
+ const FILES_TO_CONVERT = ["base.js", "extra.js"];
13
+ const OUTPUT_DIR = "css";
14
+
15
+ // Options passed to the JS functions (simulating plugin config)
16
+ const DEFAULT_OPTIONS = {
17
+ root: ":root",
18
+ prefix: "clampography",
19
+ };
20
+
21
+ /**
22
+ * Import the JS module dynamically
23
+ */
24
+ async function loadJSModule(filePath) {
25
+ const module = await import(`./${filePath}`);
26
+ const exported = module.default;
27
+
28
+ // Check if it's a function (new structure) or object (old structure)
29
+ if (typeof exported === "function") {
30
+ // Invoke the function with default options
31
+ return exported(DEFAULT_OPTIONS);
32
+ }
33
+
34
+ return exported;
35
+ }
36
+
37
+ /**
38
+ * Convert JS object to CSS string
39
+ */
40
+ function toCSSString(obj, indent = 0) {
41
+ const spaces = " ".repeat(indent);
42
+ const lines = [];
43
+
44
+ for (const [key, value] of Object.entries(obj)) {
45
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
46
+ // It's a selector or nested object
47
+
48
+ // Special handling for @layer base wrapper if present in object
49
+ if (key === "@layer base") {
50
+ lines.push(`${spaces}@layer base {`);
51
+ lines.push(toCSSString(value, indent + 1));
52
+ lines.push(`${spaces}}`);
53
+ } else {
54
+ lines.push("");
55
+ lines.push(`${spaces}${key} {`);
56
+
57
+ // Recursive call for nested properties or media queries
58
+ // But first, separate properties from nested objects
59
+ const properties = [];
60
+ const nestedObjects = {};
61
+
62
+ for (const [prop, val] of Object.entries(value)) {
63
+ if (typeof val === "object" && val !== null) {
64
+ nestedObjects[prop] = val;
65
+ } else {
66
+ properties.push(`${spaces} ${prop}: ${val};`);
67
+ }
68
+ }
69
+
70
+ // Add properties first
71
+ lines.push(...properties);
72
+
73
+ // Then process nested objects (like pseudo-elements or media queries inside)
74
+ if (Object.keys(nestedObjects).length > 0) {
75
+ // This handles nesting if the JS structure supports it (like SCSS nesting)
76
+ // Standard CSS doesn't support nesting without post-processing,
77
+ // but we'll output it as nested blocks for clarity if present.
78
+ lines.push(toCSSString(nestedObjects, indent + 1));
79
+ }
80
+
81
+ lines.push(`${spaces}}`);
82
+ }
83
+ }
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+
88
+ /**
89
+ * Convert JS object to minified CSS string
90
+ */
91
+ function toMinifiedCSS(obj) {
92
+ const parts = [];
93
+
94
+ for (const [key, value] of Object.entries(obj)) {
95
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
96
+ if (key === "@layer base") {
97
+ parts.push(`@layer base{${toMinifiedCSS(value)}}`);
98
+ } else {
99
+ const properties = [];
100
+ const nestedParts = [];
101
+
102
+ for (const [prop, val] of Object.entries(value)) {
103
+ if (typeof val === "object" && val !== null) {
104
+ nestedParts.push(toMinifiedCSS({ [prop]: val }));
105
+ } else {
106
+ properties.push(`${prop}:${val}`);
107
+ }
108
+ }
109
+
110
+ let blockContent = properties.join(";");
111
+ if (nestedParts.length > 0) {
112
+ blockContent += nestedParts.join("");
113
+ }
114
+
115
+ if (blockContent.length > 0) {
116
+ parts.push(`${key}{${blockContent}}`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return parts.join("");
122
+ }
123
+
124
+ /**
125
+ * Get output file names from input file name
126
+ */
127
+ function getOutputFileNames(inputFile) {
128
+ const baseNameWithoutExt = basename(inputFile, ".js");
129
+ return {
130
+ css: `${baseNameWithoutExt}.css`,
131
+ min: `${baseNameWithoutExt}.css.min`,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Convert single JS file to CSS
137
+ */
138
+ async function convertFile(inputFile) {
139
+ const outputNames = getOutputFileNames(inputFile);
140
+ const outputCSS = resolve(OUTPUT_DIR, outputNames.css);
141
+ const outputMin = resolve(OUTPUT_DIR, outputNames.min);
142
+
143
+ try {
144
+ console.log(`\n📖 Loading ${inputFile}...`);
145
+ const jsObject = await loadJSModule(inputFile);
146
+
147
+ console.log(`⚙️ Converting to CSS...`);
148
+ let cssContent = toCSSString(jsObject, 0);
149
+
150
+ // Explicitly wrap in @layer base for the final file
151
+ // (Assuming the JS object is just the rules, without @layer wrapper)
152
+ cssContent = `@layer base {\n${cssContent}\n}\n`;
153
+
154
+ console.log(`💾 Writing ${outputNames.css}...`);
155
+ writeFileSync(outputCSS, cssContent, "utf-8");
156
+
157
+ // Generate minified version
158
+ console.log(`⚙️ Generating minified version...`);
159
+ let minifiedContent = toMinifiedCSS(jsObject);
160
+ minifiedContent = `@layer base{${minifiedContent}}`;
161
+
162
+ console.log(`💾 Writing ${outputNames.min}...`);
163
+ writeFileSync(outputMin, minifiedContent, "utf-8");
164
+
165
+ // Show file sizes
166
+ const cssSize = statSync(outputCSS).size;
167
+ const minSize = statSync(outputMin).size;
168
+ const reduction = ((1 - minSize / cssSize) * 100).toFixed(1);
169
+
170
+ console.log(`✅ ${inputFile} converted successfully!`);
171
+ console.log(` 📄 ${outputNames.css}: ${(cssSize / 1024).toFixed(2)} KB`);
172
+ console.log(
173
+ ` 📄 ${outputNames.min}: ${
174
+ (minSize / 1024).toFixed(2)
175
+ } KB (${reduction}% smaller)`,
176
+ );
177
+
178
+ return {
179
+ input: inputFile,
180
+ output: outputNames.css,
181
+ outputMin: outputNames.min,
182
+ cssSize,
183
+ minSize,
184
+ reduction,
185
+ };
186
+ } catch (error) {
187
+ console.error(`❌ Error converting ${inputFile}:`, error.message);
188
+ return null;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Main conversion function
194
+ */
195
+ async function convertAllFiles() {
196
+ console.log("🚀 Starting CSS conversion...");
197
+ console.log(`📁 Output directory: ${OUTPUT_DIR}`);
198
+ console.log(`📝 Files to convert: ${FILES_TO_CONVERT.length}`);
199
+
200
+ // Create output directory if it doesn't exist
201
+ if (!existsSync(OUTPUT_DIR)) {
202
+ console.log(`📁 Creating output directory: ${OUTPUT_DIR}`);
203
+ mkdirSync(OUTPUT_DIR, { recursive: true });
204
+ }
205
+
206
+ const results = [];
207
+
208
+ // Convert all files
209
+ for (const file of FILES_TO_CONVERT) {
210
+ const result = await convertFile(file);
211
+ if (result) {
212
+ results.push(result);
213
+ }
214
+ }
215
+
216
+ // Summary
217
+ console.log("\n" + "=".repeat(60));
218
+ console.log("📊 CONVERSION SUMMARY");
219
+ console.log("=".repeat(60));
220
+
221
+ if (results.length === 0) {
222
+ console.log("❌ No files were converted successfully.");
223
+ process.exit(1);
224
+ }
225
+
226
+ console.log(
227
+ `✅ Successfully converted: ${results.length}/${FILES_TO_CONVERT.length} files\n`,
228
+ );
229
+
230
+ let totalCSSSize = 0;
231
+ let totalMinSize = 0;
232
+
233
+ results.forEach((result) => {
234
+ console.log(`📄 ${result.input}`);
235
+ console.log(
236
+ ` → ${OUTPUT_DIR}/${result.output} (${
237
+ (result.cssSize / 1024).toFixed(2)
238
+ } KB)`,
239
+ );
240
+ console.log(
241
+ ` → ${OUTPUT_DIR}/${result.outputMin} (${
242
+ (result.minSize / 1024).toFixed(2)
243
+ } KB, ${result.reduction}% smaller)`,
244
+ );
245
+ totalCSSSize += result.cssSize;
246
+ totalMinSize += result.minSize;
247
+ });
248
+
249
+ const totalReduction = ((1 - totalMinSize / totalCSSSize) * 100).toFixed(1);
250
+
251
+ console.log("\n" + "-".repeat(60));
252
+ console.log(`📊 Total CSS size: ${(totalCSSSize / 1024).toFixed(2)} KB`);
253
+ console.log(`📊 Total Minified size: ${(totalMinSize / 1024).toFixed(2)} KB`);
254
+ console.log(`📊 Total reduction: ${totalReduction}%`);
255
+ console.log("=".repeat(60));
256
+ console.log("🎉 All conversions completed!");
257
+ }
258
+
259
+ // Run conversion
260
+ convertAllFiles().catch((error) => {
261
+ console.error("❌ Fatal error:", error.message);
262
+ console.error(error.stack);
263
+ process.exit(1);
264
+ });
@@ -0,0 +1,444 @@
1
+ @layer base {
2
+
3
+ :root {
4
+ --spacing-xs: clamp(0.5rem, 0.375rem + 0.625vw, 0.75rem);
5
+ --spacing-sm: clamp(0.75rem, 0.5625rem + 0.9375vw, 1.25rem);
6
+ --spacing-md: clamp(1rem, 0.75rem + 1.25vw, 1.5rem);
7
+ --spacing-lg: clamp(1.5rem, 1.125rem + 1.875vw, 2.5rem);
8
+ --spacing-xl: clamp(2rem, 1.5rem + 2.5vw, 3rem);
9
+ --scroll-offset: 5rem;
10
+ --font-family-base: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
11
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
12
+ font-family: var(--font-family-base);
13
+ font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
14
+ line-height: 1.75;
15
+ text-rendering: optimizeLegibility;
16
+ -webkit-font-smoothing: antialiased;
17
+ -moz-osx-font-smoothing: grayscale;
18
+ text-wrap: pretty;
19
+ }
20
+
21
+ :root :where(h1, h2, h3, h4, h5, h6) {
22
+ font-weight: 600;
23
+ scroll-margin-top: var(--scroll-offset);
24
+ }
25
+
26
+ :root h1 {
27
+ font-size: clamp(2.25rem, 1.95rem + 1.5vw, 3rem);
28
+ line-height: 1.1111;
29
+ font-weight: 800;
30
+ margin-top: 0;
31
+ margin-bottom: var(--spacing-xl);
32
+ }
33
+
34
+ :root h2 {
35
+ font-size: clamp(1.5rem, 1.35rem + 0.75vw, 1.875rem);
36
+ line-height: 1.3333;
37
+ font-weight: 700;
38
+ margin-top: var(--spacing-xl);
39
+ margin-bottom: var(--spacing-md);
40
+ }
41
+
42
+ :root h3 {
43
+ font-size: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
44
+ line-height: 1.5;
45
+ margin-top: var(--spacing-lg);
46
+ margin-bottom: var(--spacing-sm);
47
+ }
48
+
49
+ :root h4 {
50
+ font-size: clamp(1rem, 0.975rem + 0.125vw, 1.125rem);
51
+ line-height: 1.5;
52
+ margin-top: var(--spacing-lg);
53
+ margin-bottom: var(--spacing-sm);
54
+ }
55
+
56
+ :root h5 {
57
+ font-size: 1rem;
58
+ line-height: 1.5;
59
+ margin-top: var(--spacing-md);
60
+ margin-bottom: var(--spacing-xs);
61
+ }
62
+
63
+ :root h6 {
64
+ font-size: 0.875rem;
65
+ line-height: 1.5;
66
+ margin-top: var(--spacing-md);
67
+ margin-bottom: var(--spacing-xs);
68
+ }
69
+
70
+ :root :is(h1, h2, h3, h4, h5, h6):first-child {
71
+ margin-top: 0;
72
+ }
73
+
74
+ :root a {
75
+ text-decoration-line: underline;
76
+ cursor: pointer;
77
+ }
78
+
79
+ :root :where(h1, h2, h3, h4, h5, h6) a {
80
+ text-decoration: none;
81
+ }
82
+
83
+ :root menu {
84
+ list-style: none;
85
+ margin-bottom: var(--spacing-md);
86
+ padding-left: 0;
87
+ }
88
+
89
+ :root menu > li::before {
90
+ display: none;
91
+ }
92
+
93
+ :root hgroup {
94
+ margin-bottom: var(--spacing-lg);
95
+ }
96
+
97
+ :root hgroup :where(h1, h2, h3, h4, h5, h6) {
98
+ margin-bottom: var(--spacing-xs);
99
+ }
100
+
101
+ :root hgroup :where(p) {
102
+ margin-top: 0;
103
+ margin-bottom: 0;
104
+ font-size: 0.875em;
105
+ font-weight: 400;
106
+ line-height: 1.5;
107
+ }
108
+
109
+ :root p {
110
+ line-height: 1.75;
111
+ margin-bottom: var(--spacing-md);
112
+ }
113
+
114
+ :root :where(strong, b) {
115
+ font-weight: 700;
116
+ }
117
+
118
+ :root :where(em, i, cite, var) {
119
+ font-style: italic;
120
+ }
121
+
122
+ :root dfn {
123
+ font-style: italic;
124
+ font-weight: 600;
125
+ }
126
+
127
+ :root small {
128
+ font-size: 0.875em;
129
+ line-height: 1.5;
130
+ }
131
+
132
+ :root :where(code, kbd, samp) {
133
+ font-family: var(--font-family-mono);
134
+ font-size: 0.875em;
135
+ }
136
+
137
+ :root kbd {
138
+ font-weight: 600;
139
+ }
140
+
141
+ :root data {
142
+ font-variant-numeric: tabular-nums;
143
+ }
144
+
145
+ :root :where(sub, sup) {
146
+ font-size: 0.75em;
147
+ line-height: 0;
148
+ position: relative;
149
+ vertical-align: baseline;
150
+ }
151
+
152
+ :root sup {
153
+ top: -0.5em;
154
+ }
155
+
156
+ :root sub {
157
+ bottom: -0.25em;
158
+ }
159
+
160
+ :root abbr[title] {
161
+ text-decoration: underline dotted;
162
+ cursor: help;
163
+ }
164
+
165
+ :root del {
166
+ text-decoration: line-through;
167
+ }
168
+
169
+ :root ins {
170
+ text-decoration: underline;
171
+ }
172
+
173
+ :root s {
174
+ text-decoration: line-through;
175
+ }
176
+
177
+ :root u {
178
+ text-decoration: underline;
179
+ }
180
+
181
+ :root mark {
182
+ font-style: normal;
183
+ font-weight: inherit;
184
+ }
185
+
186
+ :root address {
187
+ font-style: italic;
188
+ margin-top: var(--spacing-md);
189
+ margin-bottom: var(--spacing-md);
190
+ }
191
+
192
+ :root time {
193
+ font-style: normal;
194
+ font-variant-numeric: tabular-nums;
195
+ }
196
+
197
+ :root blockquote {
198
+ margin-top: var(--spacing-lg);
199
+ margin-bottom: var(--spacing-lg);
200
+ padding-left: var(--spacing-md);
201
+ }
202
+
203
+ :root blockquote blockquote {
204
+ margin-top: var(--spacing-sm);
205
+ margin-bottom: var(--spacing-sm);
206
+ padding-left: var(--spacing-sm);
207
+ }
208
+
209
+ :root q {
210
+ font-style: inherit;
211
+ }
212
+
213
+ :root :where(ul, ol) {
214
+ list-style: none;
215
+ margin-bottom: var(--spacing-md);
216
+ padding-left: clamp(1.5rem, 1.25rem + 1.25vw, 2rem);
217
+ }
218
+
219
+ :root li {
220
+ position: relative;
221
+ padding-left: 0.375em;
222
+ }
223
+
224
+ :root li + li {
225
+ margin-top: var(--spacing-xs);
226
+ }
227
+
228
+ :root li > ul, :root li > ol {
229
+ margin-top: var(--spacing-xs);
230
+ margin-bottom: var(--spacing-sm);
231
+ padding-left: var(--spacing-md);
232
+ }
233
+
234
+ :root ul > li::before {
235
+ content: '';
236
+ position: absolute;
237
+ left: -1.125em;
238
+ top: calc(0.875em - 0.1875em);
239
+ width: 0.375em;
240
+ height: 0.375em;
241
+ background-color: currentColor;
242
+ border-radius: 50%;
243
+ }
244
+
245
+ :root ol {
246
+ counter-reset: list-counter;
247
+ }
248
+
249
+ :root ol > li {
250
+ counter-increment: list-counter;
251
+ }
252
+
253
+ :root ol > li::before {
254
+ content: counter(list-counter) '.';
255
+ position: absolute;
256
+ left: -2.5em;
257
+ width: 1.75em;
258
+ text-align: right;
259
+ font-weight: 600;
260
+ color: currentColor;
261
+ }
262
+
263
+ :root dl {
264
+ margin-top: var(--spacing-md);
265
+ margin-bottom: var(--spacing-md);
266
+ }
267
+
268
+ :root dt {
269
+ font-weight: 600;
270
+ margin-top: var(--spacing-sm);
271
+ }
272
+
273
+ :root dt:first-child {
274
+ margin-top: 0;
275
+ }
276
+
277
+ :root dd {
278
+ margin-left: var(--spacing-md);
279
+ }
280
+
281
+ :root dt + dd {
282
+ margin-top: var(--spacing-xs);
283
+ }
284
+
285
+ :root dd + dd {
286
+ margin-top: var(--spacing-xs);
287
+ }
288
+
289
+ :root dd:last-child {
290
+ margin-bottom: 0;
291
+ }
292
+
293
+ :root pre {
294
+ margin-top: var(--spacing-md);
295
+ margin-bottom: var(--spacing-md);
296
+ font-family: var(--font-family-mono);
297
+ line-height: 1.6;
298
+ overflow-x: auto;
299
+ }
300
+
301
+ :root pre code {
302
+ font-size: inherit;
303
+ padding: 0;
304
+ background: none;
305
+ border-radius: 0;
306
+ }
307
+
308
+ :root fieldset {
309
+ margin-top: var(--spacing-md);
310
+ margin-bottom: var(--spacing-md);
311
+ padding: var(--spacing-md);
312
+ }
313
+
314
+ :root legend {
315
+ font-weight: 600;
316
+ padding: 0 var(--spacing-xs);
317
+ }
318
+
319
+ :root label {
320
+ display: inline-block;
321
+ font-weight: 600;
322
+ margin-bottom: var(--spacing-xs);
323
+ }
324
+
325
+ :root output {
326
+ display: inline-block;
327
+ font-variant-numeric: tabular-nums;
328
+ }
329
+
330
+ :root :where(meter, progress) {
331
+ display: inline-block;
332
+ vertical-align: middle;
333
+ }
334
+
335
+ :root input, :root button, :root textarea, :root select, :root optgroup {
336
+ font-family: inherit;
337
+ font-size: 100%;
338
+ line-height: inherit;
339
+ }
340
+
341
+ :root textarea {
342
+ line-height: 1.5;
343
+ }
344
+
345
+ :root button, :root [type='button'], :root [type='reset'], :root [type='submit'] {
346
+ cursor: pointer;
347
+ }
348
+
349
+ :root :where(img, video, canvas, audio, iframe, svg) {
350
+ max-width: 100%;
351
+ height: auto;
352
+ vertical-align: middle;
353
+ }
354
+
355
+ :root figure {
356
+ margin-top: var(--spacing-lg);
357
+ margin-bottom: var(--spacing-lg);
358
+ }
359
+
360
+ :root figcaption {
361
+ margin-top: 0.375rem;
362
+ font-size: 0.875em;
363
+ line-height: 1.5;
364
+ }
365
+
366
+ :root table {
367
+ width: 100%;
368
+ margin-top: var(--spacing-md);
369
+ margin-bottom: var(--spacing-md);
370
+ border-collapse: collapse;
371
+ font-size: 1em;
372
+ line-height: 1.6;
373
+ }
374
+
375
+ :root caption {
376
+ margin-bottom: var(--spacing-xs);
377
+ font-size: 0.875em;
378
+ font-weight: 600;
379
+ text-align: left;
380
+ }
381
+
382
+ :root th, :root td {
383
+ padding: var(--spacing-xs) var(--spacing-sm);
384
+ text-align: left;
385
+ }
386
+
387
+ :root th {
388
+ font-weight: 600;
389
+ }
390
+
391
+ :root thead th {
392
+ vertical-align: bottom;
393
+ }
394
+
395
+ :root tbody th, :root tbody td {
396
+ vertical-align: top;
397
+ }
398
+
399
+ :root tfoot th, :root tfoot td {
400
+ vertical-align: top;
401
+ }
402
+
403
+ :root tbody + tbody {
404
+ border-top-width: 2px;
405
+ }
406
+
407
+ :root hr {
408
+ margin-top: var(--spacing-xl);
409
+ margin-bottom: var(--spacing-xl);
410
+ border: 0;
411
+ border-top: 1px solid;
412
+ }
413
+
414
+ :root :where(:focus, :focus-visible) {
415
+ outline-offset: 2px;
416
+ }
417
+
418
+ :root details {
419
+ margin-top: var(--spacing-md);
420
+ margin-bottom: var(--spacing-md);
421
+ }
422
+
423
+ :root summary {
424
+ cursor: pointer;
425
+ font-weight: 600;
426
+ }
427
+
428
+ :root details[open] > summary {
429
+ margin-bottom: var(--spacing-xs);
430
+ }
431
+
432
+ :root dialog {
433
+ font-size: inherit;
434
+ line-height: inherit;
435
+ }
436
+
437
+ :root :where(h1, h2, h3, h4, h5, h6, p, ul:not(li > ul, li > ol), ol:not(li > ul, li > ol), dl, blockquote, figure, table, pre):first-child {
438
+ margin-top: 0;
439
+ }
440
+
441
+ :root :where(p, ul, ol, dl, blockquote, figure, table, pre):last-child {
442
+ margin-bottom: 0;
443
+ }
444
+ }
@@ -0,0 +1 @@
1
+ @layer base{:root{--spacing-xs:clamp(0.5rem, 0.375rem + 0.625vw, 0.75rem);--spacing-sm:clamp(0.75rem, 0.5625rem + 0.9375vw, 1.25rem);--spacing-md:clamp(1rem, 0.75rem + 1.25vw, 1.5rem);--spacing-lg:clamp(1.5rem, 1.125rem + 1.875vw, 2.5rem);--spacing-xl:clamp(2rem, 1.5rem + 2.5vw, 3rem);--scroll-offset:5rem;--font-family-base:system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;--font-family-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;font-family:var(--font-family-base);font-size:clamp(1rem, 0.95rem + 0.25vw, 1.125rem);line-height:1.75;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-wrap:pretty}:root :where(h1, h2, h3, h4, h5, h6){font-weight:600;scroll-margin-top:var(--scroll-offset)}:root h1{font-size:clamp(2.25rem, 1.95rem + 1.5vw, 3rem);line-height:1.1111;font-weight:800;margin-top:0;margin-bottom:var(--spacing-xl)}:root h2{font-size:clamp(1.5rem, 1.35rem + 0.75vw, 1.875rem);line-height:1.3333;font-weight:700;margin-top:var(--spacing-xl);margin-bottom:var(--spacing-md)}:root h3{font-size:clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);line-height:1.5;margin-top:var(--spacing-lg);margin-bottom:var(--spacing-sm)}:root h4{font-size:clamp(1rem, 0.975rem + 0.125vw, 1.125rem);line-height:1.5;margin-top:var(--spacing-lg);margin-bottom:var(--spacing-sm)}:root h5{font-size:1rem;line-height:1.5;margin-top:var(--spacing-md);margin-bottom:var(--spacing-xs)}:root h6{font-size:0.875rem;line-height:1.5;margin-top:var(--spacing-md);margin-bottom:var(--spacing-xs)}:root :is(h1, h2, h3, h4, h5, h6):first-child{margin-top:0}:root a{text-decoration-line:underline;cursor:pointer}:root :where(h1, h2, h3, h4, h5, h6) a{text-decoration:none}:root menu{list-style:none;margin-bottom:var(--spacing-md);padding-left:0}:root menu > li::before{display:none}:root hgroup{margin-bottom:var(--spacing-lg)}:root hgroup :where(h1, h2, h3, h4, h5, h6){margin-bottom:var(--spacing-xs)}:root hgroup :where(p){margin-top:0;margin-bottom:0;font-size:0.875em;font-weight:400;line-height:1.5}:root p{line-height:1.75;margin-bottom:var(--spacing-md)}:root :where(strong, b){font-weight:700}:root :where(em, i, cite, var){font-style:italic}:root dfn{font-style:italic;font-weight:600}:root small{font-size:0.875em;line-height:1.5}:root :where(code, kbd, samp){font-family:var(--font-family-mono);font-size:0.875em}:root kbd{font-weight:600}:root data{font-variant-numeric:tabular-nums}:root :where(sub, sup){font-size:0.75em;line-height:0;position:relative;vertical-align:baseline}:root sup{top:-0.5em}:root sub{bottom:-0.25em}:root abbr[title]{text-decoration:underline dotted;cursor:help}:root del{text-decoration:line-through}:root ins{text-decoration:underline}:root s{text-decoration:line-through}:root u{text-decoration:underline}:root mark{font-style:normal;font-weight:inherit}:root address{font-style:italic;margin-top:var(--spacing-md);margin-bottom:var(--spacing-md)}:root time{font-style:normal;font-variant-numeric:tabular-nums}:root blockquote{margin-top:var(--spacing-lg);margin-bottom:var(--spacing-lg);padding-left:var(--spacing-md)}:root blockquote blockquote{margin-top:var(--spacing-sm);margin-bottom:var(--spacing-sm);padding-left:var(--spacing-sm)}:root q{font-style:inherit}:root :where(ul, ol){list-style:none;margin-bottom:var(--spacing-md);padding-left:clamp(1.5rem, 1.25rem + 1.25vw, 2rem)}:root li{position:relative;padding-left:0.375em}:root li + li{margin-top:var(--spacing-xs)}:root li > ul, :root li > ol{margin-top:var(--spacing-xs);margin-bottom:var(--spacing-sm);padding-left:var(--spacing-md)}:root ul > li::before{content:'';position:absolute;left:-1.125em;top:calc(0.875em - 0.1875em);width:0.375em;height:0.375em;background-color:currentColor;border-radius:50%}:root ol{counter-reset:list-counter}:root ol > li{counter-increment:list-counter}:root ol > li::before{content:counter(list-counter) '.';position:absolute;left:-2.5em;width:1.75em;text-align:right;font-weight:600;color:currentColor}:root dl{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md)}:root dt{font-weight:600;margin-top:var(--spacing-sm)}:root dt:first-child{margin-top:0}:root dd{margin-left:var(--spacing-md)}:root dt + dd{margin-top:var(--spacing-xs)}:root dd + dd{margin-top:var(--spacing-xs)}:root dd:last-child{margin-bottom:0}:root pre{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md);font-family:var(--font-family-mono);line-height:1.6;overflow-x:auto}:root pre code{font-size:inherit;padding:0;background:none;border-radius:0}:root fieldset{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md);padding:var(--spacing-md)}:root legend{font-weight:600;padding:0 var(--spacing-xs)}:root label{display:inline-block;font-weight:600;margin-bottom:var(--spacing-xs)}:root output{display:inline-block;font-variant-numeric:tabular-nums}:root :where(meter, progress){display:inline-block;vertical-align:middle}:root input, :root button, :root textarea, :root select, :root optgroup{font-family:inherit;font-size:100%;line-height:inherit}:root textarea{line-height:1.5}:root button, :root [type='button'], :root [type='reset'], :root [type='submit']{cursor:pointer}:root :where(img, video, canvas, audio, iframe, svg){max-width:100%;height:auto;vertical-align:middle}:root figure{margin-top:var(--spacing-lg);margin-bottom:var(--spacing-lg)}:root figcaption{margin-top:0.375rem;font-size:0.875em;line-height:1.5}:root table{width:100%;margin-top:var(--spacing-md);margin-bottom:var(--spacing-md);border-collapse:collapse;font-size:1em;line-height:1.6}:root caption{margin-bottom:var(--spacing-xs);font-size:0.875em;font-weight:600;text-align:left}:root th, :root td{padding:var(--spacing-xs) var(--spacing-sm);text-align:left}:root th{font-weight:600}:root thead th{vertical-align:bottom}:root tbody th, :root tbody td{vertical-align:top}:root tfoot th, :root tfoot td{vertical-align:top}:root tbody + tbody{border-top-width:2px}:root hr{margin-top:var(--spacing-xl);margin-bottom:var(--spacing-xl);border:0;border-top:1px solid}:root :where(:focus, :focus-visible){outline-offset:2px}:root details{margin-top:var(--spacing-md);margin-bottom:var(--spacing-md)}:root summary{cursor:pointer;font-weight:600}:root details[open] > summary{margin-bottom:var(--spacing-xs)}:root dialog{font-size:inherit;line-height:inherit}:root :where(h1, h2, h3, h4, h5, h6, p, ul:not(li > ul, li > ol), ol:not(li > ul, li > ol), dl, blockquote, figure, table, pre):first-child{margin-top:0}:root :where(p, ul, ol, dl, blockquote, figure, table, pre):last-child{margin-bottom:0}}