Author: Kevin Schoon [me@kevinschoon.com]
Hash: 7e4043bf6811a5aa497ac3b3c76e9616bbc3631b
Timestamp: Mon, 06 May 2024 19:48:30 +0000 (2 months ago)

+2768 -2434 +/-93 browse
redesign themeing system
redesign themeing system

This is a complete redesign of Ayllu's themeing system as well as it's default
theme and documentation site.

New features include:

* Ability to mix between built-in compiled and user provided external themes.
* Lightning CSS[1] support eliminating the need for sassc.
* Change from picocss to Open Props[2] which has better support for user
themeing.
* Additional ability to customize all SVG contents used in the UI.
* Ability to customize RSS pages via a theme based XSL file.
* General readability and design improvements (thanks toastal).
* Improvements for using legacy browsers like Dillo, Lynx, etc.
* Some accessibility improvements (WIP still).

1. https://lightningcss.dev/
2. https://open-props.style/
1diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md
2index 29a70bd..f173e9f 100644
3--- a/ATTRIBUTIONS.md
4+++ b/ATTRIBUTIONS.md
5 @@ -14,9 +14,14 @@ Ayllu would not be possible without many free software projects.
6 * [tree-sitter](https://github.com/tree-sitter/tree-sitter)
7 * [comrak](https://github.com/kivikakk/comrak)
8 * [tokei](https://github.com/XAMPPRocky/tokei/)
9+ * [lightningcss](https://docs.rs/lightningcss/latest/lightningcss/index.html)
10
11 And many more, see the `Cargo.toml` file.
12
13+ ##### MIT
14+
15+ [open-props.style](https://open-props.style/) used for base CSS and normalizing.
16+
17 ##### WTFPL
18
19 The "smart git" http backend is inspired / modified from [rgit](https://github.com/w4/rgit).
20 diff --git a/Cargo.lock b/Cargo.lock
21index ca1b622..aea4172 100644
22--- a/Cargo.lock
23+++ b/Cargo.lock
24 @@ -28,6 +28,17 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
25
26 [[package]]
27 name = "ahash"
28+ version = "0.7.8"
29+ source = "registry+https://github.com/rust-lang/crates.io-index"
30+ checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
31+ dependencies = [
32+ "getrandom",
33+ "once_cell",
34+ "version_check",
35+ ]
36+
37+ [[package]]
38+ name = "ahash"
39 version = "0.8.11"
40 source = "registry+https://github.com/rust-lang/crates.io-index"
41 checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
42 @@ -517,9 +528,11 @@ dependencies = [
43 "globwalk",
44 "headers",
45 "httparse",
46+ "include_dir",
47 "lazy_static",
48 "libloading 0.8.3",
49 "libsqlite3-sys",
50+ "lightningcss",
51 "log",
52 "mime",
53 "mime_guess",
54 @@ -710,6 +723,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
55 checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
56
57 [[package]]
58+ name = "base64-simd"
59+ version = "0.7.0"
60+ source = "registry+https://github.com/rust-lang/crates.io-index"
61+ checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5"
62+ dependencies = [
63+ "simd-abstraction",
64+ ]
65+
66+ [[package]]
67 name = "base64ct"
68 version = "1.6.0"
69 source = "registry+https://github.com/rust-lang/crates.io-index"
70 @@ -761,6 +783,18 @@ dependencies = [
71 ]
72
73 [[package]]
74+ name = "bitvec"
75+ version = "1.0.1"
76+ source = "registry+https://github.com/rust-lang/crates.io-index"
77+ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
78+ dependencies = [
79+ "funty",
80+ "radium",
81+ "tap",
82+ "wyz",
83+ ]
84+
85+ [[package]]
86 name = "blake2"
87 version = "0.10.6"
88 source = "registry+https://github.com/rust-lang/crates.io-index"
89 @@ -811,6 +845,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
90 checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
91
92 [[package]]
93+ name = "bytecheck"
94+ version = "0.6.12"
95+ source = "registry+https://github.com/rust-lang/crates.io-index"
96+ checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
97+ dependencies = [
98+ "bytecheck_derive",
99+ "ptr_meta",
100+ "simdutf8",
101+ ]
102+
103+ [[package]]
104+ name = "bytecheck_derive"
105+ version = "0.6.12"
106+ source = "registry+https://github.com/rust-lang/crates.io-index"
107+ checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
108+ dependencies = [
109+ "proc-macro2",
110+ "quote",
111+ "syn 1.0.109",
112+ ]
113+
114+ [[package]]
115 name = "bytecount"
116 version = "0.6.7"
117 source = "registry+https://github.com/rust-lang/crates.io-index"
118 @@ -879,7 +935,7 @@ checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e"
119 dependencies = [
120 "chrono",
121 "chrono-tz-build",
122- "phf",
123+ "phf 0.11.2",
124 ]
125
126 [[package]]
127 @@ -889,8 +945,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
128 checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f"
129 dependencies = [
130 "parse-zoneinfo",
131- "phf",
132- "phf_codegen",
133+ "phf 0.11.2",
134+ "phf_codegen 0.11.2",
135 ]
136
137 [[package]]
138 @@ -1030,6 +1086,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
139 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
140
141 [[package]]
142+ name = "const-str"
143+ version = "0.3.2"
144+ source = "registry+https://github.com/rust-lang/crates.io-index"
145+ checksum = "21077772762a1002bb421c3af42ac1725fa56066bfc53d9a55bb79905df2aaf3"
146+ dependencies = [
147+ "const-str-proc-macro",
148+ ]
149+
150+ [[package]]
151+ name = "const-str-proc-macro"
152+ version = "0.3.2"
153+ source = "registry+https://github.com/rust-lang/crates.io-index"
154+ checksum = "5e1e0fdd2e5d3041e530e1b21158aeeef8b5d0e306bc5c1e3d6cf0930d10e25a"
155+ dependencies = [
156+ "proc-macro2",
157+ "quote",
158+ "syn 1.0.109",
159+ ]
160+
161+ [[package]]
162 name = "cookie"
163 version = "0.18.0"
164 source = "registry+https://github.com/rust-lang/crates.io-index"
165 @@ -1185,6 +1261,38 @@ dependencies = [
166 ]
167
168 [[package]]
169+ name = "cssparser"
170+ version = "0.33.0"
171+ source = "registry+https://github.com/rust-lang/crates.io-index"
172+ checksum = "9be934d936a0fbed5bcdc01042b770de1398bf79d0e192f49fa7faea0e99281e"
173+ dependencies = [
174+ "cssparser-macros",
175+ "dtoa-short",
176+ "itoa",
177+ "phf 0.11.2",
178+ "smallvec",
179+ ]
180+
181+ [[package]]
182+ name = "cssparser-color"
183+ version = "0.1.0"
184+ source = "registry+https://github.com/rust-lang/crates.io-index"
185+ checksum = "556c099a61d85989d7af52b692e35a8d68a57e7df8c6d07563dc0778b3960c9f"
186+ dependencies = [
187+ "cssparser",
188+ ]
189+
190+ [[package]]
191+ name = "cssparser-macros"
192+ version = "0.6.1"
193+ source = "registry+https://github.com/rust-lang/crates.io-index"
194+ checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
195+ dependencies = [
196+ "quote",
197+ "syn 2.0.52",
198+ ]
199+
200+ [[package]]
201 name = "darling"
202 version = "0.14.4"
203 source = "registry+https://github.com/rust-lang/crates.io-index"
204 @@ -1231,12 +1339,34 @@ dependencies = [
205 ]
206
207 [[package]]
208+ name = "dashmap"
209+ version = "5.5.3"
210+ source = "registry+https://github.com/rust-lang/crates.io-index"
211+ checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
212+ dependencies = [
213+ "cfg-if 1.0.0",
214+ "hashbrown 0.14.3",
215+ "lock_api",
216+ "once_cell",
217+ "parking_lot_core 0.9.9",
218+ ]
219+
220+ [[package]]
221 name = "data-encoding"
222 version = "2.5.0"
223 source = "registry+https://github.com/rust-lang/crates.io-index"
224 checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
225
226 [[package]]
227+ name = "data-url"
228+ version = "0.1.1"
229+ source = "registry+https://github.com/rust-lang/crates.io-index"
230+ checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193"
231+ dependencies = [
232+ "matches",
233+ ]
234+
235+ [[package]]
236 name = "der"
237 version = "0.7.8"
238 source = "registry+https://github.com/rust-lang/crates.io-index"
239 @@ -1377,6 +1507,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
240 checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
241
242 [[package]]
243+ name = "dtoa"
244+ version = "1.0.9"
245+ source = "registry+https://github.com/rust-lang/crates.io-index"
246+ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
247+
248+ [[package]]
249+ name = "dtoa-short"
250+ version = "0.3.4"
251+ source = "registry+https://github.com/rust-lang/crates.io-index"
252+ checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74"
253+ dependencies = [
254+ "dtoa",
255+ ]
256+
257+ [[package]]
258 name = "dwrote"
259 version = "0.11.0"
260 source = "registry+https://github.com/rust-lang/crates.io-index"
261 @@ -1871,6 +2016,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
262 checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
263
264 [[package]]
265+ name = "funty"
266+ version = "2.0.0"
267+ source = "registry+https://github.com/rust-lang/crates.io-index"
268+ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
269+
270+ [[package]]
271 name = "futures"
272 version = "0.3.30"
273 source = "registry+https://github.com/rust-lang/crates.io-index"
274 @@ -1999,6 +2150,15 @@ dependencies = [
275 ]
276
277 [[package]]
278+ name = "fxhash"
279+ version = "0.2.1"
280+ source = "registry+https://github.com/rust-lang/crates.io-index"
281+ checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
282+ dependencies = [
283+ "byteorder",
284+ ]
285+
286+ [[package]]
287 name = "generic-array"
288 version = "0.14.7"
289 source = "registry+https://github.com/rust-lang/crates.io-index"
290 @@ -2053,6 +2213,12 @@ dependencies = [
291 ]
292
293 [[package]]
294+ name = "glob"
295+ version = "0.3.1"
296+ source = "registry+https://github.com/rust-lang/crates.io-index"
297+ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
298+
299+ [[package]]
300 name = "globset"
301 version = "0.4.14"
302 source = "registry+https://github.com/rust-lang/crates.io-index"
303 @@ -2143,6 +2309,9 @@ name = "hashbrown"
304 version = "0.12.3"
305 source = "registry+https://github.com/rust-lang/crates.io-index"
306 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
307+ dependencies = [
308+ "ahash 0.7.8",
309+ ]
310
311 [[package]]
312 name = "hashbrown"
313 @@ -2150,7 +2319,7 @@ version = "0.14.3"
314 source = "registry+https://github.com/rust-lang/crates.io-index"
315 checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
316 dependencies = [
317- "ahash",
318+ "ahash 0.8.11",
319 "allocator-api2",
320 ]
321
322 @@ -2555,6 +2724,26 @@ dependencies = [
323 ]
324
325 [[package]]
326+ name = "include_dir"
327+ version = "0.7.3"
328+ source = "registry+https://github.com/rust-lang/crates.io-index"
329+ checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
330+ dependencies = [
331+ "glob",
332+ "include_dir_macros",
333+ ]
334+
335+ [[package]]
336+ name = "include_dir_macros"
337+ version = "0.7.3"
338+ source = "registry+https://github.com/rust-lang/crates.io-index"
339+ checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
340+ dependencies = [
341+ "proc-macro2",
342+ "quote",
343+ ]
344+
345+ [[package]]
346 name = "indexmap"
347 version = "1.9.3"
348 source = "registry+https://github.com/rust-lang/crates.io-index"
349 @@ -2742,7 +2931,7 @@ version = "0.17.1"
350 source = "registry+https://github.com/rust-lang/crates.io-index"
351 checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978"
352 dependencies = [
353- "ahash",
354+ "ahash 0.8.11",
355 "anyhow",
356 "base64 0.21.7",
357 "bytecount",
358 @@ -2924,6 +3113,31 @@ dependencies = [
359 ]
360
361 [[package]]
362+ name = "lightningcss"
363+ version = "1.0.0-alpha.55"
364+ source = "registry+https://github.com/rust-lang/crates.io-index"
365+ checksum = "3bd5bed3814fb631bfc1e24c2be6f7e86a9837c660909acab79a38374dcb8798"
366+ dependencies = [
367+ "ahash 0.8.11",
368+ "bitflags 2.4.2",
369+ "const-str",
370+ "cssparser",
371+ "cssparser-color",
372+ "dashmap 5.5.3",
373+ "data-encoding",
374+ "getrandom",
375+ "itertools 0.10.5",
376+ "lazy_static",
377+ "parcel_selectors",
378+ "parcel_sourcemap",
379+ "paste",
380+ "pathdiff",
381+ "rayon",
382+ "serde",
383+ "smallvec",
384+ ]
385+
386+ [[package]]
387 name = "line-wrap"
388 version = "0.1.1"
389 source = "registry+https://github.com/rust-lang/crates.io-index"
390 @@ -3708,12 +3922,48 @@ dependencies = [
391 ]
392
393 [[package]]
394+ name = "outref"
395+ version = "0.1.0"
396+ source = "registry+https://github.com/rust-lang/crates.io-index"
397+ checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4"
398+
399+ [[package]]
400 name = "overload"
401 version = "0.1.1"
402 source = "registry+https://github.com/rust-lang/crates.io-index"
403 checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
404
405 [[package]]
406+ name = "parcel_selectors"
407+ version = "0.26.4"
408+ source = "registry+https://github.com/rust-lang/crates.io-index"
409+ checksum = "05d74befe2d076330d9a58bf9ca2da424568724ab278adf15fb5718253133887"
410+ dependencies = [
411+ "bitflags 2.4.2",
412+ "cssparser",
413+ "fxhash",
414+ "log",
415+ "phf 0.10.1",
416+ "phf_codegen 0.10.0",
417+ "precomputed-hash",
418+ "smallvec",
419+ ]
420+
421+ [[package]]
422+ name = "parcel_sourcemap"
423+ version = "2.1.1"
424+ source = "registry+https://github.com/rust-lang/crates.io-index"
425+ checksum = "485b74d7218068b2b7c0e3ff12fbc61ae11d57cb5d8224f525bd304c6be05bbb"
426+ dependencies = [
427+ "base64-simd",
428+ "data-url",
429+ "rkyv",
430+ "serde",
431+ "serde_json",
432+ "vlq",
433+ ]
434+
435+ [[package]]
436 name = "parking"
437 version = "2.2.0"
438 source = "registry+https://github.com/rust-lang/crates.io-index"
439 @@ -3783,6 +4033,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
440 checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
441
442 [[package]]
443+ name = "pathdiff"
444+ version = "0.2.1"
445+ source = "registry+https://github.com/rust-lang/crates.io-index"
446+ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
447+
448+ [[package]]
449 name = "pathfinder_geometry"
450 version = "0.5.1"
451 source = "registry+https://github.com/rust-lang/crates.io-index"
452 @@ -3882,25 +4138,55 @@ dependencies = [
453
454 [[package]]
455 name = "phf"
456+ version = "0.10.1"
457+ source = "registry+https://github.com/rust-lang/crates.io-index"
458+ checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
459+ dependencies = [
460+ "phf_shared 0.10.0",
461+ ]
462+
463+ [[package]]
464+ name = "phf"
465 version = "0.11.2"
466 source = "registry+https://github.com/rust-lang/crates.io-index"
467 checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
468 dependencies = [
469+ "phf_macros",
470 "phf_shared 0.11.2",
471 ]
472
473 [[package]]
474 name = "phf_codegen"
475+ version = "0.10.0"
476+ source = "registry+https://github.com/rust-lang/crates.io-index"
477+ checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
478+ dependencies = [
479+ "phf_generator 0.10.0",
480+ "phf_shared 0.10.0",
481+ ]
482+
483+ [[package]]
484+ name = "phf_codegen"
485 version = "0.11.2"
486 source = "registry+https://github.com/rust-lang/crates.io-index"
487 checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
488 dependencies = [
489- "phf_generator",
490+ "phf_generator 0.11.2",
491 "phf_shared 0.11.2",
492 ]
493
494 [[package]]
495 name = "phf_generator"
496+ version = "0.10.0"
497+ source = "registry+https://github.com/rust-lang/crates.io-index"
498+ checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
499+ dependencies = [
500+ "phf_shared 0.10.0",
501+ "rand",
502+ ]
503+
504+ [[package]]
505+ name = "phf_generator"
506 version = "0.11.2"
507 source = "registry+https://github.com/rust-lang/crates.io-index"
508 checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
509 @@ -3910,6 +4196,19 @@ dependencies = [
510 ]
511
512 [[package]]
513+ name = "phf_macros"
514+ version = "0.11.2"
515+ source = "registry+https://github.com/rust-lang/crates.io-index"
516+ checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
517+ dependencies = [
518+ "phf_generator 0.11.2",
519+ "phf_shared 0.11.2",
520+ "proc-macro2",
521+ "quote",
522+ "syn 2.0.52",
523+ ]
524+
525+ [[package]]
526 name = "phf_shared"
527 version = "0.10.0"
528 source = "registry+https://github.com/rust-lang/crates.io-index"
529 @@ -4140,6 +4439,26 @@ dependencies = [
530 ]
531
532 [[package]]
533+ name = "ptr_meta"
534+ version = "0.1.4"
535+ source = "registry+https://github.com/rust-lang/crates.io-index"
536+ checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
537+ dependencies = [
538+ "ptr_meta_derive",
539+ ]
540+
541+ [[package]]
542+ name = "ptr_meta_derive"
543+ version = "0.1.4"
544+ source = "registry+https://github.com/rust-lang/crates.io-index"
545+ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
546+ dependencies = [
547+ "proc-macro2",
548+ "quote",
549+ "syn 1.0.109",
550+ ]
551+
552+ [[package]]
553 name = "quick-error"
554 version = "1.2.3"
555 source = "registry+https://github.com/rust-lang/crates.io-index"
556 @@ -4193,6 +4512,12 @@ dependencies = [
557 ]
558
559 [[package]]
560+ name = "radium"
561+ version = "0.7.0"
562+ source = "registry+https://github.com/rust-lang/crates.io-index"
563+ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
564+
565+ [[package]]
566 name = "rand"
567 version = "0.8.5"
568 source = "registry+https://github.com/rust-lang/crates.io-index"
569 @@ -4316,6 +4641,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
570 checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
571
572 [[package]]
573+ name = "rend"
574+ version = "0.4.2"
575+ source = "registry+https://github.com/rust-lang/crates.io-index"
576+ checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
577+ dependencies = [
578+ "bytecheck",
579+ ]
580+
581+ [[package]]
582 name = "reqwest"
583 version = "0.11.26"
584 source = "registry+https://github.com/rust-lang/crates.io-index"
585 @@ -4425,6 +4759,35 @@ dependencies = [
586 ]
587
588 [[package]]
589+ name = "rkyv"
590+ version = "0.7.44"
591+ source = "registry+https://github.com/rust-lang/crates.io-index"
592+ checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
593+ dependencies = [
594+ "bitvec",
595+ "bytecheck",
596+ "bytes",
597+ "hashbrown 0.12.3",
598+ "ptr_meta",
599+ "rend",
600+ "rkyv_derive",
601+ "seahash",
602+ "tinyvec",
603+ "uuid",
604+ ]
605+
606+ [[package]]
607+ name = "rkyv_derive"
608+ version = "0.7.44"
609+ source = "registry+https://github.com/rust-lang/crates.io-index"
610+ checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
611+ dependencies = [
612+ "proc-macro2",
613+ "quote",
614+ "syn 1.0.109",
615+ ]
616+
617+ [[package]]
618 name = "rsa"
619 version = "0.9.6"
620 source = "registry+https://github.com/rust-lang/crates.io-index"
621 @@ -4646,6 +5009,12 @@ dependencies = [
622 ]
623
624 [[package]]
625+ name = "seahash"
626+ version = "4.1.0"
627+ source = "registry+https://github.com/rust-lang/crates.io-index"
628+ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
629+
630+ [[package]]
631 name = "security-framework"
632 version = "2.9.2"
633 source = "registry+https://github.com/rust-lang/crates.io-index"
634 @@ -4859,12 +5228,27 @@ dependencies = [
635 ]
636
637 [[package]]
638+ name = "simd-abstraction"
639+ version = "0.7.1"
640+ source = "registry+https://github.com/rust-lang/crates.io-index"
641+ checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987"
642+ dependencies = [
643+ "outref",
644+ ]
645+
646+ [[package]]
647 name = "simd-adler32"
648 version = "0.3.7"
649 source = "registry+https://github.com/rust-lang/crates.io-index"
650 checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
651
652 [[package]]
653+ name = "simdutf8"
654+ version = "0.1.4"
655+ source = "registry+https://github.com/rust-lang/crates.io-index"
656+ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
657+
658+ [[package]]
659 name = "simple-counter"
660 version = "0.1.0"
661 source = "registry+https://github.com/rust-lang/crates.io-index"
662 @@ -5007,7 +5391,7 @@ version = "0.7.4"
663 source = "registry+https://github.com/rust-lang/crates.io-index"
664 checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6"
665 dependencies = [
666- "ahash",
667+ "ahash 0.8.11",
668 "atoi",
669 "byteorder",
670 "bytes",
671 @@ -5349,6 +5733,12 @@ dependencies = [
672 ]
673
674 [[package]]
675+ name = "tap"
676+ version = "1.0.1"
677+ source = "registry+https://github.com/rust-lang/crates.io-index"
678+ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
679+
680+ [[package]]
681 name = "tarpc"
682 version = "0.34.0"
683 source = "registry+https://github.com/rust-lang/crates.io-index"
684 @@ -5560,7 +5950,7 @@ dependencies = [
685 "aho-corasick 0.7.20",
686 "clap 2.34.0",
687 "crossbeam-channel",
688- "dashmap",
689+ "dashmap 4.0.2",
690 "dirs",
691 "encoding_rs_io",
692 "env_logger 0.8.4",
693 @@ -6122,6 +6512,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
694 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
695
696 [[package]]
697+ name = "vlq"
698+ version = "0.5.1"
699+ source = "registry+https://github.com/rust-lang/crates.io-index"
700+ checksum = "65dd7eed29412da847b0f78bcec0ac98588165988a8cfe41d4ea1d429f8ccfff"
701+
702+ [[package]]
703 name = "void"
704 version = "1.0.2"
705 source = "registry+https://github.com/rust-lang/crates.io-index"
706 @@ -6544,6 +6940,15 @@ dependencies = [
707 ]
708
709 [[package]]
710+ name = "wyz"
711+ version = "0.5.1"
712+ source = "registry+https://github.com/rust-lang/crates.io-index"
713+ checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
714+ dependencies = [
715+ "tap",
716+ ]
717+
718+ [[package]]
719 name = "xdg"
720 version = "2.5.2"
721 source = "registry+https://github.com/rust-lang/crates.io-index"
722 diff --git a/ayllu/Cargo.toml b/ayllu/Cargo.toml
723index 00d511a..5a84546 100644
724--- a/ayllu/Cargo.toml
725+++ b/ayllu/Cargo.toml
726 @@ -66,6 +66,7 @@ tokio-stream = "0.1.15"
727 httparse = "1.8.0"
728 thiserror = "1.0.58"
729 headers = "0.4.0"
730+ include_dir = { version = "0.7.3", features = ["glob"] }
731
732 # NOTE: this must be cautiously updated along with sqlx and rusqlite.
733 [dependencies.libsqlite3-sys]
734 @@ -73,3 +74,4 @@ version = "0.27.0"
735
736 [build-dependencies]
737 cc="*"
738+ lightningcss = "1.0.0-alpha.55"
739 diff --git a/ayllu/build.rs b/ayllu/build.rs
740new file mode 100644
741index 0000000..abafb99
742--- /dev/null
743+++ b/ayllu/build.rs
744 @@ -0,0 +1,30 @@
745+ use lightningcss::{
746+ bundler::{Bundler, FileProvider},
747+ stylesheet::{ParserOptions, PrinterOptions},
748+ };
749+
750+ use std::env::var;
751+ use std::fs::{create_dir_all, File};
752+ use std::io::prelude::*;
753+ use std::path::Path;
754+
755+ fn main() {
756+ let fs = FileProvider::new();
757+ let mut bundler = Bundler::new(&fs, None, ParserOptions::default());
758+ // compiles a minified version of the base theme which all additional
759+ // themes inherit from.
760+ let out_dir = var("OUT_DIR").unwrap();
761+ let stylesheet = bundler
762+ .bundle(Path::new("themes/default/theme.css"))
763+ .unwrap();
764+ let css = stylesheet
765+ .to_css(PrinterOptions {
766+ minify: true,
767+ ..Default::default()
768+ })
769+ .unwrap();
770+ let theme_dir = Path::new(&out_dir).join("themes/default");
771+ create_dir_all(theme_dir.clone()).unwrap();
772+ let mut file = File::create(theme_dir.join("theme.min.css")).unwrap();
773+ write!(file, "{}", css.code).unwrap();
774+ }
775 diff --git a/ayllu/colors/nord.css b/ayllu/colors/nord.css
776new file mode 100644
777index 0000000..e492c4f
778--- /dev/null
779+++ b/ayllu/colors/nord.css
780 @@ -0,0 +1,231 @@
781+ /*
782+ * Copyright (c) 2016-present Sven Greb <development@svengreb.de>
783+ * This source code is licensed under the MIT license found in the license file.
784+ */
785+
786+ /*
787+ * References:
788+ * 1. https://www.w3.org/TR/css-variables
789+ * 2. https://www.w3.org/TR/selectors/#root-pseudo
790+ * 3. https://drafts.csswg.org/css-variables
791+ * 4. https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables
792+ * 5. https://warpspire.com/kss
793+ * 6. https://github.com/kss-node/kss-node
794+ */
795+
796+ /*
797+ An arctic, north-bluish color palette.
798+ Created for the clean- and minimal flat design pattern to achieve a optimal focus and readability for code syntax
799+ highlighting and UI.
800+ It consists of a total of sixteen, carefully selected, dimmed pastel colors for a eye-comfortable, but yet colorful
801+ ambiance.
802+
803+ Styleguide Nord
804+ */
805+
806+ :root {
807+ /*
808+ Base component color of "Polar Night".
809+
810+ Used for texts, backgrounds, carets and structuring characters like curly- and square brackets.
811+
812+ Markup:
813+ <div style="background-color:#2e3440; width=60; height=60"></div>
814+
815+ Styleguide Nord - Polar Night
816+ */
817+ --nord0: #2e3440;
818+
819+ /*
820+ Lighter shade color of the base component color.
821+
822+ Used as a lighter background color for UI elements like status bars.
823+
824+ Markup:
825+ <div style="background-color:#3b4252; width=60; height=60"></div>
826+
827+ Styleguide Nord - Polar Night
828+ */
829+ --nord1: #3b4252;
830+
831+ /*
832+ Lighter shade color of the base component color.
833+
834+ Used as line highlighting in the editor.
835+ In the UI scope it may be used as selection- and highlight color.
836+
837+ Markup:
838+ <div style="background-color:#434c5e; width=60; height=60"></div>
839+
840+ Styleguide Nord - Polar Night
841+ */
842+ --nord2: #434c5e;
843+
844+ /*
845+ Lighter shade color of the base component color.
846+
847+ Used for comments, invisibles, indent- and wrap guide marker.
848+ In the UI scope used as pseudoclass color for disabled elements.
849+
850+ Markup:
851+ <div style="background-color:#4c566a; width=60; height=60"></div>
852+
853+ Styleguide Nord - Polar Night
854+ */
855+ --nord3: #4c566a;
856+
857+ /*
858+ Base component color of "Snow Storm".
859+
860+ Main color for text, variables, constants and attributes.
861+ In the UI scope used as semi-light background depending on the theme shading design.
862+
863+ Markup:
864+ <div style="background-color:#d8dee9; width=60; height=60"></div>
865+
866+ Styleguide Nord - Snow Storm
867+ */
868+ --nord4: #d8dee9;
869+
870+ /*
871+ Lighter shade color of the base component color.
872+
873+ Used as a lighter background color for UI elements like status bars.
874+ Used as semi-light background depending on the theme shading design.
875+
876+ Markup:
877+ <div style="background-color:#e5e9f0; width=60; height=60"></div>
878+
879+ Styleguide Nord - Snow Storm
880+ */
881+ --nord5: #e5e9f0;
882+
883+ /*
884+ Lighter shade color of the base component color.
885+
886+ Used for punctuations, carets and structuring characters like curly- and square brackets.
887+ In the UI scope used as background, selection- and highlight color depending on the theme shading design.
888+
889+ Markup:
890+ <div style="background-color:#eceff4; width=60; height=60"></div>
891+
892+ Styleguide Nord - Snow Storm
893+ */
894+ --nord6: #eceff4;
895+
896+ /*
897+ Bluish core color.
898+
899+ Used for classes, types and documentation tags.
900+
901+ Markup:
902+ <div style="background-color:#8fbcbb; width=60; height=60"></div>
903+
904+ Styleguide Nord - Frost
905+ */
906+ --nord7: #8fbcbb;
907+
908+ /*
909+ Bluish core accent color.
910+
911+ Represents the accent color of the color palette.
912+ Main color for primary UI elements and methods/functions.
913+
914+ Can be used for
915+ - Markup quotes
916+ - Markup link URLs
917+
918+ Markup:
919+ <div style="background-color:#88c0d0; width=60; height=60"></div>
920+
921+ Styleguide Nord - Frost
922+ */
923+ --nord8: #88c0d0;
924+
925+ /*
926+ Bluish core color.
927+
928+ Used for language-specific syntactic/reserved support characters and keywords, operators, tags, units and
929+ punctuations like (semi)colons,commas and braces.
930+
931+ Markup:
932+ <div style="background-color:#81a1c1; width=60; height=60"></div>
933+
934+ Styleguide Nord - Frost
935+ */
936+ --nord9: #81a1c1;
937+
938+ /*
939+ Bluish core color.
940+
941+ Used for markup doctypes, import/include/require statements, pre-processor statements and at-rules (`@`).
942+
943+ Markup:
944+ <div style="background-color:#5e81ac; width=60; height=60"></div>
945+
946+ Styleguide Nord - Frost
947+ */
948+ --nord10: #5e81ac;
949+
950+ /*
951+ Colorful component color.
952+
953+ Used for errors, git/diff deletion and linter marker.
954+
955+ Markup:
956+ <div style="background-color:#bf616a; width=60; height=60"></div>
957+
958+ Styleguide Nord - Aurora
959+ */
960+ --nord11: #bf616a;
961+
962+ /*
963+ Colorful component color.
964+
965+ Used for annotations.
966+
967+ Markup:
968+ <div style="background-color:#d08770; width=60; height=60"></div>
969+
970+ Styleguide Nord - Aurora
971+ */
972+ --nord12: #d08770;
973+
974+ /*
975+ Colorful component color.
976+
977+ Used for escape characters, regular expressions and markup entities.
978+ In the UI scope used for warnings and git/diff renamings.
979+
980+ Markup:
981+ <div style="background-color:#ebcb8b; width=60; height=60"></div>
982+
983+ Styleguide Nord - Aurora
984+ */
985+ --nord13: #ebcb8b;
986+
987+ /*
988+ Colorful component color.
989+
990+ Main color for strings and attribute values.
991+ In the UI scope used for git/diff additions and success visualizations.
992+
993+ Markup:
994+ <div style="background-color:#a3be8c; width=60; height=60"></div>
995+
996+ Styleguide Nord - Aurora
997+ */
998+ --nord14: #a3be8c;
999+
1000+ /*
1001+ Colorful component color.
1002+
1003+ Used for numbers.
1004+
1005+ Markup:
1006+ <div style="background-color:#b48ead; width=60; height=60"></div>
1007+
1008+ Styleguide Nord - Aurora
1009+ */
1010+ --nord15: #b48ead;
1011+ }
1012 diff --git a/ayllu/src/config.rs b/ayllu/src/config.rs
1013index 3e88b9e..792bf10 100644
1014--- a/ayllu/src/config.rs
1015+++ b/ayllu/src/config.rs
1016 @@ -1,7 +1,8 @@
1017 use std::collections::HashMap;
1018 use std::error::Error;
1019- use std::fs::{metadata, read_dir};
1020+ use std::fs::metadata;
1021 use std::num::NonZeroUsize;
1022+ use std::path::PathBuf;
1023 use std::thread::available_parallelism;
1024
1025 use comrak::ComrakOptions;
1026 @@ -52,34 +53,25 @@ pub struct List {
1027 }
1028
1029 #[derive(Deserialize, Serialize, Clone, Debug)]
1030+ pub struct Theme {
1031+ pub name: String,
1032+ pub path: PathBuf,
1033+ }
1034+
1035+ #[derive(Deserialize, Serialize, Clone, Debug)]
1036 pub struct Web {
1037- #[serde(default = "Web::default_themes_path")]
1038- pub themes_path: String,
1039- #[serde(default = "Web::default_base_theme")]
1040- pub base_theme: String,
1041+ pub themes_path: Option<String>,
1042 #[serde(default = "Web::default_default_theme")]
1043 pub default_theme: String,
1044- #[serde(default = "Web::default_themes")]
1045- pub themes: Vec<String>,
1046+ pub themes: Option<Vec<Theme>>,
1047 #[serde(default = "Web::default_unsafe_markdown")]
1048 pub unsafe_markdown: bool,
1049 }
1050
1051 impl Web {
1052- fn default_themes_path() -> String {
1053- String::from("/usr/lib/ayllu/themes")
1054- }
1055-
1056- fn default_base_theme() -> String {
1057- String::from("default")
1058- }
1059-
1060- fn default_themes() -> Vec<String> {
1061- Vec::new()
1062- }
1063
1064 fn default_default_theme() -> String {
1065- String::from("tokyonight")
1066+ String::from("default")
1067 }
1068
1069 fn default_unsafe_markdown() -> bool {
1070 @@ -95,7 +87,7 @@ pub struct Database {
1071 pub migrate: bool,
1072 #[serde(default = "Database::migrations_default")]
1073 /// path to migrations
1074- pub migrations: String
1075+ pub migrations: String,
1076 }
1077
1078 impl Database {
1079 @@ -277,32 +269,9 @@ pub struct Config {
1080
1081 impl Configurable for Config {
1082 fn validate(&mut self) -> Result<(), Box<dyn Error>> {
1083- // load themes from the file system when the configuration is loaded, they
1084- // are made available in the configuration page of the UI.
1085- let mut themes: Vec<String> = Vec::new();
1086-
1087- match read_dir(&self.web.themes_path) {
1088- Ok(paths) => {
1089- for dir in paths.into_iter() {
1090- let dir_path = dir?.path();
1091- let filename = dir_path.file_name().unwrap();
1092- let theme_name = filename.to_str().unwrap();
1093- themes.push(theme_name.to_string());
1094- }
1095- }
1096- Err(e) => {
1097- return Err(format!(
1098- "failed to load themes from path: {} ({})",
1099- self.web.themes_path, e
1100- )
1101- .into())
1102- }
1103- }
1104-
1105 let parsed_url = Url::parse(&self.origin)?;
1106 self.domain = parsed_url.domain().map(|domain| domain.to_string());
1107
1108- self.web.themes = themes;
1109 // verify collection names are all valid
1110 if let Some(collection) = self.collections.iter().find(|collection| {
1111 BANNED_COLLECTION_NAMES
1112 diff --git a/ayllu/src/web2/highlight.rs b/ayllu/src/web2/highlight.rs
1113index ff7d9b4..c276560 100644
1114--- a/ayllu/src/web2/highlight.rs
1115+++ b/ayllu/src/web2/highlight.rs
1116 @@ -183,7 +183,7 @@ impl Loader {
1117 fn render_lines(lines: Vec<&str>, show_line_numbers: bool) -> String {
1118 let buf = Vec::new();
1119 let mut file = Cursor::new(buf);
1120- write!(&mut file, "<table>").unwrap();
1121+ write!(&mut file, "<table class=\"code\">").unwrap();
1122 for (i, line) in lines.into_iter().enumerate() {
1123 if show_line_numbers {
1124 write!(&mut file, "<tr><td class=line-number>{:?}</td>", i + 1).unwrap();
1125 @@ -206,7 +206,7 @@ impl Highlighter {
1126 let mut classes: Vec<String> = Vec::new();
1127
1128 for name in names.iter() {
1129- classes.push(format!("class=\"ts_{}\"", name));
1130+ classes.push(format!("class=\"ts_{}\"", name.replace('.', "_")));
1131 }
1132 Self {
1133 names: names.to_vec(),
1134 diff --git a/ayllu/src/web2/middleware/error.rs b/ayllu/src/web2/middleware/error.rs
1135index 956589e..57edae0 100644
1136--- a/ayllu/src/web2/middleware/error.rs
1137+++ b/ayllu/src/web2/middleware/error.rs
1138 @@ -6,14 +6,13 @@ use axum::{
1139 middleware::Next,
1140 response::{Html, IntoResponse, Response},
1141 };
1142- use tera::Tera;
1143
1144 use crate::config::Config;
1145 use crate::web2::error::Error;
1146 use crate::web2::extractors::config::ConfigReader;
1147- use crate::web2::terautil::{Loader, Options};
1148+ use crate::web2::terautil::{Loader, Options, Themes};
1149
1150- pub type State = Arc<(Config, Vec<(String, Tera)>)>;
1151+ pub type State = Arc<(Config, Themes)>;
1152
1153 pub async fn middleware(
1154 extract::State(state): extract::State<State>,
1155 @@ -29,7 +28,7 @@ pub async fn middleware(
1156 };
1157 // reload the theme since the middleware may not have ran yet
1158 let loader = Loader {
1159- templates: state.1.clone(),
1160+ themes: state.1.clone(),
1161 default_theme: state.0.web.default_theme.clone(),
1162 };
1163 let (template, mut ctx) = loader.load(
1164 diff --git a/ayllu/src/web2/middleware/rpc_initiator.rs b/ayllu/src/web2/middleware/rpc_initiator.rs
1165index 0ef7534..5497747 100644
1166--- a/ayllu/src/web2/middleware/rpc_initiator.rs
1167+++ b/ayllu/src/web2/middleware/rpc_initiator.rs
1168 @@ -10,16 +10,16 @@ use axum::{
1169 response::Response,
1170 };
1171 use mime::TEXT_HTML_UTF_8;
1172- use tera::Tera;
1173 use tracing::log;
1174
1175 use crate::config::Config;
1176 use crate::web2::config::Config as ClientConfig;
1177 use crate::web2::extractors::config::ConfigReader;
1178+ use crate::web2::terautil::Themes;
1179 use ayllu_api::{mail::ServerClient as MailClient, xmpp::ServerClient as XmppClient};
1180 use ayllu_rpc::tarpc::{client, serde_transport::unix, tokio_serde::formats::Bincode};
1181
1182- pub type State = Arc<(Config, Vec<(String, Tera)>, &'static [Kind])>;
1183+ pub type State = Arc<(Config, Themes, &'static [Kind])>;
1184
1185 pub enum Kind {
1186 Mail,
1187 @@ -39,12 +39,12 @@ fn plugin_not_enabled(
1188 plugin_name: &str,
1189 cfg: &Config,
1190 client_config: ClientConfig,
1191- templates: &[(String, Tera)],
1192+ templates: &Themes,
1193 ) -> Response {
1194 log::warn!("returning error due to missing plugin: {}", plugin_name);
1195 // reload the theme since the middleware may not have ran yet
1196 let loader = Loader {
1197- templates: templates.to_vec(),
1198+ themes: templates.clone(),
1199 default_theme: cfg.web.default_theme.clone(),
1200 };
1201 let (template, mut ctx) = loader.load(
1202 diff --git a/ayllu/src/web2/middleware/template.rs b/ayllu/src/web2/middleware/template.rs
1203index 58f93cc..4efe163 100644
1204--- a/ayllu/src/web2/middleware/template.rs
1205+++ b/ayllu/src/web2/middleware/template.rs
1206 @@ -6,10 +6,10 @@ use axum::{extract, middleware::Next, response::Response};
1207
1208 use crate::config::Config;
1209 use crate::web2::extractors::config::ConfigReader;
1210- use crate::web2::terautil::{Loader, Options};
1211+ use crate::web2::terautil::{Loader, Options, Themes};
1212
1213 pub type Template = (Tera, TeraContext);
1214- pub type State = extract::State<Arc<(Config, Vec<(String, Tera)>)>>;
1215+ pub type State = extract::State<Arc<(Config, Themes)>>;
1216
1217 #[derive(Deserialize)]
1218 pub struct CommonParams {
1219 @@ -25,7 +25,7 @@ pub async fn middleware(
1220 next: Next,
1221 ) -> Response {
1222 let loader = Loader {
1223- templates: state.1.clone(),
1224+ themes: state.1.clone(),
1225 default_theme: state.0.web.default_theme.clone(),
1226 };
1227 let (template, ctx) = loader.load(
1228 diff --git a/ayllu/src/web2/routes/assets.rs b/ayllu/src/web2/routes/assets.rs
1229index 976f19b..700baba 100644
1230--- a/ayllu/src/web2/routes/assets.rs
1231+++ b/ayllu/src/web2/routes/assets.rs
1232 @@ -1,68 +1,134 @@
1233- use std::path::PathBuf;
1234-
1235- use axum::{body::Body, extract::Path, http::header, response::Response, Extension};
1236-
1237- use tokio::fs::File;
1238- use tokio_util::io::ReaderStream;
1239+ use std::path::Path;
1240+ use std::sync::Arc;
1241
1242+ use axum::{
1243+ body::{Body, Bytes},
1244+ extract,
1245+ http::header,
1246+ response::Response,
1247+ };
1248+ use bytes::{BufMut, BytesMut};
1249 use mime;
1250- use mime_guess;
1251+ use mime_guess::MimeGuess;
1252+ use serde::Deserialize;
1253+ use tokio::fs::{read, File};
1254+ use tokio_util::io::ReaderStream;
1255
1256 use crate::config::Config;
1257 use crate::web2::error::Error;
1258 use crate::web2::extractors::config::ConfigReader;
1259+ use crate::web2::terautil::{Assets, Themes};
1260+
1261+ pub type State = Arc<(Config, Themes)>;
1262
1263- pub async fn serve_css(
1264- Extension(system_config): Extension<Config>,
1265+ #[derive(Debug, Deserialize, Default)]
1266+ pub struct Resource {
1267+ pub name: String,
1268+ }
1269+
1270+ async fn to_response(assets: &Assets, path: &str) -> Result<Response, Error> {
1271+ let guess = MimeGuess::from_path(Path::new(path)).first_or_octet_stream();
1272+ let content_type = guess.to_string();
1273+ tracing::debug!("serving static asset {}: {}", content_type, path);
1274+ if let Some(asset) = assets.0.get(path) {
1275+ match asset {
1276+ crate::web2::terautil::Asset::Raw(content) => {
1277+ let bytes = Bytes::from(content.clone());
1278+ Ok(Response::builder()
1279+ .header(header::CONTENT_TYPE, content_type)
1280+ .body(Body::from(bytes))
1281+ .unwrap())
1282+ }
1283+ crate::web2::terautil::Asset::FilePath(path) => {
1284+ let file = File::open(path).await?;
1285+ let stream = ReaderStream::new(file);
1286+ Ok(Response::builder()
1287+ .header(header::CONTENT_TYPE, content_type)
1288+ .body(Body::from_stream(stream))
1289+ .unwrap())
1290+ }
1291+ }
1292+ } else {
1293+ Ok(Response::builder().status(404).body(Body::empty()).unwrap())
1294+ }
1295+ }
1296+
1297+ pub async fn serve_stylesheet(
1298+ extract::State(state): extract::State<State>,
1299 ConfigReader(user_config): ConfigReader,
1300 ) -> Result<Response, Error> {
1301- let themes_path = &system_config.web.themes_path.clone();
1302- let mut file_path = PathBuf::from(themes_path);
1303- file_path.push(user_config.theme.unwrap_or(system_config.web.default_theme));
1304- file_path.push("main.min.css");
1305- let file = tokio::fs::File::open(file_path).await?;
1306- let stream = ReaderStream::new(file);
1307- Ok(Response::builder()
1308- .header(header::CONTENT_TYPE, mime::TEXT_CSS.as_ref())
1309- .body(Body::from_stream(stream))
1310- .unwrap())
1311+ if let Some(theme_name) = user_config.theme {
1312+ if let Some(theme) = state.1 .0.get(&theme_name) {
1313+ tracing::debug!("serving alternative theme: {}", theme_name);
1314+ let default_theme = state
1315+ .1
1316+ .0
1317+ .get("default")
1318+ .expect("default theme not configured");
1319+ let base_stylesheet = default_theme
1320+ .1
1321+ .0
1322+ .get("main.min.css")
1323+ .expect("default stylesheet not configured");
1324+ let base_stylesheet_bytes = match base_stylesheet {
1325+ crate::web2::terautil::Asset::Raw(content) => content.clone(),
1326+ crate::web2::terautil::Asset::FilePath(path) => read(path).await?,
1327+ };
1328+ if let Some(stylesheet) = theme.1 .0.get("theme.css") {
1329+ let stylesheet = match stylesheet {
1330+ crate::web2::terautil::Asset::Raw(content) => {
1331+ let mut raw_theme = BytesMut::from(base_stylesheet_bytes.as_slice());
1332+ raw_theme.put(&b"\n"[..]);
1333+ raw_theme.put_slice(content.as_slice());
1334+ raw_theme
1335+ }
1336+ crate::web2::terautil::Asset::FilePath(path) => {
1337+ let mut raw_theme = BytesMut::from(base_stylesheet_bytes.as_slice());
1338+ raw_theme.put(&b"\n"[..]);
1339+ raw_theme.put_slice(read(path).await?.as_slice());
1340+ // let mut raw_theme = read(path).await?.as_slice();
1341+ raw_theme
1342+ }
1343+ };
1344+ return Ok(Response::builder()
1345+ .status(200)
1346+ .header(header::CONTENT_TYPE, mime::TEXT_CSS_UTF_8.to_string())
1347+ .body(Body::from(stylesheet.to_vec()))
1348+ .unwrap());
1349+ }
1350+ }
1351+ };
1352+ to_response(
1353+ &state
1354+ .1
1355+ .0
1356+ .get("default")
1357+ .expect("default theme not configured")
1358+ .1,
1359+ "main.min.css",
1360+ )
1361+ .await
1362 }
1363
1364 pub async fn serve_asset(
1365- Extension(system_config): Extension<Config>,
1366+ extract::Path(resource): extract::Path<Resource>,
1367+ extract::State(state): extract::State<State>,
1368 ConfigReader(user_config): ConfigReader,
1369- Path(asset): Path<String>,
1370 ) -> Result<Response, Error> {
1371- let themes_path = &system_config.web.themes_path.clone();
1372- let mut file_path = PathBuf::from(themes_path);
1373- file_path.push(user_config.theme.unwrap_or(system_config.web.default_theme));
1374- file_path.push("assets");
1375- file_path.push(&asset);
1376- let file_path = file_path.to_str().unwrap();
1377- let file = {
1378- match File::open(file_path).await {
1379- Ok(file) => {
1380- log::debug!("loaded asset from: {}", file_path);
1381- file
1382- }
1383- Err(_) => {
1384- let mut fallback_path = PathBuf::from(themes_path);
1385- fallback_path.push(system_config.web.base_theme.clone());
1386- fallback_path.push("assets");
1387- fallback_path.push(&asset);
1388- let fallback_path = fallback_path.to_str().unwrap();
1389- let file = File::open(fallback_path).await?;
1390- log::debug!("loaded asset from: {}", fallback_path);
1391- file
1392- }
1393+ if let Some(theme_name) = user_config.theme {
1394+ if let Some(theme) = state.1 .0.get(&theme_name) {
1395+ tracing::debug!("serving alternative theme: {}", theme_name);
1396+ return to_response(&theme.1, &resource.name).await;
1397 }
1398 };
1399- let stream = ReaderStream::new(file);
1400- let mime = mime_guess::from_path(file_path)
1401- .first_or_octet_stream()
1402- .to_string();
1403- Ok(Response::builder()
1404- .header("Content-Type", mime)
1405- .body(Body::from_stream(stream))
1406- .unwrap())
1407+ to_response(
1408+ &state
1409+ .1
1410+ .0
1411+ .get("default")
1412+ .expect("default theme is not configured")
1413+ .1,
1414+ &resource.name,
1415+ )
1416+ .await
1417 }
1418 diff --git a/ayllu/src/web2/routes/config.rs b/ayllu/src/web2/routes/config.rs
1419index 85d7184..788efba 100644
1420--- a/ayllu/src/web2/routes/config.rs
1421+++ b/ayllu/src/web2/routes/config.rs
1422 @@ -14,12 +14,13 @@ use crate::web2::navigation;
1423
1424 pub async fn serve(
1425 Extension(cfg): Extension<SystemConfig>,
1426+ Extension(themes): Extension<Vec<String>>,
1427 ConfigReader(client_config): ConfigReader,
1428 Extension((templates, mut ctx)): Extension<Template>,
1429 ) -> Result<Html<String>, Error> {
1430 ctx.insert("title", "config");
1431 ctx.insert("config", &client_config);
1432- ctx.insert("themes", &cfg.web.themes);
1433+ ctx.insert("themes", &themes);
1434 ctx.insert(
1435 "nav_elements",
1436 &navigation::global("config", cfg.mail.is_some()),
1437 diff --git a/ayllu/src/web2/routes/rss.rs b/ayllu/src/web2/routes/rss.rs
1438index cf80278..e309cf3 100644
1439--- a/ayllu/src/web2/routes/rss.rs
1440+++ b/ayllu/src/web2/routes/rss.rs
1441 @@ -21,7 +21,7 @@ use ayllu_git::{Commit, Scanner, Stats, Tag, Wrapper};
1442
1443 fn stylesheet_hack(input: String) -> String {
1444 const ORIGINAL: &str = r#"<?xml version="1.0" encoding="utf-8"?>"#;
1445- const REPLACEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="/static/assets/feed.xsl" type="text/xsl"?>"#;
1446+ const REPLACEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="/static/feed.xsl" type="text/xsl"?>"#;
1447 input.replacen(ORIGINAL, REPLACEMENT, 1)
1448 }
1449
1450 diff --git a/ayllu/src/web2/server.rs b/ayllu/src/web2/server.rs
1451index 770b355..8cfc4cd 100644
1452--- a/ayllu/src/web2/server.rs
1453+++ b/ayllu/src/web2/server.rs
1454 @@ -1,6 +1,5 @@
1455 use std::collections::HashMap;
1456 use std::error::Error;
1457- use std::fs;
1458 use std::net::SocketAddrV4;
1459 use std::sync::Arc;
1460
1461 @@ -8,8 +7,6 @@ use axum::{
1462 body::Body, extract::Request, middleware::from_fn_with_state, routing, routing::MethodFilter,
1463 Extension, Router, ServiceExt,
1464 };
1465- use globwalk::glob_builder;
1466- use tera::Tera;
1467 use tokio::net::TcpListener;
1468 use tower::Layer;
1469 use tower_http::{
1470 @@ -47,7 +44,7 @@ use crate::web2::routes::rest::ping;
1471 use crate::web2::routes::robots;
1472 use crate::web2::routes::rss;
1473 use crate::web2::routes::xmpp;
1474- use crate::web2::terautil;
1475+ use crate::web2::terautil::load_themes;
1476 use ayllu_database::Builder;
1477
1478 pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1479 @@ -99,54 +96,14 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1480 .build()
1481 .await?;
1482
1483- // NOTE: files modified on the file system will see their changes
1484- // immediately in the rendered server output however added new files
1485- // will require that the server be restarted.
1486- let templates_path = format!("{}/{}/templates/*", cfg.web.themes_path, cfg.web.base_theme);
1487- // map of all loaded themes at startup, new themes require that the server
1488- // be restarted for now.
1489- let mut templates: Vec<(String, Tera)> = Vec::new();
1490- let mut templates_base = Tera::new(&templates_path)?;
1491- templates_base.register_filter("friendly_time", terautil::FriendlyTime {});
1492- templates_base.register_filter("format_epoch", terautil::FormatEpoch {});
1493- templates_base.register_filter("filemode", terautil::FileMode {});
1494- templates_base.register_filter("human_bytes", terautil::HumanBytes {});
1495- templates_base.register_filter("emoji", terautil::Emojis {});
1496+ let themes = load_themes(cfg.web.themes.as_ref().map_or(vec![], |themes| {
1497+ themes
1498+ .iter()
1499+ .map(|theme| (theme.name.clone(), theme.path.clone()))
1500+ .collect()
1501+ }))?;
1502
1503- for theme in cfg.web.themes.iter().filter(|item| *item != "default") {
1504- let templates_base_path = format!("{}/{}/templates", cfg.web.themes_path, theme);
1505- if fs::metadata(templates_base_path.clone()).is_err() {
1506- // its valid to include a theme without any templates
1507- continue;
1508- }
1509- let templates_path = format!("{}/*.html", templates_base_path);
1510- let mut template_files: Vec<(String, Option<String>)> = Vec::new();
1511- match glob_builder(templates_path).build() {
1512- Ok(globwalker) => {
1513- for entry in globwalker.into_iter() {
1514- let entry = entry?;
1515- let path = String::from(entry.path().to_str().unwrap());
1516- let name = String::from(entry.file_name().to_str().unwrap());
1517- log::info!("loaded template override {} {}", path, name);
1518- template_files.push((path, Some(name)));
1519- }
1520- }
1521- Err(_) => {
1522- continue;
1523- }
1524- }
1525- let mut tera_theme = templates_base.clone();
1526- if !template_files.is_empty() {
1527- log::info!(
1528- "loaded {} template files for theme {}",
1529- template_files.len(),
1530- theme
1531- );
1532- tera_theme.add_template_files(template_files)?;
1533- }
1534- templates.push((theme.clone(), tera_theme));
1535- }
1536- templates.push((String::from("default"), templates_base));
1537+ let theme_names: Vec<String> = themes.0.iter().map(|theme| theme.0.clone()).collect();
1538
1539 let site_mapping = if cfg.sites.enabled {
1540 sites::sites(cfg.clone())?
1541 @@ -173,8 +130,9 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1542 .route("/rss/1m.xml", routing::get(rss::feed_1m))
1543 .route("/about", routing::get(about::serve))
1544 .route("/config", routing::get(config::serve).post(config::update))
1545+ .layer(Extension(theme_names))
1546 .layer(from_fn_with_state(
1547- Arc::new((cfg.clone(), templates.clone())),
1548+ Arc::new((cfg.clone(), themes.clone())),
1549 template::middleware,
1550 )),
1551 )
1552 @@ -198,11 +156,11 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1553 .route("/thread/:list_id/:thread_id", routing::get(mail::thread))
1554 .route("/message/:list_id/:message_id", routing::get(mail::message))
1555 .layer(from_fn_with_state(
1556- Arc::new((cfg.clone(), templates.clone(), mail_required_plugins)),
1557+ Arc::new((cfg.clone(), themes.clone(), mail_required_plugins)),
1558 rpc_initiator::required,
1559 ))
1560 .layer(from_fn_with_state(
1561- Arc::new((cfg.clone(), templates.clone())),
1562+ Arc::new((cfg.clone(), themes.clone())),
1563 template::middleware,
1564 )),
1565 )
1566 @@ -213,11 +171,11 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1567 .route("/:channel", routing::get(xmpp::channel))
1568 .route("/:channel/:last_message", routing::get(xmpp::channel))
1569 .layer(from_fn_with_state(
1570- Arc::new((cfg.clone(), templates.clone(), xmpp_required_plugins)),
1571+ Arc::new((cfg.clone(), themes.clone(), xmpp_required_plugins)),
1572 rpc_initiator::required,
1573 ))
1574 .layer(from_fn_with_state(
1575- Arc::new((cfg.clone(), templates.clone())),
1576+ Arc::new((cfg.clone(), themes.clone())),
1577 template::middleware,
1578 )),
1579 )
1580 @@ -275,7 +233,7 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1581 rpc_initiator::optional,
1582 ))
1583 .layer(from_fn_with_state(
1584- Arc::new((cfg.clone(), templates.clone())),
1585+ Arc::new((cfg.clone(), themes.clone())),
1586 template::middleware,
1587 )),
1588 )
1589 @@ -286,15 +244,20 @@ pub async fn serve(cfg: &Config) -> Result<(), Box<dyn Error>> {
1590 config: Arc::new(cfg.clone()),
1591 })),
1592 )
1593- .route("/static/main.min.css", routing::get(assets::serve_css))
1594- .route("/static/assets/:asset", routing::get(assets::serve_asset))
1595+ .nest(
1596+ "/static",
1597+ Router::new()
1598+ .route("/main.min.css", routing::get(assets::serve_stylesheet))
1599+ .route("/:name", routing::get(assets::serve_asset))
1600+ .with_state(Arc::new((cfg.clone(), themes.clone()))),
1601+ )
1602 .layer(Extension(cfg.clone()))
1603 .layer(Extension(Arc::new(db)))
1604 .layer(Extension(highlighter))
1605 .layer(Extension(adapter))
1606 // error handling
1607 .layer(from_fn_with_state(
1608- Arc::new((cfg.clone(), templates.clone())),
1609+ Arc::new((cfg.clone(), themes.clone())),
1610 error::middleware,
1611 ))
1612 // git hosted static sites
1613 diff --git a/ayllu/src/web2/terautil/filters.rs b/ayllu/src/web2/terautil/filters.rs
1614index 6eef1cc..4aa2288 100644
1615--- a/ayllu/src/web2/terautil/filters.rs
1616+++ b/ayllu/src/web2/terautil/filters.rs
1617 @@ -1,9 +1,12 @@
1618+ use std::fs::read_to_string;
1619+
1620 use tera::{to_value, Filter, Result, Value};
1621
1622 use file_mode::Mode;
1623 use time::{format_description::well_known, OffsetDateTime};
1624
1625 use crate::time as ctime;
1626+ use crate::web2::terautil::themes::Assets;
1627 use crate::web2::util;
1628
1629 pub struct FriendlyTime {}
1630 @@ -48,33 +51,28 @@ impl Filter for HumanBytes {
1631 }
1632 }
1633
1634- pub struct Emojis {}
1635+ pub struct Emojis(pub Assets);
1636
1637 impl Filter for Emojis {
1638 fn filter(&self, value: &Value, _: &std::collections::HashMap<String, Value>) -> Result<Value> {
1639- // TODO: load these dynamically with the rest of the theme assets
1640 let name = value.as_str().unwrap();
1641- let svg_content = match name {
1642- "books" => include_str!("../../../themes/default/assets/books.svg"),
1643- "building" => include_str!("../../../themes/default/assets/building.svg"),
1644- "check" => include_str!("../../../themes/default/assets/check.svg"),
1645- "chat" => include_str!("../../../themes/default/assets/chat.svg"),
1646- "code" => include_str!("../../../themes/default/assets/code.svg"),
1647- "feed" => include_str!("../../../themes/default/assets/feed.svg"),
1648- "moon" => include_str!("../../../themes/default/assets/moon.svg"),
1649- "textile-pattern-1" => {
1650- include_str!("../../../themes/default/assets/textile-pattern-1.svg")
1651- }
1652- "textile-pattern-2" => {
1653- include_str!("../../../themes/default/assets/textile-pattern-2.svg")
1654+ if let Some(asset) = self.0 .0.get(name) {
1655+ match asset {
1656+ super::themes::Asset::Raw(content) => {
1657+ let content = String::from_utf8_lossy(content);
1658+ return Ok(to_value(content)?);
1659+ }
1660+ super::themes::Asset::FilePath(path) => {
1661+ let file_name = path.file_name().unwrap().to_string_lossy();
1662+ if file_name == name {
1663+ if let Ok(content) = read_to_string(path) {
1664+ return Ok(to_value(content)?);
1665+ }
1666+ }
1667+ }
1668 }
1669- "question" => include_str!("../../../themes/default/assets/question.svg"),
1670- "rss" => include_str!("../../../themes/default/assets/rss.svg"),
1671- "scale" => include_str!("../../../themes/default/assets/scale.svg"),
1672- "xmpp" => include_str!("../../../themes/default/assets/xmpp.svg"),
1673- _ => "",
1674 };
1675- let result = to_value(svg_content)?;
1676- Ok(result)
1677+ tracing::warn!("failed to load theme asset: {}", name);
1678+ Ok(to_value("")?)
1679 }
1680 }
1681 diff --git a/ayllu/src/web2/terautil/loader.rs b/ayllu/src/web2/terautil/loader.rs
1682index 54d3933..9c9b37c 100644
1683--- a/ayllu/src/web2/terautil/loader.rs
1684+++ b/ayllu/src/web2/terautil/loader.rs
1685 @@ -1,6 +1,8 @@
1686 use serde::{Deserialize, Serialize};
1687 use tera::{Context, Tera};
1688
1689+ use super::Themes;
1690+
1691 const DEFAULT_NAV: &[(&str, &str)] = &[];
1692
1693 /// top-level theme options available in all pages
1694 @@ -17,27 +19,32 @@ pub struct Options {
1695 }
1696
1697 pub struct Loader {
1698- pub templates: Vec<(String, Tera)>,
1699+ pub themes: Themes,
1700 pub default_theme: String,
1701 }
1702
1703 impl Loader {
1704- pub fn load(&self, options: Options, theme_name: Option<String>) -> (Tera, Context) {
1705- let _default_template = || {
1706- self.templates
1707- .iter()
1708- .find(|template| template.0 == self.default_theme)
1709- .unwrap()
1710- .1
1711+ fn theme(&self, name: Option<&str>) -> super::themes::Theme {
1712+ let default_tmpl = || {
1713+ self.themes
1714+ .0
1715+ .get(&self.default_theme)
1716+ .expect("default theme is not configured")
1717 .clone()
1718 };
1719- let template = match theme_name {
1720- Some(name) => match self.templates.iter().find(|template| template.0 == name) {
1721- Some(template) => template.1.clone(),
1722- None => _default_template(),
1723+
1724+ match name {
1725+ Some(name) => match self.themes.0.get(name) {
1726+ Some(theme) => theme.clone(),
1727+ None => default_tmpl(),
1728 },
1729- None => _default_template(),
1730- };
1731+ None => default_tmpl(),
1732+ }
1733+ }
1734+
1735+ // load the tera context of the theme or default theme if unspecified
1736+ pub fn load(&self, options: Options, theme_name: Option<String>) -> (Tera, Context) {
1737+ let theme = self.theme(theme_name.as_deref());
1738 let mut ctx = Context::new();
1739 ctx.insert("title", "");
1740 ctx.insert("origin", &options.origin);
1741 @@ -50,6 +57,11 @@ impl Loader {
1742 ctx.insert("path", &options.path);
1743 ctx.insert("fluid", &false);
1744 ctx.insert("subpath_mode", &options.subpath_mode);
1745- (template, ctx)
1746+ (theme.0, ctx)
1747+ }
1748+
1749+ // load assets for the configured theme or default theme if unspecified
1750+ pub fn assets(&self, theme_name: Option<&str>) -> super::themes::Assets {
1751+ self.theme(theme_name).1
1752 }
1753 }
1754 diff --git a/ayllu/src/web2/terautil/mod.rs b/ayllu/src/web2/terautil/mod.rs
1755index b06a8ca..5d8b6e9 100644
1756--- a/ayllu/src/web2/terautil/mod.rs
1757+++ b/ayllu/src/web2/terautil/mod.rs
1758 @@ -1,5 +1,6 @@
1759 mod filters;
1760 mod loader;
1761+ mod themes;
1762
1763- pub use filters::*;
1764- pub use loader::{Loader, Options};
1765+ pub use loader::*;
1766+ pub use themes::*;
1767 diff --git a/ayllu/src/web2/terautil/themes.rs b/ayllu/src/web2/terautil/themes.rs
1768new file mode 100644
1769index 0000000..705ab70
1770--- /dev/null
1771+++ b/ayllu/src/web2/terautil/themes.rs
1772 @@ -0,0 +1,177 @@
1773+ use std::collections::BTreeMap;
1774+ use std::fs::metadata;
1775+ use std::io::Error as IoError;
1776+ use std::path::PathBuf;
1777+
1778+ use globwalk::{glob_builder, DirEntry, FileType, GlobError, WalkError};
1779+ use include_dir::{include_dir, Dir};
1780+ use tera::{Error as TeraError, Tera};
1781+
1782+ use crate::web2::terautil::filters;
1783+
1784+ /// An error occured while initializing themes
1785+ #[derive(thiserror::Error, Debug)]
1786+ pub enum Error {
1787+ #[error("Theme Related IO Error: {0}")]
1788+ Io(#[from] IoError),
1789+ #[error("Template Error: {0}")]
1790+ Config(#[from] TeraError),
1791+ #[error("Theme Filepath Error: {0}")]
1792+ Glob(#[from] GlobError),
1793+ #[error("Problem Loading Theme: {0}")]
1794+ Walk(#[from] WalkError),
1795+ }
1796+
1797+ // Ayllu comes with a collection of builtin themes
1798+ static BUILTIN_THEMES_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/themes");
1799+
1800+ static BASE_CSS_TEMPLATE: &str =
1801+ include_str!(concat!(env!("OUT_DIR"), "/themes/default/theme.min.css"));
1802+
1803+ /// An asset file that may either be in-memory or on the file system, all
1804+ /// assets are initialized at start time and dynamic loading of files from the
1805+ /// os is unsupported.
1806+ #[derive(Clone, Debug)]
1807+ pub enum Asset {
1808+ /// Path offset from /static and the actual file contents loaded in memory
1809+ Raw(Vec<u8>),
1810+ /// Path offset from /static and a full filesystem path loaded at startup
1811+ FilePath(PathBuf),
1812+ }
1813+
1814+ /// Collection of all the assets associated with the given theme that are
1815+ /// served either from memory or on the file system.
1816+ #[derive(Clone, Debug)]
1817+ pub struct Assets(pub BTreeMap<String, Asset>);
1818+
1819+ /// Theme is a tuple containing a unique name, a tera template, and an optional
1820+ /// stylesheet. If no stylesheet is provided then the default base stylesheet
1821+ /// that is compiled during compilation is served. If an additional stylesheet
1822+ /// is loaded from disk then the default one and the custom one is served.
1823+ #[derive(Clone)]
1824+ pub struct Theme(pub Tera, pub Assets);
1825+
1826+ /// Collection of themes which are a mixture of in-memory and user provided
1827+ /// themes.
1828+ #[derive(Clone)]
1829+ pub struct Themes(pub BTreeMap<String, Theme>);
1830+
1831+ /// load is responsible for constructing Tera templates which can come from
1832+ /// built-in memory objects or from the file system in the case of user defined
1833+ /// templates. User defined stylesheets and custom built-in themes both inherit
1834+ /// configuration from the default theme.
1835+ ///
1836+ /// Order of theme loading:
1837+ ///
1838+ /// [default] # compiled in-memory
1839+ /// |
1840+ /// [built-in custom themes] # compiled in-memory
1841+ /// |
1842+ /// [user defined themes] # on disk
1843+ pub fn load_themes(user_themes: Vec<(String, PathBuf)>) -> Result<Themes, Error> {
1844+ let mut themes: BTreeMap<String, Theme> = BTreeMap::new();
1845+ let mut default_tmpl = Tera::default();
1846+ default_tmpl.register_filter("friendly_time", filters::FriendlyTime {});
1847+ default_tmpl.register_filter("format_epoch", filters::FormatEpoch {});
1848+ default_tmpl.register_filter("filemode", filters::FileMode {});
1849+ default_tmpl.register_filter("human_bytes", filters::HumanBytes {});
1850+
1851+ default_tmpl.add_raw_templates(
1852+ BUILTIN_THEMES_DIR
1853+ .find("default/templates/*.html")
1854+ .unwrap()
1855+ .filter_map(|tmpl_file| match tmpl_file.as_file() {
1856+ Some(file) => {
1857+ let file_name = file.path().file_name().unwrap().to_str().unwrap();
1858+ Some((file_name.to_string(), file.contents_utf8().unwrap()))
1859+ }
1860+ None => None,
1861+ }),
1862+ )?;
1863+
1864+ let mut default_assets = BTreeMap::from_iter(
1865+ BUILTIN_THEMES_DIR
1866+ .find("default/assets/*")
1867+ .unwrap()
1868+ .filter_map(|f| match f.as_file() {
1869+ Some(file) => {
1870+ let file_name = file.path().file_name().unwrap().to_str().unwrap();
1871+ tracing::debug!("loaded asset: {}", file_name);
1872+ Some((file_name.to_string(), Asset::Raw(file.contents().to_vec())))
1873+ }
1874+ None => None,
1875+ }),
1876+ );
1877+
1878+ // base CSS theme all themes extend
1879+ default_assets.insert(
1880+ "main.min.css".to_string(),
1881+ Asset::Raw(BASE_CSS_TEMPLATE.as_bytes().to_vec()),
1882+ );
1883+
1884+ let assets = Assets(default_assets.clone());
1885+
1886+ default_tmpl.register_filter("emoji", filters::Emojis(assets.clone()));
1887+ // load the default theme and it's stylesheet
1888+ themes.insert(String::from("default"), Theme(default_tmpl.clone(), assets));
1889+
1890+ // TODO: load other built-in themes
1891+
1892+ // load user themes
1893+ for (name, path) in user_themes {
1894+ tracing::info!("loading user defined theme: {} @ {:?}", name, path);
1895+
1896+ let templates_path = path.join("templates");
1897+ let glob_pattern = format!("{}/*.html", templates_path.to_string_lossy());
1898+ tracing::debug!("template glob pattern: {}", glob_pattern);
1899+
1900+ let mut theme_templates = default_tmpl.clone();
1901+
1902+ for template in glob_builder(glob_pattern)
1903+ .max_depth(1)
1904+ .file_type(FileType::FILE)
1905+ .build()?
1906+ {
1907+ let template = template?;
1908+ let template_name = template.file_name();
1909+ let template_name = template_name.to_string_lossy().to_string();
1910+ tracing::debug!("overriding template theme: {:?}", template_name);
1911+ theme_templates.add_template_file(template.path(), Some(template_name.as_str()))?;
1912+ }
1913+
1914+ let assets_path = path.join("assets");
1915+ let glob_pattern = format!("{}/*", assets_path.to_string_lossy());
1916+ tracing::debug!("assets glob pattern: {}", glob_pattern);
1917+
1918+ let theme_assets: Result<Vec<DirEntry>, globwalk::WalkError> = glob_builder(glob_pattern)
1919+ .file_type(FileType::FILE)
1920+ .max_depth(1)
1921+ .build()?
1922+ .collect();
1923+
1924+ let mut theme_assets = BTreeMap::from_iter(theme_assets?.into_iter().map(|file| {
1925+ let file_name = file.path().file_name().unwrap().to_str().unwrap();
1926+ tracing::debug!("loaded theme asset {}", file_name);
1927+ (file_name.to_string(), Asset::FilePath(file.into_path()))
1928+ }));
1929+
1930+ let mut base_assets = default_assets.clone();
1931+
1932+ let theme_css_file = path.join("theme.css");
1933+ if metadata(theme_css_file.as_path()).is_ok() {
1934+ theme_assets.insert(
1935+ "theme.css".to_string(),
1936+ Asset::FilePath(theme_css_file.to_path_buf()),
1937+ );
1938+ tracing::debug!("extended theme with {:?}", theme_css_file);
1939+ }
1940+
1941+ base_assets.append(&mut theme_assets);
1942+ let theme_assets = Assets(base_assets);
1943+
1944+ theme_templates.register_filter("emoji", filters::Emojis(theme_assets.clone()));
1945+ themes.insert(name.clone(), Theme(theme_templates, theme_assets));
1946+ }
1947+
1948+ Ok(Themes(themes))
1949+ }
1950 diff --git a/ayllu/themes/adwaita/templates/.gitkeep b/ayllu/themes/adwaita/templates/.gitkeep
1951deleted file mode 100644
1952index e69de29..0000000
1953--- a/ayllu/themes/adwaita/templates/.gitkeep
1954+++ /dev/null
1955 diff --git a/ayllu/themes/adwaita/theme.scss b/ayllu/themes/adwaita/theme.scss
1956deleted file mode 100644
1957index 4716ec0..0000000
1958--- a/ayllu/themes/adwaita/theme.scss
1959+++ /dev/null
1960 @@ -1,171 +0,0 @@
1961- // https://en.wikipedia.org/wiki/Adwaita_(design_language)
1962- // https://developer.gnome.org/hig/reference/palette.html
1963-
1964- $blue1: rgb(153, 193, 241);
1965- $blue2: rgb(98, 160, 234 );
1966- $blue3: rgb(53, 132, 228 );
1967- $blue4: rgb(28, 113, 216 );
1968- $blue5: rgb(26, 95, 180 );
1969- $green1: rgb(143, 240, 164);
1970- $green2: rgb(87, 227 ,137);
1971- $green3: rgb(51, 209 ,122);
1972- $green4: rgb(46, 194 ,126);
1973- $green5: rgb(38, 162 ,105);
1974- $yellow1: rgb(249, 240, 107);
1975- $yellow2: rgb(248, 228, 92);
1976- $yellow3: rgb(246, 211, 45);
1977- $yellow4: rgb(245, 194, 17);
1978- $yellow5: rgb(229, 165, 10);
1979- $orange1: rgb(255, 190, 111);
1980- $orange2: rgb(255, 163, 72);
1981- $orange3: rgb(255, 120, 0);
1982- $orange4: rgb(230, 97, 0);
1983- $orange5: rgb(198, 70, 0);
1984- $red1: rgb(246, 97, 81);
1985- $red2: rgb(237, 51, 59);
1986- $red3: rgb(224, 27, 36);
1987- $red4: rgb(192, 28, 40);
1988- $red5: rgb(165, 29, 45);
1989- $purple1: rgb(220, 138, 221);
1990- $purple2: rgb(192, 97, 203);
1991- $purple3: rgb(145, 65, 172);
1992- $purple4: rgb(129, 61, 156);
1993- $purple5: rgb(97, 53, 131);
1994- $brown1: rgb(205, 171, 143);
1995- $brown2: rgb(181, 131, 90);
1996- $brown3: rgb(152, 106, 68);
1997- $brown4: rgb(134, 94, 60);
1998- $brown5: rgb(99, 69, 44);
1999- $light1: rgb(255, 255, 255);
2000- $light2: rgb(246, 245, 244);
2001- $light3: rgb(222, 221, 218);
2002- $light4: rgb(192, 191, 188);
2003- $light5: rgb(154, 153, 150);
2004- $dark1: rgb(119, 118, 123);
2005- $dark2: rgb(94, 92, 100);
2006- $dark3: rgb(61, 56, 70);
2007- $dark4: rgb(36, 31, 49);
2008- $dark5: rgb(0, 0, 0);
2009-
2010- // pico overrides
2011- $grey-50: $light2; /// 96%
2012- $grey-100: darken($grey-50, 5%);
2013- $grey-200: darken($grey-50, 10%);
2014- $grey-300: darken($grey-50, 20%);
2015- $grey-400: darken($grey-50, 30%);
2016- $grey-500: darken($grey-50, 40%);
2017- $grey-600: darken($grey-50, 50%);
2018- $grey-700: darken($grey-50, 60%);
2019- $grey-800: darken($grey-50, 70%);
2020- $grey-900: darken($grey-50, 80%);
2021-
2022-
2023- // Light Blue
2024- $primary-50: $blue1;
2025- $primary-100: darken($primary-50, 5%);
2026- $primary-200: darken($primary-50, 10%);
2027- $primary-300: darken($primary-50, 20%);
2028- $primary-400: darken($primary-50, 25%);
2029- $primary-500: darken($primary-50, 30%);
2030- $primary-600: darken($primary-50, 35%);
2031- $primary-700: darken($primary-50, 40%);
2032- $primary-800: darken($primary-50, 45%);
2033- $primary-900: darken($primary-50, 50%);
2034-
2035- // Black & White
2036- $black: $dark1;
2037- $white: $light1;
2038-
2039- // Amber
2040- $amber-50: $orange1;
2041- $amber-100: darken($amber-50, 5%);
2042- $amber-200: darken($amber-50, 10%);
2043- $amber-300: darken($amber-50, 15%);
2044- $amber-400: darken($amber-50, 20%);
2045- $amber-500: darken($amber-50, 25%);
2046- $amber-600: darken($amber-50, 30%);
2047- $amber-700: darken($amber-50, 35%);
2048- $amber-800: darken($amber-50, 40%);
2049- $amber-900: darken($amber-50, 45%);
2050-
2051- // Green
2052- $green-50: $green1;
2053- $green-100: darken($green-50, 5%);
2054- $green-200: darken($green-50, 10%);
2055- $green-300: darken($green-50, 15%);
2056- $green-400: darken($green-50, 20%);
2057- $green-500: darken($green-50, 25%);
2058- $green-600: darken($green-50, 30%);
2059- $green-700: darken($green-50, 35%);
2060- $green-800: darken($green-50, 40%);
2061- $green-900: darken($green-50, 45%);
2062-
2063- // Red
2064- $red-50: $red1;
2065- $red-100: darken($red-50, 5%);
2066- $red-200: darken($red-50, 10%);
2067- $red-300: darken($red-50, 15%);
2068- $red-400: darken($red-50, 20%);
2069- $red-500: darken($red-50, 25%);
2070- $red-600: darken($red-50, 30%);
2071- $red-700: darken($red-50, 35%);
2072- $red-800: darken($red-50, 40%);
2073- $red-900: darken($red-50, 45%);
2074-
2075- $primary-500: $blue1;
2076- $primary-600: $blue2;
2077- $primary-700: $blue3;
2078-
2079- //
2080- //
2081- // tree sitter highlighting
2082- // https://github.com/Mofiqul/adwaita.nvim
2083- //
2084- // extra colors for highlighting
2085- $teal1: #93DDC2;
2086- $teal2: #5BC8AF;
2087- $teal3: #33B2A4;
2088- $teal4: #26A1A2;
2089- $teal5: #218787;
2090- $violet2: #7D8AC7;
2091- $violet3: #6362C8;
2092- $violet4: #4E57BA;
2093-
2094- span.ts_attribute {color: $orange4};
2095- span.ts_constant {color: $purple4};
2096- span.ts_function.builtin {color: $blue4};
2097- span.ts_function {color: $blue4};
2098- span.ts_keyword {color: $orange4};
2099- span.ts_operator {color: $purple4};
2100- span.ts_property {color: $blue1};
2101- span.ts_punctuation {color: $blue1};
2102- span.ts_punctuation.bracket {color: $blue1};
2103- span.ts_punctuation.delimiter {color: $blue1};
2104- span.ts_string {color: $teal2};
2105- span.ts_string.special {color: $teal3};
2106- span.ts_tag {color: $teal2};
2107- span.ts_type {color: $teal2};
2108- span.ts_type.builtin {color: $blue1};
2109- span.ts_variable {color: $light4};
2110- span.ts_variable.builtin {color: $light4};
2111- span.ts_variable.parameter {color: $light4};
2112-
2113- @media (prefers-color-scheme: dark) {
2114- span.label {
2115- color: $dark5;
2116- }
2117- }
2118-
2119- $positive: $green1;
2120- $negative: $red1;
2121-
2122- $blame-border: $purple1;
2123-
2124- $highlighted: $purple1;
2125- $icon-background: $purple1;
2126-
2127- $chart-color: $purple1;
2128-
2129- @import "@picocss/pico/scss/pico";
2130- @import "../default/layout.scss";
2131- @import "../default/base.scss";
2132 diff --git a/ayllu/themes/default/assets/ayllu_logo.svg b/ayllu/themes/default/assets/ayllu_logo.svg
2133deleted file mode 100644
2134index d1db83a..0000000
2135--- a/ayllu/themes/default/assets/ayllu_logo.svg
2136+++ /dev/null
2137 @@ -1,101 +0,0 @@
2138- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2139- <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.0 (1.0+r73+1)" sodipodi:docname="textil-inca-pattern-3a.svg" version="1.1" viewBox="0 0 902 702">
2140- <metadata>
2141- <rdf:RDF>
2142- <cc:Work rdf:about="">
2143- <dc:format>image/svg+xml</dc:format>
2144- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
2145- </cc:Work>
2146- </rdf:RDF>
2147- </metadata>
2148- <sodipodi:namedview inkscape:current-layer="svg159" inkscape:window-maximized="1" inkscape:window-y="27" inkscape:window-x="67" inkscape:cy="467.33333" inkscape:cx="600.66667" inkscape:zoom="0.75746799" showgrid="false" id="namedview161" inkscape:window-height="1025" inkscape:window-width="1853" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff"/>
2149- <g fill-rule="evenodd" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-opacity="1">
2150- <g id="brown" fill="#5c451c" stroke="#5c451c">
2151- <path style="" d="M 1,1 H 901 V 701 H 1 Z m 0,0"/>
2152- </g>
2153- <g id="orange" fill="#ffa300" stroke="#ffa300">
2154- <path d="m 641,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2155- <path d="m 121,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2156- <path d="m 401,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2157- <path d="m 441,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2158- </g>
2159- <g id="turquoise" fill="#358794" stroke="#358794">
2160- <path d="m 701,221 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2161- <path d="m 701,481 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2162- <path d="m 621,361 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2163- <path d="m 621,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2164- <path d="m 201,221 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 H 81 v 20 H 61 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2165- <path d="M 201,481 H 181 V 461 H 161 V 441 H 141 V 421 H 121 V 401 H 101 V 381 H 81 V 361 H 61 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2166- <path d="m 281,361 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2167- <path d="m 281,341 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2168- <path d="m 441,121 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2169- <path d="m 261,361 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2170- <path d="m 461,121 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2171- <path d="m 641,361 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2172- </g>
2173- <g id="white" fill="#ffffff" stroke="#ffffff">
2174- <path d="m 701,161 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2175- <path d="m 701,541 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2176- <path d="m 441,61 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2177- <path d="m 441,641 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2178- <path d="m 461,61 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2179- <path d="m 621,421 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2180- <path d="m 621,281 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2181- <path d="m 281,281 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2182- <path d="m 201,161 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 H 81 v 20 H 61 v 20 H 41 v 20 H 21 v 20 H 1 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2183- <path d="M 201,541 H 181 V 521 H 161 V 501 H 141 V 481 H 121 V 461 H 101 V 441 H 81 V 421 H 61 V 401 H 41 V 381 H 21 V 361 H 1 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2184- <path d="m 461,641 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2185- <path d="m 281,421 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2186- <path d="m 701,181 h 20 v -80 h 100 v 40 h -40 v -20 h -20 v 40 h 80 V 81 H 701 Z m 0,0"/>
2187- <path d="M 721,181 H 701 V 101 H 601 v 40 h 40 v -20 h 20 v 40 H 581 V 81 h 140 z m 0,0"/>
2188- <path d="m 701,521 h 20 v 80 h 100 v -40 h -40 v 20 h -20 v -40 h 80 v 80 H 701 Z m 0,0"/>
2189- <path d="m 721,521 h -20 v 80 H 601 v -40 h 40 v 20 h 20 v -40 h -80 v 80 h 140 z m 0,0"/>
2190- <path d="m 181,521 h 20 v 80 h 100 v -40 h -40 v 20 h -20 v -40 h 80 v 80 H 181 Z m 0,0"/>
2191- <path d="m 201,521 h -20 v 80 H 81 v -40 h 40 v 20 h 20 V 541 H 61 v 80 h 140 z m 0,0"/>
2192- <path d="m 181,181 h 20 v -80 h 100 v 40 h -40 v -20 h -20 v 40 h 80 V 81 H 181 Z m 0,0"/>
2193- <path d="M 201,181 H 181 V 101 H 81 v 40 h 40 v -20 h 20 v 40 H 61 V 81 h 140 z m 0,0"/>
2194- <path d="m 441,181 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2195- <path d="m 461,181 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2196- <path d="m 441,521 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2197- <path d="m 461,521 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2198- <path d="M 1,21 H 881 V 61 H 1 Z m 0,0"/>
2199- <path d="m 1,641 h 880 v 40 H 1 Z m 0,0"/>
2200- <path d="m 421,281 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2201- <path d="m 681,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2202- <path d="m 161,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2203- <path d="m 421,401 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2204- <path d="m 401,301 h 20 v 20 h -20 z m 0,0"/>
2205- <path d="m 381,321 h 20 v 20 h -20 z m 0,0"/>
2206- <path d="m 361,341 h 20 v 20 h -20 z m 0,0"/>
2207- <path d="m 381,361 h 20 v 20 h -20 z m 0,0"/>
2208- <path d="m 401,381 h 20 v 20 h -20 z m 0,0"/>
2209- <path d="m 501,321 h 20 v 20 h -20 z m 0,0"/>
2210- <path d="m 521,341 h 20 v 20 h -20 z m 0,0"/>
2211- <path d="m 501,361 h 20 v 20 h -20 z m 0,0"/>
2212- <path d="m 481,381 h 20 v 20 h -20 z m 0,0"/>
2213- <path d="m 421,341 h 20 v 20 h -20 z m 0,0"/>
2214- <path d="m 461,341 h 20 v 20 h -20 z m 0,0"/>
2215- <path d="m 881,21 h 20 v 40 h -20 z m 0,0"/>
2216- <path d="m 881,641 h 20 v 40 h -20 z m 0,0"/>
2217- </g>
2218- <g id="red" fill="#de3333" stroke="#de3333">
2219- <path d="m 801,201 h 60 V 81 h 40 v 220 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2220- <path d="M 101,201 H 41 V 81 H 1 v 220 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2221- <path d="M 101,501 H 41 V 621 H 1 V 401 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2222- <path d="m 801,501 h 60 v 120 h 40 V 401 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2223- <path d="m 381,341 h 20 v 20 h -20 z m 0,0"/>
2224- <path d="m 401,321 h 20 v 20 h -20 z m 0,0"/>
2225- <path d="m 421,301 h 20 v 20 h -20 z m 0,0"/>
2226- <path d="m 461,301 h 20 v 20 h -20 z m 0,0"/>
2227- <path d="m 481,321 h 20 v 20 h -20 z m 0,0"/>
2228- <path d="m 501,341 h 20 v 20 h -20 z m 0,0"/>
2229- <path d="m 481,361 h 20 v 20 h -20 z m 0,0"/>
2230- <path d="m 461,381 h 20 v 20 h -20 z m 0,0"/>
2231- <path d="m 441,361 h 20 v 20 h -20 z m 0,0"/>
2232- <path d="m 421,381 h 20 v 20 h -20 z m 0,0"/>
2233- <path d="m 401,361 h 20 v 20 h -20 z m 0,0"/>
2234- <path d="m 881,81 h 20 v 220 h -20 z m 0,0"/>
2235- <path d="m 881,401 h 20 v 220 h -20 z m 0,0"/>
2236- </g>
2237- </g>
2238- </svg>
2239 diff --git a/ayllu/themes/default/assets/feed.xsl b/ayllu/themes/default/assets/feed.xsl
2240index 530f31a..35a356d 100644
2241--- a/ayllu/themes/default/assets/feed.xsl
2242+++ b/ayllu/themes/default/assets/feed.xsl
2243 @@ -14,11 +14,11 @@
2244 <link rel="stylesheet" href="/static/main.min.css" />
2245 </head>
2246 <body>
2247- <main class="container">
2248+ <main class="column">
2249 <article>
2250 <header>
2251 <h1>
2252- <img class="rss-icon-feed" src="/static/assets/feed.svg"/>
2253+ <img class="rss-icon-feed" src="/static/feed.svg"/>
2254 <a>
2255 <xsl:attribute name="href"> <xsl:value-of select="/rss/channel/link"/> </xsl:attribute>
2256 <xsl:value-of select="/rss/channel/title"/>
2257 @@ -30,7 +30,7 @@
2258 <p>Copy the link from the address bar into your feed reader to receive regular updates.</p>
2259 </article>
2260 <xsl:for-each select="/rss/channel/item">
2261- <article class="entry">
2262+ <article class="card">
2263 <header>
2264 <h3>
2265 <a>
2266 diff --git a/ayllu/themes/default/assets/logo.svg b/ayllu/themes/default/assets/logo.svg
2267index 8e43fd6..d1db83a 100644
2268--- a/ayllu/themes/default/assets/logo.svg
2269+++ b/ayllu/themes/default/assets/logo.svg
2270 @@ -1,78 +1,101 @@
2271 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2272- <svg
2273- id="emoji"
2274- viewBox="0 0 234.58891 59.331596"
2275- version="1.1"
2276- sodipodi:docname="ayllu.svg"
2277- width="234.58891"
2278- height="59.331596"
2279- inkscape:export-filename="logo.png"
2280- inkscape:export-xdpi="96"
2281- inkscape:export-ydpi="96"
2282- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
2283- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
2284- xmlns="http://www.w3.org/2000/svg"
2285- xmlns:svg="http://www.w3.org/2000/svg">
2286- <defs
2287- id="defs4043">
2288- <rect
2289- x="67.923203"
2290- y="12.228936"
2291- width="188.06631"
2292- height="68.492889"
2293- id="rect7485" />
2294- </defs>
2295- <sodipodi:namedview
2296- id="namedview4041"
2297- pagecolor="#ffffff"
2298- bordercolor="#999999"
2299- borderopacity="1"
2300- inkscape:showpageshadow="0"
2301- inkscape:pageopacity="0"
2302- inkscape:pagecheckerboard="0"
2303- inkscape:deskcolor="#d1d1d1"
2304- showgrid="true">
2305- <inkscape:grid
2306- type="xygrid"
2307- id="grid7481"
2308- originx="0"
2309- originy="0" />
2310- </sodipodi:namedview>
2311- <g
2312- id="color"
2313- transform="translate(-6.3635189,-6.0201046)">
2314- <path
2315- fill="#fcea2b"
2316- stroke="none"
2317- d="m 7.3634,42.4095 c 4.5525,6.1703 11.874,10.1726 20.1303,10.1726 13.8071,0 25,-11.1929 25,-25 0,-8.5226 -4.2646,-16.0492 -10.7763,-20.5621 13.0383,2.8385 22.7812,14.4426 22.7812,28.3317 0,16.0163 -12.9837,29 -29,29 -13.5877,0 -24.9889,-9.3288 -28.1352,-21.9422 z"
2318- id="path4029" />
2319- <path
2320- fill="#f1b31c"
2321- stroke="none"
2322- d="m 45.8373,9.2108 c 8.25,4.25 16.1946,11.8724 16.1946,24.6742 0,15.4494 -12.5242,27.9735 -27.9735,27.9735 -9.2431,0 -19.7524,-4.8353 -24.294,-15.5436 0,0 4.3805,18.6568 25.7189,18.665 C 54.8103,64.9873 63.5252,44.3581 63.5252,44.3581 70.033,12.3815 45.8373,9.2108 45.8373,9.2108 Z"
2323- id="path4031" />
2324+ <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:version="1.0 (1.0+r73+1)" sodipodi:docname="textil-inca-pattern-3a.svg" version="1.1" viewBox="0 0 902 702">
2325+ <metadata>
2326+ <rdf:RDF>
2327+ <cc:Work rdf:about="">
2328+ <dc:format>image/svg+xml</dc:format>
2329+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
2330+ </cc:Work>
2331+ </rdf:RDF>
2332+ </metadata>
2333+ <sodipodi:namedview inkscape:current-layer="svg159" inkscape:window-maximized="1" inkscape:window-y="27" inkscape:window-x="67" inkscape:cy="467.33333" inkscape:cx="600.66667" inkscape:zoom="0.75746799" showgrid="false" id="namedview161" inkscape:window-height="1025" inkscape:window-width="1853" inkscape:pageshadow="2" inkscape:pageopacity="0" guidetolerance="10" gridtolerance="10" objecttolerance="10" borderopacity="1" bordercolor="#666666" pagecolor="#ffffff"/>
2334+ <g fill-rule="evenodd" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-opacity="1">
2335+ <g id="brown" fill="#5c451c" stroke="#5c451c">
2336+ <path style="" d="M 1,1 H 901 V 701 H 1 Z m 0,0"/>
2337+ </g>
2338+ <g id="orange" fill="#ffa300" stroke="#ffa300">
2339+ <path d="m 641,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2340+ <path d="m 121,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2341+ <path d="m 401,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2342+ <path d="m 441,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2343+ </g>
2344+ <g id="turquoise" fill="#358794" stroke="#358794">
2345+ <path d="m 701,221 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2346+ <path d="m 701,481 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2347+ <path d="m 621,361 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2348+ <path d="m 621,341 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2349+ <path d="m 201,221 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 H 81 v 20 H 61 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2350+ <path d="M 201,481 H 181 V 461 H 161 V 441 H 141 V 421 H 121 V 401 H 101 V 381 H 81 V 361 H 61 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2351+ <path d="m 281,361 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2352+ <path d="m 281,341 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2353+ <path d="m 441,121 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2354+ <path d="m 261,361 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2355+ <path d="m 461,121 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2356+ <path d="m 641,361 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2357+ </g>
2358+ <g id="white" fill="#ffffff" stroke="#ffffff">
2359+ <path d="m 701,161 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2360+ <path d="m 701,541 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2361+ <path d="m 441,61 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2362+ <path d="m 441,641 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2363+ <path d="m 461,61 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2364+ <path d="m 621,421 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 40 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2365+ <path d="m 621,281 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -40 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2366+ <path d="m 281,281 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2367+ <path d="m 201,161 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 H 81 v 20 H 61 v 20 H 41 v 20 H 21 v 20 H 1 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2368+ <path d="M 201,541 H 181 V 521 H 161 V 501 H 141 V 481 H 121 V 461 H 101 V 441 H 81 V 421 H 61 V 401 H 41 V 381 H 21 V 361 H 1 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2369+ <path d="m 461,641 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -40 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2370+ <path d="m 281,421 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 40 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2371+ <path d="m 701,181 h 20 v -80 h 100 v 40 h -40 v -20 h -20 v 40 h 80 V 81 H 701 Z m 0,0"/>
2372+ <path d="M 721,181 H 701 V 101 H 601 v 40 h 40 v -20 h 20 v 40 H 581 V 81 h 140 z m 0,0"/>
2373+ <path d="m 701,521 h 20 v 80 h 100 v -40 h -40 v 20 h -20 v -40 h 80 v 80 H 701 Z m 0,0"/>
2374+ <path d="m 721,521 h -20 v 80 H 601 v -40 h 40 v 20 h 20 v -40 h -80 v 80 h 140 z m 0,0"/>
2375+ <path d="m 181,521 h 20 v 80 h 100 v -40 h -40 v 20 h -20 v -40 h 80 v 80 H 181 Z m 0,0"/>
2376+ <path d="m 201,521 h -20 v 80 H 81 v -40 h 40 v 20 h 20 V 541 H 61 v 80 h 140 z m 0,0"/>
2377+ <path d="m 181,181 h 20 v -80 h 100 v 40 h -40 v -20 h -20 v 40 h 80 V 81 H 181 Z m 0,0"/>
2378+ <path d="M 201,181 H 181 V 101 H 81 v 40 h 40 v -20 h 20 v 40 H 61 V 81 h 140 z m 0,0"/>
2379+ <path d="m 441,181 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h -40 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2380+ <path d="m 461,181 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h 40 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2381+ <path d="m 441,521 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h -40 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2382+ <path d="m 461,521 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h 40 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2383+ <path d="M 1,21 H 881 V 61 H 1 Z m 0,0"/>
2384+ <path d="m 1,641 h 880 v 40 H 1 Z m 0,0"/>
2385+ <path d="m 421,281 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2386+ <path d="m 681,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2387+ <path d="m 161,341 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2388+ <path d="m 421,401 h 20 v -20 h 20 v 20 h 20 v 20 h -20 v 20 h -20 v -20 h -20 z m 0,0"/>
2389+ <path d="m 401,301 h 20 v 20 h -20 z m 0,0"/>
2390+ <path d="m 381,321 h 20 v 20 h -20 z m 0,0"/>
2391+ <path d="m 361,341 h 20 v 20 h -20 z m 0,0"/>
2392+ <path d="m 381,361 h 20 v 20 h -20 z m 0,0"/>
2393+ <path d="m 401,381 h 20 v 20 h -20 z m 0,0"/>
2394+ <path d="m 501,321 h 20 v 20 h -20 z m 0,0"/>
2395+ <path d="m 521,341 h 20 v 20 h -20 z m 0,0"/>
2396+ <path d="m 501,361 h 20 v 20 h -20 z m 0,0"/>
2397+ <path d="m 481,381 h 20 v 20 h -20 z m 0,0"/>
2398+ <path d="m 421,341 h 20 v 20 h -20 z m 0,0"/>
2399+ <path d="m 461,341 h 20 v 20 h -20 z m 0,0"/>
2400+ <path d="m 881,21 h 20 v 40 h -20 z m 0,0"/>
2401+ <path d="m 881,641 h 20 v 40 h -20 z m 0,0"/>
2402+ </g>
2403+ <g id="red" fill="#de3333" stroke="#de3333">
2404+ <path d="m 801,201 h 60 V 81 h 40 v 220 h -20 v -20 h -20 v -20 h -20 v -20 h -20 v -20 h -20 z m 0,0"/>
2405+ <path d="M 101,201 H 41 V 81 H 1 v 220 h 20 v -20 h 20 v -20 h 20 v -20 h 20 v -20 h 20 z m 0,0"/>
2406+ <path d="M 101,501 H 41 V 621 H 1 V 401 h 20 v 20 h 20 v 20 h 20 v 20 h 20 v 20 h 20 z m 0,0"/>
2407+ <path d="m 801,501 h 60 v 120 h 40 V 401 h -20 v 20 h -20 v 20 h -20 v 20 h -20 v 20 h -20 z m 0,0"/>
2408+ <path d="m 381,341 h 20 v 20 h -20 z m 0,0"/>
2409+ <path d="m 401,321 h 20 v 20 h -20 z m 0,0"/>
2410+ <path d="m 421,301 h 20 v 20 h -20 z m 0,0"/>
2411+ <path d="m 461,301 h 20 v 20 h -20 z m 0,0"/>
2412+ <path d="m 481,321 h 20 v 20 h -20 z m 0,0"/>
2413+ <path d="m 501,341 h 20 v 20 h -20 z m 0,0"/>
2414+ <path d="m 481,361 h 20 v 20 h -20 z m 0,0"/>
2415+ <path d="m 461,381 h 20 v 20 h -20 z m 0,0"/>
2416+ <path d="m 441,361 h 20 v 20 h -20 z m 0,0"/>
2417+ <path d="m 421,381 h 20 v 20 h -20 z m 0,0"/>
2418+ <path d="m 401,361 h 20 v 20 h -20 z m 0,0"/>
2419+ <path d="m 881,81 h 20 v 220 h -20 z m 0,0"/>
2420+ <path d="m 881,401 h 20 v 220 h -20 z m 0,0"/>
2421+ </g>
2422 </g>
2423- <g
2424- id="line"
2425- transform="translate(-6.3635189,-6.0201046)">
2426- <path
2427- fill="none"
2428- stroke="#000000"
2429- stroke-linecap="round"
2430- stroke-linejoin="round"
2431- stroke-miterlimit="10"
2432- stroke-width="2"
2433- d="m 7.3634,42.4095 c 4.5525,6.1703 11.874,10.1726 20.1303,10.1726 13.8071,0 25,-11.1929 25,-25 0,-8.5226 -4.2646,-16.0492 -10.7763,-20.5621 13.0383,2.8385 22.7812,14.4426 22.7812,28.3317 0,16.0163 -12.9837,29 -29,29 -13.5877,0 -24.9889,-9.3288 -28.1352,-21.9422 z"
2434- id="path4037" />
2435- </g>
2436- <text
2437- xml:space="preserve"
2438- id="text7483"
2439- style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;white-space:pre;shape-inside:url(#rect7485);fill:#000000;fill-opacity:1;stroke:none"
2440- transform="translate(-3.6949385,-4.8457367)"><tspan
2441- x="67.923828"
2442- y="48.623523"
2443- id="tspan7896"><tspan
2444- style="font-weight:bold;font-family:serif;-inkscape-font-specification:'serif Bold'"
2445- id="tspan7894">ayllu</tspan></tspan></text>
2446 </svg>
2447 diff --git a/ayllu/themes/default/assets/moon.svg b/ayllu/themes/default/assets/moon.svg
2448deleted file mode 100644
2449index 51596eb..0000000
2450--- a/ayllu/themes/default/assets/moon.svg
2451+++ /dev/null
2452 @@ -1,12 +0,0 @@
2453- <svg id="emoji" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
2454- <g id="color">
2455- <path fill="#FCEA2B" stroke="none" d="M7.3634,42.4095c4.5525,6.1703,11.874,10.1726,20.1303,10.1726c13.8071,0,25-11.1929,25-25 c0-8.5226-4.2646-16.0492-10.7763-20.5621c13.0383,2.8385,22.7812,14.4426,22.7812,28.3317c0,16.0163-12.9837,29-29,29 C21.9109,64.3517,10.5097,55.0229,7.3634,42.4095z"/>
2456- <path fill="#F1B31C" stroke="none" d="M45.8373,9.2108c8.25,4.25,16.1946,11.8724,16.1946,24.6742c0,15.4494-12.5242,27.9735-27.9735,27.9735 c-9.2431,0-19.7524-4.8353-24.294-15.5436c0,0,4.3805,18.6568,25.7189,18.665c19.327,0.0074,28.0419-20.6218,28.0419-20.6218 C70.033,12.3815,45.8373,9.2108,45.8373,9.2108z"/>
2457- </g>
2458- <g id="hair"/>
2459- <g id="skin"/>
2460- <g id="skin-shadow"/>
2461- <g id="line">
2462- <path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M7.3634,42.4095c4.5525,6.1703,11.874,10.1726,20.1303,10.1726c13.8071,0,25-11.1929,25-25 c0-8.5226-4.2646-16.0492-10.7763-20.5621c13.0383,2.8385,22.7812,14.4426,22.7812,28.3317c0,16.0163-12.9837,29-29,29 C21.9109,64.3517,10.5097,55.0229,7.3634,42.4095z"/>
2463- </g>
2464- </svg>
2465 diff --git a/ayllu/themes/default/base.scss b/ayllu/themes/default/base.scss
2466deleted file mode 100644
2467index 6f9fd58..0000000
2468--- a/ayllu/themes/default/base.scss
2469+++ /dev/null
2470 @@ -1,557 +0,0 @@
2471- ::-webkit-scrollbar {
2472- display: none;
2473- }
2474-
2475- :root {
2476- --spacing: 0;
2477- --font-family: monospace;
2478- --font-size: 14px;
2479- }
2480-
2481- pre {
2482- white-space: pre-wrap;
2483- background-color: unset;
2484- color: unset;
2485- font-size: unset;
2486- }
2487-
2488- table {
2489- margin-bottom: 0px;
2490- }
2491-
2492- table {
2493- overflow: hidden;
2494- }
2495-
2496- table td {
2497- padding-left: 2px;
2498- padding-right: 2px;
2499- }
2500-
2501- table tbody tr td {
2502- overflow: hidden;
2503- white-space: nowrap;
2504- }
2505-
2506- table th {
2507- overflow: hidden;
2508- white-space: nowrap;
2509- font-weight: bold;
2510- text-decoration: underline;
2511- }
2512-
2513- header.tree-preamble {
2514- font-size: smaller;
2515- }
2516-
2517- .wide {
2518- display: flex;
2519- flex-direction: row;
2520- justify-content: space-between;
2521- row-gap: 10px;
2522- }
2523-
2524- .wide {
2525- div:nth-child(2) {
2526- margin-left: 10px;
2527- }
2528- }
2529-
2530- nav.small a {
2531- font-size: small;
2532- }
2533-
2534- ul.submenu {
2535- font-size: smaller;
2536- height: 25px;
2537- }
2538-
2539- pre {
2540- overflow: scroll;
2541- }
2542-
2543- [role="button"] {
2544- padding: 0;
2545- margin-top: 2px;
2546- margin-bottom: 2px;
2547- }
2548-
2549- button.small {
2550- display: initial;
2551- width: initial;
2552- }
2553-
2554- nav {
2555- border-radius: var(--border-radius);
2556- margin-left: 10px;
2557- margin-right: 10px;
2558- }
2559-
2560- code {
2561- padding: 0;
2562- }
2563-
2564- code.highlighted {
2565- width: 100%;
2566- overflow: scroll;
2567- }
2568-
2569- footer.main {
2570- padding-top: 2em;
2571- text-align: center;
2572- font-size: smaller;
2573- }
2574-
2575- h1, h2, h3, h4, h5, h6 {
2576- --typography-spacing-vertical: .1em;
2577- //margin-top: unset;
2578- //margin-bottom: unset;
2579- }
2580-
2581- li.active > a {
2582- text-decoration: underline;
2583- }
2584-
2585- .logo > svg {
2586- width: 72px;
2587- }
2588-
2589- @media (prefers-color-scheme: dark) {
2590- .logo {
2591- }
2592- }
2593-
2594- footer {
2595- height: 2em;
2596- }
2597-
2598- article {
2599- box-shadow: none;
2600- }
2601-
2602- article.clone > input {
2603- font-size: smaller;
2604- height: 30px;
2605- text-align: center;
2606- }
2607-
2608- .blob-preview {
2609- text-align: center;
2610- border-style: solid;
2611- border-color: pink;
2612- margin-top: 2em;
2613- padding: 1em;
2614- }
2615-
2616- .line-number {
2617- user-select: none;
2618- // text-align: right;
2619- padding: 0 2px;
2620- width: 10px;
2621- }
2622-
2623- .line {
2624- white-space: pre;
2625- padding-left: 5px;
2626- }
2627-
2628- .icon-header > h1,h2,h3,h4,h5 {
2629- display: inline-block;
2630- }
2631-
2632- .icon-header > h1,h2,h3,h4,h5 > a {
2633- color: none;
2634- text-decoration: inherit;
2635- }
2636-
2637- .icon-header > svg {
2638- height: 2em;
2639- // border: 2px solid;
2640- float: right;
2641- margin-top: 2px;
2642- margin-left: 2px;
2643- }
2644-
2645- .icon > svg {
2646- height: 1.5em;
2647- }
2648-
2649- .icon-header.contrast > svg {
2650- border: 2px solid;
2651- }
2652-
2653- .emoji {
2654- float: right;
2655- }
2656-
2657- .emoji > svg {
2658- height: 30px;
2659- }
2660-
2661- header.repo div {
2662- display: inline-block;
2663- }
2664-
2665- article > header {
2666- padding: 3px;
2667- }
2668-
2669- ul.language-list li {
2670- list-style: none;
2671- }
2672-
2673- ul.author-list li {
2674- list-style: none;
2675- }
2676-
2677- span.right {
2678- float: right;
2679- };
2680-
2681- span.tiny {
2682- font-size: .7em;
2683- font-weight: bold;
2684- }
2685-
2686- span.center {
2687- text-align: center;
2688- }
2689-
2690- span.tiny-text {
2691- font-size: smaller;
2692- }
2693-
2694- span.hint {
2695- font-weight: bold;
2696- text-decoration: underline;
2697- }
2698-
2699- span.h1 {
2700- font-size: x-large;
2701- font-weight: bold;
2702- }
2703-
2704- span.header-text {
2705- font-size: 1.2em;
2706- font-weight: bold;
2707- }
2708-
2709- span.sub-header {
2710- font-size: 1em;
2711- font-weight: bold;
2712- }
2713-
2714- span.header {
2715- font-size: large;
2716- }
2717-
2718- span.timestamp {
2719- float: right;
2720- }
2721-
2722- span.author {
2723- float: right;
2724- }
2725-
2726- article.index-listing {
2727- margin-top: 25px;
2728- margin-bottom: 15px;
2729- }
2730-
2731- article.index-listing > article {
2732- padding: 3px;
2733- }
2734-
2735- article.listing > article {
2736- padding: 5px;
2737- }
2738-
2739- span.labels {
2740- font-weight: bold;
2741- }
2742-
2743- span.label {
2744- border: solid 2px;
2745- }
2746-
2747- span.labels {
2748- border-radius: 5px 5px 5px 5px;
2749- }
2750-
2751- section.blame {
2752- display: grid;
2753- grid-template-columns: 1fr 5fr;
2754- }
2755-
2756- div.clone {
2757- display: flex;
2758- height: 45px;
2759- font-size: smaller;
2760- }
2761-
2762- div.clone-url > input {
2763- height: 2em !important;
2764- }
2765-
2766- div.clone > div {
2767- padding: 5px;
2768- line-height: 45px;
2769- }
2770-
2771- div.clone-url {
2772- flex: auto;
2773- }
2774-
2775- section.blame {
2776- article { margin-top: 0px !important; }
2777- }
2778-
2779- article.blame-right {
2780- padding: 0;
2781- border-left: solid 1px ;
2782- }
2783-
2784- article.blame-left {
2785- padding: 0;
2786- border-right: solid 1px;
2787-
2788- table tbody tr td {
2789- border-left: solid 1px;
2790- border-right: solid 1px;
2791- text-align: left;
2792- }
2793- }
2794-
2795-
2796- .readme {
2797- // fix markdown list rendering
2798- ul {
2799- padding-right: revert;
2800- padding-left: revert;
2801- padding-inline-start: revert;
2802- padding-inline-end: revert;
2803- }
2804- }
2805-
2806- img.rss-icon-feed {
2807- height: 4em;
2808- }
2809-
2810- div.table {
2811- padding: .5em;
2812- }
2813-
2814- div.readme {
2815- padding: 1em;
2816- }
2817-
2818- .term-width {
2819- word-wrap: break-word;
2820- width: 600px;
2821- }
2822-
2823- footer.tree {
2824- font-size: smaller;
2825- }
2826-
2827- // refs
2828-
2829- section.refs-container {
2830- display: grid;
2831- grid-template-columns: 3fr 1fr;
2832- }
2833-
2834- article.ref-right {
2835- margin-left: 1em;
2836- }
2837-
2838-
2839- @media screen and (max-width:1000px) {
2840- section.refs-container {
2841- grid-template-columns: 1fr;
2842- }
2843-
2844- article.ref-right {
2845- margin-left: 0;
2846- }
2847- }
2848-
2849- section.controls {
2850- text-align: center;
2851- border: 1px;
2852- }
2853-
2854- section.viewer {
2855- text-align: center;
2856- border: 1px;
2857- }
2858-
2859- // repo
2860-
2861- div.repo-container {
2862- display: grid;
2863- grid-template-columns: 3fr 1fr;
2864- }
2865-
2866- div.user-box {
2867- display: grid;
2868- grid-template-columns: 1fr 4fr;
2869- }
2870-
2871- div.user-box > .name {
2872- font-weight: bold;
2873- text-decoration: underline;
2874- }
2875-
2876- div.commit_message {
2877- padding-top: 1em;
2878- }
2879-
2880- div.user-box > .details {
2881- text-align: right;
2882- }
2883-
2884- .expand-xl {display: none}
2885-
2886- div.tree > table {
2887- }
2888-
2889- div.tree {
2890- padding-right: 5px;
2891- padding-left: 5px;
2892- }
2893-
2894- article.tree {
2895- margin-bottom: 5px;
2896- }
2897-
2898- section.repo-right {
2899- text-align: center;
2900- }
2901-
2902- section.repo-right {
2903- margin-left: 1em;
2904- }
2905-
2906- section.repo-right > section {
2907- text-align: center;
2908- }
2909-
2910- section.repo-right > article {
2911- display: block;
2912- text-align: center;
2913- }
2914-
2915- .spaced > div:first-child {
2916- margin-left: 2px;
2917- }
2918-
2919- .panel {
2920- background: var(--card-background-color);
2921- padding: .3em;
2922- }
2923-
2924- table.commits {
2925- width: 100%;
2926- }
2927-
2928- table.commits > tbody > tr > td.message {
2929- word-wrap: break-word;
2930- white-space: normal !important;
2931- }
2932-
2933- section.thread-view > article {
2934- padding-top: 1em;
2935- padding-bottom: 1em;
2936- }
2937-
2938- div.chart {
2939- text-align: center;
2940- }
2941-
2942- // breakpoints and adjustments for different screen sizes
2943- @if map-get($breakpoints, "md") {
2944- @media (max-width: map-get($breakpoints, "md")) {
2945- .collapse {display: none}
2946- section.repo-right {
2947- margin-top: 1em;
2948- margin-left: unset;
2949- }
2950- section.viewer > svg {
2951- width: 400px !important;
2952- }
2953- }
2954- }
2955-
2956- @if map-get($breakpoints, "xl") {
2957- @media (max-width: map-get($breakpoints, "xl")) {
2958- section.viewer > svg {
2959- width: 550px;
2960- }
2961- section.repo-right {
2962- margin-top: 1em;
2963- margin-left: unset;
2964- }
2965- div.tree > table > thead > tr > th:nth-child(5) {
2966- text-align: right;
2967- }
2968- div.tree > table > tbody > tr > td:nth-child(5) {
2969- text-align: right;
2970- }
2971- article.repo {
2972- grid-template-columns: repeat(2, 1fr);
2973- }
2974- div.repo-container {
2975- grid-template-columns: 1fr;
2976- }
2977- }
2978- }
2979-
2980-
2981- @if map-get($breakpoints, "xl") {
2982- @media (min-width: map-get($breakpoints, "xl")) {
2983- .expand-xl {display: revert}
2984- }
2985- }
2986-
2987- // code highlighting
2988-
2989- article.blame-right {
2990- border-left: solid $blame-border 1px !important;
2991- }
2992-
2993- article.blame-left {
2994- border-right: solid $blame-border 1px !important;
2995- }
2996-
2997- article > header.highlighted {
2998- background-color: $highlighted;
2999- }
3000-
3001- article > footer {
3002- background-color: unset;
3003- }
3004-
3005- .icon-header.contrast > svg {
3006- background-color: $icon-background;
3007- }
3008-
3009- article.repo-right {
3010- background-color: unset;
3011- }
3012-
3013- article.repo-right > article {
3014- background-color: unset;
3015- }
3016-
3017- article.repo-right > article > header {
3018- background-color: unset;
3019- }
3020-
3021- .positive {
3022- color: $positive;
3023- }
3024-
3025- .negative {
3026- color: $negative;
3027- }
3028 diff --git a/ayllu/themes/default/layout.scss b/ayllu/themes/default/layout.scss
3029deleted file mode 100644
3030index de45bde..0000000
3031--- a/ayllu/themes/default/layout.scss
3032+++ /dev/null
3033 @@ -1,70 +0,0 @@
3034- $breakpoints: (
3035- xs: 0,
3036- sm: 576px,
3037- md: 768px,
3038- lg: 992px,
3039- xl: 1200px,
3040- xxl: 1500px,
3041- xxxl: 2200px
3042- );
3043-
3044- // Viewports
3045- $viewports: (
3046- // 'null' disable the viewport on a breakpoint
3047- sm: 510px,
3048- md: 700px,
3049- lg: 920px,
3050- xl: 1130px,
3051- xxl: 1480px,
3052- xxxl: 2190px
3053- );
3054-
3055-
3056- .container,
3057- .container-fluid {
3058- width: 100%;
3059- margin-right: auto;
3060- margin-left: auto;
3061- padding-right: var(--spacing);
3062- padding-left: var(--spacing);
3063- }
3064-
3065- .container {
3066- @if map-get($breakpoints, "sm") {
3067- @media (min-width: map-get($breakpoints, "sm")) {
3068- max-width: map-get($viewports, "sm");
3069- padding-right: 0;
3070- padding-left: 0;
3071- }
3072- }
3073-
3074- @if map-get($breakpoints, "md") {
3075- @media (min-width: map-get($breakpoints, "md")) {
3076- max-width: map-get($viewports, "md");
3077- }
3078- }
3079-
3080- @if map-get($breakpoints, "lg") {
3081- @media (min-width: map-get($breakpoints, "lg")) {
3082- max-width: map-get($viewports, "lg");
3083- }
3084- }
3085-
3086- @if map-get($breakpoints, "xl") {
3087- @media (min-width: map-get($breakpoints, "xl")) {
3088- max-width: map-get($viewports, "xl");
3089- }
3090- }
3091-
3092- @if map-get($breakpoints, "xxl") {
3093- @media (min-width: map-get($breakpoints, "xxl")) {
3094- max-width: map-get($viewports, "xxl");
3095- }
3096- }
3097-
3098- @if map-get($breakpoints, "xxxl") {
3099- @media (min-width: map-get($breakpoints, "xxxl")) {
3100- max-width: map-get($viewports, "xxxl");
3101- }
3102- }
3103- }
3104 diff --git a/ayllu/themes/default/templates/404.html b/ayllu/themes/default/templates/404.html
3105index d8bac44..ef22b82 100644
3106--- a/ayllu/themes/default/templates/404.html
3107+++ b/ayllu/themes/default/templates/404.html
3108 @@ -1,8 +1,9 @@
3109 {% extends "base.html" %}
3110 {% block content %}
3111- <!-- https://openclipart.org/detail/310184/fair-labyrinth -->
3112- <h1> Not Found ({{status_code}})</h1>
3113- <p> Unable to find the resource you have requested, perhaps try looking somewhere else? </p>
3114- <h4> Error Message: </h4>
3115- <pre>{{ error_message }}</pre>
3116+ <section class="stretch">
3117+ <h1>Not Found ({{ status_code }})</h1>
3118+ <p>Unable to find the resource you have requested, perhaps try looking somewhere else?</p>
3119+ <h4>Error Message:</h4>
3120+ <p>{{ error_message }}</p>
3121+ </section>
3122 {% endblock %}
3123 diff --git a/ayllu/themes/default/templates/5xx.html b/ayllu/themes/default/templates/5xx.html
3124index e010a62..e0870d3 100644
3125--- a/ayllu/themes/default/templates/5xx.html
3126+++ b/ayllu/themes/default/templates/5xx.html
3127 @@ -1,7 +1,9 @@
3128 {% extends "base.html" %}
3129 {% block content %}
3130- <h1> Internal Service Error ({{status_code}})</h1>
3131- <p> Something has gone wrong processing your request, please be patient. </p>
3132- <h4> Error Message: </h4>
3133- <pre>{{ error_message }}</pre>
3134+ <section class="strech">
3135+ <h1>Internal Service Error ({{ status_code }})</h1>
3136+ <p>Something has gone wrong processing your request, please be patient.</p>
3137+ <h4>Error Message:</h4>
3138+ <p>{{ error_message }}</p>
3139+ </section>
3140 {% endblock %}
3141 diff --git a/ayllu/themes/default/templates/about.html b/ayllu/themes/default/templates/about.html
3142index e319b72..652e78a 100644
3143--- a/ayllu/themes/default/templates/about.html
3144+++ b/ayllu/themes/default/templates/about.html
3145 @@ -1,9 +1,6 @@
3146 {% extends "base.html" %}
3147 {% block content %}
3148- <section>
3149- <article>
3150- <header></header>
3151- {{ blurb | safe }}
3152- </article>
3153- </section>
3154+ <section>
3155+ <article>{{ blurb | safe }}</article>
3156+ </section>
3157 {% endblock %}
3158 diff --git a/ayllu/themes/default/templates/authors.html b/ayllu/themes/default/templates/authors.html
3159index 84eb9d4..6469e57 100644
3160--- a/ayllu/themes/default/templates/authors.html
3161+++ b/ayllu/themes/default/templates/authors.html
3162 @@ -1,21 +1,23 @@
3163 {% extends "base.html" %}
3164 {% block content %}
3165- <section>
3166- <article>
3167- <header><h1>Authors</h1></header>
3168- {% for author in authors %}
3169- <article>
3170- <div class="wide">
3171- <div>
3172- <a href="/{{collection}}/{{name}}/log?username={{author.username | urlencode}}&email={{author.email | urlencode}}">{{author.username}}</a>
3173- </div>
3174- <div>
3175- <span class="positive">+{{author.lines_added}}</span>
3176- <span class="negative">-{{author.lines_removed}}</span>
3177- {{author.percentage}}% ({{author.count}})
3178- </div>
3179- </div>
3180- </article>
3181- {% endfor %}
3182- </article>
3183- {% endblock %}
3184+ <section>
3185+ <article>
3186+ <header>
3187+ <h1>Authors</h1>
3188+ </header>
3189+ {% for author in authors %}
3190+ <article>
3191+ <div class="wide">
3192+ <div>
3193+ <a href="/{{ collection }}/{{ name }}/log?username={{ author.username | urlencode }}&email={{ author.email | urlencode }}">{{ author.username }}</a>
3194+ </div>
3195+ <div>
3196+ <span class="positive">+{{ author.lines_added }}</span>
3197+ <span class="negative">-{{ author.lines_removed }}</span>
3198+ {{ author.percentage }}% ({{ author.count }})
3199+ </div>
3200+ </div>
3201+ </article>
3202+ {% endfor %}
3203+ </article>
3204+ {% endblock %}
3205 diff --git a/ayllu/themes/default/templates/base.html b/ayllu/themes/default/templates/base.html
3206index 191259d..831e4cb 100644
3207--- a/ayllu/themes/default/templates/base.html
3208+++ b/ayllu/themes/default/templates/base.html
3209 @@ -7,9 +7,7 @@
3210 {% block title %}{{ title }}{% endblock %}
3211 </title>
3212 <link rel="stylesheet" href="/static/main.min.css" />
3213- <link href="/static/assets/ayllu_logo.svg"
3214- rel="icon"
3215- type="image/svg+xml" />
3216+ <link href="/static/logo.svg" rel="icon" type="image/svg+xml" />
3217 {% block head %}{% endblock %}
3218 </head>
3219 <body>
3220 @@ -18,15 +16,13 @@
3221 {% block navigation %}
3222 {% include "nav.html" %}
3223 {% endblock %}
3224- {% if fluid %}
3225- <main class="container-fluid">
3226- {% else %}
3227- <main class="container">
3228- {% endif %}
3229+ <main>
3230+ <div class="container">
3231 {% block content %}{% endblock %}
3232- </main>
3233- <footer class="main">
3234- {% if render_time %}rendered in {{ render_time }}ms{% endif %}
3235- </footer>
3236- </body>
3237- </html>
3238+ </div>
3239+ </main>
3240+ <footer>
3241+ {% if render_time %}rendered in {{ render_time }}ms{% endif %}
3242+ </footer>
3243+ </body>
3244+ </html>
3245 diff --git a/ayllu/themes/default/templates/blame.html b/ayllu/themes/default/templates/blame.html
3246index 9a0a83b..f4a5941 100644
3247--- a/ayllu/themes/default/templates/blame.html
3248+++ b/ayllu/themes/default/templates/blame.html
3249 @@ -1,33 +1,38 @@
3250 {% import "macros.html" as macros %}
3251 {% extends "base.html" %}
3252 {% block content %}
3253- <article>
3254+ <section class="stretch">
3255 <header>
3256- {{ macros::navigation(items=subnav_elements, title="Blame") }}
3257+ {{ macros::navigation(items=subnav_elements, title="Blame") }}
3258 </header>
3259 <section class="blame">
3260 {% if is_renderable %}
3261 <article class="blame-left">
3262- <pre><table><tbody>
3263- {%- for line in blame_lines -%}
3264- {%- set n_lines = line.end - line.start -%}
3265- <tr>
3266- <td>{{line.author_name}} <a href="/{{collection}}/{{name}}/commit/{{line.commit_id}}">{{line.timestamp}}</a></td>
3267- {%- for _ in range(end=n_lines-1) -%}
3268- <tr><td>&nbsp;</td><td></td></tr>
3269- {%- endfor -%}
3270- {% endfor %}</tbody></table></pre>
3271- </article>
3272- <article class="blame-right">
3273- <pre>{{ content | safe }}</pre>
3274- </article>
3275- {% else %}
3276- <article>
3277- <header>
3278+ <table class="code">
3279+ <tbody>
3280+ {%- for line in blame_lines -%}
3281+ {%- set n_lines = line.end - line.start -%}
3282+ <tr>
3283+ <td>
3284+ {{ line.author_name }} <a href="/{{ collection }}/{{ name }}/commit/{{ line.commit_id }}">{{ line.timestamp }}</a>
3285+ </td>
3286+ {%- for _ in range(end=n_lines-1) -%}
3287+ <tr>
3288+ <td>&nbsp;</td>
3289+ <td></td>
3290+ </tr>
3291+ {%- endfor -%}
3292+ </tr>
3293+ {% endfor %}
3294+ </tbody>
3295+ </table>
3296+ </article>
3297+ <article class="blame-right">{{ content.1 | safe }}</article>
3298+ {% else %}
3299+ <article>
3300 <h2>This content is not blameable</h2>
3301- </header>
3302- </article>
3303- {% endif %}
3304+ </article>
3305+ {% endif %}
3306+ </section>
3307 </section>
3308- </article>
3309- {% endblock %}
3310+ {% endblock %}
3311 diff --git a/ayllu/themes/default/templates/blob.html b/ayllu/themes/default/templates/blob.html
3312index bc4d2e9..76d8ddf 100644
3313--- a/ayllu/themes/default/templates/blob.html
3314+++ b/ayllu/themes/default/templates/blob.html
3315 @@ -1,40 +1,40 @@
3316 {% import "macros.html" as macros %}
3317 {% extends "base.html" %}
3318 {% block content %}
3319- <section>
3320- <article>
3321- <header>
3322- {{ macros::navigation(items=subnav_elements, title="Blob") }}
3323- </br>
3324- {%- if hint -%}
3325- <span class="hint" style="color: {{color}}">{{hint}}</span></br>
3326- {%- endif -%}
3327- <span>{{file_name}}</span>
3328- <span class="right"> {{ file_mode | filemode }} {{ file_size | human_bytes}} </span>
3329- </header>
3330- {% if is_binary %}
3331- <div class="blob-preview">
3332- {% if is_image %}
3333- <img src="{{ raw_url }}"></img>
3334- {% elif is_video %}
3335- <video controls>
3336- <source src="{{raw_url}}" type="{{file_type}}" />
3337- <p><a href="{{raw_url}}">raw url</a></p>
3338- </video>
3339- {% elif is_audio %}
3340- <audio src="{{raw_url}}" type="{{file_type}}"></audio>
3341- {% else %}
3342- <center><h3>Cannot render binary content</h3></center>
3343- <a href="{{ raw_url }}">download</a>
3344- {% endif %}
3345- </div>
3346- {% else %}
3347- {% if is_markdown %}
3348- <div class="readme">{{ content | safe }}</div>
3349- {% else %}
3350- <code class="highlighted">{{ content | safe }}</code>
3351- {% endif %}
3352- {% endif %}
3353- </article>
3354+ <section class="column">
3355+ {{ macros::navigation(items=subnav_elements, title="Blob") }}
3356+ {%- if hint -%}
3357+ <span class="hint" style="color: {{ color }}">{{ hint }}</span>
3358+ {%- endif -%}
3359+ <span>{{ file_name }}</span>
3360+ <span class="right">{{ file_mode | filemode }} {{ file_size | human_bytes }}</span>
3361+ {% if is_binary %}
3362+ <div class="blob-preview">
3363+ {% if is_image %}
3364+ <img src="{{ raw_url }}">
3365+ </img>
3366+ {% elif is_video %}
3367+ <video controls>
3368+ <source src="{{ raw_url }}" type="{{ file_type }}" />
3369+ <p>
3370+ <a href="{{ raw_url }}">raw url</a>
3371+ </p>
3372+ </video>
3373+ {% elif is_audio %}
3374+ <audio src="{{ raw_url }}" type="{{ file_type }}"></audio>
3375+ {% else %}
3376+ <center>
3377+ <h3>Cannot render binary content</h3>
3378+ </center>
3379+ <a href="{{ raw_url }}">download</a>
3380+ {% endif %}
3381+ </div>
3382+ {% else %}
3383+ {% if is_markdown %}
3384+ <div class="readme">{{ content | safe }}</div>
3385+ {% else %}
3386+ {{ content | safe }}
3387+ {% endif %}
3388+ {% endif %}
3389 </section>
3390 {% endblock %}
3391 diff --git a/ayllu/themes/default/templates/branches.html b/ayllu/themes/default/templates/branches.html
3392index 84ddc1d..d92ae8f 100644
3393--- a/ayllu/themes/default/templates/branches.html
3394+++ b/ayllu/themes/default/templates/branches.html
3395 @@ -1,26 +1,27 @@
3396 {% import "macros.html" as macros %}
3397 {% extends "base.html" %}
3398 {% block content %}
3399- <section>
3400- <article>
3401- <header>
3402- {{ macros::navigation(items=refnav, title="Branches") }}
3403- </header>
3404- {% for branch in branches %}
3405- <article class="inner">
3406- <header class="wide">
3407- <div><h3><a href="/{{collection}}/{{name}}/tree/{{branch.name | urlencode }}">{{ branch.name }}</a></h3></div>
3408- <div><h5>{{ branch.commit.epoch | friendly_time }}<h5></div>
3409- </header>
3410- <p>{{branch.commit.message}}</p>
3411- <footer class="wide">
3412- <div>{{ branch.commit.author_name }}</div>
3413- <div>
3414- <a href="/{{collection}}/{{name}}/commit/{{branch.commit.id}}" role="button">{{branch.commit.id | truncate(length=8, end="")}}</a>
3415- </div>
3416- </footer>
3417- </article>
3418- {% endfor %}
3419- </article>
3420- </section>
3421+ <section class="stretch">
3422+ {{ macros::navigation(items=refnav, title="Branches") }}
3423+ {% for branch in branches %}
3424+ <section class="branch card">
3425+ <section class="segmented-4">
3426+ <div>
3427+ <h5>
3428+ <a href="/{{ collection }}/{{ name }}/tree/{{ branch.name | urlencode }}">{{ branch.name }}</a>
3429+ </h5>
3430+ </div>
3431+ <div>{{ branch.head.epoch | friendly_time }}</div>
3432+ <div>{{ branch.head.author_name }}</div>
3433+ <div>
3434+ <a href="/{{ collection }}/{{ name }}/commit/{{ branch.head.id }}"
3435+ role="button">{{ branch.head.id | truncate(length=8, end="") }}</a>
3436+ </div>
3437+ </section>
3438+ <section class="message">
3439+ <pre>{{ branch.head.message }}</pre>
3440+ </section>
3441+ </section>
3442+ {% endfor %}
3443+ </section>
3444 {% endblock %}
3445 diff --git a/ayllu/themes/default/templates/build.html b/ayllu/themes/default/templates/build.html
3446index 2f667cc..b373e02 100644
3447--- a/ayllu/themes/default/templates/build.html
3448+++ b/ayllu/themes/default/templates/build.html
3449 @@ -1,29 +1,31 @@
3450 {% extends "base.html" %}
3451 {% block content %}
3452- <section>
3453- <article>
3454- <header>
3455- <h1>Build: {{ build.id }}</h1>
3456- </header>
3457- <table>
3458- <thead>
3459- <th> date </th>
3460- <th> runtime </th>
3461- <th> success </th>
3462- </thead>
3463- <tr>
3464- <td> {{ build.timestamp }} </td>
3465- <td> {{ build.runtime }} </td>
3466- <td> {{ build.success }} </td>
3467- </tr>
3468- </table>
3469- </article>
3470- {% for step in steps %}
3471- <article>
3472- <header> {{ step.0 }} </header>
3473- <code> {{ step.1 }} </code>
3474- <code> {{ step.2 }} </code>
3475- </article>
3476- {% endfor %}
3477- </section>
3478+ <section>
3479+ <article>
3480+ <header>
3481+ <h1>Build: {{ build.id }}</h1>
3482+ </header>
3483+ <table>
3484+ <thead>
3485+ <th>date</th>
3486+ <th>runtime</th>
3487+ <th>success</th>
3488+ </thead>
3489+ <tr>
3490+ <td>{{ build.timestamp }}</td>
3491+ <td>{{ build.runtime }}</td>
3492+ <td>{{ build.success }}</td>
3493+ </tr>
3494+ </table>
3495+ </article>
3496+ {% for step in steps %}
3497+ <article>
3498+ <header>
3499+ {{ step.0 }}
3500+ </header>
3501+ <code> {{ step.1 }} </code>
3502+ <code> {{ step.2 }} </code>
3503+ </article>
3504+ {% endfor %}
3505+ </section>
3506 {% endblock %}
3507 diff --git a/ayllu/themes/default/templates/builds.html b/ayllu/themes/default/templates/builds.html
3508index 9e8e383..4f83aaa 100644
3509--- a/ayllu/themes/default/templates/builds.html
3510+++ b/ayllu/themes/default/templates/builds.html
3511 @@ -1,26 +1,28 @@
3512 {% extends "base.html" %}
3513 {% block content %}
3514- <section>
3515- <article>
3516- <header>
3517- <h1>Builds</h1>
3518- </header>
3519- <table>
3520- <thead>
3521- <th> id </th>
3522- <th> date </th>
3523- <th> runtime </th>
3524- <th> success </th>
3525- </thead>
3526- {% for build in builds %}
3527- <tr>
3528- <td> <a href="/{{collection}}/{{name}}/builds/{{build.id}}">{{ build.id }}</a></td>
3529- <td> {{ build.timestamp }} </td>
3530- <td> {{ build.runtime }} </td>
3531- <td> {{ build.success }} </td>
3532- </tr>
3533- {% endfor %}
3534- </table>
3535- </article>
3536- </section>
3537+ <section>
3538+ <article>
3539+ <header>
3540+ <h1>Builds</h1>
3541+ </header>
3542+ <table>
3543+ <thead>
3544+ <th>id</th>
3545+ <th>date</th>
3546+ <th>runtime</th>
3547+ <th>success</th>
3548+ </thead>
3549+ {% for build in builds %}
3550+ <tr>
3551+ <td>
3552+ <a href="/{{ collection }}/{{ name }}/builds/{{ build.id }}">{{ build.id }}</a>
3553+ </td>
3554+ <td>{{ build.timestamp }}</td>
3555+ <td>{{ build.runtime }}</td>
3556+ <td>{{ build.success }}</td>
3557+ </tr>
3558+ {% endfor %}
3559+ </table>
3560+ </article>
3561+ </section>
3562 {% endblock %}
3563 diff --git a/ayllu/themes/default/templates/channel.html b/ayllu/themes/default/templates/channel.html
3564index 81a29a2..e49bdd7 100644
3565--- a/ayllu/themes/default/templates/channel.html
3566+++ b/ayllu/themes/default/templates/channel.html
3567 @@ -1,27 +1,27 @@
3568 {% import "macros.html" as macros %}
3569 {% extends "base.html" %}
3570 {% block content %}
3571- <section>
3572- <article>
3573- <header>
3574- {{ macros::navigation(items=discnav, title=channel) }}
3575- </header>
3576- <table>
3577- <thead>
3578- <th> nick </th>
3579- <th> timestamp </th>
3580- <th> message</th>
3581- </thead>
3582- <tbody>
3583- {% for message in messages %}
3584- <tr>
3585- <td> {{ message.nickname }} </td>
3586- <td> {{ message.timestamp }} </td>
3587- <td>{{ message.body | escape }}</td>
3588- </tr>
3589- {% endfor %}
3590- </tbody>
3591- </table>
3592- </article>
3593- </section>
3594+ <section>
3595+ <article>
3596+ <header>
3597+ {{ macros::navigation(items=discnav, title=channel) }}
3598+ </header>
3599+ <table>
3600+ <thead>
3601+ <th>nick</th>
3602+ <th>timestamp</th>
3603+ <th>message</th>
3604+ </thead>
3605+ <tbody>
3606+ {% for message in messages %}
3607+ <tr>
3608+ <td>{{ message.nickname }}</td>
3609+ <td>{{ message.timestamp }}</td>
3610+ <td>{{ message.body | escape }}</td>
3611+ </tr>
3612+ {% endfor %}
3613+ </tbody>
3614+ </table>
3615+ </article>
3616+ </section>
3617 {% endblock %}
3618 diff --git a/ayllu/themes/default/templates/channels.html b/ayllu/themes/default/templates/channels.html
3619index 75e4a03..8926e29 100644
3620--- a/ayllu/themes/default/templates/channels.html
3621+++ b/ayllu/themes/default/templates/channels.html
3622 @@ -1,27 +1,29 @@
3623 {% import "macros.html" as macros %}
3624 {% extends "base.html" %}
3625 {% block content %}
3626- <section>
3627- <article>
3628- <header>
3629- {{ macros::navigation(items=discnav, title="XMPP Channels") }}
3630- </header>
3631- <table>
3632- <thead>
3633- <th> name </th>
3634- <th> online </th>
3635- <th> messages </th>
3636- </thead>
3637- <tbody>
3638- {% for channel in channels %}
3639- <tr>
3640- <td><a href="/discuss/xmpp/{{channel.name}}">{{ channel.name }}</a></td>
3641- <td>{{ channel.n_users }}</td>
3642- <td>{{ channel.n_messages }}</td>
3643- </tr>
3644- {% endfor %}
3645- </tbody>
3646- </table>
3647- </article>
3648- </section>
3649+ <section>
3650+ <article>
3651+ <header>
3652+ {{ macros::navigation(items=discnav, title="XMPP Channels") }}
3653+ </header>
3654+ <table>
3655+ <thead>
3656+ <th>name</th>
3657+ <th>online</th>
3658+ <th>messages</th>
3659+ </thead>
3660+ <tbody>
3661+ {% for channel in channels %}
3662+ <tr>
3663+ <td>
3664+ <a href="/discuss/xmpp/{{ channel.name }}">{{ channel.name }}</a>
3665+ </td>
3666+ <td>{{ channel.n_users }}</td>
3667+ <td>{{ channel.n_messages }}</td>
3668+ </tr>
3669+ {% endfor %}
3670+ </tbody>
3671+ </table>
3672+ </article>
3673+ </section>
3674 {% endblock %}
3675 diff --git a/ayllu/themes/default/templates/chart.html b/ayllu/themes/default/templates/chart.html
3676index ed03896..040b61a 100644
3677--- a/ayllu/themes/default/templates/chart.html
3678+++ b/ayllu/themes/default/templates/chart.html
3679 @@ -1,20 +1,10 @@
3680+ {% import "macros.html" as macros %}
3681 {% extends "base.html" %}
3682 {% block content %}
3683- <section>
3684- <article>
3685- <header>
3686- <nav>
3687- <ul><h1>{{ chart_title }}</h1></ul>
3688- <ul>
3689- {% for item in chartnav %}
3690- <li {% if item.2 %} class="active" {% endif %}> <a href="{{ item.1 }}">{{ item.0 }}</a></li>
3691- {% endfor %}
3692- </ul>
3693- </nav>
3694- </header>
3695- <section class="viewer">
3696- {{ chart | safe }}
3697+ <section class="column">
3698+ {{ macros::navigation(items=chartnav, title=chart_title) }}
3699+ <section class="viewer">
3700+ {{ chart | safe }}
3701+ </section>
3702 </section>
3703- </article>
3704- </section>
3705 {% endblock %}
3706 diff --git a/ayllu/themes/default/templates/collection.html b/ayllu/themes/default/templates/collection.html
3707index 0b8224b..e92ca3b 100644
3708--- a/ayllu/themes/default/templates/collection.html
3709+++ b/ayllu/themes/default/templates/collection.html
3710 @@ -1,23 +1,24 @@
3711 {% extends "base.html" %}
3712 {% block content %}
3713- <section class="index">
3714- <article>
3715- <header>
3716- <span class="h1">{{ collection.name }} </span>
3717- <span class="right">{{ collection.description }}</b> {%- if is_hidden -%} <span class="negative">[hidden]</span> {%- endif -%}</span>
3718- </header>
3719- {% for repo in repositories %}
3720- <article>
3721- <span class="header">
3722- <a href="/{{collection.name}}/{{repo.name}}">{{ repo.name }}</a>
3723- </span>
3724- <span class="right">{{repo.description}}</span>
3725- <footer>
3726- <span><small>{{repo.age}}</small></span>
3727- <span class="right">{{repo.activity}}</span>
3728- </footer>
3729- </article>
3730- {% endfor %}
3731- </article>
3732- </section>
3733+ <section class="column">
3734+ <header>
3735+ <h1>
3736+ <a href="/{{ collection.name }}">{{ collection.name }}</a>
3737+ {%- if is_hidden %} <span class="negative">[hidden]</span> {%- endif -%}
3738+ </h1>
3739+ {{ collection.description }}
3740+ </header>
3741+ {% for repo in repositories %}
3742+ <article>
3743+ <section>
3744+ <span class="bold"><a href="/{{ collection.name }}/{{ repo.name }}">{{ repo.name }}</a></span>
3745+ <span class="right">{{ repo.description }}</span>
3746+ </section>
3747+ <section>
3748+ <span>{{ repo.age }}</span>
3749+ <span class="right">{{ repo.activity }}</span>
3750+ </section>
3751+ </article>
3752+ {% endfor %}
3753+ </section>
3754 {% endblock %}
3755 diff --git a/ayllu/themes/default/templates/commit.html b/ayllu/themes/default/templates/commit.html
3756index b23617e..6c9b9c6 100644
3757--- a/ayllu/themes/default/templates/commit.html
3758+++ b/ayllu/themes/default/templates/commit.html
3759 @@ -1,61 +1,48 @@
3760 {% extends "base.html" %}
3761+ {% import "macros.html" as macros %}
3762 {% block content %}
3763- <article>
3764- <header class="wide">
3765- <h1>Commit</h1>
3766- <span class="right">{{ commit.epoch | friendly_time }}</span>
3767- </header>
3768- <article>
3769- <div class="wide">
3770- <div> <b> Author: </b> </div>
3771- <div>
3772- <a href="/{{collection}}/{{name}}/log?username={{commit.author_name | urlencode}}&email={{commit.author_email | urlencode}}">{{commit.author_name}}</a>
3773- [<a href="mailto://{{commit.author_email}}">{{commit.author_email}}</a>]
3774- </div>
3775- </div>
3776- {% if distinct_author %}
3777- <div class="wide">
3778- <div><b>Committer:</b></div>
3779- <div>
3780- <a href="#">{{commit.committer_name}}</a>
3781- [<a href="mailto://{{commit.committer_email}}">{{commit.committer_email}}</a>]
3782- {{ commit.committer_epoch | format_epoch }}
3783- </div>
3784- </div>
3785- {% endif %}
3786- <div class="wide">
3787- <div><b>Hash:</b></div>
3788- {% if commit.is_verified %}
3789- <span class="positive">
3790- {% else %}
3791- <span class="negative">
3792- {% endif %}
3793- {{commit.id}}
3794- </span>
3795- </div>
3796- <div class="wide">
3797- <div> <b> Time: </b> </div>
3798- <div> {{ commit.author_epoch | format_epoch }} </div>
3799- </div>
3800- <b> Message: </b>
3801- </br>
3802- <i>{{ commit.summary }}</i>
3803- </article>
3804- {% if extended_commit_message %}
3805- <div class="commit_message">
3806- <pre>{{ commit.message }}</pre>
3807- </div>
3808- {% endif %}
3809- <footer>
3810- <span class="positive">+{{stats.insertions}}</span>
3811- <span class="negative">-{{stats.deletions}}</span>
3812- <span>+/-{{stats.files_changed}}</span>
3813- <span class="right">
3814- <a href="/{{collection}}/{{name}}/tree/{{commit_hash}}" role="button">browse</a>
3815- </span>
3816- </footer>
3817- </article>
3818- <article>
3819- <pre>{{diff | safe}}</pre>
3820- </article>
3821+ <section class="stretch">
3822+ <section>
3823+ <span><b>Author:</b></span>
3824+ <span class="right">
3825+ <a href="/{{ collection }}/{{ name }}/log?username={{ commit.author_name | urlencode }}&email={{ commit.author_email | urlencode }}">{{ commit.author_name }}</a>
3826+ [<a href="mailto://{{ commit.author_email }}">{{ commit.author_email }}</a>]
3827+ </span>
3828+ </section>
3829+ {% if distinct_author %}
3830+ <section>
3831+ <span><b>Committer:</b></span>
3832+ <span class="right">
3833+ <a href="#">{{ commit.committer_name }}</a>
3834+ [<a href="mailto://{{ commit.committer_email }}">{{ commit.committer_email }}</a>]
3835+ {{ commit.committer_epoch | format_epoch }}
3836+ </span>
3837+ </section>
3838+ {% endif %}
3839+ <section>
3840+ <span><b>Hash:</b></span>
3841+ <span class="right {% if commit.is_verified -%} positive {%- else -%} negative {%- endif -%}">{{ commit.id }}</span>
3842+ </section>
3843+ <section>
3844+ <span><b>Timestamp:</b></span>
3845+ <span class="right">{{ commit.author_epoch | format_epoch }} ({{ commit.epoch | friendly_time }})</span>
3846+ </section>
3847+ <br />
3848+ <span class="positive">+{{ stats.insertions }}</span>
3849+ <span class="negative">-{{ stats.deletions }}</span>
3850+ <span>+/-{{ stats.files_changed }}</span>
3851+ <span>
3852+ <a href="/{{ collection }}/{{ name }}/tree/{{ commit_hash }}"
3853+ role="button">browse</a>
3854+ </span>
3855+ <section>
3856+ <b>{{ commit.summary }}</b>
3857+ {% if extended_commit_message %}
3858+ <br />
3859+ <br />
3860+ <p class="commit-message">{{ commit.message }}</p>
3861+ {% endif %}
3862+ </section>
3863+ {{ diff.1 | safe }}
3864+ </section>
3865 {% endblock %}
3866 diff --git a/ayllu/themes/default/templates/config.html b/ayllu/themes/default/templates/config.html
3867index 349356a..d1191be 100644
3868--- a/ayllu/themes/default/templates/config.html
3869+++ b/ayllu/themes/default/templates/config.html
3870 @@ -1,25 +1,44 @@
3871 {% extends "base.html" %}
3872 {% block content %}
3873- <div class="container">
3874- <article>
3875- <header><h1>Site Configuration</h1></header>
3876- <form action="/config" method="POST">
3877- <label for="theme"><h5>items-per-page</h5></label>
3878- <select id="items_per_page" name="items_per_page" required>
3879- <option value="50" {%- if config.items_per_page == 50 -%} selected {%- endif -%}>50</option>
3880- <option value="100" {%- if config.items_per_page == 100 -%} selected {%- endif -%}>100</option>
3881- <option value="150" {%- if config.items_per_page == 150 -%} selected {%- endif -%}>150</option>
3882- <option value="200" {%- if config.items_per_page == 200 -%} selected {%- endif -%}>200</option>
3883- </select>
3884- <label for="theme"><h5>theme</h5></label>
3885- <select id="theme" name="theme" required>
3886- {% for theme in themes %}
3887- <option value="{{theme}}" {%- if theme == config.theme -%} selected {%- endif -%}>{{ theme }}</option>
3888- {% endfor %}
3889- </select>
3890- <!-- Button -->
3891- <button type="submit">Submit</button>
3892- </form>
3893- </article>
3894- </div>
3895+ <section class="config-panel">
3896+ <header>
3897+ <h1>Site Configuration</h1>
3898+ </header>
3899+ <form action="/config" method="POST">
3900+ <label for="theme">
3901+ <h3>items-per-page</h3>
3902+ </label>
3903+ <select id="items_per_page" name="items_per_page" required>
3904+ <option value="50"
3905+ {%- if config.items_per_page == 50 -%}
3906+ selected
3907+ {%- endif -%}>50</option>
3908+ <option value="100"
3909+ {%- if config.items_per_page == 100 -%}
3910+ selected
3911+ {%- endif -%}>100</option>
3912+ <option value="150"
3913+ {%- if config.items_per_page == 150 -%}
3914+ selected
3915+ {%- endif -%}>150</option>
3916+ <option value="200"
3917+ {%- if config.items_per_page == 200 -%}
3918+ selected
3919+ {%- endif -%}>200</option>
3920+ </select>
3921+ <label for="theme">
3922+ <h3>theme</h3>
3923+ </label>
3924+ <select id="theme" name="theme" required>
3925+ {% for theme in themes %}
3926+ <option value="{{ theme }}"
3927+ {%- if theme == config.theme -%}
3928+ selected
3929+ {%- endif -%}>{{ theme }}</option>
3930+ {% endfor %}
3931+ </select>
3932+ <!-- Button -->
3933+ <button type="submit">Submit</button>
3934+ </form>
3935+ </section>
3936 {% endblock %}
3937 diff --git a/ayllu/themes/default/templates/graphql.html b/ayllu/themes/default/templates/graphql.html
3938deleted file mode 100644
3939index b8d9155..0000000
3940--- a/ayllu/themes/default/templates/graphql.html
3941+++ /dev/null
3942 @@ -1,6 +0,0 @@
3943- {% extends "base.html" %}
3944- {% block content %}
3945- <p>
3946- Graphql API
3947- </p>
3948- {% endblock %}
3949 diff --git a/ayllu/themes/default/templates/index.html b/ayllu/themes/default/templates/index.html
3950index 40ac8a5..87eb054 100644
3951--- a/ayllu/themes/default/templates/index.html
3952+++ b/ayllu/themes/default/templates/index.html
3953 @@ -1,37 +1,40 @@
3954 {% extends "base.html" %}
3955 {% block content %}
3956- <section class="index">
3957- <header class="wide"><div><h3>Collections</h3></div>
3958- <div>
3959- <div class="icon-header">
3960- <h3>Subscribe</h3>
3961- {{ "feed" | emoji | safe }}
3962- </div>
3963- [<a href="/rss/firehose.xml">*</a>,
3964- <a href="/rss/1d.xml">1d</a>,
3965- <a href="/rss/1w.xml">1w</a>,
3966- <a href="/rss/1m.xml">1m</a>]
3967- </div>
3968- </header>
3969- {% for collection in collections %}
3970- <article class="index-listing">
3971- <header class="wide">
3972- <div><h5><a href="/{{collection.name}}">{{ collection.name }}</a></h5></div>
3973- <div><b>{{ collection.description }}</b></div>
3974- </header>
3975- {% for repo in collection.repositories %}
3976- <article>
3977- <header class="wide">
3978- <div><a href="/{{collection.name}}/{{repo.name}}">{{ repo.name }}</a></div>
3979- <div>{{repo.description}}</div>
3980- </header>
3981- <div class="wide">
3982- <div>{{repo.age}}</div>
3983- <div>{{repo.activity}}</div>
3984- </footer>
3985- </article>
3986- {% endfor %}
3987- </article>
3988- {% endfor %}
3989- </section>
3990- {% endblock %}
3991+ <section class="column">
3992+ <section class="collections">
3993+ <header>
3994+ <h2>Collections</h2>
3995+ <div class="rss-links">
3996+ {{ "feed.svg" | emoji | safe }}
3997+ <p>
3998+ [<a href="/rss/firehose.xml">*</a>,
3999+ <a href="/rss/1d.xml">1d</a>,
4000+ <a href="/rss/1w.xml">1w</a>,
4001+ <a href="/rss/1m.xml">1m</a>]
4002+ </p>
4003+ </div>
4004+ </header>
4005+ </section>
4006+ <section>
4007+ {% for collection in collections %}
4008+ <article class="collection">
4009+ <h3>
4010+ <a href="/{{ collection.name }}">{{ collection.name }}</a>
4011+ </h3>
4012+ <span class="description">{{ collection.description }}</span>
4013+ <section class="repositories">
4014+ {% for repo in collection.repositories %}
4015+ <article class="segmented-4 card">
4016+ <div class="name">
4017+ <a href="/{{ collection.name }}/{{ repo.name }}">{{ repo.name }}</a>
4018+ </div>
4019+ <div class="description">{{ repo.description }}</div>
4020+ <div class="age">{{ repo.age }}</div>
4021+ <div class="activity">{{ repo.activity }}</div>
4022+ </article>
4023+ {% endfor %}
4024+ </section>
4025+ </article>
4026+ {% endfor %}
4027+ </section>
4028+ {% endblock %}
4029 diff --git a/ayllu/themes/default/templates/lists.html b/ayllu/themes/default/templates/lists.html
4030index afbcce5..0f413de 100644
4031--- a/ayllu/themes/default/templates/lists.html
4032+++ b/ayllu/themes/default/templates/lists.html
4033 @@ -1,29 +1,31 @@
4034 {% import "macros.html" as macros %}
4035 {% extends "base.html" %}
4036 {% block content %}
4037- <section>
4038- <article>
4039- <header>
4040- <h1> Mailing Lists </h1>
4041- </header>
4042- <table>
4043- <thead>
4044- <th> id </th>
4045- <th> name </th>
4046- <th> description </th>
4047- <th> address </th>
4048- </thead>
4049- <tbody>
4050- {% for list in lists %}
4051- <tr>
4052- <td><a href="/mail/{{list.id}}">{{ list.id }}</a></td>
4053- <td>{{ list.name }}</td>
4054- <td>{{ list.description }}</td>
4055- <td>{{ list.address }}</td>
4056- </tr>
4057- {% endfor %}
4058- </tbody>
4059- </table>
4060- </article>
4061- </section>
4062+ <section>
4063+ <article>
4064+ <header>
4065+ <h1>Mailing Lists</h1>
4066+ </header>
4067+ <table>
4068+ <thead>
4069+ <th>id</th>
4070+ <th>name</th>
4071+ <th>description</th>
4072+ <th>address</th>
4073+ </thead>
4074+ <tbody>
4075+ {% for list in lists %}
4076+ <tr>
4077+ <td>
4078+ <a href="/mail/{{ list.id }}">{{ list.id }}</a>
4079+ </td>
4080+ <td>{{ list.name }}</td>
4081+ <td>{{ list.description }}</td>
4082+ <td>{{ list.address }}</td>
4083+ </tr>
4084+ {% endfor %}
4085+ </tbody>
4086+ </table>
4087+ </article>
4088+ </section>
4089 {% endblock %}
4090 diff --git a/ayllu/themes/default/templates/log.html b/ayllu/themes/default/templates/log.html
4091index fe97650..7710405 100644
4092--- a/ayllu/themes/default/templates/log.html
4093+++ b/ayllu/themes/default/templates/log.html
4094 @@ -1,61 +1,38 @@
4095 {% import "macros.html" as macros %}
4096 {% extends "base.html" %}
4097 {% block content %}
4098- <section>
4099- <article>
4100- {% if file_view %}
4101- <header>
4102- {{ macros::navigation(items=subnav_elements, title="Log") }}
4103- </header>
4104- {% else %}
4105- <header>
4106- <h1>Log</h1>
4107- </header>
4108- {% endif %}
4109- <table class="commits">
4110- <thead>
4111- <tr>
4112- <th class="collapse">Author</th>
4113- <th class="collapse">Signature</th>
4114- <!--<th class="collapse">Hash</th>-->
4115- <th>Message</th>
4116- <th>Date</th>
4117- </tr>
4118- </thead>
4119- <tbody>
4120- {% for commit in commits %}
4121- <tr>
4122- <td class="collapse"><a href="/{{collection}}/{{name}}/log?username={{commit.author_name | urlencode}}&email={{commit.author_email | urlencode}}">{{commit.author_name}}</a></td>
4123- <!-- <td class="collapse"> 🔒......</td>-->
4124- <td class="collapse">
4125- {% if commit.is_verified %}
4126- <span class="positive">
4127- {% else %}
4128- <span class="negative">
4129- {% endif %}
4130- {{ commit.id | truncate(length=8, end="") }}
4131- </span>
4132- </td>
4133- <td class="message">
4134- <a href="/{{collection}}/{{name}}/commit/{{commit.id}}">
4135- {{ commit.summary | truncate(length=80) }}</a>
4136- {% if commit.is_extended %}
4137- <br/>
4138- <br/>
4139- <pre>{{ commit.message }}</pre>
4140- {% endif %}
4141- </td>
4142- <td> {{ commit.epoch | friendly_time }} </td>
4143- </tr>
4144- {% endfor %}
4145- </tbody>
4146- </table>
4147- {% if has_more %}
4148- <footer class="pagination">
4149- {%- set last_commit = commits | last -%}
4150- <span class="right"><a href="/{{collection}}/{{name}}/log/{{last_commit.id}}"><b>next</b></a></span>
4151- </footer>
4152- {% endif %}
4153- </article>
4154- </section>
4155- {% endblock %}
4156+ <div class="stretch">
4157+ {% for commit in commits %}
4158+ <section class="commit card">
4159+ <section class="segmented-3">
4160+ <div class="signature">
4161+ {% if commit.is_verified %}
4162+ <span class="positive">
4163+ {% else %}
4164+ <span class="negative">
4165+ {% endif %}
4166+ <a href="/{{ collection }}/{{ name }}/commit/{{ commit.id }}">{{ commit.id | truncate(length=16, end="") }}</a>
4167+ </span>
4168+ </div>
4169+ <div class="author">
4170+ <a href="/{{ collection }}/{{ name }}/log?username={{ commit.author_name | urlencode }}&email={{ commit.author_email | urlencode }}">{{ commit.author_name }}</a>
4171+ </div>
4172+ <div class="timestamp">{{ commit.epoch | friendly_time }}</div>
4173+ </section>
4174+ <div class="message">
4175+ {% if commit.is_extended %}
4176+ <pre>{{ commit.message }}</pre>
4177+ {% else %}
4178+ {{ commit.summary }}
4179+ {% endif %}
4180+ </div>
4181+ </section>
4182+ {% endfor %}
4183+ {% if has_more %}
4184+ <footer class="pagination">
4185+ {%- set last_commit = commits | last -%}
4186+ <span class="right"><a href="/{{ collection }}/{{ name }}/log/{{ last_commit.id }}"><b>next</b></a></span>
4187+ </footer>
4188+ {% endif %}
4189+ </div>
4190+ {% endblock %}
4191 diff --git a/ayllu/themes/default/templates/macros.html b/ayllu/themes/default/templates/macros.html
4192index c636ae9..4d07b8c 100644
4193--- a/ayllu/themes/default/templates/macros.html
4194+++ b/ayllu/themes/default/templates/macros.html
4195 @@ -1,10 +1,16 @@
4196 {% macro navigation(items, title="") %}
4197- <nav>
4198- <ul><h1>{{title}}</h1></ul>
4199- <ul>
4200- {% for item in items %}
4201- <li {% if item.2 %} class="active" {% endif %}> <a href="{{ item.1 }}">{{ item.0 }}</a></li>
4202- {% endfor %}
4203- </ul>
4204- </nav>
4205+ <nav class="subnav">
4206+ <ul>
4207+ <h4>{{ title }}</h4>
4208+ </ul>
4209+ <ul>
4210+ {% for item in items %}
4211+ <li {% if item.2 %}class="active"{% endif %}>
4212+ <a href="{{ item.1 }}">{{ item.0 }}</a>
4213+ </li>
4214+ {% endfor %}
4215+ </ul>
4216+ </nav>
4217 {% endmacro navigation %}
4218+ {% macro commit_badge(commit, extended=false) %}
4219+ {% endmacro commit_badge %}
4220 diff --git a/ayllu/themes/default/templates/nav.html b/ayllu/themes/default/templates/nav.html
4221index 2492a79..5a502e6 100644
4222--- a/ayllu/themes/default/templates/nav.html
4223+++ b/ayllu/themes/default/templates/nav.html
4224 @@ -1,16 +1,12 @@
4225 <nav>
4226- <ul>
4227- <li>
4228- <a href="{%- if subpath_mode -%}/browse{%- else -%}/{%- endif -%}">
4229- <div class="logo">{{ "textile-pattern-1" | emoji | safe }}
4230- <h3>&emsp;</h3>
4231- </div>
4232- </a>
4233- </li>
4234- </ul>
4235- <ul>
4236- {% for item in nav_elements %}
4237- <li {% if item.2 %} class="active" {% endif %}> <a href="{{ item.1 }}">{{ item.0 }}</a></li>
4238- {% endfor %}
4239- </ul>
4240+ <a href="{%- if subpath_mode -%}/browse{%- else -%}/{%- endif -%}">
4241+ <div class="logo">{{ "logo.svg" | emoji | safe }}</div>
4242+ </a>
4243+ <ul>
4244+ {% for item in nav_elements %}
4245+ <li {% if item.2 %}class="active"{% endif %}>
4246+ <a href="{{ item.1 }}">{{ item.0 }}</a>
4247+ </li>
4248+ {% endfor %}
4249+ </ul>
4250 </nav>
4251 diff --git a/ayllu/themes/default/templates/post.html b/ayllu/themes/default/templates/post.html
4252index 10a0e41..5880bb1 100644
4253--- a/ayllu/themes/default/templates/post.html
4254+++ b/ayllu/themes/default/templates/post.html
4255 @@ -1,16 +1,21 @@
4256 {% extends "base.html" %}
4257 {% block content %}
4258- <section class="thread-view">
4259- <article>
4260- <header>
4261- <h1>{{message.message_id}}</h1></br>
4262- <h4> Export Message </h4>
4263- <p> <a href="/mail/export/{{list_id}}/{{thread_id}}/{{message.message_id}}">mbox</a><p>
4264- <b>From: {{ message.from_address }}</b></br>
4265- <b>Subject: {{ message.subject }}</b></br>
4266- <span class="right">{{ message.created_at | format_epoch }}</span>
4267- </header>
4268- <pre>{{ message.text | safe }}</pre>
4269- </article>
4270+ <section class="thread-view">
4271+ <article>
4272+ <header>
4273+ <h1>{{ message.message_id }}</h1>
4274+ </br>
4275+ <h4>Export Message</h4>
4276+ <p>
4277+ <a href="/mail/export/{{ list_id }}/{{ thread_id }}/{{ message.message_id }}">mbox</a>
4278+ <p>
4279+ <b>From: {{ message.from_address }}</b>
4280+ </br>
4281+ <b>Subject: {{ message.subject }}</b>
4282+ </br>
4283+ <span class="right">{{ message.created_at | format_epoch }}</span>
4284+ </header>
4285+ <pre>{{ message.text | safe }}</pre>
4286+ </article>
4287 </section>
4288 {% endblock %}
4289 diff --git a/ayllu/themes/default/templates/repo.html b/ayllu/themes/default/templates/repo.html
4290index 631f670..20c69a6 100644
4291--- a/ayllu/themes/default/templates/repo.html
4292+++ b/ayllu/themes/default/templates/repo.html
4293 @@ -1,178 +1,157 @@
4294 {% extends "base.html" %}
4295 {% block content %}
4296- {% if show_details %}
4297- <div class="repo-container">
4298- {% endif %}
4299- <section class="repo-left">
4300- <article class=tree>
4301- <header class="tree-preamble">
4302- <div class="wide">
4303- <span>{{ latest_commit.author_name }} {{latest_commit.epoch | friendly_time }}</span>
4304- <h6>{{ commit_count }} commits</h6>
4305- </div>
4306- <div>
4307- <span class="right">
4308- <h6><b>{{refname}}</b></h6>
4309- </span>
4310- </div>
4311- {% if latest_commit.is_verified %}
4312- <span class="positive">
4313- {% else %}
4314- <span class="negative">
4315- {% endif %}
4316- {{latest_commit.id | truncate(length=8, end="")}}
4317- </span>
4318- </br>
4319- <a href="/{{collection}}/{{name}}/commit/{{latest_commit.id}}">
4320- {{latest_commit.summary | truncate(length=60)}}
4321- </a>
4322- </header>
4323- <div class="tree">
4324- <table>
4325- <thead>
4326- <tr>
4327- <th scope="col">file</th>
4328- <th class="collapse" scope="col">commit</th>
4329- <th class="expand-xl" scope="col">size</th>
4330- <th class="expand-xl" scope="col">mode</th>
4331- <th scope="col">time</th>
4332- </tr>
4333- </thead>
4334- <tbody>
4335- {% for item in tree %}
4336- <tr>
4337- {% if item.0.submodule %}
4338- <td>{{ item.0.name }} @ {{ item.0.submodule }}</td>
4339- {% else %}
4340- <td>
4341- <a href="{{item.0.name | make_url(kind=item.0.kind)}}">
4342- {{item.0.name}}
4343- {% if item.0.kind == "Submodule" %}
4344- <span class="tiny">ref</span>
4345- {% endif %}
4346- {% if item.0.kind == "Pointer" %}
4347- <span class="tiny">ptr</span>
4348- {% endif %}
4349- </a>
4350- </td>
4351- {% endif %}
4352- <td class="collapse">
4353- <a href="/{{collection}}/{{name}}/commit/{{item.1.id}}">{{ item.1.summary | truncate(length=60)}}</a></td>
4354- <td class="expand-xl">{{item.0.size | human_bytes }}</td>
4355- <td class="expand-xl">{{item.0.mode | filemode }}</td>
4356- <td> <a href="/{{collection}}/{{name}}/commit/{{item.1.id}}">{{ item.1.epoch | friendly_time }}</a></td>
4357- </tr>
4358- {% endfor %}
4359- </tbody>
4360- </table>
4361- </div>
4362- </article>
4363- <article class="readme">
4364- <header> {{ rendered_file_name }} </header>
4365- <div class="readme">{{ readme | safe }}</div>
4366- </article>
4367- </section>
4368- {% if show_details %}
4369- <section class="repo-right">
4370- <article>
4371- <div class="panel">
4372- <section class="repo-section">
4373- <div class="icon-header contrast">
4374- <h3>Clone</h3>
4375- </div>
4376- <div class="clone">
4377- <div><b>HTTP</b></div>
4378- <div class="clone-url"><input type="text" value="{{http_clone_url}}" readonly></div>
4379- </div>
4380- {% if git_clone_url %}
4381- <div class="clone">
4382- <div style="margin-right: 1ch;"><b>SSH</b></div>
4383- <div class="clone-url"><input type="text" value="{{git_clone_url}}" readonly></div>
4384- </div>
4385- {% endif %}
4386- </section>
4387- {% if chat_links or email_links %}
4388- <section class="repo-section">
4389- <div class="icon-header contrast"><h3>Discussion</h3>
4390- </div>
4391- {% if chat_links %}
4392- <h6> Chat </h6>
4393- {% for chat in chat_links %}
4394- <em>{{chat.description}}, status: </em>
4395- <b class={%- if chat.users_online -%}"positive"{%- else -%}"negative"{%- endif -%}
4396- data-tooltip="{%- if chat.users_online -%} {{chat.users_online}} Users Online {%- else -%} Offline {%- endif -%}">
4397- [{%- if chat.users_online -%}{{chat.users_online}}{%- else -%}?{%- endif -%}]
4398- </b>
4399- <div class="clone">
4400- <div>
4401- <b>{{chat.kind}}</b>
4402- </div>
4403- <div class="clone-url"><input type="text" value="{{chat.url}}" readonly></div>
4404- </div>
4405- {% endfor %}
4406- {% endif %}
4407- {% if email_links %}
4408- <h6> Mailing Lists </h6>
4409- {% for email in email_links %}
4410- <em>{{email.description}}, 100+ threads</em>
4411- <div class="clone">
4412- <div>
4413- <b>mail</b>
4414- </div>
4415- <div class="clone-url"><input type="text" value="{{email.url}}" readonly></div>
4416- </div>
4417- {% endfor %}
4418- {% endif %}
4419- </section>
4420- {% endif %}
4421- <section class="repo-section">
4422- <h3> Subscribe<span class="icon">{{ "feed" | emoji | safe }}</span></h3>
4423- </br>
4424- [<a href="{{rss_link_all}}">*</a>,
4425- <a href="{{rss_link_1d}}">1d</a>,
4426- <a href="{{rss_link_1w}}">1w</a>,
4427- <a href="{{rss_link_1m}}">1m</a>]
4428- </section>
4429- {% if sites_url %}
4430- <section class="repo-section">
4431- <div class="icon-header contrast">
4432- <h3>Homepage</h3>
4433- </div>
4434- <a href={{sites_url}}>{{sites_url}}</a>
4435- </section>
4436- {% endif %}
4437- <section class="repo-section">
4438- <div class="icon-header contrast"><h3>License</h3>
4439- </div>
4440- <i><b>{{ license }}</b></i>
4441- </section>
4442- <section class="repo-section">
4443- <div class="icon-header contrast"><h3><a href="/{{collection}}/{{name}}/authors">Authors</a></h3>
4444- </div>
4445- </a>
4446- <ul class="author-list">
4447- {% if authors %}
4448- {% for author in authors %}
4449- <li> {{ author.0 }}: <i> {{ author.2 }}% </i> </li>
4450- {% endfor %}
4451- {% endif %}
4452- </ul>
4453- </section>
4454- <div class="chart">
4455- <a href="/{{collection}}/{{name}}/chart/activity/{{latest_commit.id}}">
4456- {{ activity_chart | safe }}
4457- </a>
4458- </div>
4459- <div class="chart">
4460- <a href="/{{collection}}/{{name}}/chart/languages/{{latest_commit.id}}">
4461- {{ language_chart | safe }}
4462- </a>
4463- </div>
4464- </div>
4465- </article>
4466- </section>
4467- {% endif %}
4468- {% if show_details %}
4469- </div>
4470+ <section class="repo-left column">
4471+ <section>
4472+ <span>{{ latest_commit.author_name }} {{ latest_commit.epoch | friendly_time }}</span>
4473+ </section>
4474+ <section>
4475+ <span class="bold">{{ commit_count }} commits</span>
4476+ </section>
4477+ <span>
4478+ {% if latest_commit.is_verified %}
4479+ <span class="positive">
4480+ {% else %}
4481+ <span class="negative">
4482+ {% endif %}
4483+ {{ latest_commit.id | truncate(length=8, end="") }}
4484+ </span>
4485+ </span>
4486+ <span class="right">
4487+ <span class="bold yellow-badge">{{ refname }}</span>
4488+ </span>
4489+ </span>
4490+ </br>
4491+ <span>
4492+ <a href="/{{ collection }}/{{ name }}/commit/{{ latest_commit.id }}">
4493+ {{ latest_commit.summary | truncate(length=60) }}
4494+ </a>
4495+ </span>
4496+ <table class="tree">
4497+ <thead>
4498+ <tr>
4499+ <th scope="col">file</th>
4500+ <th class="collapse" scope="col">commit</th>
4501+ <th class="expand-xl" scope="col">size</th>
4502+ <th class="expand-xl" scope="col">mode</th>
4503+ <th scope="col">time</th>
4504+ </tr>
4505+ </thead>
4506+ <tbody>
4507+ {% for item in tree %}
4508+ <tr>
4509+ {% if item.0.submodule %}
4510+ <td>{{ item.0.name }} @ {{ item.0.submodule }}</td>
4511+ {% else %}
4512+ <td>
4513+ <a href="{{ item.0.name | make_url(kind=item.0.kind) }}">
4514+ {{ item.0.name }}
4515+ {% if item.0.kind == "Submodule" %}<span class="tiny">ref</span>{% endif %}
4516+ {% if item.0.kind == "Pointer" %}<span class="tiny">ptr</span>{% endif %}
4517+ </a>
4518+ </td>
4519+ {% endif %}
4520+ <td class="collapse">
4521+ <a href="/{{ collection }}/{{ name }}/commit/{{ item.1.id }}">{{ item.1.summary | truncate(length=60) }}</a>
4522+ </td>
4523+ <td class="expand-xl">{{ item.0.size | human_bytes }}</td>
4524+ <td class="expand-xl">{{ item.0.mode | filemode }}</td>
4525+ <td>
4526+ <a href="/{{ collection }}/{{ name }}/commit/{{ item.1.id }}">{{ item.1.epoch | friendly_time }}</a>
4527+ </td>
4528+ </tr>
4529+ {% endfor %}
4530+ </tbody>
4531+ </table>
4532+ <section class="readme">
4533+ {{ readme | safe }}
4534+ </section>
4535+ </section>
4536+ {% if show_details %}
4537+ <section class="repo-right column">
4538+ <section class="repo-section">
4539+ <h3>Clone</h3>
4540+ <span class="bold">HTTP</span>
4541+ <input type="text" value="{{ http_clone_url }}" readonly>
4542+ {% if git_clone_url %}
4543+ <span class="bold">SSH</span>
4544+ <input type="text" value="{{ git_clone_url }}" readonly>
4545+ {% endif %}
4546+ </section>
4547+ {% if chat_links or email_links %}
4548+ <section class="repo-section">
4549+ <h3>Discussion</h3>
4550+ {% if chat_links %}
4551+ <h6>Chat</h6>
4552+ {% for chat in chat_links %}
4553+ <em>{{ chat.description }}, status:</em>
4554+ <b class="{%- if chat.users_online -%}"
4555+ "
4556+ positive
4557+ "
4558+ {%- else -%}
4559+ "
4560+ negative
4561+ "
4562+ {%- endif -%}
4563+ data-tooltip="{%- if chat.users_online -%} {{ chat.users_online }} Users Online {%- else -%} Offline {%- endif -%}">
4564+ [{%- if chat.users_online -%}{{ chat.users_online }}{%- else -%}?{%- endif -%}]
4565+ </b>
4566+ <b>{{ chat.kind }}</b>
4567+ <input type="text" value="{{ chat.url }}" readonly>
4568+ {% endfor %}
4569+ {% endif %}
4570+ {% if email_links %}
4571+ <h6>Mailing Lists</h6>
4572+ {% for email in email_links %}
4573+ <em>{{ email.description }}, 100+ threads</em>
4574+ <b>mail</b>
4575+ <input type="text" value="{{ email.url }}" readonly>
4576+ {% endfor %}
4577+ {% endif %}
4578+ </section>
4579+ {% endif %}
4580+ <section>
4581+ <h3>Subscribe</h3>
4582+ <div class="rss-links">
4583+ {{ "feed.svg" | emoji | safe }}
4584+ <p>
4585+ [<a href="{{ rss_link_all }}">*</a>,
4586+ <a href="{{ rss_link_1d }}">1d</a>,
4587+ <a href="{{ rss_link_1w }}">1w</a>,
4588+ <a href="{{ rss_link_1m }}">1m</a>]
4589+ </p>
4590+ </div>
4591+ </section>
4592+ {% if sites_url %}
4593+ <section class="repo-section">
4594+ <div class="icon-header contrast">
4595+ <h3>Homepage</h3>
4596+ </div>
4597+ <a href={{ sites_url }}>{{ sites_url }}</a>
4598+ </section>
4599+ {% endif %}
4600+ <section class="repo-section">
4601+ <div class="icon-header contrast">
4602+ <h3>License</h3>
4603+ </div>
4604+ <i><b>{{ license }}</b></i>
4605+ </section>
4606+ <section>
4607+ <h3>
4608+ <a href="/{{ collection }}/{{ name }}/authors">Authors</a>
4609+ </h3>
4610+ {% if authors %}
4611+ {% for author in authors %}
4612+ <span>{{ author.0 }}:</span> <i> <span class="right">{{ author.2 }}% </i></span>
4613+ </br>
4614+ {% endfor %}
4615+ {% endif %}
4616+ </section>
4617+ <section>
4618+ <h3>Analysis</h3>
4619+ <a href="/{{ collection }}/{{ name }}/chart/activity/{{ latest_commit.id }}">{{ activity_chart | safe }}</a>
4620+ <a href="/{{ collection }}/{{ name }}/chart/languages/{{ latest_commit.id }}">{{ language_chart | safe }}</a>
4621+ </section>
4622+ </section>
4623 {% endif %}
4624 {% endblock %}
4625 diff --git a/ayllu/themes/default/templates/rss_summary.html b/ayllu/themes/default/templates/rss_summary.html
4626index ca85e9a..0b98b21 100644
4627--- a/ayllu/themes/default/templates/rss_summary.html
4628+++ b/ayllu/themes/default/templates/rss_summary.html
4629 @@ -1,24 +1,31 @@
4630- <center>
4631- <h1> Summary from {{start_date}} to {{end_date}} </h1>
4632- <h2>{{n_tags}} tags and {{n_commits}} commits added {% if entries | length > 1 %} across {{n_projects}} projects{%- endif -%} </h2>
4633- </center>
4634-
4635- {% for entry in entries %}
4636- <h2> Updates For {{entry.name}} </h2>
4637-
4638- {% if entry.tags | length > 0 %}
4639- </br><h3> <u>New Tags</u> </h3>
4640- {% endif %}
4641-
4642- {% if entry.commits | length > 0 %}
4643- </br><h3> <u>New Commits</u> </h3>
4644- {% for commit in entry.commits %}
4645- <article>
4646- <header>
4647- <h4><a href="{{origin}}/{{entry.name}}/commit/{{commit.id}}">{{commit.summary}} - {{commit.author_name}}</a></h4>
4648- </header>
4649- <pre>{{commit.message}}</pre>
4650- </article>
4651- {% endfor %}
4652+ <h1>Summary from {{ start_date }} to {{ end_date }}</h1>
4653+ <h2>
4654+ {{ n_tags }} tags and {{ n_commits }} commits added
4655+ {% if entries | length > 1 %}
4656+ across {{ n_projects }} projects{%- endif -%}
4657+ </h2>
4658+ {% for entry in entries %}
4659+ <h2>Updates For {{ entry.name }}</h2>
4660+ {% if entry.tags | length > 0 %}
4661+ </br>
4662+ <h3>
4663+ <u>New Tags</u>
4664+ </h3>
4665+ {% endif %}
4666+ {% if entry.commits | length > 0 %}
4667+ </br>
4668+ <h3>
4669+ <u>New Commits</u>
4670+ </h3>
4671+ {% for commit in entry.commits %}
4672+ <article>
4673+ <header>
4674+ <h4>
4675+ <a href="{{ origin }}/{{ entry.name }}/commit/{{ commit.id }}">{{ commit.summary }} - {{ commit.author_name }}</a>
4676+ </h4>
4677+ </header>
4678+ <pre>{{commit.message}}</pre>
4679+ </article>
4680+ {% endfor %}
4681 {% endif %}
4682 {% endfor %}
4683 diff --git a/ayllu/themes/default/templates/tags.html b/ayllu/themes/default/templates/tags.html
4684index 2813ddd..911c22a 100644
4685--- a/ayllu/themes/default/templates/tags.html
4686+++ b/ayllu/themes/default/templates/tags.html
4687 @@ -1,28 +1,31 @@
4688 {% import "macros.html" as macros %}
4689 {% extends "base.html" %}
4690 {% block content %}
4691- <section>
4692- <article>
4693- <header>
4694- {{ macros::navigation(items=refnav, title="Tags") }}
4695- </header>
4696- {% for tag in tags %}
4697- <article class="inner">
4698- <header class="wide">
4699- <div><h3><a href="/{{collection}}/{{name}}/tree/{{tag.name | urlencode}}">{{ tag.name }}</a></h3></div>
4700- <div><h5>{{ tag.commit.epoch | friendly_time }}<h5></div>
4701- </header>
4702- <pre>{{ tag.summary }}</pre>
4703- <footer class="wide">
4704- <div>{{ tag.author_name }}</div>
4705- <div>
4706- <a href="/{{collection}}/{{name}}/refs/archive/{{tag.name}}.tar.gz" role="button">{{tag.name}}.tar.gz</a>
4707- <a href="/{{collection}}/{{name}}/commit/{{tag.commit.id}}"
4708- role="button">{{tag.commit.id | truncate(length=8, end="")}}</a>
4709- </div>
4710- </footer>
4711- </article>
4712- {% endfor %}
4713- </article>
4714- </section>
4715+ <section class="stretch">
4716+ {{ macros::navigation(items=refnav, title="Tags") }}
4717+ {% for tag in tags %}
4718+ <section class="tag card">
4719+ <section class="segmented-5">
4720+ <div>
4721+ <h4>
4722+ <a href="/{{ collection }}/{{ name }}/tree/{{ tag.name | urlencode }}">{{ tag.name }}</a>
4723+ </h4>
4724+ </div>
4725+ <div>
4726+ <a href="/{{ collection }}/{{ name }}/commit/{{ tag.commit.id }}"
4727+ role="button">{{ tag.commit.id | truncate(length=8, end="") }}</a>
4728+ </div>
4729+ <div>{{ tag.commit.epoch | friendly_time }}</div>
4730+ <div>{{ tag.author_name }}</div>
4731+ <div>
4732+ <a href="/{{ collection }}/{{ name }}/refs/archive/{{ tag.name }}.tar.gz"
4733+ role="button">{{ tag.name }}.tar.gz</a>
4734+ </div>
4735+ </section>
4736+ <div class="message">
4737+ <pre>{{ tag.summary }}</pre>
4738+ </div>
4739+ </section>
4740+ {% endfor %}
4741+ </section>
4742 {% endblock %}
4743 diff --git a/ayllu/themes/default/templates/thread.html b/ayllu/themes/default/templates/thread.html
4744index 9d75245..af9d62d 100644
4745--- a/ayllu/themes/default/templates/thread.html
4746+++ b/ayllu/themes/default/templates/thread.html
4747 @@ -1,23 +1,28 @@
4748 {% extends "base.html" %}
4749 {% block content %}
4750- <section class="thread-view">
4751- <article>
4752- <header>
4753- <h1>{{ subject }}</h1></br>
4754- <h4> Export Thread </h4>
4755- <p><a href="/mail/export/{{list_id}}/{{thread_id}}">mbox</a></p>
4756- </header>
4757- {% for reply in messages %}
4758- <article>
4759- <header>
4760- <b>From: {{ reply.from_address }}</b></br>
4761- <b>Subject: {{ reply.subject }} </b></br>
4762- <b><a href="/mail/message/{{list_id}}/{{reply.message_id}}">{{ reply.message_id }}</a></b>
4763- <span class="right">{{ reply.created_at | format_epoch }}</span>
4764- </header>
4765- <pre>{{ reply.text | safe }}</pre>
4766- </article>
4767- {% endfor %}
4768- </section>
4769+ <section class="thread-view">
4770+ <article>
4771+ <header>
4772+ <h1>{{ subject }}</h1>
4773+ </br>
4774+ <h4>Export Thread</h4>
4775+ <p>
4776+ <a href="/mail/export/{{ list_id }}/{{ thread_id }}">mbox</a>
4777+ </p>
4778+ </header>
4779+ {% for reply in messages %}
4780+ <article>
4781+ <header>
4782+ <b>From: {{ reply.from_address }}</b>
4783+ </br>
4784+ <b>Subject: {{ reply.subject }} </b>
4785+ </br>
4786+ <b><a href="/mail/message/{{ list_id }}/{{ reply.message_id }}">{{ reply.message_id }}</a></b>
4787+ <span class="right">{{ reply.created_at | format_epoch }}</span>
4788+ </header>
4789+ <pre>{{ reply.text | safe }}</pre>
4790+ </article>
4791+ {% endfor %}
4792+ </section>
4793 </article>
4794 {% endblock %}
4795 diff --git a/ayllu/themes/default/templates/threads.html b/ayllu/themes/default/templates/threads.html
4796index 1b2c733..7b74feb 100644
4797--- a/ayllu/themes/default/templates/threads.html
4798+++ b/ayllu/themes/default/templates/threads.html
4799 @@ -1,45 +1,53 @@
4800 {% import "macros.html" as macros %}
4801 {% extends "base.html" %}
4802 {% block content %}
4803- <section>
4804- <article>
4805- <header>
4806- <h1> {{ list.name }} </h1>
4807- <span class="right labels">
4808- {% for topic in list.topics %}
4809- <span class="feature">{{topic}}</span>
4810- {% endfor %}
4811- </span>
4812- </header>
4813- <div class="mailing-list-details">
4814- <h4> {{ list.description }} </h4>
4815- </br>
4816- <h4> Subscribe </h4>
4817- <p> Send an e-mail to <a href="mailto:{{request_address}}?subject=subscribe">{{request_address}}</a> with the following subject: <code>subscribe</code> </p>
4818- <h4> Unsubscribe </h4>
4819- <p> Send an e-mail to <a href="mailto:{{ request_address}}?subject=unsubscribe">{{request_address}}</a> with the following subject: <code>unsubscribe</code> </p>
4820- <h4> Export List </h4>
4821- <p><a href="/mail/export/{{list.id}}">mbox</a></p>
4822- </div>
4823- <h4> Messages </h4>
4824- <table>
4825- <thead>
4826- <th> from </th>
4827- <th> date </th>
4828- <th> subject </th>
4829- <th> replies </th>
4830- </thead>
4831- <tbody>
4832- {% for thread in threads %}
4833- <tr>
4834- <td>{{ thread.from }}</a></td>
4835- <td>{{thread.timestamp | format_epoch }}</td>
4836- <td><a href="/mail/thread/{{list.id}}/{{thread.message_id}}">{{thread.subject}}</a></td>
4837- <td>{{thread.n_replies}}</td>
4838- </tr>
4839- {% endfor %}
4840- </tbody>
4841- </table>
4842- </article>
4843+ <section>
4844+ <article>
4845+ <header>
4846+ <h1>{{ list.name }}</h1>
4847+ <span class="right labels">
4848+ {% for topic in list.topics %}<span class="feature">{{ topic }}</span>{% endfor %}
4849+ </span>
4850+ </header>
4851+ <div class="mailing-list-details">
4852+ <h4>{{ list.description }}</h4>
4853+ </br>
4854+ <h4>Subscribe</h4>
4855+ <p>
4856+ Send an e-mail to <a href="mailto:{{ request_address }}?subject=subscribe">{{ request_address }}</a> with the following subject: <code>subscribe</code>
4857+ </p>
4858+ <h4>Unsubscribe</h4>
4859+ <p>
4860+ Send an e-mail to <a href="mailto:{{ request_address }}?subject=unsubscribe">{{ request_address }}</a> with the following subject: <code>unsubscribe</code>
4861+ </p>
4862+ <h4>Export List</h4>
4863+ <p>
4864+ <a href="/mail/export/{{ list.id }}">mbox</a>
4865+ </p>
4866+ </div>
4867+ <h4>Messages</h4>
4868+ <table>
4869+ <thead>
4870+ <th>from</th>
4871+ <th>date</th>
4872+ <th>subject</th>
4873+ <th>replies</th>
4874+ </thead>
4875+ <tbody>
4876+ {% for thread in threads %}
4877+ <tr>
4878+ <td>
4879+ <a>{{ thread.from }}</a>
4880+ </td>
4881+ <td>{{ thread.timestamp | format_epoch }}</td>
4882+ <td>
4883+ <a href="/mail/thread/{{ list.id }}/{{ thread.message_id }}">{{ thread.subject }}</a>
4884+ </td>
4885+ <td>{{ thread.n_replies }}</td>
4886+ </tr>
4887+ {% endfor %}
4888+ </tbody>
4889+ </table>
4890+ </article>
4891 </section>
4892 {% endblock %}
4893 diff --git a/ayllu/themes/default/templates/user.html b/ayllu/themes/default/templates/user.html
4894index fda2ec3..311042b 100644
4895--- a/ayllu/themes/default/templates/user.html
4896+++ b/ayllu/themes/default/templates/user.html
4897 @@ -1,4 +1,2 @@
4898 {% extends "base.html" %}
4899- {% block content %}
4900- User Profile
4901- {% endblock %}
4902+ {% block content %}User Profile{% endblock %}
4903 diff --git a/ayllu/themes/default/theme.css b/ayllu/themes/default/theme.css
4904new file mode 100644
4905index 0000000..fbba54c
4906--- /dev/null
4907+++ b/ayllu/themes/default/theme.css
4908 @@ -0,0 +1,545 @@
4909+ @import url("../../../node_modules/open-props/open-props.min.css");
4910+ @import url("../../../node_modules/open-props/normalize.min.css");
4911+ @import url("../../../node_modules/open-props/buttons.min.css");
4912+
4913+ @import url("../../colors/nord.css");
4914+
4915+ :root {
4916+ font-size: 80%;
4917+ }
4918+
4919+ a:visited {
4920+ color: var(--link);
4921+ text-decoration: none;
4922+ }
4923+
4924+ table {
4925+ min-width: 100%;
4926+ }
4927+
4928+ nav {
4929+ display: flex;
4930+ justify-content: space-between;
4931+ align-items: center;
4932+ background-color: var(--surface-1);
4933+ padding: 10px;
4934+ color: var(--text-1);
4935+ box-shadow: var(--shadow-3);
4936+ }
4937+
4938+ nav.subnav {
4939+ background-color: var(--surface-2);
4940+ color: var(--text-1);
4941+ box-shadow: var(--shadow-5);
4942+ }
4943+
4944+ nav .logo {
4945+ font-size: 20px;
4946+ font-weight: bold;
4947+ }
4948+
4949+ nav ul {
4950+ list-style-type: none;
4951+ margin: 0;
4952+ padding: 0;
4953+ display: flex;
4954+ }
4955+
4956+ nav ul li {
4957+ margin-left: 20px;
4958+ }
4959+
4960+ nav ul li a {
4961+ color: var(--link);
4962+ text-decoration: none;
4963+ }
4964+
4965+ .right {
4966+ float: right;
4967+ text-align: right;
4968+ }
4969+
4970+ span .right {
4971+ float: right;
4972+ text-align: right;
4973+ }
4974+
4975+ span.bold {
4976+ font-weight: var(--font-weight-7);
4977+ }
4978+
4979+ span.right {
4980+ float: right;
4981+ }
4982+
4983+ span.hint {
4984+ padding: 5px;
4985+ font-weight: bold;
4986+ text-decoration: underline;
4987+ }
4988+
4989+ .rss-links {
4990+ display: flex;
4991+ align-items: center;
4992+ }
4993+
4994+ .rss-links #emoji {
4995+ margin-right: 10px;
4996+ }
4997+
4998+ .rss-links p {
4999+ float: right;
5000+ }
5001+
5002+ .container {
5003+ max-width: 1800px;
5004+ margin: 0 auto;
5005+ padding: 20px;
5006+ display: flex;
5007+ flex-wrap: wrap;
5008+ }
5009+
5010+ .column {
5011+ flex: 1;
5012+ padding: 20px;
5013+ }
5014+
5015+ .row {
5016+ display: flex;
5017+ flex-wrap: wrap;
5018+ }
5019+
5020+ .repo-left {
5021+ flex: 3;
5022+ }
5023+
5024+ .repo-right {
5025+ flex: 1;
5026+ }
5027+
5028+ /* Media query for smaller screens */
5029+ @media (max-width: 768px) {
5030+ .column {
5031+ flex: none;
5032+ width: 100%;
5033+ }
5034+
5035+ .right {
5036+ order: 1;
5037+ }
5038+ }
5039+
5040+ p.commit-message {
5041+ font-size: var(--font-size-0);
5042+ font-weight: var(--font-weight-0);
5043+ }
5044+
5045+ header {
5046+ color: var(--text-1);
5047+ margin-bottom: 1em;
5048+ }
5049+
5050+ section.blame {
5051+ display: grid;
5052+ grid-template-columns: 1fr 5fr;
5053+ }
5054+
5055+ section.blame {
5056+ article {
5057+ margin-top: 0px !important;
5058+ }
5059+ }
5060+
5061+ article.blame-right {
5062+ padding: 0;
5063+ width: 100%;
5064+ }
5065+
5066+ article.blame-left {
5067+ padding: 0;
5068+ table tbody tr td {
5069+ text-align: left;
5070+ }
5071+ }
5072+
5073+ footer {
5074+ padding-top: 2em;
5075+ text-align: center;
5076+ font-size: smaller;
5077+ }
5078+
5079+ .blob-preview {
5080+ text-align: center;
5081+ border-style: solid;
5082+ border-color: pink;
5083+ margin-top: 2em;
5084+ padding: 1em;
5085+ }
5086+
5087+ .line-number {
5088+ user-select: none;
5089+ /* text-align: right;*/
5090+ width: .5em;
5091+ }
5092+
5093+ .line {
5094+ white-space: pre;
5095+ padding-left: 5px;
5096+ }
5097+
5098+ .positive {
5099+ color: var(--text-2);
5100+ text-shadow:
5101+ 0 0 10px var(--green-5),
5102+ 0 0 25px var(--green-7);
5103+ }
5104+
5105+ .negative {
5106+ color: var(--text-2);
5107+ text-shadow:
5108+ 0 0 10px var(--red-5),
5109+ 0 0 25px var(--red-7);
5110+ }
5111+
5112+ table.code {
5113+ font-family: var(--font-mono);
5114+ text-align: revert !important;
5115+ width: 100%;
5116+ margin-top: 1em;
5117+ }
5118+
5119+ .tree {
5120+ --tree-border-width: 1px;
5121+ --tree-border-color: var(--surface-2);
5122+ --tree-cell-pad: var(--size-1);
5123+ --tree-font: var(--font-mono);
5124+ --tree-text: var(--text-2);
5125+ border-collapse: collapse;
5126+ border: none;
5127+ background: none;
5128+ color: var(--tree-text); /* non-links get de-emphasized */
5129+ font-family: var(--tree-font);
5130+ }
5131+
5132+ .tree :is(td, th) {
5133+ padding: var(--tree-cell-pad);
5134+ border-block: var(--tree-border-width) solid var(--tree-border-color);
5135+ border-radius: 0;
5136+ text-align: start;
5137+ }
5138+
5139+ /* add classes for these? here is commit… and/or utility class */
5140+ /* this cuts off messages about where Git will in logs */
5141+ .tree :is(td, th):nth-child(2) {
5142+ overflow: hidden;
5143+ max-inline-size: 70ch;
5144+ white-space: nowrap;
5145+ text-overflow: ellipsis;
5146+ }
5147+
5148+ .tree :is(td, th):last-of-type {
5149+ text-align: end;
5150+ }
5151+
5152+ code.highlighted {
5153+ width: 100%;
5154+ overflow: scroll;
5155+ }
5156+
5157+ .code > tbody > tr > td {
5158+ text-align: revert;
5159+ padding: 0 0 0 0;
5160+ padding-right: .5em;
5161+ }
5162+
5163+ .yellow-badge {
5164+ padding-inline: var(--size-1);
5165+ border-width: var(--border-size-1);
5166+ border-color: var(--pink-6);
5167+ color: var(--pink-2);
5168+ border-radius: var(--radius-round);
5169+ }
5170+
5171+ #emoji {
5172+ max-width: 40px;
5173+ }
5174+
5175+ .card {
5176+ margin: 1em;
5177+ padding: 1em;
5178+ }
5179+
5180+ section.viewer {
5181+ margin-top: 5em;
5182+ text-align: center;
5183+ }
5184+
5185+ section.viewer svg {
5186+ display: inline-block;
5187+ margin: 0 auto;
5188+ }
5189+
5190+ section.readme {
5191+ margin-top: 2.5em;
5192+ }
5193+
5194+ .stretch {
5195+ width: 100%;
5196+ }
5197+
5198+ li.active {
5199+ color: var(--text-2);
5200+ text-shadow:
5201+ 0 0 10px var(--camo-5),
5202+ 0 0 25px var(--camo-7);
5203+ }
5204+
5205+ @media (--OSdark) {
5206+ html {
5207+ background: var(--stone-5);
5208+ color: var(--stone-7);
5209+ }
5210+ }
5211+
5212+ article.collection {
5213+ padding-top: 2em;
5214+ padding-bottom: 2em;
5215+ }
5216+
5217+ section.repositories {
5218+ padding-top: 1em;
5219+ }
5220+
5221+ section.repositories > * {
5222+ margin-top: 1em;
5223+ }
5224+
5225+ article.collection .description {
5226+ font-weight: var(--font-weight-1);
5227+ }
5228+
5229+ article.repository > .description {
5230+ font-weight: var(--font-weight-4);
5231+ margin-top: 5px;
5232+ }
5233+
5234+ .segmented-5 {
5235+ display: flex;
5236+ justify-content: space-between;
5237+ flex-wrap: wrap;
5238+ }
5239+
5240+ .segmented-5 > * {
5241+ flex-basis: 20%;
5242+ }
5243+
5244+ @media (width < 768px) {
5245+ .segmented-5 > * {
5246+ flex-basis: 70%;
5247+ }
5248+ }
5249+
5250+ @media (width < 480px) {
5251+ .segmented-5 > * {
5252+ flex-basis: 100%;
5253+ }
5254+ }
5255+
5256+ .segmented-4 {
5257+ display: flex;
5258+ justify-content: space-between;
5259+ flex-wrap: wrap;
5260+ }
5261+
5262+ .segmented-4 > * {
5263+ flex-basis: 25%;
5264+ }
5265+
5266+ @media (width < 768px) {
5267+ .segmented-4 > * {
5268+ flex-basis: 50%;
5269+ }
5270+ }
5271+
5272+ @media (width < 480px) {
5273+ .segmented-4 > * {
5274+ flex-basis: 100%;
5275+ }
5276+ }
5277+
5278+ .segmented-3 {
5279+ display: flex;
5280+ justify-content: space-between;
5281+ flex-wrap: wrap;
5282+ }
5283+
5284+ .segmented-3 > * {
5285+ flex-basis: 33%;
5286+ }
5287+
5288+ @media (width < 768px) {
5289+ .segmented-3 > * {
5290+ flex-basis: 100%;
5291+ }
5292+ }
5293+
5294+ @media (width < 768px) {
5295+ .collapse {
5296+ display: none;
5297+ }
5298+ }
5299+
5300+ .segmented-2 {
5301+ display: flex;
5302+ justify-content: space-between;
5303+ flex-wrap: wrap;
5304+ }
5305+
5306+ .segmented-2 > * {
5307+ flex-basis: 50%;
5308+ }
5309+
5310+ @media (width < 768px) {
5311+ .segmented-2 > * {
5312+ flex-basis: 100%;
5313+ }
5314+ }
5315+
5316+ @media (width < 768px) {
5317+ .collapse {
5318+ display: none;
5319+ }
5320+ }
5321+
5322+ .expand-xl {
5323+ display: none;
5324+ }
5325+
5326+ @media (width > 1500px) {
5327+ .expand-xl {
5328+ display: revert;
5329+ }
5330+ }
5331+
5332+ section.log {
5333+ }
5334+
5335+ article.log-entry {
5336+ box-shadow: var(--shadow-3);
5337+ overflow: auto;
5338+ scrollbar-width: 0;
5339+ margin: 1em;
5340+ }
5341+
5342+ .branch {
5343+ }
5344+
5345+ .message {
5346+ margin-top: 1em;
5347+ font-size: var(--font-size-0);
5348+ font-weight: var(--font-weight-0);
5349+ }
5350+
5351+ section.config-panel > form > button {
5352+ margin-top: 1em;
5353+ display: block;
5354+ }
5355+
5356+ img.rss-icon-feed {
5357+ height: 4em;
5358+ }
5359+
5360+
5361+ table.code {
5362+ background-color: var(--nord0);
5363+ }
5364+
5365+ tr .line {
5366+ background-color: var(--nord0);
5367+ }
5368+
5369+ tr .line-number {
5370+ background-color: var(--nord0);
5371+ }
5372+
5373+ /* base syntax highlighting */
5374+
5375+ span.ts_attribute {
5376+ color: var(--nord14);
5377+ }
5378+
5379+ span.ts_comment {
5380+ color: var(--nord3);
5381+ }
5382+
5383+ span.ts_constant {
5384+ color: var(--nord4);
5385+ }
5386+
5387+ span.ts_function_builtin {
5388+ color: var(--nord9);
5389+ }
5390+
5391+ span.ts_function {
5392+ color: var(--nord8);
5393+ }
5394+
5395+ span.ts_keyword {
5396+ color: var(--nord12);
5397+ }
5398+
5399+ span.ts_operator {
5400+ color: var(--nord9);
5401+ }
5402+
5403+ span.ts_property {
5404+ color: var(--nord11);
5405+ }
5406+
5407+ span.ts_punctuation {
5408+ color: var(--nord5);
5409+ }
5410+
5411+ span.ts_punctuation_bracket {
5412+ color: var(--nord9);
5413+ }
5414+
5415+ span.ts_punctuation_delimiter {
5416+ color: var(--nord10);
5417+ }
5418+
5419+ span.ts_punctuation_delimiter {
5420+ color: var(--nord10);
5421+ }
5422+
5423+ span.ts_string {
5424+ color: var(--nord-14);
5425+ }
5426+
5427+ span.ts_string_special {
5428+ color: var(--nord12);
5429+ }
5430+
5431+ span.ts_tag {
5432+ color: var(--nord7);
5433+ }
5434+
5435+ span.ts_type {
5436+ color: var(--nord7);
5437+ }
5438+
5439+ span.ts_type_builtin {
5440+ color: var(--nord13);
5441+ }
5442+
5443+ span.ts_variable {
5444+ color: var(--nord4);
5445+ }
5446+
5447+ span.ts_variable_builtin {
5448+ color: var(--nord5);
5449+ }
5450+
5451+ span.ts_variable_parameter {
5452+ color: var(--nord8);
5453+ }
5454 diff --git a/ayllu/themes/default/theme.scss b/ayllu/themes/default/theme.scss
5455deleted file mode 100644
5456index 3c4052d..0000000
5457--- a/ayllu/themes/default/theme.scss
5458+++ /dev/null
5459 @@ -1,83 +0,0 @@
5460- // monochrome
5461-
5462- // Navy-Grey
5463- $grey-hue: 205;
5464- $grey-50: hsl($grey-hue, 20%, 94%);
5465- $grey-100: hsl($grey-hue, 18%, 86%);
5466- $grey-200: hsl($grey-hue, 16%, 77%);
5467- $grey-300: hsl($grey-hue, 14%, 68%);
5468- $grey-400: hsl($grey-hue, 12%, 59%);
5469- $grey-500: hsl($grey-hue, 10%, 50%);
5470- $grey-600: hsl($grey-hue, 15%, 41%);
5471- $grey-700: hsl($grey-hue, 20%, 32%);
5472- $grey-800: hsl($grey-hue, 25%, 23%);
5473- $grey-900: hsl($grey-hue, 30%, 15%);
5474-
5475- // Light Blue
5476- $primary-hue: $grey-hue;
5477- $primary-50: hsl($grey-hue, 20%, 94%);
5478- $primary-100: hsl($grey-hue, 18%, 86%);
5479- $primary-200: hsl($grey-hue, 16%, 77%);
5480- $primary-300: hsl($grey-hue, 14%, 68%);
5481- $primary-400: hsl($grey-hue, 12%, 59%);
5482- $primary-500: hsl($grey-hue, 10%, 50%);
5483- $primary-600: hsl($grey-hue, 15%, 41%);
5484- $primary-700: hsl($grey-hue, 20%, 32%);
5485- $primary-800: hsl($grey-hue, 25%, 23%);
5486- $primary-900: hsl($grey-hue, 30%, 15%);
5487-
5488- // Black & White
5489- $black: #000;
5490- $white: #fff;
5491-
5492- // Amber
5493- $amber-50: hsl($grey-hue, 20%, 94%);
5494- $amber-100: hsl($grey-hue, 18%, 86%);
5495- $amber-200: hsl($grey-hue, 16%, 77%);
5496- $amber-300: hsl($grey-hue, 14%, 68%);
5497- $amber-400: hsl($grey-hue, 12%, 59%);
5498- $amber-500: hsl($grey-hue, 10%, 50%);
5499- $amber-600: hsl($grey-hue, 15%, 41%);
5500- $amber-700: hsl($grey-hue, 20%, 32%);
5501- $amber-800: hsl($grey-hue, 25%, 23%);
5502- $amber-900: hsl($grey-hue, 30%, 15%);
5503-
5504- // Green
5505- $green-50: hsl($grey-hue, 20%, 94%);
5506- $green-100: hsl($grey-hue, 18%, 86%);
5507- $green-200: hsl($grey-hue, 16%, 77%);
5508- $green-300: hsl($grey-hue, 14%, 68%);
5509- $green-400: hsl($grey-hue, 12%, 59%);
5510- $green-500: hsl($grey-hue, 10%, 50%);
5511- $green-600: hsl($grey-hue, 15%, 41%);
5512- $green-700: hsl($grey-hue, 20%, 32%);
5513- $green-800: hsl($grey-hue, 25%, 23%);
5514- $green-900: hsl($grey-hue, 30%, 15%);
5515-
5516- // Red
5517- $red-50: hsl($grey-hue, 20%, 94%);
5518- $red-100: hsl($grey-hue, 18%, 86%);
5519- $red-200: hsl($grey-hue, 16%, 77%);
5520- $red-300: hsl($grey-hue, 14%, 68%);
5521- $red-400: hsl($grey-hue, 12%, 59%);
5522- $red-500: hsl($grey-hue, 10%, 50%);
5523- $red-600: hsl($grey-hue, 15%, 41%);
5524- $red-700: hsl($grey-hue, 20%, 32%);
5525- $red-800: hsl($grey-hue, 25%, 23%);
5526- $red-900: hsl($grey-hue, 30%, 15%);
5527-
5528- $positive: $black;
5529- $negative: $black;
5530-
5531- $blame-border: $black;
5532-
5533- $highlighted: $black;
5534- $icon-background: $black;
5535-
5536- $chart-color: $black;
5537-
5538- // NOTE: colors need to be defined above since they're
5539- // read as variables in pico.
5540- @import "@picocss/pico/scss/pico";
5541- @import "layout.scss";
5542- @import "base.scss";
5543 diff --git a/ayllu/themes/tokyonight/templates/about.html b/ayllu/themes/tokyonight/templates/about.html
5544deleted file mode 100644
5545index e319b72..0000000
5546--- a/ayllu/themes/tokyonight/templates/about.html
5547+++ /dev/null
5548 @@ -1,9 +0,0 @@
5549- {% extends "base.html" %}
5550- {% block content %}
5551- <section>
5552- <article>
5553- <header></header>
5554- {{ blurb | safe }}
5555- </article>
5556- </section>
5557- {% endblock %}
5558 diff --git a/ayllu/themes/tokyonight/theme.scss b/ayllu/themes/tokyonight/theme.scss
5559deleted file mode 100644
5560index 97551d3..0000000
5561--- a/ayllu/themes/tokyonight/theme.scss
5562+++ /dev/null
5563 @@ -1,97 +0,0 @@
5564- // https://github.com/folke/tokyonight.nvim/blob/main/lua/tokyonight/colors.lua
5565- $bg_dark: #1e2030;
5566- $bg: #222436;
5567- $bg_highlight: #2f334d;
5568- $terminal_black: #444a73;
5569- $fg: #c8d3f5;
5570- $fg_dark: #828bb8;
5571- $fg_gutter: #3b4261;
5572- $dark3: #545c7e;
5573- $comment: #7a88cf;
5574- $dark5: #737aa2;
5575- $blue0: #3e68d7;
5576- $blue: #82aaff;
5577- $cyan: #86e1fc;
5578- $blue1: #65bcff;
5579- $blue2: #0db9d7;
5580- $blue5: #89ddff;
5581- $blue6: #b4f9f8;
5582- $blue7: #394b70;
5583- $purple: #fca7ea;
5584- $magenta2: #ff007c;
5585- $magenta: #c099ff;
5586- $orange: #ff966c;
5587- $yellow: #ffc777;
5588- $green: #c3e88d;
5589- $green1: #4fd6be;
5590- $green2: #41a6b5;
5591- $teal: #4fd6be;
5592- $red: #ff757f;
5593- $red1: #c53b53;
5594-
5595- //
5596- //
5597- // tree sitter highlighting
5598- //
5599- //
5600-
5601- span.ts_attribute {color: $teal};
5602- span.ts_constant {color: $orange};
5603- span.ts_function.builtin {color: $blue};
5604- span.ts_function {color: $magenta}
5605- span.ts_keyword {color: $purple};
5606- span.ts_operator {color: $blue5};
5607- span.ts_property {color: $green1};
5608- span.ts_punctuation {color: $blue};
5609- span.ts_punctuation.bracket {color: $fg_dark};
5610- span.ts_punctuation.delimiter {color: $blue5};
5611- span.ts_string {color: $green};
5612- span.ts_string.special {color: $blue}
5613- span.ts_tag {color: $blue};
5614- span.ts_type {color: $blue1};
5615- span.ts_type.builtin {color: $blue1};
5616- span.ts_variable {color: $fg};
5617- span.ts_variable.builtin {color: $red};
5618- span.ts_variable.parameter {color: $yellow}
5619-
5620- @media (prefers-color-scheme: light) {
5621- span.ts_attribute {color: darken($teal, 40%)};
5622- span.ts_constant {color: darken($orange, 40%)};
5623- span.ts_function.builtin {color: darken($blue, 40%)};
5624- span.ts_function {color: darken($magenta, 20%)}
5625- span.ts_keyword {color: darken($purple, 40%)};
5626- span.ts_operator {color: darken($blue5, 40%)};
5627- span.ts_property {color: darken($green1, 40%)};
5628- span.ts_punctuation {color: darken($blue, 40%)};
5629- span.ts_punctuation.bracket {color: $fg};
5630- span.ts_punctuation.delimiter {color: darken($blue5, 40%)};
5631- span.ts_string {color: darken($green, 40%)};
5632- span.ts_string.special {color: darken($blue, 40%)}
5633- span.ts_tag {color: darken($blue, 40%)};
5634- span.ts_type {color: darken($blue1, 40%)};
5635- span.ts_type.builtin {color: darken($blue1, 40%)};
5636- span.ts_variable {color: darken($fg, 40%)};
5637- span.ts_variable.builtin {color: darken($red, 40%)};
5638- span.ts_variable.parameter {color: darken($yellow, 40%)}
5639- }
5640-
5641-
5642- @media (prefers-color-scheme: dark) {
5643- span.label {
5644- color: $fg_dark;
5645- }
5646- }
5647-
5648- $positive: $green;
5649- $negative: $red;
5650-
5651- $blame-border: $purple;
5652-
5653- $highlighted: $purple;
5654- $icon-background: $purple;
5655-
5656- $chart-color: $purple;
5657-
5658- @import "@picocss/pico/scss/pico";
5659- @import "../default/layout.scss";
5660- @import "../default/base.scss";
5661 diff --git a/config.example.toml b/config.example.toml
5662index 1a9f152..4afc1cd 100644
5663--- a/config.example.toml
5664+++ b/config.example.toml
5665 @@ -97,13 +97,15 @@ links = [
5666 address = "localhost:8080"
5667
5668 [web]
5669- # path to the directory containing all themes system themes, at least one theme,
5670- # typically "default" must be present in order for the service to function.
5671- # themes_path = "content"
5672 # default theme that will be used when none is specified in user configuration.
5673 # additionally the default theme will be used to load assets that are not
5674 # overriden in the actively selected theme.
5675 # default_theme = "default"
5676+ # [[web.themes]]
5677+ # unique name to identify the theme in the web UI
5678+ # name = "my-theme"
5679+ # path to the directory containing the theme assets
5680+ # path = "/usr/lib/ayllu/themes/my-theme"
5681
5682 # static hosting
5683 # see an example Nginx configuration in contrib/nginx/nginx.conf
5684 @@ -132,6 +134,7 @@ migrate = true
5685 # exposed as CSS classes which can be used to create themes.
5686 keywords = [
5687 "attribute",
5688+ "comment",
5689 "constant",
5690 "function.builtin",
5691 "function",
5692 diff --git a/contrib/example-theme/README.md b/contrib/example-theme/README.md
5693new file mode 100644
5694index 0000000..98a5092
5695--- /dev/null
5696+++ b/contrib/example-theme/README.md
5697 @@ -0,0 +1,12 @@
5698+ # example-theme
5699+
5700+ Example theme that is loaded from the file system.
5701+
5702+ ## File Structure
5703+
5704+ theme.css # extends the base css file in default/theme.css
5705+ templates/ # override any templates in default/*.html
5706+ templates/about.html
5707+ assets/ # static assets served
5708+ assets/logo.svg # override any base assets in default/assets/*
5709+ assets/extra-file.png # additional static assets
5710 diff --git a/contrib/example-theme/assets/logo.svg b/contrib/example-theme/assets/logo.svg
5711new file mode 100644
5712index 0000000..51596eb
5713--- /dev/null
5714+++ b/contrib/example-theme/assets/logo.svg
5715 @@ -0,0 +1,12 @@
5716+ <svg id="emoji" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
5717+ <g id="color">
5718+ <path fill="#FCEA2B" stroke="none" d="M7.3634,42.4095c4.5525,6.1703,11.874,10.1726,20.1303,10.1726c13.8071,0,25-11.1929,25-25 c0-8.5226-4.2646-16.0492-10.7763-20.5621c13.0383,2.8385,22.7812,14.4426,22.7812,28.3317c0,16.0163-12.9837,29-29,29 C21.9109,64.3517,10.5097,55.0229,7.3634,42.4095z"/>
5719+ <path fill="#F1B31C" stroke="none" d="M45.8373,9.2108c8.25,4.25,16.1946,11.8724,16.1946,24.6742c0,15.4494-12.5242,27.9735-27.9735,27.9735 c-9.2431,0-19.7524-4.8353-24.294-15.5436c0,0,4.3805,18.6568,25.7189,18.665c19.327,0.0074,28.0419-20.6218,28.0419-20.6218 C70.033,12.3815,45.8373,9.2108,45.8373,9.2108z"/>
5720+ </g>
5721+ <g id="hair"/>
5722+ <g id="skin"/>
5723+ <g id="skin-shadow"/>
5724+ <g id="line">
5725+ <path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M7.3634,42.4095c4.5525,6.1703,11.874,10.1726,20.1303,10.1726c13.8071,0,25-11.1929,25-25 c0-8.5226-4.2646-16.0492-10.7763-20.5621c13.0383,2.8385,22.7812,14.4426,22.7812,28.3317c0,16.0163-12.9837,29-29,29 C21.9109,64.3517,10.5097,55.0229,7.3634,42.4095z"/>
5726+ </g>
5727+ </svg>
5728 diff --git a/contrib/example-theme/templates/about.html b/contrib/example-theme/templates/about.html
5729new file mode 100644
5730index 0000000..d1302bc
5731--- /dev/null
5732+++ b/contrib/example-theme/templates/about.html
5733 @@ -0,0 +1,2 @@
5734+ {% extends "base.html" %}
5735+ {% block content %}<h1>Custom About Page</h1>{% endblock %}
5736 diff --git a/contrib/example-theme/theme.css b/contrib/example-theme/theme.css
5737new file mode 100644
5738index 0000000..5f9c70c
5739--- /dev/null
5740+++ b/contrib/example-theme/theme.css
5741 @@ -0,0 +1,4 @@
5742+ /* An Example Theme */
5743+ div.logo > svg {
5744+ min-width: 3em;
5745+ }
5746 diff --git a/package.json b/package.json
5747index b6118fb..b89e131 100644
5748--- a/package.json
5749+++ b/package.json
5750 @@ -4,11 +4,11 @@
5751 "main": "index.js",
5752 "repository": {
5753 "type": "git",
5754- "url": "https://src.kevinschoon.com/projects/ayllu"
5755+ "url": "https://ayllu-forge.org/projects/ayllu"
5756 },
5757 "author": "",
5758 "license": "AGPL-3.0",
5759 "dependencies": {
5760- "@picocss/pico": "^1.5.10"
5761+ "open-props": "^1.7.4"
5762 }
5763 }
5764 diff --git a/scripts/check_build_dependencies.sh b/scripts/check_build_dependencies.sh
5765index cfb4893..e6c40da 100755
5766--- a/scripts/check_build_dependencies.sh
5767+++ b/scripts/check_build_dependencies.sh
5768 @@ -31,6 +31,7 @@ check_cmd "sassc"
5769 check_cmd "sqlx"
5770 check_cmd "sqlite3"
5771 check_cmd "npm"
5772+ check_cmd "djlint" # format / lint jinja html templates
5773
5774 # check for openssl which annoyingly is required to build sqlx-cli if that is
5775 # being installed from a non-distribution package i.e. cargo install
5776 diff --git a/scripts/compile_stylesheets.sh b/scripts/compile_stylesheets.sh
5777index aae7402..1a24852 100755
5778--- a/scripts/compile_stylesheets.sh
5779+++ b/scripts/compile_stylesheets.sh
5780 @@ -1,8 +1 @@
5781 #!/bin/sh
5782-
5783- files=$(find ayllu/themes -maxdepth 2 -name 'theme.scss')
5784-
5785- for theme_path in ${files} ; do
5786- sassc --style compressed -I node_modules \
5787- "${theme_path}" "$(dirname "${theme_path}")/main.min.css";
5788- done
5789 diff --git a/scripts/format_all_templates.sh b/scripts/format_all_templates.sh
5790new file mode 100755
5791index 0000000..1ef2506
5792--- /dev/null
5793+++ b/scripts/format_all_templates.sh
5794 @@ -0,0 +1,3 @@
5795+ #!/bin/sh
5796+ #
5797+ find . -name '*.html' -not -path './www/public/*' -exec djlint --reformat {} \;
5798 diff --git a/scripts/lint_all_templates.sh b/scripts/lint_all_templates.sh
5799new file mode 100755
5800index 0000000..74d6c22
5801--- /dev/null
5802+++ b/scripts/lint_all_templates.sh
5803 @@ -0,0 +1,3 @@
5804+ #!/bin/sh
5805+ #
5806+ find . -name '*.html' -not -path './www/public/*' -exec djlint {} \;
5807 diff --git a/www/content/_index.md b/www/content/_index.md
5808index e2f4e5a..532857f 100644
5809--- a/www/content/_index.md
5810+++ b/www/content/_index.md
5811 @@ -3,61 +3,53 @@ title = "index"
5812 template = "index.html"
5813 +++
5814
5815- <div class="screenshot">
5816- <a href="https://ayllu-forge.org/projects/ayllu">
5817- <img class="screenshot night" src="/assets/images/ui.png" /></a>
5818- <img class="screenshot day" src="/assets/images/ui-day.png" /></a>
5819- </div>
5820-
5821- # Welcome to Ayllu!
5822-
5823- #### A Hyper Performant & Hackable Code Forge Built on Open Standards
5824+ # Hyper Performant & Hackable Code Forge Built on Open Standards
5825
5826 Ayllu is a lightweight code forge designed to enable individuals and
5827 community projects develop software in collaboration across open internet
5828 standards.
5829
5830- ##### General Purpose Git UI
5831+ ## General Purpose Git UI
5832
5833 General purpose web based UI for git.
5834 Browse hosted projects, view contributors, source code, blame, releases, etc.
5835
5836- ##### Source Code Analysis
5837+ ## Source Code Analysis
5838
5839 Super accurate syntax highlighting and code composition insight across project
5840 history.
5841
5842- ##### Incremental Jobs
5843+ ## Incremental Jobs
5844
5845 A built-in configurable incremental jobs system controlled via Git hooks and/or
5846 cron to extend forge functionality.
5847
5848- ##### Extensible Plugin System
5849+ ## Extensible Plugin System
5850
5851 An extensible RPC based plugin system allows adding large external integrations.
5852
5853- ##### Completely Themeable
5854+ ## Completely Themeable
5855
5856 The web interface is fully customizable, a few themes
5857 [already](https://ayllu-forge.org/config) exist and more are on the way.
5858
5859- ##### Web 1.5
5860+ ## Web 1.5
5861
5862 Super fast web frontend with first class support for RSS based project
5863 subscriptions. Ayllu loads faster than any other code forge in existence.
5864 If it doesn't, it's a bug!
5865
5866- ##### Static Hosting
5867+ ## Static Hosting
5868
5869 Serve static websites directly from a git repository making project sites,
5870 documentation, or other assets simple to self-host.
5871
5872- ##### Mailing List Management
5873+ ## Mailing List Management
5874
5875 Full featured email based software development workflows and mailing list
5876 management based on [Mailpot](https://git.meli-email.org/meli/mailpot) 🍯.
5877
5878- ##### Much More
5879+ ## Much More
5880
5881 New features planned such as continuous integration, mailing list support,
5882 external API, federation, and more.
5883 diff --git a/www/content/docs/architecture.md b/www/content/docs/architecture.md
5884index 8dd6597..27b98ec 100644
5885--- a/www/content/docs/architecture.md
5886+++ b/www/content/docs/architecture.md
5887 @@ -4,6 +4,8 @@ slug = "architecture"
5888 weight = 0
5889 +++
5890
5891+ # The Architecture of Ayllu
5892+
5893 **Ayllu is a new software project and it's design is evolving.**
5894
5895 It's basic architecture is that of a single binary providing a web interface to
5896 diff --git a/www/content/docs/themes.md b/www/content/docs/themes.md
5897new file mode 100644
5898index 0000000..3cf71f7
5899--- /dev/null
5900+++ b/www/content/docs/themes.md
5901 @@ -0,0 +1,61 @@
5902+ +++
5903+ title = "Themes"
5904+ weight = 1
5905+ +++
5906+
5907+ # Themeing Ayllu
5908+
5909+ Ayllu can be customized in a variety of ways including custom CSS, modifying
5910+ Jinja based HTML templates, or by adding new static assets such as images.
5911+ Available themes can be viewed and enabled from the `/config` page.
5912+
5913+ ## The Default Theme
5914+
5915+ The default theme is located in `ayllu/themes/default` and is served as a base
5916+ and all themes serve content from this theme if they do not override it's
5917+ assets. It is possible to change the default theme by setting it in your Ayllu
5918+ configuration file.
5919+
5920+ [web]
5921+ default_theme = "my-custom-theme"
5922+
5923+ By changing the default theme all additionally loaded themes will inherit the
5924+ templates and assets of the default theme.
5925+
5926+ ## Built-in vs External Themes
5927+
5928+ ### Built-in Themes
5929+
5930+ Built-in themes are compiled into the Ayllu binary and available for selection
5931+ from the config section of the web interface. These themes are available at
5932+ `ayllu/themes/`. These themes use [lightning css](https://lightningcss.dev/)
5933+ at build time to build the theme's `main.min.css` file.
5934+
5935+ ### External Themes
5936+
5937+ Additional themes can be loaded directly from the file system and allow the
5938+ addition of new themes without recompiling Ayllu.
5939+
5940+ [web]
5941+
5942+ default_theme = "default"
5943+
5944+ [[web.theme]]
5945+
5946+ name = "my-custom-theme"
5947+ # path to a directory of theme templates, assets, and stylesheets
5948+ path = "/var/lib/ayllu/themes/my-custom-theme"
5949+
5950+ ### File System Layout
5951+
5952+ All files are optional in themes, an empty directory is considered a valid
5953+ theme and will result in an identical configuration to the `default_theme`.
5954+
5955+ assets/ # SVG and other static assets served from /assets
5956+ assets/logo.svg # main logo in the left hand nav bar
5957+ assets/feed.xsl # XSL file used to style RSS feeds
5958+ assets/*.svg # additional assets for various parts of the UI
5959+ templates/ # directory of Jinja templates
5960+ theme.css # additional stylesheet to extend the default
5961+
5962+ See `contrib/example-theme` for a functional custom theme to extend as a base.
5963 diff --git a/www/public/docs/architecture/index.html b/www/public/docs/architecture/index.html
5964index bfeb01a..02dfd41 100644
5965--- a/www/public/docs/architecture/index.html
5966+++ b/www/public/docs/architecture/index.html
5967 @@ -1 +1 @@
5968- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li class=active><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Architecture</h1><p><strong>Ayllu is a new software project and it's design is evolving.</strong><p>It's basic architecture is that of a single binary providing a web interface to browse and interact with Git repositories as well as an RPC server based on <a href=https://github.com/google/tarpc>tarpc</a> with an embedded client. The RPC server running in the core Ayllu binary is called the <code>job_server</code> and it provides an interface to interact with Ayllu for administrators. Much of the functionality of Ayllu is implemented via <code>RPC extensions</code> which are minimal binaries that are used to extend the core functionality of Ayllu.<p>The current architecture of Ayllu is depicted below:<p><a href=/assets/architecture.svg><img class=diagram src=/assets/architecture.svg></a></div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
5969\ No newline at end of file
5970+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li class=active><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=the-architecture-of-ayllu><a aria-label="Anchor link for: the-architecture-of-ayllu" class=zola-anchor href=#the-architecture-of-ayllu>The Architecture of Ayllu</a></h1><p><strong>Ayllu is a new software project and it's design is evolving.</strong><p>It's basic architecture is that of a single binary providing a web interface to browse and interact with Git repositories as well as an RPC server based on <a href=https://github.com/google/tarpc>tarpc</a> with an embedded client. The RPC server running in the core Ayllu binary is called the <code>job_server</code> and it provides an interface to interact with Ayllu for administrators. Much of the functionality of Ayllu is implemented via <code>RPC extensions</code> which are minimal binaries that are used to extend the core functionality of Ayllu.<p>The current architecture of Ayllu is depicted below:<p><a href=/assets/architecture.svg><img class=diagram src=/assets/architecture.svg></a></div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
5971\ No newline at end of file
5972 diff --git a/www/public/docs/builds/index.html b/www/public/docs/builds/index.html
5973index 0b018c2..fe9cfe9 100644
5974--- a/www/public/docs/builds/index.html
5975+++ b/www/public/docs/builds/index.html
5976 @@ -1,4 +1,4 @@
5977- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li class=active><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Builds</h1><p>Ayllu contains a plugin called <code>ayllu-build</code> which is responsible for executing build manifests in a CI/CD environment. The design of the build system prioritizes the ability to run the entire workflow locally.<h3 id=an-example-local-workflow><a aria-label="Anchor link for: an-example-local-workflow" class=zola-anchor href=#an-example-local-workflow>An Example Local Workflow</a></h3><p>Build manifests are written in <a href=https://nickel-lang.org/>Nickel</a>.<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>mkdir -p my-test-repo/.ayllu/build
5978+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li class=active><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><p>Ayllu contains a plugin called <code>ayllu-build</code> which is responsible for executing build manifests in a CI/CD environment. The design of the build system prioritizes the ability to run the entire workflow locally.<h3 id=an-example-local-workflow><a aria-label="Anchor link for: an-example-local-workflow" class=zola-anchor href=#an-example-local-workflow>An Example Local Workflow</a></h3><p>Build manifests are written in <a href=https://nickel-lang.org/>Nickel</a>.<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>mkdir -p my-test-repo/.ayllu/build
5979 cd my-test-repo && git init
5980 # create a simple workflow that prints "Hello" and "World"
5981 cat <&LTEOF > .ayllu/build/main.ncl
5982 diff --git a/www/public/docs/configuration/index.html b/www/public/docs/configuration/index.html
5983index 21d0927..af98fd8 100644
5984--- a/www/public/docs/configuration/index.html
5985+++ b/www/public/docs/configuration/index.html
5986 @@ -1,4 +1,4 @@
5987- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li class=active><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Configuration</h1><h1 id=initial-configuration><a aria-label="Anchor link for: initial-configuration" class=zola-anchor href=#initial-configuration>Initial Configuration</a></h1><p>After following the <a href=https://ayllu-forge.org/docs/installation>installation</a> you need to setup your configuration file and initialize the database.<h2 id=user-based-installation><a aria-label="Anchor link for: user-based-installation" class=zola-anchor href=#user-based-installation>User Based Installation</a></h2><pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>mkdir ~/.config/ayllu
5988+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li class=active><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=initial-configuration><a aria-label="Anchor link for: initial-configuration" class=zola-anchor href=#initial-configuration>Initial Configuration</a></h1><p>After following the <a href=https://ayllu-forge.org/docs/installation>installation</a> you need to setup your configuration file and initialize the database.<h2 id=user-based-installation><a aria-label="Anchor link for: user-based-installation" class=zola-anchor href=#user-based-installation>User Based Installation</a></h2><pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>mkdir ~/.config/ayllu
5989 ayllu config generate > ~/.config/ayllu/config.yaml
5990 # open up config.yaml in your favorite editor and configure at least one
5991 # collection
5992 diff --git a/www/public/docs/developers/index.html b/www/public/docs/developers/index.html
5993index 15292cb..9c8d943 100644
5994--- a/www/public/docs/developers/index.html
5995+++ b/www/public/docs/developers/index.html
5996 @@ -1,4 +1,4 @@
5997- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li class=active><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Developers</h1><h1 id=developing-components><a aria-label="Anchor link for: developing-components" class=zola-anchor href=#developing-components>Developing Components</a></h1><p>Ayllu is split up into several distinct components, to work on one of them you typically want to start one of two feedback cycles: <code>lint->test->compile</code> or <code>compile->serve</code>. To start each one you can can run <code>scripts/test.sh $COMPONENT</code> or <code>scripts/watch.sh $COMPONENT</code> and the appropriate <code>cargo watch</code> command will be started for you.<h1 id=testing-the-entire-ci-workflow><a aria-label="Anchor link for: testing-the-entire-ci-workflow" class=zola-anchor href=#testing-the-entire-ci-workflow>Testing the Entire CI Workflow</a></h1><p>You can run all of the CI tests locally by invoking <code>ayllu-build</code> directly, assuming it is compiled it can be launched like this:<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh># run all tests in .ayllu
5998+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li class=active><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=developing-components><a aria-label="Anchor link for: developing-components" class=zola-anchor href=#developing-components>Developing Components</a></h1><p>Ayllu is split up into several distinct components, to work on one of them you typically want to start one of two feedback cycles: <code>lint->test->compile</code> or <code>compile->serve</code>. To start each one you can can run <code>scripts/test.sh $COMPONENT</code> or <code>scripts/watch.sh $COMPONENT</code> and the appropriate <code>cargo watch</code> command will be started for you.<h1 id=testing-the-entire-ci-workflow><a aria-label="Anchor link for: testing-the-entire-ci-workflow" class=zola-anchor href=#testing-the-entire-ci-workflow>Testing the Entire CI Workflow</a></h1><p>You can run all of the CI tests locally by invoking <code>ayllu-build</code> directly, assuming it is compiled it can be launched like this:<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh># run all tests in .ayllu
5999 ayllu-build evaluate
6000 </code></pre><h1 id=ayllu-forge-org><a aria-label="Anchor link for: ayllu-forge-org" class=zola-anchor href=#ayllu-forge-org>ayllu-forge.org</a></h1><p>The <a href=https://ayllu-forge.org>Ayllu website</a> and project documentation are built together with <a href=https://www.getzola.org/>Zola</a> which you must install. After which you can startup a local preview of the site with the following commands:<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>cd www && zola serve
6001 </code></pre><p>The generated content are hosted directly from Ayllu and you simply need to build and commit the generated code directly into the repository.<pre class=language-sh data-lang=sh><code class=language-sh data-lang=sh>cd www && zola build
6002 diff --git a/www/public/docs/faq/index.html b/www/public/docs/faq/index.html
6003index e5f1e73..03d2494 100644
6004--- a/www/public/docs/faq/index.html
6005+++ b/www/public/docs/faq/index.html
6006 @@ -1 +1 @@
6007- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li class=active><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>FAQ</h1><h5 id=what-does-ayllu-mean><a aria-label="Anchor link for: what-does-ayllu-mean" class=zola-anchor href=#what-does-ayllu-mean>What Does "Ayllu" Mean?</a></h5><p>The name <a href=https://en.wikipedia.org/wiki/Ayllu>Ayllu</a> <em>/ˈajʎu/</em>, <em>eye-joo</em> is the Quechua word for the traditional form of a community in the Andes region of South America, particularly in Bolivia and Peru.<h5 id=do-you-provide-hosting><a aria-label="Anchor link for: do-you-provide-hosting" class=zola-anchor href=#do-you-provide-hosting>Do You Provide Hosting?</a></h5><p>Not currently. If there was enough interest the project might offer hosted environments as a way to sustain it's development.</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6008\ No newline at end of file
6009+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li class=active><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h5 id=what-does-ayllu-mean><a aria-label="Anchor link for: what-does-ayllu-mean" class=zola-anchor href=#what-does-ayllu-mean>What Does "Ayllu" Mean?</a></h5><p>The name <a href=https://en.wikipedia.org/wiki/Ayllu>Ayllu</a> <em>/ˈajʎu/</em>, <em>eye-joo</em> is the Quechua word for the traditional form of a community in the Andes region of South America, particularly in Bolivia and Peru.<h5 id=do-you-provide-hosting><a aria-label="Anchor link for: do-you-provide-hosting" class=zola-anchor href=#do-you-provide-hosting>Do You Provide Hosting?</a></h5><p>Not currently. If there was enough interest the project might offer hosted environments as a way to sustain it's development.</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6010\ No newline at end of file
6011 diff --git a/www/public/docs/index.html b/www/public/docs/index.html
6012index f116940..9b4ac14 100644
6013--- a/www/public/docs/index.html
6014+++ b/www/public/docs/index.html
6015 @@ -1 +1 @@
6016- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1 id=documentation><a aria-label="Anchor link for: documentation" class=zola-anchor href=#documentation>Documentation</a></h1><p>Welcome to the Ayllu documentation 📖, this is section serves as an authoritative reference on all things related to the Ayllu code forge!</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6017\ No newline at end of file
6018+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=documentation><a aria-label="Anchor link for: documentation" class=zola-anchor href=#documentation>Documentation</a></h1><p>Welcome to the Ayllu documentation 📖, this is section serves as an authoritative reference on all things related to the Ayllu code forge!</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6019\ No newline at end of file
6020 diff --git a/www/public/docs/installation/index.html b/www/public/docs/installation/index.html
6021index 79e62cd..7b1f4a7 100644
6022--- a/www/public/docs/installation/index.html
6023+++ b/www/public/docs/installation/index.html
6024 @@ -1,4 +1,4 @@
6025- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li class=active><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Installation</h1><h1 id=running-as-a-container><a aria-label="Anchor link for: running-as-a-container" class=zola-anchor href=#running-as-a-container>Running as a Container</a></h1><p>The quickest way to get started running Ayllu is by using a container. <a href=https://podman.io/>Podman</a> is the only supported container platform although Docker may work as well.<p>There are several different configurations that can be used to run Ayllu as a container.<ul><li><a href=https://ayllu-forge.org/docs/installation/#rootless-container-with-ayllu-running-as-root>Rootless with Ayllu running as root within the container</a><li><a href=https://ayllu-forge.org/docs/installation/#rootless-with-a-non-root-user-within-a-container>Rootless with a non-root user within the container</a><li><a href=https://ayllu-forge.org/docs/installation/#as-root-with-ayllu-running-as-a-non-root-user>As root with Ayllu running as a non-root user</a></ul><h2 id=rootless-container-with-ayllu-running-as-root><a aria-label="Anchor link for: rootless-container-with-ayllu-running-as-root" class=zola-anchor href=#rootless-container-with-ayllu-running-as-root>Rootless Container with Ayllu Running as Root</a></h2><p>This configuration works best if you want Ayllu to have access to your repositories for browsing your code locally. The tutorial assumes that you have permissions to access all of your code repositories.Note that the commands below use a separate configuration and data paths to avoid conflicting with the default paths if the Ayllu binary is installed directly on your system.<p>First create configuration and data paths that we will map into the container.<pre><code>mkdir ~/.config/ayllu-podman
6026+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li class=active><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=running-as-a-container><a aria-label="Anchor link for: running-as-a-container" class=zola-anchor href=#running-as-a-container>Running as a Container</a></h1><p>The quickest way to get started running Ayllu is by using a container. <a href=https://podman.io/>Podman</a> is the only supported container platform although Docker may work as well.<p>There are several different configurations that can be used to run Ayllu as a container.<ul><li><a href=https://ayllu-forge.org/docs/installation/#rootless-container-with-ayllu-running-as-root>Rootless with Ayllu running as root within the container</a><li><a href=https://ayllu-forge.org/docs/installation/#rootless-with-a-non-root-user-within-a-container>Rootless with a non-root user within the container</a><li><a href=https://ayllu-forge.org/docs/installation/#as-root-with-ayllu-running-as-a-non-root-user>As root with Ayllu running as a non-root user</a></ul><h2 id=rootless-container-with-ayllu-running-as-root><a aria-label="Anchor link for: rootless-container-with-ayllu-running-as-root" class=zola-anchor href=#rootless-container-with-ayllu-running-as-root>Rootless Container with Ayllu Running as Root</a></h2><p>This configuration works best if you want Ayllu to have access to your repositories for browsing your code locally. The tutorial assumes that you have permissions to access all of your code repositories.Note that the commands below use a separate configuration and data paths to avoid conflicting with the default paths if the Ayllu binary is installed directly on your system.<p>First create configuration and data paths that we will map into the container.<pre><code>mkdir ~/.config/ayllu-podman
6027 mkdir ~/.local/share/ayllu-podman
6028 </code></pre><p>Next pull the latest version of the container and generate a new configuration file.<pre><code>podman pull registry.ayllu-forge.org/projects/ayllu:main
6029 podman run --rm -ti registry.ayllu-forge.org/projects/ayllu:main ayllu config generate > ~/.config/ayllu-podman/config.toml
6030 diff --git a/www/public/docs/mail/index.html b/www/public/docs/mail/index.html
6031index b8afd24..7eac75c 100644
6032--- a/www/public/docs/mail/index.html
6033+++ b/www/public/docs/mail/index.html
6034 @@ -1 +1 @@
6035- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li class=active><a href=https://ayllu-forge.org/docs/mail/>Mail</a></ul></div><div class=doc-content><h1>Mail</h1><h1 id=mailing-list-support><a aria-label="Anchor link for: mailing-list-support" class=zola-anchor href=#mailing-list-support>Mailing List Support</a></h1><p>Ayllu has full featured support for email based development workflows. It is based on the excellent <a href=https://git.meli-email.org/meli/mailpot>mailpot</a> mailing list manager.</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6036\ No newline at end of file
6037+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li class=active><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=mailing-list-support><a aria-label="Anchor link for: mailing-list-support" class=zola-anchor href=#mailing-list-support>Mailing List Support</a></h1><p>Ayllu has full featured support for email based development workflows. It is based on the excellent <a href=https://git.meli-email.org/meli/mailpot>mailpot</a> mailing list manager.</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6038\ No newline at end of file
6039 diff --git a/www/public/docs/themes/index.html b/www/public/docs/themes/index.html
6040new file mode 100644
6041index 0000000..344aae8
6042--- /dev/null
6043+++ b/www/public/docs/themes/index.html
6044 @@ -0,0 +1,18 @@
6045+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=documentation><div class=side-panel><ul><li><a href=https://ayllu-forge.org/docs/architecture/>Architecture</a><li><a href=https://ayllu-forge.org/docs/builds/>Builds</a><li><a href=https://ayllu-forge.org/docs/configuration/>Configuration</a><li><a href=https://ayllu-forge.org/docs/developers/>Developers</a><li><a href=https://ayllu-forge.org/docs/installation/>Installation</a><li><a href=https://ayllu-forge.org/docs/faq/>FAQ</a><li><a href=https://ayllu-forge.org/docs/mail/>Mail</a><li class=active><a href=https://ayllu-forge.org/docs/themes/>Themes</a></ul></div><div class=doc-content><h1 id=themeing-ayllu><a aria-label="Anchor link for: themeing-ayllu" class=zola-anchor href=#themeing-ayllu>Themeing Ayllu</a></h1><p>Ayllu can be customized in a variety of ways including custom CSS, modifying Jinja based HTML templates, or by adding new static assets such as images. Available themes can be viewed and enabled from the <code>/config</code> page.<h2 id=the-default-theme><a aria-label="Anchor link for: the-default-theme" class=zola-anchor href=#the-default-theme>The Default Theme</a></h2><p>The default theme is located in <code>ayllu/themes/default</code> and is served as a base and all themes serve content from this theme if they do not override it's assets. It is possible to change the default theme by setting it in your Ayllu configuration file.<pre><code>[web]
6046+ default_theme = "my-custom-theme"
6047+ </code></pre><p>By changing the default theme all additionally loaded themes will inherit the templates and assets of the default theme.<h2 id=built-in-vs-external-themes><a aria-label="Anchor link for: built-in-vs-external-themes" class=zola-anchor href=#built-in-vs-external-themes>Built-in vs External Themes</a></h2><h3 id=built-in-themes><a aria-label="Anchor link for: built-in-themes" class=zola-anchor href=#built-in-themes>Built-in Themes</a></h3><p>Built-in themes are compiled into the Ayllu binary and available for selection from the config section of the web interface. These themes are available at <code>ayllu/themes/</code>. These themes use <a href=https://lightningcss.dev/>lightning css</a> at build time to build the theme's <code>main.min.css</code> file.<h3 id=external-themes><a aria-label="Anchor link for: external-themes" class=zola-anchor href=#external-themes>External Themes</a></h3><p>Additional themes can be loaded directly from the file system and allow the addition of new themes without recompiling Ayllu.<pre><code>[web]
6048+
6049+ default_theme = "default"
6050+
6051+ [[web.theme]]
6052+
6053+ name = "my-custom-theme"
6054+ # path to a directory of theme templates, assets, and stylesheets
6055+ path = "/var/lib/ayllu/themes/my-custom-theme"
6056+ </code></pre><h3 id=file-system-layout><a aria-label="Anchor link for: file-system-layout" class=zola-anchor href=#file-system-layout>File System Layout</a></h3><p>All files are optional in themes, an empty directory is considered a valid theme and will result in an identical configuration to the <code>default_theme</code>.<pre><code>assets/ # SVG and other static assets served from /assets
6057+ assets/logo.svg # main logo in the left hand nav bar
6058+ assets/feed.xsl # XSL file used to style RSS feeds
6059+ assets/*.svg # additional assets for various parts of the UI
6060+ templates/ # directory of Jinja templates
6061+ theme.css # additional stylesheet to extend the default
6062+ </code></pre><p>See <code>contrib/example-theme</code> for a functional custom theme to extend as a base.</div></div></main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6063\ No newline at end of file
6064 diff --git a/www/public/index.html b/www/public/index.html
6065index 546f763..273ce86 100644
6066--- a/www/public/index.html
6067+++ b/www/public/index.html
6068 @@ -1 +1 @@
6069- <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><div class=screenshot><a href=https://ayllu-forge.org/projects/ayllu> <img class="screenshot night" src=/assets/images/ui.png></a><img class="screenshot day" src=/assets/images/ui-day.png></div><h1 id=welcome-to-ayllu>Welcome to Ayllu!</h1><h4 id=a-hyper-performant-hackable-code-forge-built-on-open-standards>A Hyper Performant & Hackable Code Forge Built on Open Standards</h4><p>Ayllu is a lightweight code forge designed to enable individuals and community projects develop software in collaboration across open internet standards.<h5 id=general-purpose-git-ui>General Purpose Git UI</h5><p>General purpose web based UI for git. Browse hosted projects, view contributors, source code, blame, releases, etc.<h5 id=source-code-analysis>Source Code Analysis</h5><p>Super accurate syntax highlighting and code composition insight across project history.<h5 id=incremental-jobs>Incremental Jobs</h5><p>A built-in configurable incremental jobs system controlled via Git hooks and/or cron to extend forge functionality.<h5 id=extensible-plugin-system>Extensible Plugin System</h5><p>An extensible RPC based plugin system allows adding large external integrations.<h5 id=completely-themeable>Completely Themeable</h5><p>The web interface is fully customizable, a few themes <a href=https://ayllu-forge.org/config>already</a> exist and more are on the way.<h5 id=web-1-5>Web 1.5</h5><p>Super fast web frontend with first class support for RSS based project subscriptions. Ayllu loads faster than any other code forge in existence. If it doesn't, it's a bug!<h5 id=static-hosting>Static Hosting</h5><p>Serve static websites directly from a git repository making project sites, documentation, or other assets simple to self-host.<h5 id=mailing-list-management>Mailing List Management</h5><p>Full featured email based software development workflows and mailing list management based on <a href=https://git.meli-email.org/meli/mailpot>Mailpot</a> 🍯.<h5 id=much-more>Much More</h5><p>New features planned such as continuous integration, mailing list support, external API, federation, and more.</main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6070\ No newline at end of file
6071+ <!doctype html><html lang=en><head><meta charset=utf-8><link href=/main.css rel=stylesheet><link href=/assets/ayllu_logo.svg rel=icon type=image/svg+xml><link href=/assets/ayllu_logo.png rel=icon sizes=any type=image/png><title>Ayllu</title></head><nav><ul><li><a href=/> <img class=logo src=/assets/ayllu_logo.png> </a></ul><ul><li><a href=https://ayllu-forge.org/browse>browse</a><li><a href=/docs>docs</a></ul></nav><body class=container><div class=wrapper><main class=page-body><h1 id=hyper-performant-hackable-code-forge-built-on-open-standards>Hyper Performant & Hackable Code Forge Built on Open Standards</h1><p>Ayllu is a lightweight code forge designed to enable individuals and community projects develop software in collaboration across open internet standards.<h2 id=general-purpose-git-ui>General Purpose Git UI</h2><p>General purpose web based UI for git. Browse hosted projects, view contributors, source code, blame, releases, etc.<h2 id=source-code-analysis>Source Code Analysis</h2><p>Super accurate syntax highlighting and code composition insight across project history.<h2 id=incremental-jobs>Incremental Jobs</h2><p>A built-in configurable incremental jobs system controlled via Git hooks and/or cron to extend forge functionality.<h2 id=extensible-plugin-system>Extensible Plugin System</h2><p>An extensible RPC based plugin system allows adding large external integrations.<h2 id=completely-themeable>Completely Themeable</h2><p>The web interface is fully customizable, a few themes <a href=https://ayllu-forge.org/config>already</a> exist and more are on the way.<h2 id=web-1-5>Web 1.5</h2><p>Super fast web frontend with first class support for RSS based project subscriptions. Ayllu loads faster than any other code forge in existence. If it doesn't, it's a bug!<h2 id=static-hosting>Static Hosting</h2><p>Serve static websites directly from a git repository making project sites, documentation, or other assets simple to self-host.<h2 id=mailing-list-management>Mailing List Management</h2><p>Full featured email based software development workflows and mailing list management based on <a href=https://git.meli-email.org/meli/mailpot>Mailpot</a> 🍯.<h2 id=much-more>Much More</h2><p>New features planned such as continuous integration, mailing list support, external API, federation, and more.</main><footer class=page-footer>2024, <a href=/projects/ayllu/blob/main/ATTRIBUTIONS.md>attributions</a></footer></div>
6072\ No newline at end of file
6073 diff --git a/www/public/main.css b/www/public/main.css
6074index 3c0c330..d4f5cbd 100644
6075--- a/www/public/main.css
6076+++ b/www/public/main.css
6077 @@ -1,7 +1 @@
6078- /*!
6079- * Pico CSS v1.5.10 (https://picocss.com)
6080- * Copyright 2019-2023 - Licensed under MIT
6081- *
6082- * Slim version example
6083- * You can export only the modules you need
6084- */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px;--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--grid-spacing-vertical: 0;--grid-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media (min-width: 576px){:root{--font-size: 17px}}@media (min-width: 768px){:root{--font-size: 18px}}@media (min-width: 992px){:root{--font-size: 19px}}@media (min-width: 1200px){:root{--font-size: 20px}}a{--text-decoration: none}a.secondary,a.contrast{--text-decoration: underline}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type=checkbox],[type=radio]{--border-width: 2px}[type=checkbox][role=switch]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme=light],:root:not([data-theme=dark]){--background-color: #fff;--color: #415462;--h1-color: #1b2832;--h2-color: #24333e;--h3-color: #2c3d49;--h4-color: #374956;--h5-color: #415462;--h6-color: #4d606d;--muted-color: #73828c;--muted-border-color: #edf0f3;--primary: #1095c1;--primary-hover: #08769b;--primary-focus: rgba(16,149,193,.125);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #415462;--secondary-focus: rgba(89,107,120,.125);--secondary-inverse: #fff;--contrast: #1b2832;--contrast-hover: #000;--contrast-focus: rgba(89,107,120,.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: #543a26;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: #a2afb9;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #d5dce2;--form-element-disabled-border-color: #a2afb9;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211,47,47,.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67,160,71,.125);--switch-background-color: #bbc6ce;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #d5dce2;--range-active-border-color: #bbc6ce;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: #f6f8f9;--code-background-color: #edf0f3;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #b34d80;--code-property-color: #3d888f;--code-value-color: #986;--code-comment-color: #a2afb9;--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: .0145rem .029rem .174rem rgba(27,40,50,.01698), .0335rem .067rem .402rem rgba(27,40,50,.024), .0625rem .125rem .75rem rgba(27,40,50,.03), .1125rem .225rem 1.35rem rgba(27,40,50,.036), .2085rem .417rem 2.502rem rgba(27,40,50,.04302), .5rem 1rem 6rem rgba(27,40,50,.06), 0 0 0 0.0625rem rgba(27,40,50,.015);--card-sectionning-background-color: #fbfbfc;--dropdown-background-color: #fbfbfc;--dropdown-border-color: #e1e6eb;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: #edf0f3;--modal-overlay-background-color: rgba(213,220,226,.7);--progress-background-color: #d5dce2;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme=dark]{--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type=checkbox],[type=radio],[type=range]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media (min-width: 576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width: 768px){.container{max-width:700px}}@media (min-width: 992px){.container{max-width:920px}}@media (min-width: 1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media (min-width: 992px){.grid{grid-template-columns:repeat(auto-fit, minmax(0%, 1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing)*.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role=link]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration)}a:is([aria-current],:hover,:active,:focus),[role=link]:is([aria-current],:hover,:active,:focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role=link]:focus{--background-color: var(--primary-focus)}a.secondary,[role=link].secondary{--color: var(--secondary)}a.secondary:is([aria-current],:hover,:active,:focus),[role=link].secondary:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}a.secondary:focus,[role=link].secondary:focus{--background-color: var(--secondary-focus)}a.contrast,[role=link].contrast{--color: var(--contrast)}a.contrast:is([aria-current],:hover,:active,:focus),[role=link].contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}a.contrast:focus,[role=link].contrast:focus{--background-color: var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}hgroup,.headings{margin-bottom:var(--typography-spacing-vertical)}hgroup>*,.headings>*{margin-bottom:0}hgroup>*:last-child,.headings>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical)*.25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical)*.25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);border-inline-start:.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical)*.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}button,input[type=submit],input[type=button],input[type=reset],[role=button]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer}button:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),[role=button]:is([aria-current],:hover,:active,:focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type=submit]:focus,input[type=button]:focus,input[type=reset]:focus,[role=button]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--primary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).secondary,input[type=reset]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}:is(button,input[type=submit],input[type=button],[role=button]).secondary:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover);--color: var(--secondary-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).secondary:focus,input[type=reset]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--secondary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).contrast{--background-color: var(--contrast);--border-color: var(--contrast);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:is([aria-current],:hover,:active,:focus){--background-color: var(--contrast-hover);--border-color: var(--contrast-hover);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--contrast-focus)}:is(button,input[type=submit],input[type=button],[role=button]).outline,input[type=reset].outline{--background-color: transparent;--color: var(--primary)}:is(button,input[type=submit],input[type=button],[role=button]).outline:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--background-color: transparent;--color: var(--primary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary,input[type=reset].outline{--color: var(--secondary)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast{--color: var(--contrast)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing)*.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight)}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--background-color: var(--form-element-active-background-color)}input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--border-color: var(--form-element-active-border-color)}input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type=submit],[type=button],[type=reset])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color: var(--form-element-valid-active-border-color);--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color)}:where(input,select,textarea)[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color: var(--form-element-invalid-active-border-color);--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color)}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:rgba(0,0,0,0)}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}:where(input,select,textarea,.grid)+small{display:block;width:100%;margin-top:calc(var(--spacing)*-.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing)*.25)}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing)/2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0, 0, 0, 0);position:absolute}a,area,button,input,label,select,summary,textarea,[tabindex]{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal)*-1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal)*-1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical)*-1) calc(var(--nav-link-spacing-horizontal)*-1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizontal)*2);margin-inline-start:calc(var(--nav-link-spacing-horizontal)/2);content:"/";color:var(--muted-color);text-align:center}nav[aria-label=breadcrumb] a[aria-current]{background-color:rgba(0,0,0,0);color:inherit;text-decoration:none;pointer-events:none}nav [role=button]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside nav,aside ol,aside ul,aside li{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical)*.5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}p>code{font-weight:bold;text-decoration-line:underline}pre{padding:1rem;overflow:scroll;background-color:#e7e7e7}pre[data-linenos]{padding:1rem 0}pre table td{padding:0}pre table td:nth-of-type(1){text-align:center;user-select:none}pre mark{display:block;background-color:rgba(254,252,232,.9)}pre table{width:100%;border-collapse:collapse}.night{display:none}.alert{padding:20px;background-color:#f44336;color:#fff;margin-bottom:15px;text-align:center}.warning{padding:20px;background-color:#ff9800;color:#fff;margin-bottom:15px;text-align:center}.info{padding:20px;background-color:#2196f3;color:#fff;margin-bottom:15px;text-align:center}.info>a{color:#ff0}h1,h2,h3,h4,h5,h6{margin-bottom:.5em}a>button{color:#fff;text-decoration:underline}figure{overflow:clip}.blurb>button{margin-top:20px}img.screenshot{border:double 3px;border-radius:4px;max-width:400px;margin:1em}@media (min-width: 992px){img.screenshot{float:right}}img.logo{max-width:72px}html,body{box-sizing:border-box;height:100%}body>footer{margin:0px;padding:0px}.wrapper{min-height:100%;display:flex;flex-direction:column;box-sizing:border-box}.page-body{flex-grow:1}.page-footer{flex-grow:0;flex-shrink:0}footer{text-align:center;font-size:.8em}.screenshot{text-align:center}.active{text-decoration:underline}.documentation{display:flex}@media (max-width: 992px){.documentation{flex-wrap:wrap}}.documentation .doc-content{max-width:100%}.documentation .side-panel{margin-right:3em;min-width:200px}.documentation .side-panel ul>li{list-style:none;font-weight:bold;font-size:1.2em}.home-header{display:grid;grid-template-columns:50% 40%;grid-column-gap:20px;grid-row-gap:20px;justify-items:stretch;align-items:stretch}@media (max-width: 992px){.home-header{grid-template-columns:none}}.home-wrapper{display:grid;grid-template-columns:33% 33% 33%;grid-column-gap:20px;grid-row-gap:20px;justify-items:stretch;align-items:stretch}@media (max-width: 992px){.home-wrapper{grid-template-columns:none}}@media (prefers-color-scheme: dark){pre{background-color:#424242}.diagram{background:grey}.day{display:none}.night{display:initial}}
6085\ No newline at end of file
6086+ :where(html){--link:var(--indigo-7);--link-visited:var(--purple-7);--text-1:var(--gray-12);--text-2:var(--gray-7);--surface-1:var(--gray-0);--surface-2:var(--gray-2);--surface-3:var(--gray-3);--surface-4:var(--gray-4);--scrollthumb-color:var(--gray-7);-webkit-text-size-adjust:none;accent-color:var(--brand, var(--link));background-color:var(--surface-1);block-size:100%;caret-color:var(--brand, var(--link));color:var(--text-1);color-scheme:light;font-family:var(--font-sans);line-height:var(--font-lineheight-3);scrollbar-color:var(--scrollthumb-color) transparent}@media (dynamic-range: high) or (color-gamut: p3){@supports (color: color(display-p3 0 0 0)){:where(html){--link:color(display-p3 .1 .4 1);--link-visited:color(display-p3 .6 .2 1)}}}:where(html) :where(dialog){background-color:var(--surface-1)}:where(html) :where(button,.btn){--_highlight:var(--_highlight-light);--_bg:var(--_bg-light);--_ink-shadow:var(--_ink-shadow-light)}:where(html) :where(button,.btn) :where([type=reset]){--_text:var(--red-6);--_border:var(--red-3)}:where(html) :where(button,.btn,input:is([type=button],[type=submit],[type=reset]))[disabled]{--_text:var(--gray-6)}:where(html) :where(textarea,select,input:not([type=button],[type=submit],[type=reset])){background-color:var(--surface-2)}@media (prefers-color-scheme: dark){:where(html){--link:var(--indigo-3);--link-visited:var(--purple-3);--text-1:var(--gray-1);--text-2:var(--gray-4);--surface-1:var(--gray-9);--surface-2:var(--gray-8);--surface-3:var(--gray-7);--surface-4:var(--gray-6);--scrollthumb-color:var(--gray-6);--shadow-strength:10%;--shadow-color:220 40% 2%;color-scheme:dark}}:where(a[href]){color:var(--brand, var(--link))}:where(a[href]):where(:visited){color:var(--link-visited)}:focus-visible{outline-color:var(--brand, var(--link))}*,:after,:before{box-sizing:border-box}:where(:not(dialog)){margin:0}:where(:not(fieldset,progress,meter)){background-origin:border-box;background-repeat:no-repeat;border-style:solid;border-width:0}@media (prefers-reduced-motion: no-preference){:where(html){scroll-behavior:smooth}}@media (prefers-reduced-motion: no-preference){:where(:focus-visible){transition:outline-offset 145ms var(--ease-2)}:where(:not(:active):focus-visible){transition-duration:.25s}}:where(:not(:active):focus-visible){outline-offset:5px}:where(body){min-block-size:100%}:where(h1,h2,h3,h4,h5,h6){text-wrap:balance;font-weight:var(--font-weight-9);line-height:var(--font-lineheight-1)}:where(h1){font-size:var(--font-size-8);max-inline-size:var(--size-header-1)}:where(h2){font-size:var(--font-size-6);max-inline-size:var(--size-header-2)}:where(h3){font-size:var(--font-size-5)}:where(h4){font-size:var(--font-size-4)}:where(h5){font-size:var(--font-size-3)}:where(h3,h4,h5,h6,dt){max-inline-size:var(--size-header-3)}:where(p,ul,ol,dl,h6){font-size:var(--font-size-2)}:where(a,u,ins,abbr){text-underline-offset:1px}@supports (-moz-appearance: none){:where(a,u,ins,abbr){text-underline-offset:2px}}:where(a[href],area,button,input:not([type=text],[type=email],[type=number],[type=password],[type=""],[type=tel],[type=url]),label[for],select,summary,[tabindex]:not([tabindex*="-"])){cursor:pointer}:where(a[href],area,button,input,label[for],select,summary,textarea,[tabindex]:not([tabindex*="-"])){-webkit-tap-highlight-color:transparent;touch-action:manipulation}:where(a):where([href]){text-decoration-color:var(--indigo-2)}:where(a):where([href]):where(:visited){text-decoration-color:var(--purple-2)}:where(a):where(:not(:hover)){text-decoration:inherit}:where(img,svg,video,canvas,audio,iframe,embed,object){display:block}:where(img,svg,video){block-size:auto;max-inline-size:100%}:where(input,button,textarea,select),:where(input[type=file])::-webkit-file-upload-button{color:inherit;font:inherit;font-size:inherit;letter-spacing:inherit}::placeholder{color:var(--gray-7);opacity:.75}:where(input:not([type=range]),textarea){padding-block:var(--size-1);padding-inline:var(--size-2)}:where(select){padding-block:.75ch;padding-inline:var(--size-relative-4) 0}:where(textarea,select,input:not([type=button],[type=submit],[type=reset])){background-color:var(--surface-2);border-radius:var(--radius-2)}:where(textarea){resize:block}:where(input[type=checkbox],input[type=radio]){block-size:var(--size-3);inline-size:var(--size-3)}:where(svg:not([width])){inline-size:var(--size-10)}:where(code,kbd,samp,pre){font-family:var(--font-mono)}:where(:not(pre)>code,kbd){white-space:nowrap}:where(pre){direction:ltr;max-inline-size:max-content;min-inline-size:0;white-space:pre;writing-mode:lr}:where(:not(pre)>code){background:var(--surface-2);border-radius:var(--radius-2);padding:var(--size-1) var(--size-2);writing-mode:lr}:where(kbd,var){border-color:var(--surface-4);border-radius:var(--radius-2);border-width:var(--border-size-1);padding:var(--size-1) var(--size-2)}:where(mark){border-radius:var(--radius-2);padding-inline:var(--size-1)}:where(ol,ul){padding-inline-start:var(--size-8)}:where(li){padding-inline-start:var(--size-2)}:where(li,dd,figcaption){max-inline-size:var(--size-content-2)}:where(p){text-wrap:pretty;max-inline-size:var(--size-content-3)}:where(dt,summary){font-weight:var(--font-weight-7)}:where(dt:not(:first-of-type)){margin-block-start:var(--size-5)}:where(small){font-size:max(.5em,var(--font-size-0));max-inline-size:var(--size-content-1)}:where(hr){background-color:var(--surface-3);height:var(--border-size-2);margin-block:var(--size-fluid-5)}:where(figure){display:grid;gap:var(--size-2);place-items:center}:where(figure)>:where(figcaption){text-wrap:balance;font-size:var(--font-size-1)}:where(blockquote,:not(blockquote)>cite){border-inline-start-width:var(--border-size-3)}:where(blockquote){display:grid;gap:var(--size-3);max-inline-size:var(--size-content-2);padding-block:var(--size-3);padding-inline:var(--size-4)}:where(:not(blockquote)>cite){padding-inline-start:var(--size-2)}:where(summary){background:var(--surface-3);border-radius:var(--radius-2);margin:calc(var(--size-2)*-1) calc(var(--size-3)*-1);padding:var(--size-2) var(--size-3)}:where(details){background:var(--surface-2);border-radius:var(--radius-2);padding-block:var(--size-2);padding-inline:var(--size-3)}:where(details[open]>summary){border-end-end-radius:0;border-end-start-radius:0;margin-bottom:var(--size-2)}:where(fieldset){border:var(--border-size-1) solid var(--surface-4);border-radius:var(--radius-2)}:where(del){background:var(--red-9);color:var(--red-2)}:where(ins){background:var(--green-9);color:var(--green-1)}:where(abbr){text-decoration-color:var(--blue-5)}:where(dialog){background-color:var(--surface-1);border-radius:var(--radius-3);box-shadow:var(--shadow-6);color:inherit}:where(dialog)::backdrop{backdrop-filter:blur(25px)}:where(html[\:has\(dialog\[open\]\)]){overflow:hidden}:where(html:has(dialog[open])){overflow:hidden}:where(menu){display:flex;gap:var(--size-3);padding-inline-start:0}:where(sup){font-size:.5em}:where(table){--nice-inner-radius:calc(var(--radius-3) - 2px);background:var(--surface-2);border:1px solid var(--surface-2);border-radius:var(--radius-3);width:fit-content}:where(table[\:not-has\(tfoot\)] tr:last-child td:first-child){border-end-start-radius:var(--nice-inner-radius)}:where(table:not(:has(tfoot)) tr:last-child td:first-child){border-end-start-radius:var(--nice-inner-radius)}:where(table[\:not-has\(tfoot\)] tr:last-child td:last-child){border-end-end-radius:var(--nice-inner-radius)}:where(table:not(:has(tfoot)) tr:last-child td:last-child){border-end-end-radius:var(--nice-inner-radius)}:where(table thead tr:first-child th:first-child){border-start-start-radius:var(--nice-inner-radius)}:where(table thead tr:first-child th:last-child){border-start-end-radius:var(--nice-inner-radius)}:where(tfoot tr:last-child :is(th,td):first-of-type){border-end-start-radius:var(--nice-inner-radius)}:where(tfoot tr:last-child :is(th,td):last-of-type){border-end-end-radius:var(--nice-inner-radius)}:where(th){background-color:var(--surface-2);color:var(--text-1)}:where(table :is(a,button,[contenteditable]):is(:focus-visible)){outline-offset:-2px}:where(td){text-wrap:pretty;background:var(--surface-1);max-inline-size:var(--size-content-2)}:where(td,th){padding:var(--size-2);text-align:left}:where(:is(td,th):not([align])){text-align:center}:where(thead){border-collapse:collapse}:where(table tr:hover td),:where(tbody tr:nth-child(2n):hover td){background-color:var(--gray-10)}@media (prefers-color-scheme: light){:where(table tr:hover td),:where(tbody tr:nth-child(2n):hover td){background-color:#fff}}:where(table>caption){margin:var(--size-3)}:where(tfoot button){padding-block:var(--size-1);padding-inline:var(--size-3)}@media (prefers-color-scheme: dark){:where(textarea,select,input:not([type=button],[type=submit],[type=reset])){background-color:#171a1c}:where(dialog){background-color:var(--surface-2)}::placeholder{color:var(--gray-6)}}:where(html){--font-sans:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;--font-serif:ui-serif,serif;--font-mono:Dank Mono,Operator Mono,Inconsolata,Fira Mono,ui-monospace,SF Mono,Monaco,Droid Sans Mono,Source Code Pro,monospace;--font-weight-1:100;--font-weight-2:200;--font-weight-3:300;--font-weight-4:400;--font-weight-5:500;--font-weight-6:600;--font-weight-7:700;--font-weight-8:800;--font-weight-9:900;--font-lineheight-00:.95;--font-lineheight-0:1.1;--font-lineheight-1:1.25;--font-lineheight-2:1.375;--font-lineheight-3:1.5;--font-lineheight-4:1.75;--font-lineheight-5:2;--font-letterspacing-0:-.05em;--font-letterspacing-1:.025em;--font-letterspacing-2:.050em;--font-letterspacing-3:.075em;--font-letterspacing-4:.150em;--font-letterspacing-5:.500em;--font-letterspacing-6:.750em;--font-letterspacing-7:1em;--font-size-00:.5rem;--font-size-0:.75rem;--font-size-1:1rem;--font-size-2:1.1rem;--font-size-3:1.25rem;--font-size-4:1.5rem;--font-size-5:2rem;--font-size-6:2.5rem;--font-size-7:3rem;--font-size-8:3.5rem;--font-size-fluid-0:clamp(.75rem,2vw,1rem);--font-size-fluid-1:clamp(1rem,4vw,1.5rem);--font-size-fluid-2:clamp(1.5rem,6vw,2.5rem);--font-size-fluid-3:clamp(2rem,9vw,3.5rem);--size-000:-.5rem;--size-00:-.25rem;--size-1:.25rem;--size-2:.5rem;--size-3:1rem;--size-4:1.25rem;--size-5:1.5rem;--size-6:1.75rem;--size-7:2rem;--size-8:3rem;--size-9:4rem;--size-10:5rem;--size-11:7.5rem;--size-12:10rem;--size-13:15rem;--size-14:20rem;--size-15:30rem;--size-px-000:-8px;--size-px-00:-4px;--size-px-1:4px;--size-px-2:8px;--size-px-3:16px;--size-px-4:20px;--size-px-5:24px;--size-px-6:28px;--size-px-7:32px;--size-px-8:48px;--size-px-9:64px;--size-px-10:80px;--size-px-11:120px;--size-px-12:160px;--size-px-13:240px;--size-px-14:320px;--size-px-15:480px;--size-fluid-1:clamp(.5rem,1vw,1rem);--size-fluid-2:clamp(1rem,2vw,1.5rem);--size-fluid-3:clamp(1.5rem,3vw,2rem);--size-fluid-4:clamp(2rem,4vw,3rem);--size-fluid-5:clamp(4rem,5vw,5rem);--size-fluid-6:clamp(5rem,7vw,7.5rem);--size-fluid-7:clamp(7.5rem,10vw,10rem);--size-fluid-8:clamp(10rem,20vw,15rem);--size-fluid-9:clamp(15rem,30vw,20rem);--size-fluid-10:clamp(20rem,40vw,30rem);--size-content-1:20ch;--size-content-2:45ch;--size-content-3:60ch;--size-header-1:20ch;--size-header-2:25ch;--size-header-3:35ch;--size-xxs:240px;--size-xs:360px;--size-sm:480px;--size-md:768px;--size-lg:1024px;--size-xl:1440px;--size-xxl:1920px;--size-relative-000:-.5ch;--size-relative-00:-.25ch;--size-relative-1:.25ch;--size-relative-2:.5ch;--size-relative-3:1ch;--size-relative-4:1.25ch;--size-relative-5:1.5ch;--size-relative-6:1.75ch;--size-relative-7:2ch;--size-relative-8:3ch;--size-relative-9:4ch;--size-relative-10:5ch;--size-relative-11:7.5ch;--size-relative-12:10ch;--size-relative-13:15ch;--size-relative-14:20ch;--size-relative-15:30ch;--ease-1:cubic-bezier(.25,0,.5,1);--ease-2:cubic-bezier(.25,0,.4,1);--ease-3:cubic-bezier(.25,0,.3,1);--ease-4:cubic-bezier(.25,0,.2,1);--ease-5:cubic-bezier(.25,0,.1,1);--ease-in-1:cubic-bezier(.25,0,1,1);--ease-in-2:cubic-bezier(.50,0,1,1);--ease-in-3:cubic-bezier(.70,0,1,1);--ease-in-4:cubic-bezier(.90,0,1,1);--ease-in-5:cubic-bezier(1,0,1,1);--ease-out-1:cubic-bezier(0,0,.75,1);--ease-out-2:cubic-bezier(0,0,.50,1);--ease-out-3:cubic-bezier(0,0,.3,1);--ease-out-4:cubic-bezier(0,0,.1,1);--ease-out-5:cubic-bezier(0,0,0,1);--ease-in-out-1:cubic-bezier(.1,0,.9,1);--ease-in-out-2:cubic-bezier(.3,0,.7,1);--ease-in-out-3:cubic-bezier(.5,0,.5,1);--ease-in-out-4:cubic-bezier(.7,0,.3,1);--ease-in-out-5:cubic-bezier(.9,0,.1,1);--ease-elastic-out-1:cubic-bezier(.5,.75,.75,1.25);--ease-elastic-out-2:cubic-bezier(.5,1,.75,1.25);--ease-elastic-out-3:cubic-bezier(.5,1.25,.75,1.25);--ease-elastic-out-4:cubic-bezier(.5,1.5,.75,1.25);--ease-elastic-out-5:cubic-bezier(.5,1.75,.75,1.25);--ease-elastic-in-1:cubic-bezier(.5,-0.25,.75,1);--ease-elastic-in-2:cubic-bezier(.5,-0.50,.75,1);--ease-elastic-in-3:cubic-bezier(.5,-0.75,.75,1);--ease-elastic-in-4:cubic-bezier(.5,-1.00,.75,1);--ease-elastic-in-5:cubic-bezier(.5,-1.25,.75,1);--ease-elastic-in-out-1:cubic-bezier(.5,-.1,.1,1.5);--ease-elastic-in-out-2:cubic-bezier(.5,-.3,.1,1.5);--ease-elastic-in-out-3:cubic-bezier(.5,-.5,.1,1.5);--ease-elastic-in-out-4:cubic-bezier(.5,-.7,.1,1.5);--ease-elastic-in-out-5:cubic-bezier(.5,-.9,.1,1.5);--ease-step-1:steps(2);--ease-step-2:steps(3);--ease-step-3:steps(4);--ease-step-4:steps(7);--ease-step-5:steps(10);--ease-elastic-1:var(--ease-elastic-out-1);--ease-elastic-2:var(--ease-elastic-out-2);--ease-elastic-3:var(--ease-elastic-out-3);--ease-elastic-4:var(--ease-elastic-out-4);--ease-elastic-5:var(--ease-elastic-out-5);--ease-squish-1:var(--ease-elastic-in-out-1);--ease-squish-2:var(--ease-elastic-in-out-2);--ease-squish-3:var(--ease-elastic-in-out-3);--ease-squish-4:var(--ease-elastic-in-out-4);--ease-squish-5:var(--ease-elastic-in-out-5);--ease-spring-1:linear(0,0.006,0.025 2.8%,0.101 6.1%,0.539 18.9%,0.721 25.3%,0.849 31.5%,0.937 38.1%,0.968 41.8%,0.991 45.7%,1.006 50.1%,1.015 55%,1.017 63.9%,1.001);--ease-spring-2:linear(0,0.007,0.029 2.2%,0.118 4.7%,0.625 14.4%,0.826 19%,0.902,0.962,1.008 26.1%,1.041 28.7%,1.064 32.1%,1.07 36%,1.061 40.5%,1.015 53.4%,0.999 61.6%,0.995 71.2%,1);--ease-spring-3:linear(0,0.009,0.035 2.1%,0.141 4.4%,0.723 12.9%,0.938 16.7%,1.017,1.077,1.121,1.149 24.3%,1.159,1.163,1.161,1.154 29.9%,1.129 32.8%,1.051 39.6%,1.017 43.1%,0.991,0.977 51%,0.974 53.8%,0.975 57.1%,0.997 69.8%,1.003 76.9%,1);--ease-spring-4:linear(0,0.009,0.037 1.7%,0.153 3.6%,0.776 10.3%,1.001,1.142 16%,1.185,1.209 19%,1.215 19.9% 20.8%,1.199,1.165 25%,1.056 30.3%,1.008 33%,0.973,0.955 39.2%,0.953 41.1%,0.957 43.3%,0.998 53.3%,1.009 59.1% 63.7%,0.998 78.9%,1);--ease-spring-5:linear(0,0.01,0.04 1.6%,0.161 3.3%,0.816 9.4%,1.046,1.189 14.4%,1.231,1.254 17%,1.259,1.257 18.6%,1.236,1.194 22.3%,1.057 27%,0.999 29.4%,0.955 32.1%,0.942,0.935 34.9%,0.933,0.939 38.4%,1 47.3%,1.011,1.017 52.6%,1.016 56.4%,1 65.2%,0.996 70.2%,1.001 87.2%,1);--ease-bounce-1:linear(0,0.004,0.016,0.035,0.063,0.098,0.141,0.191,0.25,0.316,0.391 36.8%,0.563,0.766,1 58.8%,0.946,0.908 69.1%,0.895,0.885,0.879,0.878,0.879,0.885,0.895,0.908 89.7%,0.946,1);--ease-bounce-2:linear(0,0.004,0.016,0.035,0.063,0.098,0.141 15.1%,0.25,0.391,0.562,0.765,1,0.892 45.2%,0.849,0.815,0.788,0.769,0.757,0.753,0.757,0.769,0.788,0.815,0.85,0.892 75.2%,1 80.2%,0.973,0.954,0.943,0.939,0.943,0.954,0.973,1);--ease-bounce-3:linear(0,0.004,0.016,0.035,0.062,0.098,0.141 11.4%,0.25,0.39,0.562,0.764,1 30.3%,0.847 34.8%,0.787,0.737,0.699,0.672,0.655,0.65,0.656,0.672,0.699,0.738,0.787,0.847 61.7%,1 66.2%,0.946,0.908,0.885 74.2%,0.879,0.878,0.879,0.885 79.5%,0.908,0.946,1 87.4%,0.981,0.968,0.96,0.957,0.96,0.968,0.981,1);--ease-bounce-4:linear(0,0.004,0.016 3%,0.062,0.141,0.25,0.391,0.562 18.2%,1 24.3%,0.81,0.676 32.3%,0.629,0.595,0.575,0.568,0.575,0.595,0.629,0.676 48.2%,0.811,1 56.2%,0.918,0.86,0.825,0.814,0.825,0.86,0.918,1 77.2%,0.94 80.6%,0.925,0.92,0.925,0.94 87.5%,1 90.9%,0.974,0.965,0.974,1);--ease-bounce-5:linear(0,0.004,0.016 2.5%,0.063,0.141,0.25 10.1%,0.562,1 20.2%,0.783,0.627,0.534 30.9%,0.511,0.503,0.511,0.534 38%,0.627,0.782,1 48.7%,0.892,0.815,0.769 56.3%,0.757,0.753,0.757,0.769 61.3%,0.815,0.892,1 68.8%,0.908 72.4%,0.885,0.878,0.885,0.908 79.4%,1 83%,0.954 85.5%,0.943,0.939,0.943,0.954 90.5%,1 93%,0.977,0.97,0.977,1);--layer-1:1;--layer-2:2;--layer-3:3;--layer-4:4;--layer-5:5;--layer-important:2147483647;--shadow-color:220 3% 15%;--shadow-strength:1%;--inner-shadow-highlight:inset 0 -.5px 0 0 #fff,inset 0 .5px 0 0 rgba(0,0,0,.067);--shadow-1:0 1px 2px -1px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%));--shadow-2:0 3px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 7px 14px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%));--shadow-3:0 -1px 3px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)),0 1px 2px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)),0 2px 5px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)),0 4px 12px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)),0 12px 15px -5px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%));--shadow-4:0 -2px 5px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)),0 1px 1px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 2px 2px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 5px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)),0 9px 9px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)),0 16px 16px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 6%));--shadow-5:0 -1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)),0 2px 1px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 5px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 10px 10px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)),0 20px 20px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)),0 40px 40px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%));--shadow-6:0 -1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 2%)),0 3px 2px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 7px 5px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 3%)),0 12px 10px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 4%)),0 22px 18px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 5%)),0 41px 33px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 6%)),0 100px 80px -2px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 7%));--inner-shadow-0:inset 0 0 0 1px hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%));--inner-shadow-1:inset 0 1px 2px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)),var(--inner-shadow-highlight);--inner-shadow-2:inset 0 1px 4px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)),var(--inner-shadow-highlight);--inner-shadow-3:inset 0 2px 8px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)),var(--inner-shadow-highlight);--inner-shadow-4:inset 0 2px 14px 0 hsl(var(--shadow-color)/calc(var(--shadow-strength) + 9%)),var(--inner-shadow-highlight);--ratio-square:1;--ratio-landscape:4/3;--ratio-portrait:3/4;--ratio-widescreen:16/9;--ratio-ultrawide:18/5;--ratio-golden:1.6180/1;--gray-0:#f8f9fa;--gray-1:#f1f3f5;--gray-2:#e9ecef;--gray-3:#dee2e6;--gray-4:#ced4da;--gray-5:#adb5bd;--gray-6:#868e96;--gray-7:#495057;--gray-8:#343a40;--gray-9:#212529;--gray-10:#16191d;--gray-11:#0d0f12;--gray-12:#030507;--stone-0:#f8fafb;--stone-1:#f2f4f6;--stone-2:#ebedef;--stone-3:#e0e4e5;--stone-4:#d1d6d8;--stone-5:#b1b6b9;--stone-6:#979b9d;--stone-7:#7e8282;--stone-8:#666968;--stone-9:#50514f;--stone-10:#3a3a37;--stone-11:#252521;--stone-12:#121210;--red-0:#fff5f5;--red-1:#ffe3e3;--red-2:#ffc9c9;--red-3:#ffa8a8;--red-4:#ff8787;--red-5:#ff6b6b;--red-6:#fa5252;--red-7:#f03e3e;--red-8:#e03131;--red-9:#c92a2a;--red-10:#b02525;--red-11:#962020;--red-12:#7d1a1a;--pink-0:#fff0f6;--pink-1:#ffdeeb;--pink-2:#fcc2d7;--pink-3:#faa2c1;--pink-4:#f783ac;--pink-5:#f06595;--pink-6:#e64980;--pink-7:#d6336c;--pink-8:#c2255c;--pink-9:#a61e4d;--pink-10:#8c1941;--pink-11:#731536;--pink-12:#59102a;--purple-0:#f8f0fc;--purple-1:#f3d9fa;--purple-2:#eebefa;--purple-3:#e599f7;--purple-4:#da77f2;--purple-5:#cc5de8;--purple-6:#be4bdb;--purple-7:#ae3ec9;--purple-8:#9c36b5;--purple-9:#862e9c;--purple-10:#702682;--purple-11:#5a1e69;--purple-12:#44174f;--violet-0:#f3f0ff;--violet-1:#e5dbff;--violet-2:#d0bfff;--violet-3:#b197fc;--violet-4:#9775fa;--violet-5:#845ef7;--violet-6:#7950f2;--violet-7:#7048e8;--violet-8:#6741d9;--violet-9:#5f3dc4;--violet-10:#5235ab;--violet-11:#462d91;--violet-12:#3a2578;--indigo-0:#edf2ff;--indigo-1:#dbe4ff;--indigo-2:#bac8ff;--indigo-3:#91a7ff;--indigo-4:#748ffc;--indigo-5:#5c7cfa;--indigo-6:#4c6ef5;--indigo-7:#4263eb;--indigo-8:#3b5bdb;--indigo-9:#364fc7;--indigo-10:#2f44ad;--indigo-11:#283a94;--indigo-12:#21307a;--blue-0:#e7f5ff;--blue-1:#d0ebff;--blue-2:#a5d8ff;--blue-3:#74c0fc;--blue-4:#4dabf7;--blue-5:#339af0;--blue-6:#228be6;--blue-7:#1c7ed6;--blue-8:#1971c2;--blue-9:#1864ab;--blue-10:#145591;--blue-11:#114678;--blue-12:#0d375e;--cyan-0:#e3fafc;--cyan-1:#c5f6fa;--cyan-2:#99e9f2;--cyan-3:#66d9e8;--cyan-4:#3bc9db;--cyan-5:#22b8cf;--cyan-6:#15aabf;--cyan-7:#1098ad;--cyan-8:#0c8599;--cyan-9:#0b7285;--cyan-10:#095c6b;--cyan-11:#074652;--cyan-12:#053038;--teal-0:#e6fcf5;--teal-1:#c3fae8;--teal-2:#96f2d7;--teal-3:#63e6be;--teal-4:#38d9a9;--teal-5:#20c997;--teal-6:#12b886;--teal-7:#0ca678;--teal-8:#099268;--teal-9:#087f5b;--teal-10:#066649;--teal-11:#054d37;--teal-12:#033325;--green-0:#ebfbee;--green-1:#d3f9d8;--green-2:#b2f2bb;--green-3:#8ce99a;--green-4:#69db7c;--green-5:#51cf66;--green-6:#40c057;--green-7:#37b24d;--green-8:#2f9e44;--green-9:#2b8a3e;--green-10:#237032;--green-11:#1b5727;--green-12:#133d1b;--lime-0:#f4fce3;--lime-1:#e9fac8;--lime-2:#d8f5a2;--lime-3:#c0eb75;--lime-4:#a9e34b;--lime-5:#94d82d;--lime-6:#82c91e;--lime-7:#74b816;--lime-8:#66a80f;--lime-9:#5c940d;--lime-10:#4c7a0b;--lime-11:#3c6109;--lime-12:#2c4706;--yellow-0:#fff9db;--yellow-1:#fff3bf;--yellow-2:#ffec99;--yellow-3:#ffe066;--yellow-4:#ffd43b;--yellow-5:#fcc419;--yellow-6:#fab005;--yellow-7:#f59f00;--yellow-8:#f08c00;--yellow-9:#e67700;--yellow-10:#b35c00;--yellow-11:#804200;--yellow-12:#663500;--orange-0:#fff4e6;--orange-1:#ffe8cc;--orange-2:#ffd8a8;--orange-3:#ffc078;--orange-4:#ffa94d;--orange-5:#ff922b;--orange-6:#fd7e14;--orange-7:#f76707;--orange-8:#e8590c;--orange-9:#d9480f;--orange-10:#bf400d;--orange-11:#99330b;--orange-12:#802b09;--choco-0:#fff8dc;--choco-1:#fce1bc;--choco-2:#f7ca9e;--choco-3:#f1b280;--choco-4:#e99b62;--choco-5:#df8545;--choco-6:#d46e25;--choco-7:#bd5f1b;--choco-8:#a45117;--choco-9:#8a4513;--choco-10:#703a13;--choco-11:#572f12;--choco-12:#3d210d;--brown-0:#faf4eb;--brown-1:#ede0d1;--brown-2:#e0cab7;--brown-3:#d3b79e;--brown-4:#c5a285;--brown-5:#b78f6d;--brown-6:#a87c56;--brown-7:#956b47;--brown-8:#825b3a;--brown-9:#6f4b2d;--brown-10:#5e3a21;--brown-11:#4e2b15;--brown-12:#422412;--sand-0:#f8fafb;--sand-1:#e6e4dc;--sand-2:#d5cfbd;--sand-3:#c2b9a0;--sand-4:#aea58c;--sand-5:#9a9178;--sand-6:#867c65;--sand-7:#736a53;--sand-8:#5f5746;--sand-9:#4b4639;--sand-10:#38352d;--sand-11:#252521;--sand-12:#121210;--camo-0:#f9fbe7;--camo-1:#e8ed9c;--camo-2:#d2df4e;--camo-3:#c2ce34;--camo-4:#b5bb2e;--camo-5:#a7a827;--camo-6:#999621;--camo-7:#8c851c;--camo-8:#7e7416;--camo-9:#6d6414;--camo-10:#5d5411;--camo-11:#4d460e;--camo-12:#36300a;--jungle-0:#ecfeb0;--jungle-1:#def39a;--jungle-2:#d0e884;--jungle-3:#c2dd6e;--jungle-4:#b5d15b;--jungle-5:#a8c648;--jungle-6:#9bbb36;--jungle-7:#8fb024;--jungle-8:#84a513;--jungle-9:#7a9908;--jungle-10:#658006;--jungle-11:#516605;--jungle-12:#3d4d04;--gradient-1:linear-gradient(to bottom right,#1f005c,#5b0060,#870160,#ac255e,#ca485c,#e16b5c,#f39060,#ffb56b);--gradient-2:linear-gradient(to bottom right,#48005c,#8300e2,#a269ff);--gradient-3:radial-gradient(circle at top right,#0ff,rgba(0,255,255,0)),radial-gradient(circle at bottom left,#ff1492,rgba(255,20,146,0));--gradient-4:linear-gradient(to bottom right,#00f5a0,#00d9f5);--gradient-5:conic-gradient(from -270deg at 75% 110%,#f0f,#fffaf0);--gradient-6:conic-gradient(from -90deg at top left,#000,#fff);--gradient-7:linear-gradient(to bottom right,#72c6ef,#004e8f);--gradient-8:conic-gradient(from 90deg at 50% 0%,#111,50%,#222,#111);--gradient-9:conic-gradient(from .5turn at bottom center,#add8e6,#fff);--gradient-10:conic-gradient(from 90deg at 40% -25%,gold,#f79d03,#ee6907,#e6390a,#de0d0d,#d61039,#cf1261,#c71585,#cf1261,#d61039,#de0d0d,#ee6907,#f79d03,gold,gold,gold);--gradient-11:conic-gradient(at bottom left,#ff1493,cyan);--gradient-12:conic-gradient(from 90deg at 25% -10%,#ff4500,#d3f340,#7bee85,#afeeee,#7bee85);--gradient-13:radial-gradient(circle at 50% 200%,#000142,#3b0083,#b300c3,#ff059f,#ff4661,#ffad86,#fff3c7);--gradient-14:conic-gradient(at top right,lime,cyan);--gradient-15:linear-gradient(to bottom right,#c7d2fe,#fecaca,#fef3c7);--gradient-16:radial-gradient(circle at 50% -250%,#374151,#111827,#000);--gradient-17:conic-gradient(from -90deg at 50% -25%,blue,#8a2be2);--gradient-18:linear-gradient(0deg,rgba(255,0,0,.8),rgba(255,0,0,0) 75%),linear-gradient(60deg,rgba(255,255,0,.8),rgba(255,255,0,0) 75%),linear-gradient(120deg,rgba(0,255,0,.8),rgba(0,255,0,0) 75%),linear-gradient(180deg,rgba(0,255,255,.8),rgba(0,255,255,0) 75%),linear-gradient(240deg,rgba(0,0,255,.8),rgba(0,0,255,0) 75%),linear-gradient(300deg,rgba(255,0,255,.8),rgba(255,0,255,0) 75%);--gradient-19:linear-gradient(to bottom right,#ffe259,#ffa751);--gradient-20:conic-gradient(from -135deg at -10% center,orange,#ff7715,#ff522a,#ff3f47,#ff5482,#ff69b4);--gradient-21:conic-gradient(from -90deg at 25% 115%,red,#f06,#f0c,#c0f,#60f,#00f,#00f,#00f,#00f);--gradient-22:linear-gradient(to bottom right,#acb6e5,#86fde8);--gradient-23:linear-gradient(to bottom right,#536976,#292e49);--gradient-24:conic-gradient(from .5turn at 0% 0%,#00c476,10%,#82b0ff,90%,#00c476);--gradient-25:conic-gradient(at 125% 50%,#b78cf7,#ff7c94,#ffcf0d,#ff7c94,#b78cf7);--gradient-26:linear-gradient(to bottom right,#9796f0,#fbc7d4);--gradient-27:conic-gradient(from .5turn at bottom left,#ff1493,#639);--gradient-28:conic-gradient(from -90deg at 50% 105%,#fff,orchid);--gradient-29:radial-gradient(circle at top right,#bfb3ff,rgba(191,179,255,0)),radial-gradient(circle at bottom left,#86acf9,rgba(134,172,249,0));--gradient-30:radial-gradient(circle at top right,#00ff80,rgba(0,255,128,0)),radial-gradient(circle at bottom left,#adffd6,rgba(173,255,214,0));--noise-1:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.005' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");--noise-2:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 300 300' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.05' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");--noise-3:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.25' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");--noise-4:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 2056 2056' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");--noise-5:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 2056 2056' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23a)'/%3E%3C/svg%3E");--noise-filter-1:contrast(300%) brightness(100%);--noise-filter-2:contrast(200%) brightness(150%);--noise-filter-3:contrast(200%) brightness(250%);--noise-filter-4:contrast(200%) brightness(500%);--noise-filter-5:contrast(200%) brightness(1000%);--animation-fade-in:fade-in .5s var(--ease-3);--animation-fade-in-bloom:fade-in-bloom 2s var(--ease-3);--animation-fade-out:fade-out .5s var(--ease-3);--animation-fade-out-bloom:fade-out-bloom 2s var(--ease-3);--animation-scale-up:scale-up .5s var(--ease-3);--animation-scale-down:scale-down .5s var(--ease-3);--animation-slide-out-up:slide-out-up .5s var(--ease-3);--animation-slide-out-down:slide-out-down .5s var(--ease-3);--animation-slide-out-right:slide-out-right .5s var(--ease-3);--animation-slide-out-left:slide-out-left .5s var(--ease-3);--animation-slide-in-up:slide-in-up .5s var(--ease-3);--animation-slide-in-down:slide-in-down .5s var(--ease-3);--animation-slide-in-right:slide-in-right .5s var(--ease-3);--animation-slide-in-left:slide-in-left .5s var(--ease-3);--animation-shake-x:shake-x .75s var(--ease-out-5);--animation-shake-y:shake-y .75s var(--ease-out-5);--animation-shake-z:shake-z 1s var(--ease-in-out-3);--animation-spin:spin 2s linear infinite;--animation-ping:ping 5s var(--ease-out-3) infinite;--animation-blink:blink 1s var(--ease-out-3) infinite;--animation-float:float 3s var(--ease-in-out-3) infinite;--animation-bounce:bounce 2s var(--ease-squish-2) infinite;--animation-pulse:pulse 2s var(--ease-out-3) infinite;--border-size-1:1px;--border-size-2:2px;--border-size-3:5px;--border-size-4:10px;--border-size-5:25px;--radius-1:2px;--radius-2:5px;--radius-3:1rem;--radius-4:2rem;--radius-5:4rem;--radius-6:8rem;--radius-drawn-1:255px 15px 225px 15px/15px 225px 15px 255px;--radius-drawn-2:125px 10px 20px 185px/25px 205px 205px 25px;--radius-drawn-3:15px 255px 15px 225px/225px 15px 255px 15px;--radius-drawn-4:15px 25px 155px 25px/225px 150px 25px 115px;--radius-drawn-5:250px 25px 15px 20px/15px 80px 105px 115px;--radius-drawn-6:28px 100px 20px 15px/150px 30px 205px 225px;--radius-round:1e5px;--radius-blob-1:30% 70% 70% 30%/53% 30% 70% 47%;--radius-blob-2:53% 47% 34% 66%/63% 46% 54% 37%;--radius-blob-3:37% 63% 56% 44%/49% 56% 44% 51%;--radius-blob-4:63% 37% 37% 63%/43% 37% 63% 57%;--radius-blob-5:49% 51% 48% 52%/57% 44% 56% 43%;--radius-conditional-1:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-1));--radius-conditional-2:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-2));--radius-conditional-3:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-3));--radius-conditional-4:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-4));--radius-conditional-5:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-5));--radius-conditional-6:clamp(0px,calc(100vw - 100%) * 1e5,var(--radius-6))}@media (prefers-color-scheme: dark){:where(html){--shadow-color:220 40% 2%;--shadow-strength:25%;--inner-shadow-highlight:inset 0 -.5px 0 0 hsla(0,0%,100%,.067),inset 0 .5px 0 0 rgba(0,0,0,.467)}}@keyframes fade-in{to{opacity:1}}@keyframes fade-in-bloom{0%{filter:brightness(1) blur(20px);opacity:0}10%{filter:brightness(2) blur(10px);opacity:1}to{filter:brightness(1) blur(0);opacity:1}}@keyframes fade-out{to{opacity:0}}@keyframes fade-out-bloom{to{filter:brightness(1) blur(20px);opacity:0}10%{filter:brightness(2) blur(10px);opacity:1}0%{filter:brightness(1) blur(0);opacity:1}}@keyframes scale-up{to{transform:scale(1.25)}}@keyframes scale-down{to{transform:scale(.75)}}@keyframes slide-out-up{to{transform:translateY(-100%)}}@keyframes slide-out-down{to{transform:translateY(100%)}}@keyframes slide-out-right{to{transform:translateX(100%)}}@keyframes slide-out-left{to{transform:translateX(-100%)}}@keyframes slide-in-up{0%{transform:translateY(100%)}}@keyframes slide-in-down{0%{transform:translateY(-100%)}}@keyframes slide-in-right{0%{transform:translateX(-100%)}}@keyframes slide-in-left{0%{transform:translateX(100%)}}@keyframes shake-x{0%, to{transform:translateX(0)}20%{transform:translateX(-5%)}40%{transform:translateX(5%)}60%{transform:translateX(-5%)}80%{transform:translateX(5%)}}@keyframes shake-y{0%, to{transform:translateY(0)}20%{transform:translateY(-5%)}40%{transform:translateY(5%)}60%{transform:translateY(-5%)}80%{transform:translateY(5%)}}@keyframes shake-z{0%, to{transform:rotate(0deg)}20%{transform:rotate(-2deg)}40%{transform:rotate(2deg)}60%{transform:rotate(-2deg)}80%{transform:rotate(2deg)}}@keyframes spin{to{transform:rotate(1turn)}}@keyframes ping{90%, to{opacity:0;transform:scale(2)}}@keyframes blink{0%, to{opacity:1}50%{opacity:.5}}@keyframes float{50%{transform:translateY(-25%)}}@keyframes bounce{25%{transform:translateY(-20%)}40%{transform:translateY(-3%)}0%, 60%, to{transform:translateY(0)}}@keyframes pulse{50%{transform:scale(.9)}}@media (prefers-color-scheme: dark){@keyframes fade-in-bloom{0%{filter:brightness(1) blur(20px);opacity:0}10%{filter:brightness(.5) blur(10px);opacity:1}to{filter:brightness(1) blur(0);opacity:1}}}@media (prefers-color-scheme: dark){@keyframes fade-out-bloom{to{filter:brightness(1) blur(20px);opacity:0}10%{filter:brightness(.5) blur(10px);opacity:1}0%{filter:brightness(1) blur(0);opacity:1}}}:root{font-size:80%}a:visited{color:var(--link)}li.active{color:var(--text-2);text-shadow:0 0 10px var(--camo-5),0 0 25px var(--camo-7)}code{background:none}pre{padding:1em;color:var(--text-2);text-shadow:0 0 10px var(--camo-3),0 0 25px var(--camo-5)}div.doc-content>h1,h2,h3,h4,h5{font-size:var(--font-size-5)}img.logo{max-width:72px}html,body{box-sizing:border-box;height:100%}.content{display:flex;justify-content:center;align-items:center}body>footer{margin:0px;padding:0px}.wrapper{min-height:100%;display:flex;flex-direction:column;box-sizing:border-box}.page-body{flex-grow:1}.page-footer{flex-grow:0;flex-shrink:0}footer{text-align:center;font-size:.8em}article{padding:.5em}nav{display:flex;justify-content:space-between;align-items:center;background-color:var(--surface-1);padding:10px;color:var(--text-1);box-shadow:var(--shadow-3)}nav.subnav{background-color:var(--surface-2);color:var(--text-1);box-shadow:var(--shadow-5)}nav .logo{font-size:20px;font-weight:bold}nav ul{list-style-type:none;margin:0;padding:0;display:flex}nav ul li{margin-left:20px}nav ul li a{color:var(--link);text-decoration:none}.documentation{margin:1em}@media (width > 768px){.documentation{display:flex}}.documentation .doc-content{max-width:100%}.documentation .side-panel{margin-right:3em;margin-bottom:1em;min-width:200px}.documentation .side-panel ul>li{list-style:none;font-weight:bold;font-size:1.2em}h1,h2,h3,h4,h5{max-inline-size:none;margin-top:.5em;margin-bottom:.5em}main.page-body{margin:2em}div.side-panel>ul{padding-left:0px}.diagram{min-width:200%}
6087\ No newline at end of file
6088 diff --git a/www/public/pico/pico.classless.css b/www/public/pico/pico.classless.css
6089deleted file mode 100644
6090index dd80b93..0000000
6091--- a/www/public/pico/pico.classless.css
6092+++ /dev/null
6093 @@ -1,4 +0,0 @@
6094- /*!
6095- * Pico CSS v1.5.10 (https://picocss.com)
6096- * Copyright 2019-2023 - Licensed under MIT
6097- */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px;--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media (min-width: 576px){:root{--font-size: 17px}}@media (min-width: 768px){:root{--font-size: 18px}}@media (min-width: 992px){:root{--font-size: 19px}}@media (min-width: 1200px){:root{--font-size: 20px}}@media (min-width: 576px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 2.5)}}@media (min-width: 768px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3)}}@media (min-width: 992px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3.5)}}@media (min-width: 1200px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 4)}}@media (min-width: 576px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}@media (min-width: 992px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.75)}}@media (min-width: 1200px){article{--block-spacing-horizontal: calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing)}@media (min-width: 576px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2.5);--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 3);--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}a{--text-decoration: none}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type=checkbox],[type=radio]{--border-width: 2px}[type=checkbox][role=switch]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme=light],:root:not([data-theme=dark]){--background-color: #fff;--color: #415462;--h1-color: #1b2832;--h2-color: #24333e;--h3-color: #2c3d49;--h4-color: #374956;--h5-color: #415462;--h6-color: #4d606d;--muted-color: #73828c;--muted-border-color: #edf0f3;--primary: #1095c1;--primary-hover: #08769b;--primary-focus: rgba(16,149,193,.125);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #415462;--secondary-focus: rgba(89,107,120,.125);--secondary-inverse: #fff;--contrast: #1b2832;--contrast-hover: #000;--contrast-focus: rgba(89,107,120,.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: #543a26;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: #a2afb9;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #d5dce2;--form-element-disabled-border-color: #a2afb9;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211,47,47,.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67,160,71,.125);--switch-background-color: #bbc6ce;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #d5dce2;--range-active-border-color: #bbc6ce;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: #f6f8f9;--code-background-color: #edf0f3;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #b34d80;--code-property-color: #3d888f;--code-value-color: #986;--code-comment-color: #a2afb9;--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: .0145rem .029rem .174rem rgba(27,40,50,.01698), .0335rem .067rem .402rem rgba(27,40,50,.024), .0625rem .125rem .75rem rgba(27,40,50,.03), .1125rem .225rem 1.35rem rgba(27,40,50,.036), .2085rem .417rem 2.502rem rgba(27,40,50,.04302), .5rem 1rem 6rem rgba(27,40,50,.06), 0 0 0 0.0625rem rgba(27,40,50,.015);--card-sectionning-background-color: #fbfbfc;--dropdown-background-color: #fbfbfc;--dropdown-border-color: #e1e6eb;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: #edf0f3;--modal-overlay-background-color: rgba(213,220,226,.7);--progress-background-color: #d5dce2;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme=dark]{--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type=checkbox],[type=radio],[type=range]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal)}@media (min-width: 576px){body>header,body>main,body>footer{max-width:510px;padding-right:0;padding-left:0}}@media (min-width: 768px){body>header,body>main,body>footer{max-width:700px}}@media (min-width: 992px){body>header,body>main,body>footer{max-width:920px}}@media (min-width: 1200px){body>header,body>main,body>footer{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing)*.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role=link]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition)}a:is([aria-current],:hover,:active,:focus),[role=link]:is([aria-current],:hover,:active,:focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role=link]:focus{--background-color: var(--primary-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}hgroup{margin-bottom:var(--typography-spacing-vertical)}hgroup>*{margin-bottom:0}hgroup>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical)*.25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical)*.25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);border-inline-start:.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical)*.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}button,input[type=submit],input[type=button],input[type=reset],[role=button]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}button:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),[role=button]:is([aria-current],:hover,:active,:focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type=submit]:focus,input[type=button]:focus,input[type=reset]:focus,[role=button]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--primary-focus)}input[type=reset]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}input[type=reset]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--secondary-focus)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing)*.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--background-color: var(--form-element-active-background-color)}input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--border-color: var(--form-element-active-border-color)}input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type=submit],[type=button],[type=reset])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal) !important;padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color: var(--form-element-valid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important}:where(input,select,textarea)[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color: var(--form-element-invalid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:rgba(0,0,0,0)}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}:where(input,select,textarea)+small{display:block;width:100%;margin-top:calc(var(--spacing)*-.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing)*.25)}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-right:.375em;margin-left:0;margin-inline-start:0;margin-inline-end:.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-right:.375em;margin-bottom:0;cursor:pointer}[type=checkbox]:indeterminate{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color);--color: var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type=checkbox][role=switch]:focus{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color)}[type=checkbox][role=switch]:checked{--background-color: var(--switch-checked-background-color);--border-color: var(--switch-checked-background-color)}[type=checkbox][role=switch]:before{display:block;width:calc(1.25em - (var(--border-width) * 2));height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:checked{background-image:none}[type=checkbox][role=switch]:checked::before{margin-left:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type=checkbox][aria-invalid=false],[type=checkbox]:checked[aria-invalid=false],[type=radio][aria-invalid=false],[type=radio]:checked[aria-invalid=false],[type=checkbox][role=switch][aria-invalid=false],[type=checkbox][role=switch]:checked[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}[type=checkbox][aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=radio][aria-invalid=true],[type=radio]:checked[aria-invalid=true],[type=checkbox][role=switch][aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--icon-position: 0.75rem;--icon-width: 1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width)*-1);margin-left:var(--icon-position);opacity:0}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}@-moz-document url-prefix(){[type=date],[type=datetime-local],[type=month],[type=time],[type=week]{padding-right:var(--form-element-spacing-horizontal) !important;background-image:none !important}}[type=file]{--color: var(--muted-color);padding:calc(var(--form-element-spacing-vertical)*.5) 0;border:0;border-radius:0;background:none}[type=file]::file-selector-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::file-selector-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-webkit-file-upload-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-ms-browse{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-ms-browse:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}[type=range]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]:hover,[type=range]:focus{--range-border-color: var(--range-active-border-color);--range-thumb-color: var(--range-thumb-hover-color)}[type=range]:active{--range-thumb-color: var(--range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--icon-search),var(--icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--icon-search),var(--icon-invalid)}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing)/2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}pre,code,kbd,samp{font-size:.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}pre,code,kbd{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem .5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:none;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none !important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--transition)}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--spacing, 1rem)*.5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:none}details summary:focus:not([role=button]){color:var(--accordion-active-summary-color)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem*var(--line-height, 1.5));background-image:var(--icon-chevron-button)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>header,article>footer{margin-right:calc(var(--block-spacing-horizontal)*-1);margin-left:calc(var(--block-spacing-horizontal)*-1);padding:calc(var(--block-spacing-vertical)*.66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical)*-1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical)*-1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width: 0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;backdrop-filter:var(--modal-overlay-backdrop-filter);background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing)*2);overflow:auto}@media (min-width: 576px){dialog article{max-width:510px}}@media (min-width: 768px){dialog article{max-width:700px}}dialog article>header,dialog article>footer{padding:calc(var(--block-spacing-vertical)*.5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role=button]{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type){margin-left:calc(var(--spacing)*.5)}dialog article p:last-of-type{margin:0}dialog:not([open]),dialog[open=false]{display:none}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal)*-1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal)*-1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical)*-1) calc(var(--nav-link-spacing-horizontal)*-1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizontal)*2);margin-inline-start:calc(var(--nav-link-spacing-horizontal)/2);content:"/";color:var(--muted-color);text-align:center}nav[aria-label=breadcrumb] a[aria-current]{background-color:rgba(0,0,0,0);color:inherit;text-decoration:none;pointer-events:none}nav [role=button]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside nav,aside ol,aside ul,aside li{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical)*.5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}[dir=rtl] nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{content:"\\"}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--spacing)*.5);overflow:hidden;border:0;border-radius:var(--border-radius);background-color:var(--progress-background-color);color:var(--progress-color)}progress::-webkit-progress-bar{border-radius:var(--border-radius);background:none}progress[value]::-webkit-progress-value{background-color:var(--progress-color)}progress::-moz-progress-bar{background-color:var(--progress-color)}@media (prefers-reduced-motion: no-preference){progress:indeterminate{background:var(--progress-background-color) linear-gradient(to right, var(--progress-color) 30%, var(--progress-background-color) 30%) top left/150% 150% no-repeat;animation:progress-indeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:rgba(0,0,0,0)}progress:indeterminate::-moz-progress-bar{background-color:rgba(0,0,0,0)}}@media (prefers-reduced-motion: no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@keyframes progress-indeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}details[role=list],li[role=list]{position:relative}details[role=list] summary+ul,li[role=list]>ul{display:flex;z-index:99;position:absolute;top:auto;right:0;left:0;flex-direction:column;margin:0;padding:0;border:var(--border-width) solid var(--dropdown-border-color);border-radius:var(--border-radius);border-top-right-radius:0;border-top-left-radius:0;background-color:var(--dropdown-background-color);box-shadow:var(--card-box-shadow);color:var(--dropdown-color);white-space:nowrap}details[role=list] summary+ul li,li[role=list]>ul li{width:100%;margin-bottom:0;padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);list-style:none}details[role=list] summary+ul li:first-of-type,li[role=list]>ul li:first-of-type{margin-top:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li:last-of-type,li[role=list]>ul li:last-of-type{margin-bottom:calc(var(--form-element-spacing-vertical)*.5)}details[role=list] summary+ul li a,li[role=list]>ul li a{display:block;margin:calc(var(--form-element-spacing-vertical)*-.5) calc(var(--form-element-spacing-horizontal)*-1);padding:calc(var(--form-element-spacing-vertical)*.5) var(--form-element-spacing-horizontal);overflow:hidden;color:var(--dropdown-color);text-decoration:none;text-overflow:ellipsis}details[role=list] summary+ul li a:hover,li[role=list]>ul li a:hover{background-color:var(--dropdown-hover-background-color)}details[role=list] summary::after,li[role=list]>a::after{display:block;width:1rem;height:calc(1rem*var(--line-height, 1.5));margin-inline-start:.5rem;float:right;transform:rotate(0deg);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}details[role=list]{padding:0;border-bottom:none}details[role=list] summary{margin-bottom:0}details[role=list] summary:not([role]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2);padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--form-element-border-color);border-radius:var(--border-radius);background-color:var(--form-element-background-color);color:var(--form-element-placeholder-color);line-height:inherit;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}details[role=list] summary:not([role]):active,details[role=list] summary:not([role]):focus{border-color:var(--form-element-active-border-color);background-color:var(--form-element-active-background-color)}details[role=list] summary:not([role]):focus{box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}details[role=list][open] summary{border-bottom-right-radius:0;border-bottom-left-radius:0}details[role=list][open] summary::before{display:block;z-index:1;position:fixed;top:0;right:0;bottom:0;left:0;background:none;content:"";cursor:default}nav details[role=list] summary,nav li[role=list] a{display:flex;direction:ltr}nav details[role=list] summary+ul,nav li[role=list]>ul{min-width:fit-content;border-radius:var(--border-radius)}nav details[role=list] summary+ul li a,nav li[role=list]>ul li a{border-radius:0}nav details[role=list] summary,nav details[role=list] summary:not([role]){height:auto;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}nav details[role=list][open] summary{border-radius:var(--border-radius)}nav details[role=list] summary+ul{margin-top:var(--outline-width);margin-inline-start:0}nav details[role=list] summary[role=link]{margin-bottom:calc(var(--nav-link-spacing-vertical)*-1);line-height:var(--line-height)}nav details[role=list] summary[role=link]+ul{margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-link-spacing-horizontal)*-1)}li[role=list]:hover>ul,li[role=list] a:active~ul,li[role=list] a:focus~ul{display:flex}li[role=list]>ul{display:none;margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));margin-inline-start:calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal))}li[role=list]>a::after{background-image:var(--icon-chevron)}label>details[role=list]{margin-top:calc(var(--spacing)*.25);margin-bottom:var(--spacing)}[aria-busy=true]{cursor:progress}[aria-busy=true]:not(input,select,textarea,html)::before{display:inline-block;width:1em;height:1em;border:.1875em solid currentColor;border-radius:1em;border-right-color:rgba(0,0,0,0);content:"";vertical-align:text-bottom;vertical-align:-.125em;animation:spinner .75s linear infinite;opacity:var(--loading-spinner-opacity)}[aria-busy=true]:not(input,select,textarea,html):not(:empty)::before{margin-right:calc(var(--spacing)*.5);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)*.5)}[aria-busy=true]:not(input,select,textarea,html):empty{text-align:center}button[aria-busy=true],input[type=submit][aria-busy=true],input[type=button][aria-busy=true],input[type=reset][aria-busy=true],a[aria-busy=true]{pointer-events:none}@keyframes spinner{to{transform:rotate(360deg)}}[data-tooltip]{position:relative}[data-tooltip]:not(a,button,input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip][data-placement=top]::before,[data-tooltip][data-placement=top]::after,[data-tooltip]::before,[data-tooltip]::after{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%, -.25rem);border-radius:var(--border-radius);background:var(--tooltip-background-color);content:attr(data-tooltip);color:var(--tooltip-color);font-style:normal;font-weight:var(--font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip][data-placement=top]::after,[data-tooltip]::after{padding:0;transform:translate(-50%, 0rem);border-top:.3rem solid;border-right:.3rem solid rgba(0,0,0,0);border-left:.3rem solid rgba(0,0,0,0);border-radius:0;background-color:rgba(0,0,0,0);content:"";color:var(--tooltip-background-color)}[data-tooltip][data-placement=bottom]::before,[data-tooltip][data-placement=bottom]::after{top:100%;bottom:auto;transform:translate(-50%, .25rem)}[data-tooltip][data-placement=bottom]:after{transform:translate(-50%, -.3rem);border:.3rem solid rgba(0,0,0,0);border-bottom:.3rem solid}[data-tooltip][data-placement=left]::before,[data-tooltip][data-placement=left]::after{top:50%;right:100%;bottom:auto;left:auto;transform:translate(-.25rem, -50%)}[data-tooltip][data-placement=left]:after{transform:translate(.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-left:.3rem solid}[data-tooltip][data-placement=right]::before,[data-tooltip][data-placement=right]::after{top:50%;right:auto;bottom:auto;left:100%;transform:translate(.25rem, -50%)}[data-tooltip][data-placement=right]:after{transform:translate(-.3rem, -50%);border:.3rem solid rgba(0,0,0,0);border-right:.3rem solid}[data-tooltip]:focus::before,[data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{opacity:1}@media (hover: hover) and (pointer: fine){[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::before,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::before,[data-tooltip]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-top}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover [data-tooltip]:focus::after,[data-tooltip]:hover::after{animation-name:tooltip-caret-slide-top}[data-tooltip][data-placement=bottom]:focus::before,[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::before,[data-tooltip][data-placement=bottom]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-bottom}[data-tooltip][data-placement=bottom]:focus::after,[data-tooltip][data-placement=bottom]:hover::after{animation-name:tooltip-caret-slide-bottom}[data-tooltip][data-placement=left]:focus::before,[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::before,[data-tooltip][data-placement=left]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-left}[data-tooltip][data-placement=left]:focus::after,[data-tooltip][data-placement=left]:hover::after{animation-name:tooltip-caret-slide-left}[data-tooltip][data-placement=right]:focus::before,[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::before,[data-tooltip][data-placement=right]:hover::after{animation-duration:.2s;animation-name:tooltip-slide-right}[data-tooltip][data-placement=right]:focus::after,[data-tooltip][data-placement=right]:hover::after{animation-name:tooltip-caret-slide-right}}@keyframes tooltip-slide-top{from{transform:translate(-50%, .75rem);opacity:0}to{transform:translate(-50%, -.25rem);opacity:1}}@keyframes tooltip-caret-slide-top{from{opacity:0}50%{transform:translate(-50%, -.25rem);opacity:0}to{transform:translate(-50%, 0rem);opacity:1}}@keyframes tooltip-slide-bottom{from{transform:translate(-50%, -.75rem);opacity:0}to{transform:translate(-50%, .25rem);opacity:1}}@keyframes tooltip-caret-slide-bottom{from{opacity:0}50%{transform:translate(-50%, -.5rem);opacity:0}to{transform:translate(-50%, -.3rem);opacity:1}}@keyframes tooltip-slide-left{from{transform:translate(.75rem, -50%);opacity:0}to{transform:translate(-.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-left{from{opacity:0}50%{transform:translate(.05rem, -50%);opacity:0}to{transform:translate(.3rem, -50%);opacity:1}}@keyframes tooltip-slide-right{from{transform:translate(-.75rem, -50%);opacity:0}to{transform:translate(.25rem, -50%);opacity:1}}@keyframes tooltip-caret-slide-right{from{opacity:0}50%{transform:translate(-.05rem, -50%);opacity:0}to{transform:translate(-.3rem, -50%);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0, 0, 0, 0);position:absolute}a,area,button,input,label,select,summary,textarea,[tabindex]{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media (prefers-reduced-motion: reduce){*:not([aria-busy=true]),:not([aria-busy=true])::before,:not([aria-busy=true])::after{background-attachment:initial !important;animation-duration:1ms !important;animation-delay:-1ms !important;animation-iteration-count:1 !important;scroll-behavior:auto !important;transition-delay:0s !important;transition-duration:0s !important}}
6098\ No newline at end of file
6099 diff --git a/www/public/pico/pico.css b/www/public/pico/pico.css
6100deleted file mode 100644
6101index 90efd36..0000000
6102--- a/www/public/pico/pico.css
6103+++ /dev/null
6104 @@ -1,4 +0,0 @@
6105- /*!
6106- * Pico CSS v1.5.10 (https://picocss.com)
6107- * Copyright 2019-2023 - Licensed under MIT
6108- */:root{--font-family: system-ui, -apple-system, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--line-height: 1.5;--font-weight: 400;--font-size: 16px;--border-radius: 0.25rem;--border-width: 1px;--outline-width: 3px;--spacing: 1rem;--typography-spacing-vertical: 1.5rem;--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing);--grid-spacing-vertical: 0;--grid-spacing-horizontal: var(--spacing);--form-element-spacing-vertical: 0.75rem;--form-element-spacing-horizontal: 1rem;--nav-element-spacing-vertical: 1rem;--nav-element-spacing-horizontal: 0.5rem;--nav-link-spacing-vertical: 0.5rem;--nav-link-spacing-horizontal: 0.5rem;--form-label-font-weight: var(--font-weight);--transition: 0.2s ease-in-out;--modal-overlay-backdrop-filter: blur(0.25rem)}@media (min-width: 576px){:root{--font-size: 17px}}@media (min-width: 768px){:root{--font-size: 18px}}@media (min-width: 992px){:root{--font-size: 19px}}@media (min-width: 1200px){:root{--font-size: 20px}}@media (min-width: 576px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 2.5)}}@media (min-width: 768px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3)}}@media (min-width: 992px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 3.5)}}@media (min-width: 1200px){body>header,body>main,body>footer,section{--block-spacing-vertical: calc(var(--spacing) * 4)}}@media (min-width: 576px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}@media (min-width: 992px){article{--block-spacing-horizontal: calc(var(--spacing) * 1.75)}}@media (min-width: 1200px){article{--block-spacing-horizontal: calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2);--block-spacing-horizontal: var(--spacing)}@media (min-width: 576px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 2.5);--block-spacing-horizontal: calc(var(--spacing) * 1.25)}}@media (min-width: 768px){dialog>article{--block-spacing-vertical: calc(var(--spacing) * 3);--block-spacing-horizontal: calc(var(--spacing) * 1.5)}}a{--text-decoration: none}a.secondary,a.contrast{--text-decoration: underline}small{--font-size: 0.875em}h1,h2,h3,h4,h5,h6{--font-weight: 700}h1{--font-size: 2rem;--typography-spacing-vertical: 3rem}h2{--font-size: 1.75rem;--typography-spacing-vertical: 2.625rem}h3{--font-size: 1.5rem;--typography-spacing-vertical: 2.25rem}h4{--font-size: 1.25rem;--typography-spacing-vertical: 1.874rem}h5{--font-size: 1.125rem;--typography-spacing-vertical: 1.6875rem}[type=checkbox],[type=radio]{--border-width: 2px}[type=checkbox][role=switch]{--border-width: 3px}thead th,thead td,tfoot th,tfoot td{--border-width: 3px}:not(thead,tfoot)>*>td{--font-size: 0.875em}pre,code,kbd,samp{--font-family: "Menlo", "Consolas", "Roboto Mono", "Ubuntu Monospace", "Noto Mono", "Oxygen Mono", "Liberation Mono", monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"}kbd{--font-weight: bolder}[data-theme=light],:root:not([data-theme=dark]){--background-color: #fff;--color: #415462;--h1-color: #1b2832;--h2-color: #24333e;--h3-color: #2c3d49;--h4-color: #374956;--h5-color: #415462;--h6-color: #4d606d;--muted-color: #73828c;--muted-border-color: #edf0f3;--primary: #1095c1;--primary-hover: #08769b;--primary-focus: rgba(16,149,193,.125);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #415462;--secondary-focus: rgba(89,107,120,.125);--secondary-inverse: #fff;--contrast: #1b2832;--contrast-hover: #000;--contrast-focus: rgba(89,107,120,.125);--contrast-inverse: #fff;--mark-background-color: #fff2ca;--mark-color: #543a26;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: transparent;--form-element-border-color: #a2afb9;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: transparent;--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #d5dce2;--form-element-disabled-border-color: #a2afb9;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #c62828;--form-element-invalid-active-border-color: #d32f2f;--form-element-invalid-focus-color: rgba(211,47,47,.125);--form-element-valid-border-color: #388e3c;--form-element-valid-active-border-color: #43a047;--form-element-valid-focus-color: rgba(67,160,71,.125);--switch-background-color: #bbc6ce;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #d5dce2;--range-active-border-color: #bbc6ce;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: #f6f8f9;--code-background-color: #edf0f3;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #b34d80;--code-property-color: #3d888f;--code-value-color: #986;--code-comment-color: #a2afb9;--accordion-border-color: var(--muted-border-color);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: var(--background-color);--card-border-color: var(--muted-border-color);--card-box-shadow: .0145rem .029rem .174rem rgba(27,40,50,.01698), .0335rem .067rem .402rem rgba(27,40,50,.024), .0625rem .125rem .75rem rgba(27,40,50,.03), .1125rem .225rem 1.35rem rgba(27,40,50,.036), .2085rem .417rem 2.502rem rgba(27,40,50,.04302), .5rem 1rem 6rem rgba(27,40,50,.06), 0 0 0 0.0625rem rgba(27,40,50,.015);--card-sectionning-background-color: #fbfbfc;--dropdown-background-color: #fbfbfc;--dropdown-border-color: #e1e6eb;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: #edf0f3;--modal-overlay-background-color: rgba(213,220,226,.7);--progress-background-color: #d5dce2;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(198, 40, 40)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(65, 84, 98)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(56, 142, 60)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:light}@media only screen and (prefers-color-scheme: dark){:root:not([data-theme]){--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}}[data-theme=dark]{--background-color: #11191f;--color: #bbc6ce;--h1-color: #edf0f3;--h2-color: #e1e6eb;--h3-color: #d5dce2;--h4-color: #c8d1d8;--h5-color: #bbc6ce;--h6-color: #afbbc4;--muted-color: #73828c;--muted-border-color: #1f2d38;--primary: #1095c1;--primary-hover: #1ab3e6;--primary-focus: rgba(16,149,193,.25);--primary-inverse: #fff;--secondary: #596b78;--secondary-hover: #73828c;--secondary-focus: rgba(115,130,140,.25);--secondary-inverse: #fff;--contrast: #edf0f3;--contrast-hover: #fff;--contrast-focus: rgba(115,130,140,.25);--contrast-inverse: #000;--mark-background-color: #d1c284;--mark-color: #11191f;--ins-color: #388e3c;--del-color: #c62828;--blockquote-border-color: var(--muted-border-color);--blockquote-footer-color: var(--muted-color);--button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--form-element-background-color: #11191f;--form-element-border-color: #374956;--form-element-color: var(--color);--form-element-placeholder-color: var(--muted-color);--form-element-active-background-color: var(--form-element-background-color);--form-element-active-border-color: var(--primary);--form-element-focus-color: var(--primary-focus);--form-element-disabled-background-color: #2c3d49;--form-element-disabled-border-color: #415462;--form-element-disabled-opacity: 0.5;--form-element-invalid-border-color: #b71c1c;--form-element-invalid-active-border-color: #c62828;--form-element-invalid-focus-color: rgba(198,40,40,.25);--form-element-valid-border-color: #2e7d32;--form-element-valid-active-border-color: #388e3c;--form-element-valid-focus-color: rgba(56,142,60,.25);--switch-background-color: #374956;--switch-color: var(--primary-inverse);--switch-checked-background-color: var(--primary);--range-border-color: #24333e;--range-active-border-color: #2c3d49;--range-thumb-border-color: var(--background-color);--range-thumb-color: var(--secondary);--range-thumb-hover-color: var(--secondary-hover);--range-thumb-active-color: var(--primary);--table-border-color: var(--muted-border-color);--table-row-stripped-background-color: rgba(115,130,140,.05);--code-background-color: #18232c;--code-color: var(--muted-color);--code-kbd-background-color: var(--contrast);--code-kbd-color: var(--contrast-inverse);--code-tag-color: #a65980;--code-property-color: #599fa6;--code-value-color: #8c8473;--code-comment-color: #4d606d;--accordion-border-color: var(--muted-border-color);--accordion-active-summary-color: var(--primary);--accordion-close-summary-color: var(--color);--accordion-open-summary-color: var(--muted-color);--card-background-color: #141e26;--card-border-color: var(--card-background-color);--card-box-shadow: .0145rem .029rem .174rem rgba(0,0,0,.01698), .0335rem .067rem .402rem rgba(0,0,0,.024), .0625rem .125rem .75rem rgba(0,0,0,.03), .1125rem .225rem 1.35rem rgba(0,0,0,.036), .2085rem .417rem 2.502rem rgba(0,0,0,.04302), .5rem 1rem 6rem rgba(0,0,0,.06), 0 0 0 0.0625rem rgba(0,0,0,.015);--card-sectionning-background-color: #18232c;--dropdown-background-color: #1b2832;--dropdown-border-color: #24333e;--dropdown-box-shadow: var(--card-box-shadow);--dropdown-color: var(--color);--dropdown-hover-background-color: rgba(36,51,62,.75);--modal-overlay-background-color: rgba(36,51,62,.8);--progress-background-color: #24333e;--progress-color: var(--primary);--loading-spinner-opacity: 0.5;--tooltip-background-color: var(--contrast);--tooltip-color: var(--contrast-inverse);--icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(0, 0, 0)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(115, 130, 140)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(183, 28, 28)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(162, 175, 185)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(46, 125, 50)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");color-scheme:dark}progress,[type=checkbox],[type=radio],[type=range]{accent-color:var(--primary)}*,*::before,*::after{box-sizing:border-box;background-repeat:no-repeat}::before,::after{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-text-size-adjust:100%;text-size-adjust:100%;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);text-rendering:optimizeLegibility;overflow-wrap:break-word;cursor:default;tab-size:4}main{display:block}body{width:100%;margin:0}body>header,body>main,body>footer{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media (min-width: 576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width: 768px){.container{max-width:700px}}@media (min-width: 992px){.container{max-width:920px}}@media (min-width: 1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media (min-width: 992px){.grid{grid-template-columns:repeat(auto-fit, minmax(0%, 1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing)*.5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}a,[role=link]{--color: var(--primary);--background-color: transparent;outline:none;background-color:var(--background-color);color:var(--color);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition)}a:is([aria-current],:hover,:active,:focus),[role=link]:is([aria-current],:hover,:active,:focus){--color: var(--primary-hover);--text-decoration: underline}a:focus,[role=link]:focus{--background-color: var(--primary-focus)}a.secondary,[role=link].secondary{--color: var(--secondary)}a.secondary:is([aria-current],:hover,:active,:focus),[role=link].secondary:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}a.secondary:focus,[role=link].secondary:focus{--background-color: var(--secondary-focus)}a.contrast,[role=link].contrast{--color: var(--contrast)}a.contrast:is([aria-current],:hover,:active,:focus),[role=link].contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}a.contrast:focus,[role=link].contrast:focus{--background-color: var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color: var(--h1-color)}h2{--color: var(--h2-color)}h3{--color: var(--h3-color)}h4{--color: var(--h4-color)}h5{--color: var(--h5-color)}h6{--color: var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}hgroup,.headings{margin-bottom:var(--typography-spacing-vertical)}hgroup>*,.headings>*{margin-bottom:0}hgroup>*:last-child,.headings>*:last-child{--color: var(--muted-color);--font-weight: unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);padding-inline-start:var(--spacing);padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical)*.25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical)*.25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);border-inline-start:.25rem solid var(--blockquote-border-color);border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical)*.5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}button,input[type=submit],input[type=button],input[type=reset],[role=button]{--background-color: var(--primary);--border-color: var(--primary);--color: var(--primary-inverse);--box-shadow: var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}button:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),[role=button]:is([aria-current],:hover,:active,:focus){--background-color: var(--primary-hover);--border-color: var(--primary-hover);--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color: var(--primary-inverse)}button:focus,input[type=submit]:focus,input[type=button]:focus,input[type=reset]:focus,[role=button]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--primary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).secondary,input[type=reset]{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);cursor:pointer}:is(button,input[type=submit],input[type=button],[role=button]).secondary:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover);--color: var(--secondary-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).secondary:focus,input[type=reset]:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--secondary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).contrast{--background-color: var(--contrast);--border-color: var(--contrast);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:is([aria-current],:hover,:active,:focus){--background-color: var(--contrast-hover);--border-color: var(--contrast-hover);--color: var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:focus{--box-shadow: var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--outline-width) var(--contrast-focus)}:is(button,input[type=submit],input[type=button],[role=button]).outline,input[type=reset].outline{--background-color: transparent;--color: var(--primary)}:is(button,input[type=submit],input[type=button],[role=button]).outline:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--background-color: transparent;--color: var(--primary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary,input[type=reset].outline{--color: var(--secondary)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--color: var(--secondary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast{--color: var(--contrast)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast:is([aria-current],:hover,:active,:focus){--color: var(--contrast-hover)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:none}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem*var(--line-height) + var(--form-element-spacing-vertical)*2 + var(--border-width)*2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}label,fieldset legend{display:block;margin-bottom:calc(var(--spacing)*.25);font-weight:var(--form-label-font-weight, var(--font-weight))}input:not([type=checkbox],[type=radio]),select,textarea{width:100%}input:not([type=checkbox],[type=radio],[type=range],[type=file]),select,textarea{appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color: var(--form-element-background-color);--border-color: var(--form-element-border-color);--color: var(--form-element-color);--box-shadow: none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--background-color: var(--form-element-active-background-color)}input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),:where(select,textarea):is(:active,:focus){--border-color: var(--form-element-active-border-color)}input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,select:focus,textarea:focus{--box-shadow: 0 0 0 var(--outline-width) var(--form-element-focus-color)}input:not([type=submit],[type=button],[type=reset])[disabled],select[disabled],textarea[disabled],:where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){--background-color: var(--form-element-disabled-background-color);--border-color: var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal) !important;padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem) !important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color: var(--form-element-valid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-valid-focus-color) !important}:where(input,select,textarea)[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color: var(--form-element-invalid-active-border-color) !important;--box-shadow: 0 0 0 var(--outline-width) var(--form-element-invalid-focus-color) !important}[dir=rtl] :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}input::placeholder,input::-webkit-input-placeholder,textarea::placeholder,textarea::-webkit-input-placeholder,select:invalid{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox],[type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:rgba(0,0,0,0)}select:not([multiple],[size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple],[size]){background-position:center left .75rem}:where(input,select,textarea,.grid)+small{display:block;width:100%;margin-top:calc(var(--spacing)*-.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing)*.25)}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-right:.375em;margin-left:0;margin-inline-start:0;margin-inline-end:.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-right:.375em;margin-bottom:0;cursor:pointer}[type=checkbox]:indeterminate{--background-color: var(--primary);--border-color: var(--primary);background-image:var(--icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color: var(--primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color);--color: var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type=checkbox][role=switch]:focus{--background-color: var(--switch-background-color);--border-color: var(--switch-background-color)}[type=checkbox][role=switch]:checked{--background-color: var(--switch-checked-background-color);--border-color: var(--switch-checked-background-color)}[type=checkbox][role=switch]:before{display:block;width:calc(1.25em - (var(--border-width) * 2));height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:checked{background-image:none}[type=checkbox][role=switch]:checked::before{margin-left:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type=checkbox][aria-invalid=false],[type=checkbox]:checked[aria-invalid=false],[type=radio][aria-invalid=false],[type=radio]:checked[aria-invalid=false],[type=checkbox][role=switch][aria-invalid=false],[type=checkbox][role=switch]:checked[aria-invalid=false]{--border-color: var(--form-element-valid-border-color)}[type=checkbox][aria-invalid=true],[type=checkbox]:checked[aria-invalid=true],[type=radio][aria-invalid=true],[type=radio]:checked[aria-invalid=true],[type=checkbox][role=switch][aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true]{--border-color: var(--form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius)*.5)}input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--icon-position: 0.75rem;--icon-width: 1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width)*-1);margin-left:var(--icon-position);opacity:0}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}@-moz-document url-prefix(){[type=date],[type=datetime-local],[type=month],[type=time],[type=week]{padding-right:var(--form-element-spacing-horizontal) !important;background-image:none !important}}[type=file]{--color: var(--muted-color);padding:calc(var(--form-element-spacing-vertical)*.5) 0;border:0;border-radius:0;background:none}[type=file]::file-selector-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::file-selector-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-webkit-file-upload-button{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=file]::-ms-browse{--background-color: var(--secondary);--border-color: var(--secondary);--color: var(--secondary-inverse);margin-right:calc(var(--spacing)/2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/2);padding:calc(var(--form-element-spacing-vertical)*.5) calc(var(--form-element-spacing-horizontal)*.5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:none;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-ms-browse:is(:hover,:active,:focus){--background-color: var(--secondary-hover);--border-color: var(--secondary-hover)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}[type=range]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;transition:background-color var(--transition),transform var(--transition)}[type=range]:hover,[type=range]:focus{--range-border-color: var(--range-active-border-color);--range-thumb-color: var(--range-thumb-hover-color)}[type=range]:active{--range-thumb-color: var(--range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem) !important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--icon-search),var(--icon-valid)}input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--icon-search),var(--icon-invalid)}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}th,td{padding:calc(var(--spacing)/2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot th,tfoot td{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}pre,code,kbd,samp{font-size:.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}pre,code,kbd{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem .5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:none;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none !important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--transition)}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;margin-inline-start:calc(var(--spacing, 1rem)*.5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:none}details summary:focus:not([role=button]){color:var(--accordion-active-summary-color)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem*var(--line-height, 1.5));background-image:var(--icon-chevron-button)}details summary[role=button]:not(.outline).contrast::after{background-image:var(--icon-chevron-button-inverse)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>header,article>footer{margin-right:calc(var(--block-spacing-horizontal)*-1);margin-left:calc(var(--block-spacing-horizontal)*-1);padding:calc(var(--block-spacing-vertical)*.66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical)*-1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical)*-1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width: 0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;backdrop-filter:var(--modal-overlay-backdrop-filter);background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing)*2);overflow:auto}@media (min-width: 576px){dialog article{max-width:510px}}@media (min-width: 768px){dialog article{max-width:700px}}dialog article>header,dialog article>footer{padding:calc(var(--block-spacing-vertical)*.5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role=button]{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type){margin-left:calc(var(--spacing)*.5)}dialog article p:last-of-type{margin:0}dialog article .close{display:block;width:1rem;height:1rem;margin-top:calc(var(--block-spacing-vertical)*-.5);margin-bottom:var(--typography-spacing-vertical);margin-left:auto;background-image:var(--icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;opacity:.5;transition:opacity var(--transition)}dialog article .close:is([aria-current],:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--scrollbar-width, 0px);overflow:hidden;pointer-events:none;touch-action:none}.modal-is-open dialog{pointer-events:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-duration:.2s;animation-timing-function:ease-in-out;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{animation-duration:.8s;animation-name:modal-overlay}:where(.modal-is-opening,.modal-is-closing) dialog>article{animation-delay:.2s;animation-name:modal}.modal-is-closing dialog,.modal-is-closing dialog>article{animation-delay:0s;animation-direction:reverse}@keyframes modal-overlay{from{backdrop-filter:none;background-color:rgba(0,0,0,0)}}@keyframes modal{from{transform:translateY(-100%);opacity:0}}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal)*-1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal)*-1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing: 0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical)*-1) calc(var(--nav-link-spacing-horizontal)*-1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav[aria-label=breadcrumb]{align-items:center;justify-content:start}nav[aria-label=breadcrumb] ul li:not(:first-child){margin-inline-start:var(--nav-link-spacing-horizontal)}nav[aria-label=breadcrumb] ul li:not(:last-child) ::after{position:absolute;width:calc(var(--nav-link-spacing-horizon