this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: performance improvements

+2071 -366
+3 -3
deno.json
··· 1 1 { 2 - "version": "3.6.1", 2 + "version": "3.7.0", 3 3 "workspace": ["./data"], 4 4 "compilerOptions": { 5 5 "lib": [ ··· 43 43 "@byojs/storage": "npm:@byojs/storage@^0.12.1", 44 44 "@civility/store": "jsr:@civility/store@^0.3.1", 45 45 "@civility/ui": "jsr:@civility/ui@^0.2.3", 46 - "@civility/workers": "jsr:@civility/workers@^0.2.3", 46 + "@civility/workers": "jsr:@civility/workers@^0.2.4", 47 47 "@flashcard/core": "jsr:@flashcard/core@^0.1.0", 48 48 "@flashcard/schedulers": "jsr:@flashcard/schedulers@^0.1.0", 49 49 "@leeoniya/ufuzzy": "npm:@leeoniya/ufuzzy@^1.0.19", 50 50 "@std/assert": "jsr:@std/assert@^1.0.19", 51 51 "@std/async": "jsr:@std/async@^1.2.0", 52 - "@zod/zod": "jsr:@zod/zod@^4.3.6", 52 + "@zod/zod/mini": "jsr:@zod/zod@^4.3.6/mini", 53 53 "howler": "npm:howler@^2.2.4", 54 54 "lit": "npm:lit@^3.3.2", 55 55 "signature_pad": "npm:signature_pad@^5.1.3"
+6 -203
deno.lock
··· 5 5 "jsr:@civility/store@~0.3.1": "0.3.1", 6 6 "jsr:@civility/sync@~0.1.1": "0.1.1", 7 7 "jsr:@civility/ui@~0.2.3": "0.2.3", 8 - "jsr:@civility/workers@~0.2.3": "0.2.3", 9 - "jsr:@cliffy/ansi@1": "1.0.0", 10 - "jsr:@cliffy/ansi@1.0.0": "1.0.0", 11 - "jsr:@cliffy/command@1": "1.0.0", 12 - "jsr:@cliffy/flags@1": "1.0.0", 13 - "jsr:@cliffy/flags@1.0.0": "1.0.0", 14 - "jsr:@cliffy/internal@1.0.0": "1.0.0", 15 - "jsr:@cliffy/keycode@1": "1.0.0", 16 - "jsr:@cliffy/keycode@1.0.0": "1.0.0", 17 - "jsr:@cliffy/keypress@1": "1.0.0", 18 - "jsr:@cliffy/prompt@1": "1.0.0", 19 - "jsr:@cliffy/table@1": "1.0.0", 20 - "jsr:@cliffy/table@1.0.0": "1.0.0", 21 - "jsr:@flashcard/core@0.0.4": "0.0.4", 8 + "jsr:@civility/workers@~0.2.4": "0.2.4", 22 9 "jsr:@flashcard/core@0.1": "0.1.0", 23 - "jsr:@flashcard/core@0.1.0": "0.1.0", 24 - "jsr:@flashcard/schedulers@*": "0.0.4", 25 10 "jsr:@flashcard/schedulers@0.1": "0.1.0", 26 - "jsr:@flashcard/utils@*": "0.0.4", 27 - "jsr:@flashcard/utils@~0.1.1": "0.1.1", 28 11 "jsr:@paulmillr/qr@~0.5.5": "0.5.5", 29 - "jsr:@std/assert@0.216": "0.216.0", 30 - "jsr:@std/assert@^1.0.18": "1.0.19", 31 12 "jsr:@std/assert@^1.0.19": "1.0.19", 32 13 "jsr:@std/async@^1.2.0": "1.2.0", 33 - "jsr:@std/bytes@0.216": "0.216.0", 34 - "jsr:@std/bytes@^1.0.6": "1.0.6", 35 14 "jsr:@std/collections@^1.1.6": "1.1.6", 36 - "jsr:@std/csv@^1.0.6": "1.0.6", 37 - "jsr:@std/dotenv@~0.225.6": "0.225.6", 38 - "jsr:@std/encoding@^1.0.10": "1.0.10", 39 - "jsr:@std/fmt@^1.0.9": "1.0.9", 40 - "jsr:@std/fs@0.216": "0.216.0", 41 - "jsr:@std/fs@^1.0.23": "1.0.23", 42 15 "jsr:@std/html@^1.0.5": "1.0.5", 43 16 "jsr:@std/internal@^1.0.12": "1.0.12", 44 - "jsr:@std/io@0.216": "0.216.0", 45 - "jsr:@std/io@~0.225.3": "0.225.3", 46 - "jsr:@std/path@0.216": "0.216.0", 47 - "jsr:@std/path@^1.1.4": "1.1.4", 48 17 "jsr:@std/semver@^1.0.8": "1.0.8", 49 - "jsr:@std/streams@^1.0.9": "1.0.17", 50 - "jsr:@std/text@^1.0.17": "1.0.17", 51 18 "jsr:@zod/zod@^4.3.6": "4.3.6", 52 19 "npm:@byojs/storage@~0.12.1": "0.12.1", 53 20 "npm:@leeoniya/ufuzzy@^1.0.19": "1.0.19", ··· 59 26 "npm:kuromoji@~0.1.2": "0.1.2", 60 27 "npm:lit@^3.3.2": "3.3.2", 61 28 "npm:native-file-system-adapter@^3.0.1": "3.0.1", 62 - "npm:opencc-js@*": "1.0.5", 63 29 "npm:opencc-js@^1.0.5": "1.0.5", 64 30 "npm:pinyin-to-zhuyin@^1.0.3": "1.0.3", 65 31 "npm:pinyin-tone-tool@^1.0.5": "1.0.5", 66 - "npm:signature_pad@^5.1.3": "5.1.3", 67 - "npm:ts-fsrs@^5.2.3": "5.2.3" 32 + "npm:signature_pad@^5.1.3": "5.1.3" 68 33 }, 69 34 "jsr": { 70 35 "@civility/store@0.3.1": { 71 36 "integrity": "0438f2cdb16145a61a97f5be509cd0b34e7cbd9f71dc657feffe2a4dd7dd0ec3", 72 37 "dependencies": [ 73 - "jsr:@std/fs@^1.0.23", 74 38 "jsr:@std/semver" 75 39 ] 76 40 }, ··· 89 53 "npm:lit" 90 54 ] 91 55 }, 92 - "@civility/workers@0.2.3": { 93 - "integrity": "84130ff9b3c5d0ee133d8ed076dd86d5ea4a3bb8f49c06c114959eb4e0c66602" 94 - }, 95 - "@cliffy/ansi@1.0.0": { 96 - "integrity": "987008f74e50aa72cc1517ffccc769711734a14927bc4599e052efe1b9a840e2", 97 - "dependencies": [ 98 - "jsr:@cliffy/internal", 99 - "jsr:@std/encoding", 100 - "jsr:@std/fmt", 101 - "jsr:@std/io@~0.225.3" 102 - ] 103 - }, 104 - "@cliffy/command@1.0.0": { 105 - "integrity": "c52a241ea68857fcdaff4f3173eb404f8017d7bc35553b6f533c592b89dde7d2", 106 - "dependencies": [ 107 - "jsr:@cliffy/flags@1.0.0", 108 - "jsr:@cliffy/internal", 109 - "jsr:@cliffy/table@1.0.0", 110 - "jsr:@std/fmt", 111 - "jsr:@std/semver", 112 - "jsr:@std/text" 113 - ] 114 - }, 115 - "@cliffy/flags@1.0.0": { 116 - "integrity": "8b57698adc644da8f90422d58976362d41a4ebca39c312ca1c101585d0148feb", 117 - "dependencies": [ 118 - "jsr:@cliffy/internal", 119 - "jsr:@std/text" 120 - ] 121 - }, 122 - "@cliffy/internal@1.0.0": { 123 - "integrity": "1e17ccbcd5420093c0a93e5b3827bbdc9abac5195bacf187edc44665e54bdde6" 124 - }, 125 - "@cliffy/keycode@1.0.0": { 126 - "integrity": "755dbf007be110dcb5625f87eb61b362b6a0ca6835453af03ebd3b34d399cf14" 127 - }, 128 - "@cliffy/keypress@1.0.0": { 129 - "integrity": "dd2e33484bea5fedf9bad5ed4aa0248a53373427d70cb94de4aad3052f948cea", 130 - "dependencies": [ 131 - "jsr:@cliffy/internal", 132 - "jsr:@cliffy/keycode@1.0.0" 133 - ] 134 - }, 135 - "@cliffy/prompt@1.0.0": { 136 - "integrity": "48b4cd35199fda7832f35e1fe0a3e8bc2b1ea49ba57b4ec0e29e22db44e8ca9f", 137 - "dependencies": [ 138 - "jsr:@cliffy/ansi@1.0.0", 139 - "jsr:@cliffy/internal", 140 - "jsr:@cliffy/keycode@1.0.0", 141 - "jsr:@std/assert@^1.0.18", 142 - "jsr:@std/fmt", 143 - "jsr:@std/io@~0.225.3", 144 - "jsr:@std/path@^1.1.4", 145 - "jsr:@std/text" 146 - ] 147 - }, 148 - "@cliffy/table@1.0.0": { 149 - "integrity": "3fdaa9e1ef1ea62022108adabd826932bdea8dd05497079896febcd41322907f", 150 - "dependencies": [ 151 - "jsr:@std/fmt" 152 - ] 153 - }, 154 - "@flashcard/core@0.0.4": { 155 - "integrity": "251f76a99d931e263fdd7182d0a01efa4cdc1af1b84fb78efdf890dcf8e1190b" 56 + "@civility/workers@0.2.4": { 57 + "integrity": "38fafb96bc15a988e7723bc9b021394bdfef842c8e8372b960ec2476e5c74b43" 156 58 }, 157 59 "@flashcard/core@0.1.0": { 158 60 "integrity": "8c1379f8738d28bdaaf6ab7f30d35913c22a9ba7e86c74e22cd93ee3d58d4e15", 159 61 "dependencies": [ 160 62 "jsr:@civility/store@~0.3.1", 161 63 "jsr:@std/collections" 162 - ] 163 - }, 164 - "@flashcard/schedulers@0.0.4": { 165 - "integrity": "50deef40beeb1e2700a0d2526b35e7dc15ad6b0e4ca0e1fc2ca3ab7daf0da7a8", 166 - "dependencies": [ 167 - "jsr:@flashcard/core@0.0.4" 168 64 ] 169 65 }, 170 66 "@flashcard/schedulers@0.1.0": { 171 67 "integrity": "adff86f591b34cb469bc7a08ef463eb9f557e45f3f0c9f45fc27c5bdd7264f5a", 172 68 "dependencies": [ 173 - "jsr:@flashcard/core@0.1", 174 - "npm:ts-fsrs" 175 - ] 176 - }, 177 - "@flashcard/utils@0.0.4": { 178 - "integrity": "45090ff2fb7027b085f49d22a0e3e5c9e235469f43c9d5a25249d7da839d19ef", 179 - "dependencies": [ 180 - "jsr:@flashcard/core@0.0.4", 181 - "jsr:@std/fs@0.216", 182 - "jsr:@std/io@0.216", 183 - "jsr:@std/path@0.216" 184 - ] 185 - }, 186 - "@flashcard/utils@0.1.1": { 187 - "integrity": "a22b83b8ef743c43e50366c405ce252eddb95bd9ee7b164a5cf0d0cd141747fb", 188 - "dependencies": [ 189 - "jsr:@flashcard/core@0.1", 190 - "jsr:@std/io@~0.225.3", 191 - "jsr:@std/path@^1.1.4" 69 + "jsr:@flashcard/core" 192 70 ] 193 71 }, 194 72 "@paulmillr/qr@0.5.5": { 195 73 "integrity": "2f8ff22c8d2194f2147eac1b3093f5e85f648c0a8005d5635a617fb72bf5ae38" 196 - }, 197 - "@std/assert@0.216.0": { 198 - "integrity": "f65392edf3bec39e2d1028e51f1da549db81691034b244eaba83318a757c0c2e" 199 74 }, 200 75 "@std/assert@1.0.19": { 201 76 "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e", ··· 206 81 "@std/async@1.2.0": { 207 82 "integrity": "c059c6f6d95ca7cc012ae8e8d7164d1697113d54b0b679e4372b354b11c2dee5" 208 83 }, 209 - "@std/bytes@0.216.0": { 210 - "integrity": "aa3c633dbfdaeb03333a0924ee839563cf615163d1945978792f2132acb452a5" 211 - }, 212 - "@std/bytes@1.0.6": { 213 - "integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a" 214 - }, 215 84 "@std/collections@1.1.6": { 216 85 "integrity": "b458160ce65ea5ad35da05d0a5cbee4b583677c8b443a10d7beb0c4ac63f2baa" 217 86 }, 218 - "@std/csv@1.0.6": { 219 - "integrity": "52ef0e62799a0028d278fa04762f17f9bd263fad9a8e7f98c14fbd371d62d9fd", 220 - "dependencies": [ 221 - "jsr:@std/streams" 222 - ] 223 - }, 224 - "@std/dotenv@0.225.6": { 225 - "integrity": "1d6f9db72f565bd26790fa034c26e45ecb260b5245417be76c2279e5734c421b" 226 - }, 227 - "@std/encoding@1.0.10": { 228 - "integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1" 229 - }, 230 - "@std/fmt@1.0.9": { 231 - "integrity": "2487343e8899fb2be5d0e3d35013e54477ada198854e52dd05ed0422eddcabe0" 232 - }, 233 - "@std/fs@0.216.0": { 234 - "integrity": "5ed8d2b3aa820d42dd84ee612aed5e4e24f20046709d41d18024ae2ac7463ed6", 235 - "dependencies": [ 236 - "jsr:@std/assert@0.216", 237 - "jsr:@std/path@0.216" 238 - ] 239 - }, 240 - "@std/fs@1.0.23": { 241 - "integrity": "3ecbae4ce4fee03b180fa710caff36bb5adb66631c46a6460aaad49515565a37", 242 - "dependencies": [ 243 - "jsr:@std/internal", 244 - "jsr:@std/path@^1.1.4" 245 - ] 246 - }, 247 87 "@std/html@1.0.5": { 248 88 "integrity": "4e2d693f474cae8c16a920fa5e15a3b72267b94b84667f11a50c6dd1cb18d35e" 249 89 }, 250 90 "@std/internal@1.0.12": { 251 91 "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" 252 92 }, 253 - "@std/io@0.216.0": { 254 - "integrity": "ea24055b4fbeb2a26efcab0179b2bf56306e0b7cedaa56c24072549803784f1f", 255 - "dependencies": [ 256 - "jsr:@std/assert@0.216", 257 - "jsr:@std/bytes@0.216" 258 - ] 259 - }, 260 - "@std/io@0.225.3": { 261 - "integrity": "27b07b591384d12d7b568f39e61dff966b8230559122df1e9fd11cc068f7ddd1", 262 - "dependencies": [ 263 - "jsr:@std/bytes@^1.0.6" 264 - ] 265 - }, 266 - "@std/path@0.216.0": { 267 - "integrity": "85c121f1b43f7fb17e225567c4f151b3a67796ed8c619d37f9d61ea46116a2f0", 268 - "dependencies": [ 269 - "jsr:@std/assert@0.216" 270 - ] 271 - }, 272 - "@std/path@1.1.4": { 273 - "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5", 274 - "dependencies": [ 275 - "jsr:@std/internal" 276 - ] 277 - }, 278 93 "@std/semver@1.0.8": { 279 94 "integrity": "dc830e8b8b6a380c895d53fbfd1258dc253704ca57bbe1629ac65fd7830179b7" 280 - }, 281 - "@std/streams@1.0.17": { 282 - "integrity": "7859f3d9deed83cf4b41f19223d4a67661b3d3819e9fc117698f493bf5992140", 283 - "dependencies": [ 284 - "jsr:@std/bytes@^1.0.6" 285 - ] 286 - }, 287 - "@std/text@1.0.17": { 288 - "integrity": "4b2c4ef67ae5b6c1dfd447c81c83a43718f52e3c7e748d8b33f694aba9895f95" 289 95 }, 290 96 "@zod/zod@4.3.6": { 291 97 "integrity": "7144e5e11f8ffc3cf6e2fca624f6597a8762898aac9868cc8938e9398b96ffe4" ··· 474 280 "safe-buffer" 475 281 ] 476 282 }, 477 - "ts-fsrs@5.2.3": { 478 - "integrity": "sha512-R3IjceC9WfnvUin6Nx+DwqEzh3Qil6Gg2yEHqvocUcC7Nbi+xDrFg/1fKaYBT0tJedDnDAguXMSX0hijhi859w==" 479 - }, 480 283 "universalify@2.0.1": { 481 284 "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" 482 285 }, ··· 505 308 "jsr:@civility/store@~0.3.1", 506 309 "jsr:@civility/sync@~0.1.1", 507 310 "jsr:@civility/ui@~0.2.3", 508 - "jsr:@civility/workers@~0.2.3", 311 + "jsr:@civility/workers@~0.2.4", 509 312 "jsr:@flashcard/core@0.1", 510 313 "jsr:@flashcard/schedulers@0.1", 511 314 "jsr:@std/assert@^1.0.19",
+44 -33
www/index.html
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <meta name="viewport" content="width=device-width"> 6 + <meta name="mobile-web-app-capable" content="yes"> 7 + <meta name="apple-mobile-web-app-capable" content="yes"> 8 + <meta name="description" content="An app for learning Hanzi and Kanji"> 3 9 4 - <head> 5 - <meta charset="utf-8" /> 6 - <meta name="viewport" content="width=device-width"> 7 - <meta name="mobile-web-app-capable" content="yes"> 8 - <meta name="apple-mobile-web-app-capable" content="yes"> 9 - <meta name="description" content="An app for learning Hanzi and Kanji"> 10 + <link rel="manifest" href="manifest.json" /> 11 + <link rel="icon" type="image/x-icon" href="/dist/icons/icon.ico" /> 12 + <link rel="apple-touch-icon" href="/dist/icons/icon.png"> 10 13 11 - <link rel="manifest" href="manifest.json" /> 12 - <link rel="icon" type="image/x-icon" href="/dist/icons/icon.ico" /> 13 - <link rel="apple-touch-icon" href="/dist/icons/icon.png"> 14 + <link rel="canonical" href="https://hanzi.bpev.me" /> 15 + <title>HanziApp</title> 14 16 15 - <link rel="canonical" href="https://hanzi.bpev.me" /> 16 - <title>HanziApp</title> 17 + <link 18 + rel="preload" 19 + href="/static/styles/civility.min.css" 20 + as="style" 21 + onload="this.onload=null;this.rel='stylesheet'" 22 + /> 23 + <noscript> 24 + <link rel="stylesheet" href="/static/styles/civility.min.css" /> 25 + </noscript> 17 26 18 - <link rel="preload" href="/static/styles/civility.min.css" as="style" 19 - onload="this.onload=null;this.rel='stylesheet'" /> 20 - <noscript> 21 - <link rel="stylesheet" href="/static/styles/civility.min.css" /> 22 - </noscript> 27 + <link 28 + rel="preload" 29 + href="/static/styles/theme.css" 30 + as="style" 31 + onload="this.onload=null;this.rel='stylesheet'" 32 + /> 33 + <noscript> 34 + <link rel="stylesheet" href="/static/styles/theme.css" /> 35 + </noscript> 23 36 24 - <link rel="preload" href="/static/styles/theme.css" as="style" onload="this.onload=null;this.rel='stylesheet'" /> 25 - <noscript> 26 - <link rel="stylesheet" href="/static/styles/theme.css" /> 27 - </noscript> 28 - 29 - <link rel="preload" href="/static/styles/utilities.min.css" as="style" 30 - onload="this.onload=null;this.rel='stylesheet'" /> 31 - <noscript> 32 - <link rel="stylesheet" href="/static/styles/utilities.min.css" /> 33 - </noscript> 34 - 35 - <script src="/dist/index.js" type="module"></script> 36 - </head> 37 + <link 38 + rel="preload" 39 + href="/static/styles/utilities.min.css" 40 + as="style" 41 + onload="this.onload=null;this.rel='stylesheet'" 42 + /> 43 + <noscript> 44 + <link rel="stylesheet" href="/static/styles/utilities.min.css" /> 45 + </noscript> 37 46 38 - <body style="margin: 0"> 39 - <header></header><ui-sub-header></ui-sub-header> 40 - <main></main> 41 - </body> 47 + <script src="/dist/index.js" type="module"></script> 48 + </head> 42 49 50 + <body style="margin: 0"> 51 + <header></header><ui-sub-header></ui-sub-header> 52 + <main></main> 53 + </body> 43 54 </html>
+36 -33
www/models/schema/v0.ts
··· 1 - import { z } from '@zod/zod' 1 + import { z } from '@zod/zod/mini' 2 2 import { Locale } from '$/enums.ts' 3 3 import { CardSortMethod } from '@flashcard/core' 4 4 5 - export const threshold = z.union([ 5 + export const threshold = z.optional(z.union([ 6 6 z.date(), 7 - z.iso.datetime().transform((s) => new Date(s)), 8 - ]).optional() 7 + z.pipe(z.iso.datetime(), z.transform((s) => new Date(s))), 8 + ])) 9 9 10 10 export const Assignment = z.object({ 11 11 difficulty: z.number(), ··· 13 13 interval: z.number(), 14 14 repetition: z.number(), 15 15 subjectId: z.string(), 16 - markedCompleted: z.boolean().default(false), 16 + markedCompleted: z._default(z.boolean(), false), 17 17 lastStudiedAt: threshold, 18 18 unlockedAt: threshold, 19 19 availableAt: threshold, ··· 26 26 27 27 export const OverrideProps = z.object({ 28 28 subjectId: z.string(), 29 - primaryMeaning: z.string().optional(), 30 - auxMeanings: z.string().optional(), 31 - meaningHint: z.string().optional(), 32 - meaningMnemonic: z.string().optional(), 33 - readingHint: z.string().optional(), 34 - readingMnemonic: z.string().optional(), 29 + primaryMeaning: z.optional(z.string()), 30 + auxMeanings: z.optional(z.string()), 31 + meaningHint: z.optional(z.string()), 32 + meaningMnemonic: z.optional(z.string()), 33 + readingHint: z.optional(z.string()), 34 + readingMnemonic: z.optional(z.string()), 35 35 }) 36 36 37 37 export type OverrideProps = z.infer<typeof OverrideProps> 38 38 39 - export const Override = OverrideProps.extend({ 40 - [Locale.zh_CN]: OverrideProps.optional(), 41 - [Locale.zh_HK]: OverrideProps.optional(), 42 - [Locale.zh_TW]: OverrideProps.optional(), 39 + export const Override = z.object({ 40 + ...OverrideProps.shape, 41 + [Locale.zh_CN]: z.optional(OverrideProps), 42 + [Locale.zh_HK]: z.optional(OverrideProps), 43 + [Locale.zh_TW]: z.optional(OverrideProps), 43 44 }) 44 45 45 46 export type Override = z.infer<typeof Override> 46 47 47 48 export const Settings = z.object({ 48 - locale: z.enum(Locale).default(Locale.zh_CN), 49 - playAudio: z.boolean().default(true), 50 - reviewLimit: z.number().nullable().default(null), 51 - learnLimit: z.number().nullable().default(10), 52 - learnSessionSize: z.number().nullable().default(10), 53 - reviewSessionSize: z.number().nullable().default(20), 54 - cardSortMethod: z.enum(CardSortMethod).default(CardSortMethod.Paired), 55 - cardSortOrder: z.array(z.string()).nullable().default(null), 49 + locale: z._default(z.enum(Locale), Locale.zh_CN), 50 + playAudio: z._default(z.boolean(), true), 51 + reviewLimit: z._default(z.nullable(z.number()), null), 52 + learnLimit: z._default(z.nullable(z.number()), 10), 53 + learnSessionSize: z._default(z.nullable(z.number()), 10), 54 + reviewSessionSize: z._default(z.nullable(z.number()), 20), 55 + cardSortMethod: z._default(z.enum(CardSortMethod), CardSortMethod.Paired), 56 + cardSortOrder: z._default(z.nullable(z.array(z.string())), null), 56 57 }) 57 58 58 59 export type Settings = z.infer<typeof Settings> ··· 68 69 }) 69 70 70 71 export const Flags = z.object({ 71 - isFirstLoad: z.boolean().default(true), 72 - isFirstPinyin: z.boolean().default(true), 73 - isFirstPinyinVocab: z.boolean().default(true), 72 + isFirstLoad: z._default(z.boolean(), true), 73 + isFirstPinyin: z._default(z.boolean(), true), 74 + isFirstPinyinVocab: z._default(z.boolean(), true), 74 75 }) 75 76 76 77 export const defaultFlags = Flags.parse({}) ··· 82 83 }) 83 84 84 85 export const StoreState = z.object({ 85 - version: z.string().default(globalThis.__APP_VERSION__), 86 - assignments: z.record(z.string(), z.record(z.string(), Assignment)) 87 - .default(defaultAssignments), 88 - flags: Flags.default(() => defaultFlags), 89 - settings: Settings.default(() => defaultSettings), 90 - overrides: z.record(z.string(), Override).default({}), 91 - userLevels: z.record(z.string(), z.number()).default(defaultUserLevels), 86 + version: z._default(z.string(), globalThis.__APP_VERSION__), 87 + assignments: z._default( 88 + z.record(z.string(), z.record(z.string(), Assignment)), 89 + defaultAssignments, 90 + ), 91 + flags: z._default(Flags, () => defaultFlags), 92 + settings: z._default(Settings, () => defaultSettings), 93 + overrides: z._default(z.record(z.string(), Override), {}), 94 + userLevels: z._default(z.record(z.string(), z.number()), defaultUserLevels), 92 95 }) 93 96 94 97 export type StoreState = z.infer<typeof StoreState>
+42 -39
www/models/schema/v1.ts
··· 1 - import { z } from '@zod/zod' 1 + import { z } from '@zod/zod/mini' 2 2 import { Locale } from '$/enums.ts' 3 3 import { CardSortMethod } from '@flashcard/core' 4 4 5 - export const threshold = z.union([ 5 + export const threshold = z.optional(z.union([ 6 6 z.date(), 7 - z.string().transform((s) => new Date(s)), 8 - ]).optional() 7 + z.pipe(z.string(), z.transform((s: string) => new Date(s))), 8 + ])) 9 9 10 10 /** 11 11 * Allows for either Static or FSRS 12 12 * Added efactor, srsId fields and made some fields optional 13 13 */ 14 14 export const Assignment = z.object({ 15 - difficulty: z.number().optional().default(0.3), 16 - stability: z.number().optional().default(0), 17 - interval: z.number().optional().default(0), 18 - repetition: z.number().optional().default(0), 19 - srsId: z.number().optional().default(1), 20 - efactor: z.number().optional().default(0), 15 + difficulty: z._default(z.optional(z.number()), 0.3), 16 + stability: z._default(z.optional(z.number()), 0), 17 + interval: z._default(z.optional(z.number()), 0), 18 + repetition: z._default(z.optional(z.number()), 0), 19 + srsId: z._default(z.optional(z.number()), 1), 20 + efactor: z._default(z.optional(z.number()), 0), 21 21 subjectId: z.string(), 22 - markedCompleted: z.boolean().default(false), 22 + markedCompleted: z._default(z.boolean(), false), 23 23 lastStudiedAt: threshold, 24 24 unlockedAt: threshold, 25 25 availableAt: threshold, ··· 32 32 33 33 export const OverrideProps = z.object({ 34 34 subjectId: z.string(), 35 - primaryMeaning: z.string().optional(), 36 - auxMeanings: z.string().optional(), 37 - meaningHint: z.string().optional(), 38 - meaningMnemonic: z.string().optional(), 39 - readingHint: z.string().optional(), 40 - readingMnemonic: z.string().optional(), 35 + primaryMeaning: z.optional(z.string()), 36 + auxMeanings: z.optional(z.string()), 37 + meaningHint: z.optional(z.string()), 38 + meaningMnemonic: z.optional(z.string()), 39 + readingHint: z.optional(z.string()), 40 + readingMnemonic: z.optional(z.string()), 41 41 }) 42 42 43 43 export type OverrideProps = z.infer<typeof OverrideProps> 44 44 45 - export const Override = OverrideProps.extend({ 46 - [Locale.zh_CN]: OverrideProps.optional(), 47 - [Locale.zh_HK]: OverrideProps.optional(), 48 - [Locale.zh_TW]: OverrideProps.optional(), 45 + export const Override = z.object({ 46 + ...OverrideProps.shape, 47 + [Locale.zh_CN]: z.optional(OverrideProps), 48 + [Locale.zh_HK]: z.optional(OverrideProps), 49 + [Locale.zh_TW]: z.optional(OverrideProps), 49 50 }) 50 51 51 52 export type Override = z.infer<typeof Override> 52 53 53 54 export const Settings = z.object({ 54 - locale: z.enum(Locale).default(Locale.zh_CN), 55 - playAudio: z.boolean().default(true), 56 - reviewLimit: z.number().nullable().default(null), 57 - learnLimit: z.number().nullable().default(10), 58 - learnSessionSize: z.number().nullable().default(10), 59 - reviewSessionSize: z.number().nullable().default(20), 60 - cardSortMethod: z.enum(CardSortMethod).default(CardSortMethod.Paired), 61 - cardSortOrder: z.array(z.string()).nullable().default(null), 55 + locale: z._default(z.enum(Locale), Locale.zh_CN), 56 + playAudio: z._default(z.boolean(), true), 57 + reviewLimit: z._default(z.nullable(z.number()), null), 58 + learnLimit: z._default(z.nullable(z.number()), 10), 59 + learnSessionSize: z._default(z.nullable(z.number()), 10), 60 + reviewSessionSize: z._default(z.nullable(z.number()), 20), 61 + cardSortMethod: z._default(z.enum(CardSortMethod), CardSortMethod.Paired), 62 + cardSortOrder: z._default(z.nullable(z.array(z.string())), null), 62 63 }) 63 64 64 65 export type Settings = z.infer<typeof Settings> ··· 66 67 export const defaultSettings = Settings.parse({}) 67 68 68 69 export const Flags = z.object({ 69 - isFirstLoad: z.boolean().default(true), 70 - isFirstPinyin: z.boolean().default(true), 71 - isFirstPinyinVocab: z.boolean().default(true), 70 + isFirstLoad: z._default(z.boolean(), true), 71 + isFirstPinyin: z._default(z.boolean(), true), 72 + isFirstPinyinVocab: z._default(z.boolean(), true), 72 73 }) 73 74 74 75 export const defaultFlags = Flags.parse({}) ··· 87 88 }) 88 89 89 90 export const StoreState = z.object({ 90 - version: z.string().default(globalThis.__APP_VERSION__), 91 - assignments: z.record(z.string(), z.record(z.string(), Assignment)) 92 - .default(defaultAssignments), 93 - flags: Flags.default(() => defaultFlags), 94 - settings: Settings.default(() => defaultSettings), 95 - overrides: z.record(z.string(), Override).default({}), 96 - userLevels: z.record(z.string(), z.number()).default(defaultUserLevels), 91 + version: z._default(z.string(), globalThis.__APP_VERSION__), 92 + assignments: z._default( 93 + z.record(z.string(), z.record(z.string(), Assignment)), 94 + defaultAssignments, 95 + ), 96 + flags: z._default(Flags, () => defaultFlags), 97 + settings: z._default(Settings, () => defaultSettings), 98 + overrides: z._default(z.record(z.string(), Override), {}), 99 + userLevels: z._default(z.record(z.string(), z.number()), defaultUserLevels), 97 100 }) 98 101 99 102 export type StoreState = z.infer<typeof StoreState>
+44 -41
www/models/schema/v2.ts
··· 1 - import { z } from '@zod/zod' 1 + import { z } from '@zod/zod/mini' 2 2 import { Locale, Transliteration } from '$/enums.ts' 3 3 import { CardSortMethod } from '@flashcard/core' 4 4 5 - export const threshold = z.union([ 5 + export const threshold = z.optional(z.union([ 6 6 z.date(), 7 - z.string().transform((s) => new Date(s)), 8 - ]).optional() 7 + z.pipe(z.string(), z.transform((s) => new Date(s))), 8 + ])) 9 9 10 10 /** 11 11 * Allows for either Static or FSRS 12 12 */ 13 13 export const Assignment = z.object({ 14 - difficulty: z.number().optional().default(0.3), 15 - stability: z.number().optional().default(0), 16 - interval: z.number().optional().default(0), 17 - repetition: z.number().optional().default(0), 18 - srsId: z.number().optional().default(1), 19 - efactor: z.number().optional().default(0), 14 + difficulty: z._default(z.optional(z.number()), 0.3), 15 + stability: z._default(z.optional(z.number()), 0), 16 + interval: z._default(z.optional(z.number()), 0), 17 + repetition: z._default(z.optional(z.number()), 0), 18 + srsId: z._default(z.optional(z.number()), 1), 19 + efactor: z._default(z.optional(z.number()), 0), 20 20 subjectId: z.string(), 21 - markedCompleted: z.boolean().default(false), 21 + markedCompleted: z._default(z.boolean(), false), 22 22 lastStudiedAt: threshold, 23 23 unlockedAt: threshold, 24 24 availableAt: threshold, ··· 31 31 32 32 export const OverrideProps = z.object({ 33 33 subjectId: z.string(), 34 - primaryMeaning: z.string().optional(), 35 - auxMeanings: z.string().optional(), 36 - meaningHint: z.string().optional(), 37 - meaningMnemonic: z.string().optional(), 38 - readingHint: z.string().optional(), 39 - readingMnemonic: z.string().optional(), 34 + primaryMeaning: z.optional(z.string()), 35 + auxMeanings: z.optional(z.string()), 36 + meaningHint: z.optional(z.string()), 37 + meaningMnemonic: z.optional(z.string()), 38 + readingHint: z.optional(z.string()), 39 + readingMnemonic: z.optional(z.string()), 40 40 }) 41 41 42 42 export type OverrideProps = z.infer<typeof OverrideProps> 43 43 44 - export const Override = OverrideProps.extend({ 45 - [Locale.zh_CN]: OverrideProps.optional(), 46 - [Locale.zh_HK]: OverrideProps.optional(), 47 - [Locale.zh_TW]: OverrideProps.optional(), 44 + export const Override = z.object({ 45 + ...OverrideProps.shape, 46 + [Locale.zh_CN]: z.optional(OverrideProps), 47 + [Locale.zh_HK]: z.optional(OverrideProps), 48 + [Locale.zh_TW]: z.optional(OverrideProps), 48 49 }) 49 50 50 51 export type Override = z.infer<typeof Override> ··· 53 54 export type UserLang = typeof USER_LANGS[number] 54 55 55 56 export const Settings = z.object({ 56 - locale: z.string().default(Locale.zh_CN), 57 - userLang: z.enum(USER_LANGS).default(() => { 57 + locale: z._default(z.string(), Locale.zh_CN), 58 + userLang: z._default(z.enum(USER_LANGS), () => { 58 59 const lang = (typeof navigator !== 'undefined' ? navigator.language : 'en') 59 60 .split('-')[0].toLowerCase() 60 61 return (USER_LANGS as readonly string[]).includes(lang) 61 62 ? lang as UserLang 62 63 : 'en' 63 64 }), 64 - playAudio: z.boolean().default(true), 65 - reviewLimit: z.number().nullable().default(null), 66 - learnLimit: z.number().nullable().default(10), 67 - learnSessionSize: z.number().nullable().default(10), 68 - reviewSessionSize: z.number().nullable().default(20), 69 - transliteration: z.enum(Transliteration).default(Transliteration.Pinyin), 70 - cardSortMethod: z.enum(CardSortMethod).default(CardSortMethod.Paired), 71 - cardSortOrder: z.array(z.string()).nullable().default(null), 65 + playAudio: z._default(z.boolean(), true), 66 + reviewLimit: z._default(z.nullable(z.number()), null), 67 + learnLimit: z._default(z.nullable(z.number()), 10), 68 + learnSessionSize: z._default(z.nullable(z.number()), 10), 69 + reviewSessionSize: z._default(z.nullable(z.number()), 20), 70 + transliteration: z._default(z.enum(Transliteration), Transliteration.Pinyin), 71 + cardSortMethod: z._default(z.enum(CardSortMethod), CardSortMethod.Paired), 72 + cardSortOrder: z._default(z.nullable(z.array(z.string())), null), 72 73 }) 73 74 74 75 export type Settings = z.infer<typeof Settings> ··· 76 77 export const defaultSettings = Settings.parse({}) 77 78 78 79 export const Flags = z.object({ 79 - isFirstLoad: z.boolean().default(true), 80 - isFirstPinyin: z.boolean().default(true), 81 - isFirstPinyinVocab: z.boolean().default(true), 80 + isFirstLoad: z._default(z.boolean(), true), 81 + isFirstPinyin: z._default(z.boolean(), true), 82 + isFirstPinyinVocab: z._default(z.boolean(), true), 82 83 }) 83 84 84 85 export const defaultFlags = Flags.parse({}) ··· 101 102 const appVersion: string = globalThis.__APP_VERSION__ 102 103 103 104 export const StoreState = z.object({ 104 - version: z.string().default(appVersion), 105 - assignments: z.record(z.string(), z.record(z.string(), Assignment)) 106 - .default(defaultAssignments), 107 - flags: Flags.default(() => defaultFlags), 108 - settings: Settings.default(() => defaultSettings), 109 - overrides: z.record(z.string(), Override).default({}), 110 - userLevels: z.record(z.string(), z.number()).default(defaultUserLevels), 105 + version: z._default(z.string(), appVersion), 106 + assignments: z._default( 107 + z.record(z.string(), z.record(z.string(), Assignment)), 108 + defaultAssignments, 109 + ), 110 + flags: z._default(Flags, () => defaultFlags), 111 + settings: z._default(Settings, () => defaultSettings), 112 + overrides: z._default(z.record(z.string(), Override), {}), 113 + userLevels: z._default(z.record(z.string(), z.number()), defaultUserLevels), 111 114 }) 112 115 113 116 export type StoreState = z.infer<typeof StoreState>
+11 -10
www/models/schema/v3.ts
··· 1 - import { z } from '@zod/zod' 1 + import { z } from '@zod/zod/mini' 2 2 export { 3 3 Assignment, 4 4 defaultAssignments, ··· 17 17 18 18 export const JournalEntry = z.object({ 19 19 date: z.string(), 20 - text: z.string().default(''), 21 - wordIds: z.array(z.string()).default([]), 20 + text: z._default(z.string(), ''), 21 + wordIds: z._default(z.array(z.string()), []), 22 22 }) 23 23 24 24 export type JournalEntry = z.infer<typeof JournalEntry> 25 25 26 26 const Games = z.object({ 27 - journal: z.array(JournalEntry).default([]), 27 + journal: z._default(z.array(JournalEntry), []), 28 28 }) 29 29 30 30 export const Stats = z.object({ 31 - currStreak: z.number().default(0), 32 - longestStreak: z.number().default(0), 33 - lastStudiedDate: z.string().optional(), 31 + currStreak: z._default(z.number(), 0), 32 + longestStreak: z._default(z.number(), 0), 33 + lastStudiedDate: z.optional(z.string()), 34 34 }) 35 35 36 36 export type Stats = z.infer<typeof Stats> 37 37 38 - export const StoreState = V2StoreState.extend({ 39 - games: Games.default(() => Games.parse({})), 40 - stats: Stats.default(() => Stats.parse({})), 38 + export const StoreState = z.object({ 39 + ...V2StoreState.shape, 40 + games: z._default(Games, () => Games.parse({})), 41 + stats: z._default(Stats, () => Stats.parse({})), 41 42 }) 42 43 43 44 export type StoreState = z.infer<typeof StoreState>
+2 -1
www/robots.txt
··· 1 1 User-agent: * 2 - Disallow: / 2 + Disallow: /static/gen/ 3 + Disallow: /dist/
+1608 -1
www/static/styles/civility.min.css
··· 1 1 @layer normalize, base, base-theme, components, theme, utilities; 2 2 3 - /*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */@layer normalize{*,:after,:before{box-sizing:border-box}html{font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;line-height:1.15;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{margin:0}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentcolor}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}}@media (prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@layer base{:root{--black:hsl(var(--blackH),var(--blackS),var(--blackL));--gray:hsl(var(--grayH),var(--grayS),var(--grayL));--white:hsl(var(--whiteH),var(--whiteS),var(--whiteL));--blue:hsl(var(--blueH),var(--blueS),var(--blueL));--green:hsl(var(--greenH),var(--greenS),var(--greenL));--yellow:hsl(var(--yellowH),var(--yellowS),var(--yellowL));--orange:hsl(var(--orangeH),var(--orangeS),var(--orangeL));--red:hsl(var(--redH),var(--redS),var(--redL));--pink:hsl(var(--pinkH),var(--pinkS),var(--pinkL));--purple:hsl(var(--purpleH),var(--purpleS),var(--purpleL));--error:hsl(var(--errorH),var(--errorS),var(--errorL));--error-dull:hsl(var(--errorH),var(--errorS),var(--errorL),0.2);--warning:hsl(var(--warningH),var(--warningS),var(--warningL));--warning-dull:hsl(var(--warningH),var(--warningS),var(--warningL),0.2);--info:hsl(var(--infoH),var(--infoS),var(--infoL));--info-dull:hsl(var(--infoH),var(--infoS),var(--infoL),0.2);--success:hsl(var(--successH),var(--successS),var(--successL));--success-dull:hsl(var(--successH),var(--successS),var(--successL),0.2);--background:hsl(var(--backgroundH),var(--backgroundS),var(--backgroundL));--background-dull:hsl(var(--backgroundH),var(--backgroundS),var(--backgroundL),0.2);--body:hsl(var(--bodyH),var(--bodyS),var(--bodyL));--body-dull:hsl(var(--bodyH),var(--bodyS),var(--bodyL),0.2);--secondary:hsl(var(--secondaryH),var(--secondaryS),var(--secondaryL));--secondary-dull:hsl(var(--secondaryH),var(--secondaryS),var(--secondaryL),0.2);--primary:hsl(var(--primaryH),var(--primaryS),var(--primaryL));--primary-dull:hsl(var(--primaryH),var(--primaryS),var(--primaryL),0.2)}form{margin:0 0 var(--s4) 0}fieldset{border:1px solid;border-radius:var(--br-base);margin:0 0 var(--s3) 0;padding:var(--s3)}legend{font-weight:var(--fw-semibold);opacity:1;padding:0 var(--s2)}label{display:block;font-weight:var(--fw-medium);margin-bottom:var(--s2)}input[type=email],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=url],select,textarea{border:1px solid;border-radius:var(--br-base);box-sizing:border-box;font-size:var(--f5);line-height:var(--lh-base);margin-bottom:var(--s3);padding:var(--s3);transition:border-color var(--transition-base),box-shadow var(--transition-base),opacity var(--transition-base)}input:focus,select:focus,textarea:focus{opacity:1;outline:2px solid currentColor;outline-offset:2px}button,input[type=button],input[type=submit]{background:none;border:1px solid;border-radius:var(--br-base);color:inherit;cursor:pointer;display:inline-block;font-size:var(--f5);font-weight:var(--fw-medium);padding:var(--s2) var(--s2);text-decoration:none;transition:opacity var(--transition-base),transform var(--transition-fast)}button:hover,input[type=button]:hover,input[type=submit]:hover{opacity:.8;transform:translateY(-1px)}button:active,input[type=button]:active,input[type=submit]:active{transform:translateY(0)}button:disabled,input[type=button]:disabled,input[type=submit]:disabled{cursor:not-allowed;opacity:.3;transform:none}:root{--nav-drawer-duration:250ms;--nav-footer-height:72px}body{background-color:var(--background);color:var(--body);display:grid;grid-template-areas:"banner banner banner" "header header header" "subhead subhead subhead" "navhd mainhd aside" "nav main aside" "navft mainft aside" "footer footer footer";grid-template-columns:var(--nav-width,auto) 1fr var(--aside-width,0);grid-template-rows:auto auto auto auto 1fr auto auto;margin:0;min-height:100vh}}@layer base{}@layer base{body:has(footer[fixed]){padding-bottom:var(--footer-height,56px)}body>header{grid-area:header}body>nav{grid-area:nav}body>main{grid-area:main}body>aside{grid-area:aside}body>footer{grid-area:footer}body>header{background:var(--background);margin:0;padding:0;position:sticky;top:var(--banner-height,0);z-index:var(--z-sticky)}body>nav{align-self:start;display:flex;flex-direction:column;gap:var(--s1);max-height:calc(100vh - var(--banner-height, 0px) - var(--header-height, 56px) - var(--footer-height, 76px));overflow-y:auto;padding:var(--s3) var(--s2);padding-bottom:calc(var(--nav-footer-height) + var(--s3));position:sticky;top:calc(var(--banner-height, 0px) + var(--header-height, 56px))}body:not(:has(ui-nav-footer))>nav{padding-bottom:var(--s3)}body>nav ul{display:flex;flex-direction:column;gap:var(--s1);list-style:none;margin:0;padding:0}body>nav li{position:relative}body>nav a{border-radius:var(--br-base);color:inherit;display:block;font-weight:var(--fw-medium);padding:var(--s2) var(--s3);text-decoration:none;transition:opacity var(--transition-base)}body>nav a:hover{opacity:.7}body>nav a[aria-current=page]{cursor:default;opacity:.5}body>nav ul ul{border-left:1px solid;margin-left:var(--s3);margin-top:var(--s1);padding-left:var(--s3)}body>main{box-sizing:border-box;margin:0 auto;max-width:var(--main-max-width,var(--max-width-lg));padding:var(--main-padding,0);width:100%}body>aside{align-self:start;max-height:calc(100vh - var(--banner-height, 0px) - var(--header-height, 56px));overflow-y:auto;padding:var(--aside-padding,var(--s3) var(--s2));position:sticky;top:calc(var(--banner-height, 0px) + var(--header-height, 56px))}body>footer{background:var(--background);border-top:1px solid;padding:var(--footer-padding,var(--s3) var(--container-padding))}body>footer[fixed]{border-top:1px solid;bottom:0;grid-area:unset;left:0;position:fixed;right:0;z-index:var(--z-fixed)}ui-banner{grid-area:banner;top:0;z-index:var(--z-sticky)}ui-banner,ui-sub-header{background:var(--background);display:block;position:sticky}ui-sub-header{grid-area:subhead;top:calc(var(--banner-height, 0px) + var(--header-height, 0px));z-index:calc(var(--z-sticky) - 1)}ui-nav-header{align-self:start;grid-area:navhd;padding:var(--s3) var(--s2) var(--s2);top:calc(var(--banner-height, 0px) + var(--header-height, 56px))}ui-nav-footer,ui-nav-header{background:var(--background);display:block;position:sticky;z-index:var(--z-sticky)}ui-nav-footer{align-self:end;bottom:var(--footer-height,0);grid-area:navft;padding:var(--s2) var(--s2) var(--s3)}ui-main-header{display:block;grid-area:mainhd;padding:var(--s3) var(--container-padding) var(--s2)}ui-main-footer{display:block;grid-area:mainft;padding:var(--s2) var(--container-padding) var(--s3)}#nav-toggle,ui-nav-toggle{display:none}ui-nav-toggle label[for=nav-toggle]{align-items:center;background:transparent;border:1px solid;border-radius:var(--br-base);cursor:pointer;display:flex;flex-direction:column;gap:4px;height:44px;justify-content:center;padding:0;transition:opacity var(--transition-base);width:44px}ui-nav-toggle label[for=nav-toggle]:hover{opacity:.7}ui-nav-toggle label[for=nav-toggle] span,ui-nav-toggle label[for=nav-toggle]:after,ui-nav-toggle label[for=nav-toggle]:before{background:currentColor;content:"";display:block;height:2px;transition:all .3s ease;width:20px}ui-nav-toggle label[for=nav-toggle] span{pointer-events:none}#nav-toggle:checked~* ui-nav-toggle label[for=nav-toggle]:before,#nav-toggle:checked~header ui-nav-toggle label[for=nav-toggle]:before{transform:translateY(6px) rotate(45deg)}#nav-toggle:checked~* ui-nav-toggle label[for=nav-toggle] span,#nav-toggle:checked~header ui-nav-toggle label[for=nav-toggle] span{opacity:0}#nav-toggle:checked~* ui-nav-toggle label[for=nav-toggle]:after,#nav-toggle:checked~header ui-nav-toggle label[for=nav-toggle]:after{transform:translateY(-6px) rotate(-45deg)}label.nav-backdrop{background:rgba(0,0,0,.5);bottom:0;cursor:pointer;display:none;height:100vh;left:0;opacity:0;position:fixed;right:0;top:0;transition:var(--nav-drawer-duration) allow-discrete;transition-property:display,opacity,overlay;width:100vw;z-index:calc(var(--z-fixed) + 1)}#nav-toggle:checked~label.nav-backdrop{display:block;opacity:1}@starting-style{#nav-toggle:checked~label.nav-backdrop{opacity:0}}@media (max-width:900px){body{grid-template-areas:"banner" "header" "subhead" "mainhd" "main" "mainft" "footer";grid-template-columns:1fr;grid-template-rows:auto auto auto auto 1fr auto auto}body>aside,body>nav,ui-nav-footer,ui-nav-header{display:none}ui-nav-toggle{display:block}body>nav{background:var(--background);bottom:0;box-shadow:4px 0 12px rgba(0,0,0,.15);height:100vh;left:0;max-height:calc(100vh - var(--nav-header-height, 56px));overflow-y:auto;padding:var(--s3);padding-bottom:calc(var(--nav-footer-height) + var(--s3));padding-top:calc(var(--banner-height, 0px) + var(--header-height, 56px) + var(--s3));position:fixed;top:0;transform:translateX(-100%);transition:var(--nav-drawer-duration) allow-discrete;transition-property:display,transform;width:min(80vw,320px);z-index:calc(var(--z-fixed) + 2)}body:not(:has(ui-nav-footer))>nav{max-height:100vh}#nav-toggle:checked~nav{display:flex;transform:translateX(0)}@starting-style{#nav-toggle:checked~nav{transform:translateX(-100%)}}ui-nav-header{align-items:center;background:var(--background);border-bottom:1px solid;height:var(--header-height,56px);left:0;position:fixed;top:var(--banner-height,0);transform:translateX(-100%);transition:var(--nav-drawer-duration) allow-discrete;transition-property:display,transform;width:min(80vw,320px);z-index:calc(var(--z-fixed) + 3)}#nav-toggle:checked~ui-nav-header{display:flex;transform:translateX(0)}@starting-style{#nav-toggle:checked~ui-nav-header{transform:translateX(-100%)}}ui-nav-footer{align-items:center;background:var(--background);border-top:1px solid;bottom:0;height:var(--nav-footer-height);left:0;position:fixed;transform:translateX(-100%);transition:var(--nav-drawer-duration) allow-discrete;transition-property:display,transform;width:min(80vw,320px);z-index:calc(var(--z-fixed) + 3)}#nav-toggle:checked~ui-nav-footer{display:flex;transform:translateX(0)}@starting-style{#nav-toggle:checked~ui-nav-footer{transform:translateX(-100%)}}}}@layer base{h1,h2,h3,h4,h5,h6{font-weight:var(--fw-semibold);line-height:var(--lh-tight);margin:0 0 var(--s3) 0}h1{font-size:var(--f1)}h2{font-size:var(--f2)}h3{font-size:var(--f3)}h4{font-size:var(--f4)}h5{font-size:var(--f5)}h6{font-size:var(--f6)}a{color:var(--primary);text-decoration:underline;transition:color var(--transition-base)}@media (max-width:768px){h1{font-size:var(--f3)}h2,h3{font-size:var(--f4)}}@media (max-width:480px){main{padding:var(--s3)}h1{font-size:var(--f3)}h2{font-size:var(--f4)}}}@layer base{html{background-color:var(--background);color:var(--body);font-family:var(--font-family-base);font-size:var(--f5);height:100%;line-height:var(--lh-base)}p{line-height:var(--lh-relaxed);text-wrap:balance}ol,p,ul{margin:0 0 var(--s3) 0}ol,ul{padding:var(--s2) 0 var(--s2) var(--s4)}li{margin-bottom:var(--s2)}table{border-collapse:collapse;margin:0 0 var(--s4) 0;width:100%}td,th{border-bottom:1px solid;opacity:.9;padding:var(--s3) var(--s3);text-align:left}th{font-weight:var(--fw-semibold);opacity:1}code,kbd,samp{border:1px solid;border-radius:var(--br-sm);font-family:var(--font-family-mono);font-size:.9em;padding:.2em .4em}pre{border:1px solid;border-radius:var(--br-base);margin:0 0 var(--s3) 0;overflow-x:auto;padding:var(--s3)}pre code{border:none;opacity:1;padding:0}[role=tablist]{border-bottom:1px solid;display:flex;margin-bottom:var(--s3);opacity:.8}[role=tab]{background:none;border:none;border-bottom:2px solid transparent;border-radius:0;cursor:pointer;font-weight:var(--fw-medium);opacity:1;padding:var(--s3) var(--s3);transition:all var(--transition-base)}[role=tab]:hover{opacity:1}[role=tab][aria-selected=true]{border-bottom-color:currentColor;opacity:1}[role=tabpanel]{padding:var(--s3) 0}[role=tabpanel][hidden]{display:none}details{margin:0 0 var(--s2) 0}details,summary{border-radius:var(--br-base)}summary{cursor:pointer;font-weight:var(--fw-medium);opacity:1;padding:var(--s2);transition:opacity var(--transition-base)}summary:hover{opacity:.7}details[open] summary{border-radius:var(--br-base) var(--br-base) 0 0}details>:not(summary):not(ul){padding:var(--s2)}:focus-visible{outline:2px solid currentColor;outline-offset:2px}.skip-to-main{background:currentColor;border-radius:var(--br-base);color:#fff;left:6px;padding:8px;position:absolute;text-decoration:none;top:-40px;z-index:var(--z-tooltip)}.skip-to-main:focus{top:6px}}@layer base-theme{:root{--font-family-base:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;--font-family-mono:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",Menlo,monospace;--f-headline:6rem;--f-subheadline:5rem;--f1:3rem;--f2:2.25rem;--f3:1.5rem;--f4:1.25rem;--f5:1rem;--f6:0.875rem;--f7:0.75rem;--s0:0;--s1:0.25rem;--s2:0.5rem;--s3:1rem;--s4:2rem;--s5:3rem;--max-width-sm:640px;--max-width-md:768px;--max-width-lg:1024px;--max-width-xl:1280px;--max-width-2xl:1536px;--banner-height:0px;--header-height:56px;--sub-header-height:0px;--fw-normal:400;--fw-medium:500;--fw-semibold:600;--fw-bold:700;--lh-tight:1.2;--lh-base:1.5;--lh-relaxed:1.7;--br-none:0;--br-sm:0.25rem;--br-base:0.5rem;--br-lg:0.75rem;--br-xl:1rem;--br-full:9999px;--shadow-sm:0 1px 2px 0 rgba(0,0,0,.05);--shadow-base:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);--shadow-md:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--shadow-xl:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04);--transition-fast:0.15s ease-in-out;--transition-base:0.2s ease-in-out;--transition-slow:0.3s ease-in-out;--z-dropdown:1000;--z-sticky:1020;--z-fixed:1030;--z-modal-backdrop:1040;--z-modal:1050;--z-popover:1060;--z-tooltip:1070;--grid-columns:12;--container-padding:var(--s3);--ease-in:cubic-bezier(0.4,0,1,1);--ease-out:cubic-bezier(0,0,0.2,1);--ease-in-out:cubic-bezier(0.4,0,0.2,1);--blackH:0;--blackS:0%;--blackL:0%;--grayH:220;--grayS:14%;--grayL:41%;--whiteH:0;--whiteS:0%;--whiteL:100%;--blueH:217;--blueS:91%;--blueL:60%;--greenH:142;--greenS:71%;--greenL:45%;--yellowH:45;--yellowS:93%;--yellowL:47%;--orangeH:25;--orangeS:95%;--orangeL:53%;--redH:0;--redS:84%;--redL:60%;--purpleH:262;--purpleS:83%;--purpleL:58%;--errorH:var(--redH);--errorS:var(--redS);--errorL:var(--redL);--warningH:var(--orangeH);--warningS:var(--orangeS);--warningL:var(--orangeL);--infoH:var(--blueH);--infoS:var(--blueS);--infoL:var(--blueL);--successH:var(--greenH);--successS:var(--greenS);--successL:var(--greenL);--backgroundH:var(--whiteH);--backgroundS:var(--whiteS);--backgroundL:var(--whiteL);--bodyH:var(--blackH);--bodyS:var(--blackS);--bodyL:13%;--secondaryH:var(--grayH);--secondaryS:var(--grayS);--secondaryL:var(--grayL);--primaryH:var(--blueH);--primaryS:var(--blueS);--primaryL:var(--blueL)}}@layer components{ui-card{border:1px solid;border-radius:var(--br-lg);box-shadow:var(--shadow-sm);display:block;overflow:hidden;transition:all var(--transition-base)}ui-card:hover{box-shadow:var(--shadow-md);transform:translateY(-2px)}ui-card[clickable]{cursor:pointer}ui-card[clickable]:active{transform:translateY(0)}ui-badge{border:1px solid;border-radius:var(--br-full);display:inline-block;font-size:var(--f7);font-weight:var(--fw-medium);letter-spacing:.05em;padding:var(--s1) var(--s2);text-transform:uppercase}ui-badge[variant=success]{background-color:var(--success-dull);color:var(--success)}ui-badge[variant=warning]{background-color:var(--warning-dull);color:var(--warning)}ui-badge[variant=error]{background-color:var(--error-dull);color:var(--error)}ui-badge[variant=info]{background-color:var(--info-dull);color:var(--info)}ui-button-group{display:flex;flex-wrap:wrap;gap:var(--s2)}ui-button-group[orientation=vertical]{flex-direction:column}ui-button-group[attached]{gap:0}ui-button-group[attached]>button:not(:first-child),ui-button-group[attached]>input:not(:first-child){border-bottom-left-radius:0;border-left:none;border-top-left-radius:0}ui-button-group[attached]>button:not(:last-child),ui-button-group[attached]>input:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}ui-grid{display:grid;gap:var(--s3);grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}ui-grid[cols="1"]{grid-template-columns:1fr}ui-grid[cols="2"]{grid-template-columns:repeat(2,1fr)}ui-grid[cols="3"]{grid-template-columns:repeat(3,1fr)}ui-grid[cols="4"]{grid-template-columns:repeat(4,1fr)}@media (max-width:768px){ui-grid[cols="2"],ui-grid[cols="3"],ui-grid[cols="4"]{grid-template-columns:1fr}}ui-stack{display:flex;flex-direction:column;gap:var(--s3)}ui-stack[spacing=xs]{gap:var(--s1)}ui-stack[spacing=sm]{gap:var(--s2)}ui-stack[spacing=lg],ui-stack[spacing=md],ui-stack[spacing=xl]{gap:var(--s3)}ui-stack[direction=row]{align-items:center;flex-direction:row}ui-progress{border:1px solid;height:8px;overflow:hidden;width:100%}ui-progress,ui-progress:before{border-radius:var(--br-full);display:block}ui-progress:before{background-color:currentColor;content:"";height:100%;transition:width var(--transition-base);width:var(--progress-value,0)}ui-progress[variant=success]:before{background-color:var(--success)}ui-progress[variant=warning]:before{background-color:var(--warning)}ui-progress[variant=error]:before{background-color:var(--error)}ui-alert{border:1px solid;border-radius:var(--br-base);display:block;padding:var(--s3)}ui-alert[variant=success]{background-color:var(--success-dull);border-color:var(--success);color:var(--success)}ui-alert[variant=warning]{background-color:var(--warning-dull);border-color:var(--warning);color:var(--warning)}ui-alert[variant=error]{background-color:var(--error-dull);border-color:var(--error);color:var(--error)}ui-alert[variant=info]{background-color:var(--info-dull);border-color:var(--info);color:var(--info)}ui-divider{border:none;border-top:1px solid;display:block;height:1px;margin:var(--s4) 0}ui-divider[orientation=vertical]{height:auto;margin:0 var(--s3);width:1px}ui-spinner{animation:spin 1s linear infinite;border-color:currentcolor transparent;border-radius:50%;border-style:solid;border-width:2px;display:inline-block;height:20px;width:20px}ui-spinner[size=sm]{height:16px;width:16px}ui-spinner[size=lg]{border-width:3px;height:32px;width:32px}ui-breadcrumb{align-items:center;display:flex;font-size:var(--f5);gap:var(--s2)}ui-breadcrumb a{border-radius:var(--br-sm);color:var(--primary);padding:var(--s1) var(--s2);text-decoration:none;transition:opacity var(--transition-base)}ui-breadcrumb a:hover{opacity:.7}ui-breadcrumb:before{content:attr(separator);margin:0 var(--s1);opacity:.5}ui-breadcrumb:first-child:before{display:none}ui-avatar{border:1px solid;border-radius:var(--br-full);display:inline-block;font-size:var(--f5);font-weight:var(--fw-medium);height:40px;line-height:40px;overflow:hidden;text-align:center;width:40px}ui-avatar[size=sm]{font-size:var(--f7);height:32px;line-height:32px;width:32px}ui-avatar[size=lg]{font-size:var(--f4);height:56px;line-height:56px;width:56px}ui-avatar img{height:100%;-o-object-fit:cover;object-fit:cover;width:100%}ui-dropdown{display:inline-block;position:relative}ui-dropdown-content{background:var(--background);border:1px solid;border-radius:var(--br-base);box-shadow:var(--shadow-lg);left:0;min-width:200px;opacity:0;position:absolute;top:100%;transform:translateY(-8px);transition:all var(--transition-base);visibility:hidden;z-index:var(--z-dropdown)}ui-dropdown[open] ui-dropdown-content{opacity:1;transform:translateY(0);visibility:visible}ui-dropdown-item{cursor:pointer;display:block;padding:var(--s3);text-decoration:none;transition:opacity var(--transition-base)}ui-dropdown-item:hover{opacity:.7}ui-dropdown-item[active]{font-weight:var(--fw-medium);opacity:1}@media (max-width:768px){ui-dropdown-content{left:auto;min-width:150px;right:0}}.drawer-toggle{display:none}.drawer-backdrop{background:rgba(0,0,0,.5);bottom:0;cursor:pointer;display:none;height:100vh;left:0;opacity:0;position:fixed;right:0;top:0;transition:allow-discrete .25s;transition-property:display,opacity,overlay;width:100vw;z-index:calc(var(--z-fixed) + 1)}.drawer-toggle:checked~.drawer-backdrop{display:block;opacity:1}@starting-style{.drawer-toggle:checked~.drawer-backdrop{opacity:0}}ui-drawer{background:var(--background);box-shadow:4px 0 12px rgba(0,0,0,.15);display:none;overflow-y:auto;padding:var(--s3);position:fixed;transition:allow-discrete .25s;transition-property:display,transform;z-index:calc(var(--z-fixed) + 2)}ui-drawer,ui-drawer[position=left]{bottom:0;height:100vh;left:0;top:0;transform:translateX(-100%);width:min(80vw,320px)}.drawer-toggle:checked~* ui-drawer[position=left],.drawer-toggle:checked~ui-drawer[position=left]{display:block;transform:translateX(0)}@starting-style{.drawer-toggle:checked~* ui-drawer[position=left],.drawer-toggle:checked~ui-drawer[position=left]{transform:translateX(-100%)}}ui-drawer[position=right]{bottom:0;box-shadow:-4px 0 12px rgba(0,0,0,.15);height:100vh;left:auto;right:0;top:0;transform:translateX(100%);width:min(80vw,320px)}.drawer-toggle:checked~* ui-drawer[position=right],.drawer-toggle:checked~ui-drawer[position=right]{display:block;transform:translateX(0)}@starting-style{.drawer-toggle:checked~* ui-drawer[position=right],.drawer-toggle:checked~ui-drawer[position=right]{transform:translateX(100%)}}ui-drawer[position=top]{bottom:auto;box-shadow:0 4px 12px rgba(0,0,0,.15);height:min(80vh,400px);left:0;right:0;top:0;transform:translateY(-100%);width:100vw}.drawer-toggle:checked~* ui-drawer[position=top],.drawer-toggle:checked~ui-drawer[position=top]{display:block;transform:translateY(0)}@starting-style{.drawer-toggle:checked~* ui-drawer[position=top],.drawer-toggle:checked~ui-drawer[position=top]{transform:translateY(-100%)}}ui-drawer[position=bottom]{bottom:0;box-shadow:0 -4px 12px rgba(0,0,0,.15);height:min(80vh,400px);left:0;right:0;top:auto;transform:translateY(100%);width:100vw}.drawer-toggle:checked~* ui-drawer[position=bottom],.drawer-toggle:checked~ui-drawer[position=bottom]{display:block;transform:translateY(0)}@starting-style{.drawer-toggle:checked~* ui-drawer[position=bottom],.drawer-toggle:checked~ui-drawer[position=bottom]{transform:translateY(100%)}}ui-bottom-bar{align-items:stretch;display:flex;flex-direction:row;gap:0;height:100%;margin:0;padding:0;width:100%}ui-bottom-bar>a,ui-bottom-bar>button{align-items:center;background:transparent;border:none;color:inherit;cursor:pointer;display:flex;flex:1;flex-direction:column;font-size:var(--f7);font-weight:var(--fw-medium);gap:2px;justify-content:center;opacity:1;padding:var(--s1) var(--s2);text-decoration:none;transition:opacity var(--transition-base)}ui-bottom-bar>a:hover,ui-bottom-bar>button:hover{opacity:.7}ui-bottom-bar>button:disabled{cursor:not-allowed;opacity:.3}ui-bottom-bar>a[aria-current=page],ui-bottom-bar>button[aria-current=page]{opacity:.5}ui-bottom-bar .icon,ui-bottom-bar ui-icon{font-size:var(--f4);line-height:1}ui-bottom-bar[hide-text]>a>span:not(.icon),ui-bottom-bar[hide-text]>button>span:not(.icon){display:none}ui-bottom-bar[hide-text]>a,ui-bottom-bar[hide-text]>button{gap:0}ui-bottom-bar[orientation=horizontal]>a,ui-bottom-bar[orientation=horizontal]>button{flex-direction:row;gap:var(--s2)}ui-pwa-version{display:block}ui-pwa-version p{margin:0 0 var(--s2) 0}ui-pwa-version button{margin-top:var(--s1)}ui-counter{align-items:stretch;display:flex;gap:var(--s2)}ui-counter button{align-items:center;display:flex;flex-shrink:0;justify-content:center}ui-counter input[type=number]{-webkit-appearance:textfield;appearance:textfield;-moz-appearance:textfield;flex:1;margin:0;padding:0;text-align:center}ui-counter input[type=number]::-webkit-inner-spin-button,ui-counter input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none}}@layer utilities{.flex{display:flex}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.w-100{width:100%}.mw6{max-width:var(--max-width-sm)}.measure{max-width:30em}.container-sm{max-width:var(--max-width-sm)}.container-md,.container-sm{margin:0 auto;padding:0 var(--container-padding)}.container-md{max-width:var(--max-width-md)}.container-lg{max-width:var(--max-width-lg)}.container-lg,.container-xl{margin:0 auto;padding:0 var(--container-padding)}.container-xl{max-width:var(--max-width-xl)}.container-fluid{padding:0 var(--container-padding);width:100%}.pa0{padding:0}.pa1{padding:var(--s1)}.pa2{padding:var(--s2)}.pa3{padding:var(--s3)}.pa4{padding:var(--s4)}.ma0{margin:0}.ma1{margin:var(--s1)}.ma2{margin:var(--s2)}.ma3{margin:var(--s3)}.mh-auto{margin-left:auto;margin-right:auto}.mv3{margin-bottom:var(--s3);margin-top:var(--s3)}.mb2{margin-bottom:var(--s2)}.mb0{margin-bottom:0}.mt3{margin-top:var(--s3)}.mt2{margin-top:var(--s2)}.mr3{margin-right:var(--s3)}.pr3{padding-right:var(--s3)}.pl2{padding-left:var(--s2)}.pt2{padding-top:var(--s2)}.pb4{padding-bottom:var(--s4)}.pb0{padding-bottom:0}.pb3{padding-bottom:var(--s3)}.f3{font-size:var(--f3)}.f4{font-size:var(--f4)}.f5{font-size:var(--f5)}.f6{font-size:var(--f6)}.tc{text-align:center}.tr{text-align:right}.b{font-weight:var(--fw-bold)}.br2{border-radius:var(--br2)}.ba{border:1px solid}.b1{border-width:1px}.t-border{border:1px solid transparent}.border{border:1px solid}.list{list-style:none}.cursor,.pointer{cursor:pointer}.o-30{opacity:.3}.o-40{opacity:.4}.o-60{opacity:.6}.underline{text-decoration:underline}.link{text-decoration:none}.db{display:block}.h2{height:2rem}.h3{height:3rem}.input{border:1px solid;border-radius:var(--br2);padding:var(--s2)}.input-reset{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent}.shadow{box-shadow:var(--shadow-base)}.pv2{padding-bottom:var(--s2);padding-top:var(--s2)}.pv1{padding-bottom:var(--s1);padding-top:var(--s1)}.ph2{padding-left:var(--s2);padding-right:var(--s2)}.lh-copy{line-height:var(--lh-copy)}.center{text-align:center}.flex-wrap{flex-wrap:wrap}.no-select{cursor:default;-webkit-user-select:none;-moz-user-select:none;user-select:none}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.absolute-center{left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}.overflow-hidden,.text-ellipsis{overflow:hidden}.text-ellipsis{text-overflow:ellipsis;white-space:nowrap}.z-dropdown{z-index:var(--z-dropdown)}.z-modal{z-index:var(--z-modal)}.z-tooltip{z-index:var(--z-tooltip)}} 3 + /*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ 4 + @layer normalize { 5 + *, :after, :before { 6 + box-sizing: border-box; 7 + } 8 + html { 9 + font-family: 10 + system-ui, Segoe UI, Roboto, Helvetica, Arial, sans-serif, Apple Color 11 + Emoji, Segoe UI Emoji; 12 + line-height: 1.15; 13 + -webkit-text-size-adjust: 100%; 14 + -moz-tab-size: 4; 15 + -o-tab-size: 4; 16 + tab-size: 4; 17 + } 18 + body { 19 + margin: 0; 20 + } 21 + b, strong { 22 + font-weight: bolder; 23 + } 24 + code, kbd, pre, samp { 25 + font-family: 26 + ui-monospace, SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace; 27 + font-size: 1em; 28 + } 29 + small { 30 + font-size: 80%; 31 + } 32 + sub, sup { 33 + font-size: 75%; 34 + line-height: 0; 35 + position: relative; 36 + vertical-align: baseline; 37 + } 38 + sub { 39 + bottom: -0.25em; 40 + } 41 + sup { 42 + top: -0.5em; 43 + } 44 + table { 45 + border-color: currentcolor; 46 + } 47 + button, input, optgroup, select, textarea { 48 + font-family: inherit; 49 + font-size: 100%; 50 + line-height: 1.15; 51 + margin: 0; 52 + } 53 + [type='button'], [type='reset'], [type='submit'], button { 54 + -webkit-appearance: button; 55 + } 56 + legend { 57 + padding: 0; 58 + } 59 + progress { 60 + vertical-align: baseline; 61 + } 62 + ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { 63 + height: auto; 64 + } 65 + [type='search'] { 66 + -webkit-appearance: textfield; 67 + outline-offset: -2px; 68 + } 69 + ::-webkit-search-decoration { 70 + -webkit-appearance: none; 71 + } 72 + ::-webkit-file-upload-button { 73 + -webkit-appearance: button; 74 + font: inherit; 75 + } 76 + summary { 77 + display: list-item; 78 + } 79 + } 80 + @media (prefers-reduced-motion: reduce) { 81 + * { 82 + animation-duration: 0.01ms !important; 83 + animation-iteration-count: 1 !important; 84 + transition-duration: 0.01ms !important; 85 + } 86 + } 87 + @keyframes spin { 88 + 0% { 89 + transform: rotate(0deg); 90 + } 91 + to { 92 + transform: rotate(1turn); 93 + } 94 + } 95 + @layer base { 96 + :root { 97 + --black: hsl(var(--blackH), var(--blackS), var(--blackL)); 98 + --gray: hsl(var(--grayH), var(--grayS), var(--grayL)); 99 + --white: hsl(var(--whiteH), var(--whiteS), var(--whiteL)); 100 + --blue: hsl(var(--blueH), var(--blueS), var(--blueL)); 101 + --green: hsl(var(--greenH), var(--greenS), var(--greenL)); 102 + --yellow: hsl(var(--yellowH), var(--yellowS), var(--yellowL)); 103 + --orange: hsl(var(--orangeH), var(--orangeS), var(--orangeL)); 104 + --red: hsl(var(--redH), var(--redS), var(--redL)); 105 + --pink: hsl(var(--pinkH), var(--pinkS), var(--pinkL)); 106 + --purple: hsl(var(--purpleH), var(--purpleS), var(--purpleL)); 107 + --error: hsl(var(--errorH), var(--errorS), var(--errorL)); 108 + --error-dull: hsl(var(--errorH), var(--errorS), var(--errorL), 0.2); 109 + --warning: hsl(var(--warningH), var(--warningS), var(--warningL)); 110 + --warning-dull: hsl(var(--warningH), var(--warningS), var(--warningL), 0.2); 111 + --info: hsl(var(--infoH), var(--infoS), var(--infoL)); 112 + --info-dull: hsl(var(--infoH), var(--infoS), var(--infoL), 0.2); 113 + --success: hsl(var(--successH), var(--successS), var(--successL)); 114 + --success-dull: hsl(var(--successH), var(--successS), var(--successL), 0.2); 115 + --background: hsl( 116 + var(--backgroundH), 117 + var(--backgroundS), 118 + var(--backgroundL) 119 + ); 120 + --background-dull: hsl( 121 + var(--backgroundH), 122 + var(--backgroundS), 123 + var(--backgroundL), 124 + 0.2 125 + ); 126 + --body: hsl(var(--bodyH), var(--bodyS), var(--bodyL)); 127 + --body-dull: hsl(var(--bodyH), var(--bodyS), var(--bodyL), 0.2); 128 + --secondary: hsl(var(--secondaryH), var(--secondaryS), var(--secondaryL)); 129 + --secondary-dull: hsl( 130 + var(--secondaryH), 131 + var(--secondaryS), 132 + var(--secondaryL), 133 + 0.2 134 + ); 135 + --primary: hsl(var(--primaryH), var(--primaryS), var(--primaryL)); 136 + --primary-dull: hsl(var(--primaryH), var(--primaryS), var(--primaryL), 0.2); 137 + } 138 + form { 139 + margin: 0 0 var(--s4) 0; 140 + } 141 + fieldset { 142 + border: 1px solid; 143 + border-radius: var(--br-base); 144 + margin: 0 0 var(--s3) 0; 145 + padding: var(--s3); 146 + } 147 + legend { 148 + font-weight: var(--fw-semibold); 149 + opacity: 1; 150 + padding: 0 var(--s2); 151 + } 152 + label { 153 + display: block; 154 + font-weight: var(--fw-medium); 155 + margin-bottom: var(--s2); 156 + } 157 + input[type='email'], 158 + input[type='number'], 159 + input[type='password'], 160 + input[type='search'], 161 + input[type='tel'], 162 + input[type='text'], 163 + input[type='url'], 164 + select, 165 + textarea { 166 + border: 1px solid; 167 + border-radius: var(--br-base); 168 + box-sizing: border-box; 169 + font-size: var(--f5); 170 + line-height: var(--lh-base); 171 + margin-bottom: var(--s3); 172 + padding: var(--s3); 173 + transition: 174 + border-color var(--transition-base), 175 + box-shadow var(--transition-base), 176 + opacity var(--transition-base); 177 + } 178 + input:focus, select:focus, textarea:focus { 179 + opacity: 1; 180 + outline: 2px solid currentColor; 181 + outline-offset: 2px; 182 + } 183 + button, input[type='button'], input[type='submit'] { 184 + background: none; 185 + border: 1px solid; 186 + border-radius: var(--br-base); 187 + color: inherit; 188 + cursor: pointer; 189 + display: inline-block; 190 + font-size: var(--f5); 191 + font-weight: var(--fw-medium); 192 + padding: var(--s2) var(--s2); 193 + text-decoration: none; 194 + transition: 195 + opacity var(--transition-base), 196 + transform var(--transition-fast); 197 + } 198 + button:hover, input[type='button']:hover, input[type='submit']:hover { 199 + opacity: 0.8; 200 + transform: translateY(-1px); 201 + } 202 + button:active, input[type='button']:active, input[type='submit']:active { 203 + transform: translateY(0); 204 + } 205 + button:disabled, 206 + input[type='button']:disabled, 207 + input[type='submit']:disabled { 208 + cursor: not-allowed; 209 + opacity: 0.3; 210 + transform: none; 211 + } 212 + :root { 213 + --nav-drawer-duration: 250ms; 214 + --nav-footer-height: 72px; 215 + } 216 + body { 217 + background-color: var(--background); 218 + color: var(--body); 219 + display: grid; 220 + grid-template-areas: 221 + 'banner banner banner' 'header header header' 'subhead subhead subhead' 'navhd mainhd aside' 'nav main aside' 'navft mainft aside' 'footer footer footer'; 222 + grid-template-columns: var(--nav-width, auto) 1fr var(--aside-width, 0); 223 + grid-template-rows: auto auto auto auto 1fr auto auto; 224 + margin: 0; 225 + min-height: 100vh; 226 + } 227 + } 228 + @layer base {} 229 + @layer base { 230 + body:has(footer[fixed]) { 231 + padding-bottom: var(--footer-height, 56px); 232 + } 233 + body > header { 234 + grid-area: header; 235 + } 236 + body > nav { 237 + grid-area: nav; 238 + } 239 + body > main { 240 + grid-area: main; 241 + } 242 + body > aside { 243 + grid-area: aside; 244 + } 245 + body > footer { 246 + grid-area: footer; 247 + } 248 + body > header { 249 + background: var(--background); 250 + margin: 0; 251 + padding: 0; 252 + position: sticky; 253 + top: var(--banner-height, 0); 254 + z-index: var(--z-sticky); 255 + } 256 + body > nav { 257 + align-self: start; 258 + display: flex; 259 + flex-direction: column; 260 + gap: var(--s1); 261 + max-height: calc( 262 + 100vh - var(--banner-height, 0px) - var(--header-height, 56px) 263 + - var(--footer-height, 76px) 264 + ); 265 + overflow-y: auto; 266 + padding: var(--s3) var(--s2); 267 + padding-bottom: calc(var(--nav-footer-height) + var(--s3)); 268 + position: sticky; 269 + top: calc(var(--banner-height, 0px) + var(--header-height, 56px)); 270 + } 271 + body:not(:has(ui-nav-footer)) > nav { 272 + padding-bottom: var(--s3); 273 + } 274 + body > nav ul { 275 + display: flex; 276 + flex-direction: column; 277 + gap: var(--s1); 278 + list-style: none; 279 + margin: 0; 280 + padding: 0; 281 + } 282 + body > nav li { 283 + position: relative; 284 + } 285 + body > nav a { 286 + border-radius: var(--br-base); 287 + color: inherit; 288 + display: block; 289 + font-weight: var(--fw-medium); 290 + padding: var(--s2) var(--s3); 291 + text-decoration: none; 292 + transition: opacity var(--transition-base); 293 + } 294 + body > nav a:hover { 295 + opacity: 0.7; 296 + } 297 + body > nav a[aria-current='page'] { 298 + cursor: default; 299 + opacity: 0.5; 300 + } 301 + body > nav ul ul { 302 + border-left: 1px solid; 303 + margin-left: var(--s3); 304 + margin-top: var(--s1); 305 + padding-left: var(--s3); 306 + } 307 + body > main { 308 + box-sizing: border-box; 309 + margin: 0 auto; 310 + max-width: var(--main-max-width, var(--max-width-lg)); 311 + padding: var(--main-padding, 0); 312 + width: 100%; 313 + } 314 + body > aside { 315 + align-self: start; 316 + max-height: calc( 317 + 100vh - var(--banner-height, 0px) - var(--header-height, 56px) 318 + ); 319 + overflow-y: auto; 320 + padding: var(--aside-padding, var(--s3) var(--s2)); 321 + position: sticky; 322 + top: calc(var(--banner-height, 0px) + var(--header-height, 56px)); 323 + } 324 + body > footer { 325 + background: var(--background); 326 + border-top: 1px solid; 327 + padding: var(--footer-padding, var(--s3) var(--container-padding)); 328 + } 329 + body > footer[fixed] { 330 + border-top: 1px solid; 331 + bottom: 0; 332 + grid-area: unset; 333 + left: 0; 334 + position: fixed; 335 + right: 0; 336 + z-index: var(--z-fixed); 337 + } 338 + ui-banner { 339 + grid-area: banner; 340 + top: 0; 341 + z-index: var(--z-sticky); 342 + } 343 + ui-banner, ui-sub-header { 344 + background: var(--background); 345 + display: block; 346 + position: sticky; 347 + } 348 + ui-sub-header { 349 + grid-area: subhead; 350 + top: calc(var(--banner-height, 0px) + var(--header-height, 0px)); 351 + z-index: calc(var(--z-sticky) - 1); 352 + } 353 + ui-nav-header { 354 + align-self: start; 355 + grid-area: navhd; 356 + padding: var(--s3) var(--s2) var(--s2); 357 + top: calc(var(--banner-height, 0px) + var(--header-height, 56px)); 358 + } 359 + ui-nav-footer, ui-nav-header { 360 + background: var(--background); 361 + display: block; 362 + position: sticky; 363 + z-index: var(--z-sticky); 364 + } 365 + ui-nav-footer { 366 + align-self: end; 367 + bottom: var(--footer-height, 0); 368 + grid-area: navft; 369 + padding: var(--s2) var(--s2) var(--s3); 370 + } 371 + ui-main-header { 372 + display: block; 373 + grid-area: mainhd; 374 + padding: var(--s3) var(--container-padding) var(--s2); 375 + } 376 + ui-main-footer { 377 + display: block; 378 + grid-area: mainft; 379 + padding: var(--s2) var(--container-padding) var(--s3); 380 + } 381 + #nav-toggle, ui-nav-toggle { 382 + display: none; 383 + } 384 + ui-nav-toggle label[for='nav-toggle'] { 385 + align-items: center; 386 + background: transparent; 387 + border: 1px solid; 388 + border-radius: var(--br-base); 389 + cursor: pointer; 390 + display: flex; 391 + flex-direction: column; 392 + gap: 4px; 393 + height: 44px; 394 + justify-content: center; 395 + padding: 0; 396 + transition: opacity var(--transition-base); 397 + width: 44px; 398 + } 399 + ui-nav-toggle label[for='nav-toggle']:hover { 400 + opacity: 0.7; 401 + } 402 + ui-nav-toggle label[for='nav-toggle'] span, 403 + ui-nav-toggle label[for='nav-toggle']:after, 404 + ui-nav-toggle label[for='nav-toggle']:before { 405 + background: currentColor; 406 + content: ''; 407 + display: block; 408 + height: 2px; 409 + transition: all 0.3s ease; 410 + width: 20px; 411 + } 412 + ui-nav-toggle label[for='nav-toggle'] span { 413 + pointer-events: none; 414 + } 415 + #nav-toggle:checked ~ * ui-nav-toggle label[for='nav-toggle']:before, 416 + #nav-toggle:checked ~ header ui-nav-toggle label[for='nav-toggle']:before { 417 + transform: translateY(6px) rotate(45deg); 418 + } 419 + #nav-toggle:checked ~ * ui-nav-toggle label[for='nav-toggle'] span, 420 + #nav-toggle:checked ~ header ui-nav-toggle label[for='nav-toggle'] span { 421 + opacity: 0; 422 + } 423 + #nav-toggle:checked ~ * ui-nav-toggle label[for='nav-toggle']:after, 424 + #nav-toggle:checked ~ header ui-nav-toggle label[for='nav-toggle']:after { 425 + transform: translateY(-6px) rotate(-45deg); 426 + } 427 + label.nav-backdrop { 428 + background: rgba(0, 0, 0, 0.5); 429 + bottom: 0; 430 + cursor: pointer; 431 + display: none; 432 + height: 100vh; 433 + left: 0; 434 + opacity: 0; 435 + position: fixed; 436 + right: 0; 437 + top: 0; 438 + transition: var(--nav-drawer-duration) allow-discrete; 439 + transition-property: display, opacity, overlay; 440 + width: 100vw; 441 + z-index: calc(var(--z-fixed) + 1); 442 + } 443 + #nav-toggle:checked ~ label.nav-backdrop { 444 + display: block; 445 + opacity: 1; 446 + } 447 + @starting-style { 448 + #nav-toggle:checked ~ label.nav-backdrop { 449 + opacity: 0; 450 + } 451 + } 452 + @media (max-width: 900px) { 453 + body { 454 + grid-template-areas: 455 + 'banner' 'header' 'subhead' 'mainhd' 'main' 'mainft' 'footer'; 456 + grid-template-columns: 1fr; 457 + grid-template-rows: auto auto auto auto 1fr auto auto; 458 + } 459 + body > aside, body > nav, ui-nav-footer, ui-nav-header { 460 + display: none; 461 + } 462 + ui-nav-toggle { 463 + display: block; 464 + } 465 + body > nav { 466 + background: var(--background); 467 + bottom: 0; 468 + box-shadow: 4px 0 12px rgba(0, 0, 0, 0.15); 469 + height: 100vh; 470 + left: 0; 471 + max-height: calc(100vh - var(--nav-header-height, 56px)); 472 + overflow-y: auto; 473 + padding: var(--s3); 474 + padding-bottom: calc(var(--nav-footer-height) + var(--s3)); 475 + padding-top: calc( 476 + var(--banner-height, 0px) + var(--header-height, 56px) + var(--s3) 477 + ); 478 + position: fixed; 479 + top: 0; 480 + transform: translateX(-100%); 481 + transition: var(--nav-drawer-duration) allow-discrete; 482 + transition-property: display, transform; 483 + width: min(80vw, 320px); 484 + z-index: calc(var(--z-fixed) + 2); 485 + } 486 + body:not(:has(ui-nav-footer)) > nav { 487 + max-height: 100vh; 488 + } 489 + #nav-toggle:checked ~ nav { 490 + display: flex; 491 + transform: translateX(0); 492 + } 493 + @starting-style { 494 + #nav-toggle:checked ~ nav { 495 + transform: translateX(-100%); 496 + } 497 + } 498 + ui-nav-header { 499 + align-items: center; 500 + background: var(--background); 501 + border-bottom: 1px solid; 502 + height: var(--header-height, 56px); 503 + left: 0; 504 + position: fixed; 505 + top: var(--banner-height, 0); 506 + transform: translateX(-100%); 507 + transition: var(--nav-drawer-duration) allow-discrete; 508 + transition-property: display, transform; 509 + width: min(80vw, 320px); 510 + z-index: calc(var(--z-fixed) + 3); 511 + } 512 + #nav-toggle:checked ~ ui-nav-header { 513 + display: flex; 514 + transform: translateX(0); 515 + } 516 + @starting-style { 517 + #nav-toggle:checked ~ ui-nav-header { 518 + transform: translateX(-100%); 519 + } 520 + } 521 + ui-nav-footer { 522 + align-items: center; 523 + background: var(--background); 524 + border-top: 1px solid; 525 + bottom: 0; 526 + height: var(--nav-footer-height); 527 + left: 0; 528 + position: fixed; 529 + transform: translateX(-100%); 530 + transition: var(--nav-drawer-duration) allow-discrete; 531 + transition-property: display, transform; 532 + width: min(80vw, 320px); 533 + z-index: calc(var(--z-fixed) + 3); 534 + } 535 + #nav-toggle:checked ~ ui-nav-footer { 536 + display: flex; 537 + transform: translateX(0); 538 + } 539 + @starting-style { 540 + #nav-toggle:checked ~ ui-nav-footer { 541 + transform: translateX(-100%); 542 + } 543 + } 544 + } 545 + } 546 + @layer base { 547 + h1, h2, h3, h4, h5, h6 { 548 + font-weight: var(--fw-semibold); 549 + line-height: var(--lh-tight); 550 + margin: 0 0 var(--s3) 0; 551 + } 552 + h1 { 553 + font-size: var(--f1); 554 + } 555 + h2 { 556 + font-size: var(--f2); 557 + } 558 + h3 { 559 + font-size: var(--f3); 560 + } 561 + h4 { 562 + font-size: var(--f4); 563 + } 564 + h5 { 565 + font-size: var(--f5); 566 + } 567 + h6 { 568 + font-size: var(--f6); 569 + } 570 + a { 571 + color: var(--primary); 572 + text-decoration: underline; 573 + transition: color var(--transition-base); 574 + } 575 + @media (max-width: 768px) { 576 + h1 { 577 + font-size: var(--f3); 578 + } 579 + h2, h3 { 580 + font-size: var(--f4); 581 + } 582 + } 583 + @media (max-width: 480px) { 584 + main { 585 + padding: var(--s3); 586 + } 587 + h1 { 588 + font-size: var(--f3); 589 + } 590 + h2 { 591 + font-size: var(--f4); 592 + } 593 + } 594 + } 595 + @layer base { 596 + html { 597 + background-color: var(--background); 598 + color: var(--body); 599 + font-family: var(--font-family-base); 600 + font-size: var(--f5); 601 + height: 100%; 602 + line-height: var(--lh-base); 603 + } 604 + p { 605 + line-height: var(--lh-relaxed); 606 + text-wrap: balance; 607 + } 608 + ol, p, ul { 609 + margin: 0 0 var(--s3) 0; 610 + } 611 + ol, ul { 612 + padding: var(--s2) 0 var(--s2) var(--s4); 613 + } 614 + li { 615 + margin-bottom: var(--s2); 616 + } 617 + table { 618 + border-collapse: collapse; 619 + margin: 0 0 var(--s4) 0; 620 + width: 100%; 621 + } 622 + td, th { 623 + border-bottom: 1px solid; 624 + opacity: 0.9; 625 + padding: var(--s3) var(--s3); 626 + text-align: left; 627 + } 628 + th { 629 + font-weight: var(--fw-semibold); 630 + opacity: 1; 631 + } 632 + code, kbd, samp { 633 + border: 1px solid; 634 + border-radius: var(--br-sm); 635 + font-family: var(--font-family-mono); 636 + font-size: 0.9em; 637 + padding: 0.2em 0.4em; 638 + } 639 + pre { 640 + border: 1px solid; 641 + border-radius: var(--br-base); 642 + margin: 0 0 var(--s3) 0; 643 + overflow-x: auto; 644 + padding: var(--s3); 645 + } 646 + pre code { 647 + border: none; 648 + opacity: 1; 649 + padding: 0; 650 + } 651 + [role='tablist'] { 652 + border-bottom: 1px solid; 653 + display: flex; 654 + margin-bottom: var(--s3); 655 + opacity: 0.8; 656 + } 657 + [role='tab'] { 658 + background: none; 659 + border: none; 660 + border-bottom: 2px solid transparent; 661 + border-radius: 0; 662 + cursor: pointer; 663 + font-weight: var(--fw-medium); 664 + opacity: 1; 665 + padding: var(--s3) var(--s3); 666 + transition: all var(--transition-base); 667 + } 668 + [role='tab']:hover { 669 + opacity: 1; 670 + } 671 + [role='tab'][aria-selected='true'] { 672 + border-bottom-color: currentColor; 673 + opacity: 1; 674 + } 675 + [role='tabpanel'] { 676 + padding: var(--s3) 0; 677 + } 678 + [role='tabpanel'][hidden] { 679 + display: none; 680 + } 681 + details { 682 + margin: 0 0 var(--s2) 0; 683 + } 684 + details, summary { 685 + border-radius: var(--br-base); 686 + } 687 + summary { 688 + cursor: pointer; 689 + font-weight: var(--fw-medium); 690 + opacity: 1; 691 + padding: var(--s2); 692 + transition: opacity var(--transition-base); 693 + } 694 + summary:hover { 695 + opacity: 0.7; 696 + } 697 + details[open] summary { 698 + border-radius: var(--br-base) var(--br-base) 0 0; 699 + } 700 + details > :not(summary):not(ul) { 701 + padding: var(--s2); 702 + } 703 + :focus-visible { 704 + outline: 2px solid currentColor; 705 + outline-offset: 2px; 706 + } 707 + .skip-to-main { 708 + background: currentColor; 709 + border-radius: var(--br-base); 710 + color: #fff; 711 + left: 6px; 712 + padding: 8px; 713 + position: absolute; 714 + text-decoration: none; 715 + top: -40px; 716 + z-index: var(--z-tooltip); 717 + } 718 + .skip-to-main:focus { 719 + top: 6px; 720 + } 721 + } 722 + @layer base-theme { 723 + :root { 724 + --font-family-base: 725 + system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif; 726 + --font-family-mono: 727 + ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace; 728 + --f-headline: 6rem; 729 + --f-subheadline: 5rem; 730 + --f1: 3rem; 731 + --f2: 2.25rem; 732 + --f3: 1.5rem; 733 + --f4: 1.25rem; 734 + --f5: 1rem; 735 + --f6: 0.875rem; 736 + --f7: 0.75rem; 737 + --s0: 0; 738 + --s1: 0.25rem; 739 + --s2: 0.5rem; 740 + --s3: 1rem; 741 + --s4: 2rem; 742 + --s5: 3rem; 743 + --max-width-sm: 640px; 744 + --max-width-md: 768px; 745 + --max-width-lg: 1024px; 746 + --max-width-xl: 1280px; 747 + --max-width-2xl: 1536px; 748 + --banner-height: 0px; 749 + --header-height: 56px; 750 + --sub-header-height: 0px; 751 + --fw-normal: 400; 752 + --fw-medium: 500; 753 + --fw-semibold: 600; 754 + --fw-bold: 700; 755 + --lh-tight: 1.2; 756 + --lh-base: 1.5; 757 + --lh-relaxed: 1.7; 758 + --br-none: 0; 759 + --br-sm: 0.25rem; 760 + --br-base: 0.5rem; 761 + --br-lg: 0.75rem; 762 + --br-xl: 1rem; 763 + --br-full: 9999px; 764 + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); 765 + --shadow-base: 766 + 0 1px 3px 0 rgba(0, 0, 0, 0.1),0 1px 2px 0 rgba(0, 0, 0, 0.06); 767 + --shadow-md: 768 + 0 4px 6px -1px rgba(0, 0, 0, 0.1),0 2px 4px -1px rgba(0, 0, 0, 0.06); 769 + --shadow-lg: 770 + 0 10px 15px -3px rgba(0, 0, 0, 0.1),0 4px 6px -2px rgba(0, 0, 0, 0.05); 771 + --shadow-xl: 772 + 0 20px 25px -5px rgba(0, 0, 0, 0.1),0 10px 10px -5px rgba(0, 0, 0, 0.04); 773 + --transition-fast: 0.15s ease-in-out; 774 + --transition-base: 0.2s ease-in-out; 775 + --transition-slow: 0.3s ease-in-out; 776 + --z-dropdown: 1000; 777 + --z-sticky: 1020; 778 + --z-fixed: 1030; 779 + --z-modal-backdrop: 1040; 780 + --z-modal: 1050; 781 + --z-popover: 1060; 782 + --z-tooltip: 1070; 783 + --grid-columns: 12; 784 + --container-padding: var(--s3); 785 + --ease-in: cubic-bezier(0.4, 0, 1, 1); 786 + --ease-out: cubic-bezier(0, 0, 0.2, 1); 787 + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); 788 + --blackH: 0; 789 + --blackS: 0%; 790 + --blackL: 0%; 791 + --grayH: 220; 792 + --grayS: 14%; 793 + --grayL: 41%; 794 + --whiteH: 0; 795 + --whiteS: 0%; 796 + --whiteL: 100%; 797 + --blueH: 217; 798 + --blueS: 91%; 799 + --blueL: 60%; 800 + --greenH: 142; 801 + --greenS: 71%; 802 + --greenL: 45%; 803 + --yellowH: 45; 804 + --yellowS: 93%; 805 + --yellowL: 47%; 806 + --orangeH: 25; 807 + --orangeS: 95%; 808 + --orangeL: 53%; 809 + --redH: 0; 810 + --redS: 84%; 811 + --redL: 60%; 812 + --purpleH: 262; 813 + --purpleS: 83%; 814 + --purpleL: 58%; 815 + --errorH: var(--redH); 816 + --errorS: var(--redS); 817 + --errorL: var(--redL); 818 + --warningH: var(--orangeH); 819 + --warningS: var(--orangeS); 820 + --warningL: var(--orangeL); 821 + --infoH: var(--blueH); 822 + --infoS: var(--blueS); 823 + --infoL: var(--blueL); 824 + --successH: var(--greenH); 825 + --successS: var(--greenS); 826 + --successL: var(--greenL); 827 + --backgroundH: var(--whiteH); 828 + --backgroundS: var(--whiteS); 829 + --backgroundL: var(--whiteL); 830 + --bodyH: var(--blackH); 831 + --bodyS: var(--blackS); 832 + --bodyL: 13%; 833 + --secondaryH: var(--grayH); 834 + --secondaryS: var(--grayS); 835 + --secondaryL: var(--grayL); 836 + --primaryH: var(--blueH); 837 + --primaryS: var(--blueS); 838 + --primaryL: var(--blueL); 839 + } 840 + } 841 + @layer components { 842 + ui-card { 843 + border: 1px solid; 844 + border-radius: var(--br-lg); 845 + box-shadow: var(--shadow-sm); 846 + display: block; 847 + overflow: hidden; 848 + transition: all var(--transition-base); 849 + } 850 + ui-card:hover { 851 + box-shadow: var(--shadow-md); 852 + transform: translateY(-2px); 853 + } 854 + ui-card[clickable] { 855 + cursor: pointer; 856 + } 857 + ui-card[clickable]:active { 858 + transform: translateY(0); 859 + } 860 + ui-badge { 861 + border: 1px solid; 862 + border-radius: var(--br-full); 863 + display: inline-block; 864 + font-size: var(--f7); 865 + font-weight: var(--fw-medium); 866 + letter-spacing: 0.05em; 867 + padding: var(--s1) var(--s2); 868 + text-transform: uppercase; 869 + } 870 + ui-badge[variant='success'] { 871 + background-color: var(--success-dull); 872 + color: var(--success); 873 + } 874 + ui-badge[variant='warning'] { 875 + background-color: var(--warning-dull); 876 + color: var(--warning); 877 + } 878 + ui-badge[variant='error'] { 879 + background-color: var(--error-dull); 880 + color: var(--error); 881 + } 882 + ui-badge[variant='info'] { 883 + background-color: var(--info-dull); 884 + color: var(--info); 885 + } 886 + ui-button-group { 887 + display: flex; 888 + flex-wrap: wrap; 889 + gap: var(--s2); 890 + } 891 + ui-button-group[orientation='vertical'] { 892 + flex-direction: column; 893 + } 894 + ui-button-group[attached] { 895 + gap: 0; 896 + } 897 + ui-button-group[attached] > button:not(:first-child), 898 + ui-button-group[attached] > input:not(:first-child) { 899 + border-bottom-left-radius: 0; 900 + border-left: none; 901 + border-top-left-radius: 0; 902 + } 903 + ui-button-group[attached] > button:not(:last-child), 904 + ui-button-group[attached] > input:not(:last-child) { 905 + border-bottom-right-radius: 0; 906 + border-top-right-radius: 0; 907 + } 908 + ui-grid { 909 + display: grid; 910 + gap: var(--s3); 911 + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); 912 + } 913 + ui-grid[cols='1'] { 914 + grid-template-columns: 1fr; 915 + } 916 + ui-grid[cols='2'] { 917 + grid-template-columns: repeat(2, 1fr); 918 + } 919 + ui-grid[cols='3'] { 920 + grid-template-columns: repeat(3, 1fr); 921 + } 922 + ui-grid[cols='4'] { 923 + grid-template-columns: repeat(4, 1fr); 924 + } 925 + @media (max-width: 768px) { 926 + ui-grid[cols='2'], ui-grid[cols='3'], ui-grid[cols='4'] { 927 + grid-template-columns: 1fr; 928 + } 929 + } 930 + ui-stack { 931 + display: flex; 932 + flex-direction: column; 933 + gap: var(--s3); 934 + } 935 + ui-stack[spacing='xs'] { 936 + gap: var(--s1); 937 + } 938 + ui-stack[spacing='sm'] { 939 + gap: var(--s2); 940 + } 941 + ui-stack[spacing='lg'], ui-stack[spacing='md'], ui-stack[spacing='xl'] { 942 + gap: var(--s3); 943 + } 944 + ui-stack[direction='row'] { 945 + align-items: center; 946 + flex-direction: row; 947 + } 948 + ui-progress { 949 + border: 1px solid; 950 + height: 8px; 951 + overflow: hidden; 952 + width: 100%; 953 + } 954 + ui-progress, ui-progress:before { 955 + border-radius: var(--br-full); 956 + display: block; 957 + } 958 + ui-progress:before { 959 + background-color: currentColor; 960 + content: ''; 961 + height: 100%; 962 + transition: width var(--transition-base); 963 + width: var(--progress-value, 0); 964 + } 965 + ui-progress[variant='success']:before { 966 + background-color: var(--success); 967 + } 968 + ui-progress[variant='warning']:before { 969 + background-color: var(--warning); 970 + } 971 + ui-progress[variant='error']:before { 972 + background-color: var(--error); 973 + } 974 + ui-alert { 975 + border: 1px solid; 976 + border-radius: var(--br-base); 977 + display: block; 978 + padding: var(--s3); 979 + } 980 + ui-alert[variant='success'] { 981 + background-color: var(--success-dull); 982 + border-color: var(--success); 983 + color: var(--success); 984 + } 985 + ui-alert[variant='warning'] { 986 + background-color: var(--warning-dull); 987 + border-color: var(--warning); 988 + color: var(--warning); 989 + } 990 + ui-alert[variant='error'] { 991 + background-color: var(--error-dull); 992 + border-color: var(--error); 993 + color: var(--error); 994 + } 995 + ui-alert[variant='info'] { 996 + background-color: var(--info-dull); 997 + border-color: var(--info); 998 + color: var(--info); 999 + } 1000 + ui-divider { 1001 + border: none; 1002 + border-top: 1px solid; 1003 + display: block; 1004 + height: 1px; 1005 + margin: var(--s4) 0; 1006 + } 1007 + ui-divider[orientation='vertical'] { 1008 + height: auto; 1009 + margin: 0 var(--s3); 1010 + width: 1px; 1011 + } 1012 + ui-spinner { 1013 + animation: spin 1s linear infinite; 1014 + border-color: currentcolor transparent; 1015 + border-radius: 50%; 1016 + border-style: solid; 1017 + border-width: 2px; 1018 + display: inline-block; 1019 + height: 20px; 1020 + width: 20px; 1021 + } 1022 + ui-spinner[size='sm'] { 1023 + height: 16px; 1024 + width: 16px; 1025 + } 1026 + ui-spinner[size='lg'] { 1027 + border-width: 3px; 1028 + height: 32px; 1029 + width: 32px; 1030 + } 1031 + ui-breadcrumb { 1032 + align-items: center; 1033 + display: flex; 1034 + font-size: var(--f5); 1035 + gap: var(--s2); 1036 + } 1037 + ui-breadcrumb a { 1038 + border-radius: var(--br-sm); 1039 + color: var(--primary); 1040 + padding: var(--s1) var(--s2); 1041 + text-decoration: none; 1042 + transition: opacity var(--transition-base); 1043 + } 1044 + ui-breadcrumb a:hover { 1045 + opacity: 0.7; 1046 + } 1047 + ui-breadcrumb:before { 1048 + content: attr(separator); 1049 + margin: 0 var(--s1); 1050 + opacity: 0.5; 1051 + } 1052 + ui-breadcrumb:first-child:before { 1053 + display: none; 1054 + } 1055 + ui-avatar { 1056 + border: 1px solid; 1057 + border-radius: var(--br-full); 1058 + display: inline-block; 1059 + font-size: var(--f5); 1060 + font-weight: var(--fw-medium); 1061 + height: 40px; 1062 + line-height: 40px; 1063 + overflow: hidden; 1064 + text-align: center; 1065 + width: 40px; 1066 + } 1067 + ui-avatar[size='sm'] { 1068 + font-size: var(--f7); 1069 + height: 32px; 1070 + line-height: 32px; 1071 + width: 32px; 1072 + } 1073 + ui-avatar[size='lg'] { 1074 + font-size: var(--f4); 1075 + height: 56px; 1076 + line-height: 56px; 1077 + width: 56px; 1078 + } 1079 + ui-avatar img { 1080 + height: 100%; 1081 + -o-object-fit: cover; 1082 + object-fit: cover; 1083 + width: 100%; 1084 + } 1085 + ui-dropdown { 1086 + display: inline-block; 1087 + position: relative; 1088 + } 1089 + ui-dropdown-content { 1090 + background: var(--background); 1091 + border: 1px solid; 1092 + border-radius: var(--br-base); 1093 + box-shadow: var(--shadow-lg); 1094 + left: 0; 1095 + min-width: 200px; 1096 + opacity: 0; 1097 + position: absolute; 1098 + top: 100%; 1099 + transform: translateY(-8px); 1100 + transition: all var(--transition-base); 1101 + visibility: hidden; 1102 + z-index: var(--z-dropdown); 1103 + } 1104 + ui-dropdown[open] ui-dropdown-content { 1105 + opacity: 1; 1106 + transform: translateY(0); 1107 + visibility: visible; 1108 + } 1109 + ui-dropdown-item { 1110 + cursor: pointer; 1111 + display: block; 1112 + padding: var(--s3); 1113 + text-decoration: none; 1114 + transition: opacity var(--transition-base); 1115 + } 1116 + ui-dropdown-item:hover { 1117 + opacity: 0.7; 1118 + } 1119 + ui-dropdown-item[active] { 1120 + font-weight: var(--fw-medium); 1121 + opacity: 1; 1122 + } 1123 + @media (max-width: 768px) { 1124 + ui-dropdown-content { 1125 + left: auto; 1126 + min-width: 150px; 1127 + right: 0; 1128 + } 1129 + } 1130 + .drawer-toggle { 1131 + display: none; 1132 + } 1133 + .drawer-backdrop { 1134 + background: rgba(0, 0, 0, 0.5); 1135 + bottom: 0; 1136 + cursor: pointer; 1137 + display: none; 1138 + height: 100vh; 1139 + left: 0; 1140 + opacity: 0; 1141 + position: fixed; 1142 + right: 0; 1143 + top: 0; 1144 + transition: allow-discrete 0.25s; 1145 + transition-property: display, opacity, overlay; 1146 + width: 100vw; 1147 + z-index: calc(var(--z-fixed) + 1); 1148 + } 1149 + .drawer-toggle:checked ~ .drawer-backdrop { 1150 + display: block; 1151 + opacity: 1; 1152 + } 1153 + @starting-style { 1154 + .drawer-toggle:checked ~ .drawer-backdrop { 1155 + opacity: 0; 1156 + } 1157 + } 1158 + ui-drawer { 1159 + background: var(--background); 1160 + box-shadow: 4px 0 12px rgba(0, 0, 0, 0.15); 1161 + display: none; 1162 + overflow-y: auto; 1163 + padding: var(--s3); 1164 + position: fixed; 1165 + transition: allow-discrete 0.25s; 1166 + transition-property: display, transform; 1167 + z-index: calc(var(--z-fixed) + 2); 1168 + } 1169 + ui-drawer, ui-drawer[position='left'] { 1170 + bottom: 0; 1171 + height: 100vh; 1172 + left: 0; 1173 + top: 0; 1174 + transform: translateX(-100%); 1175 + width: min(80vw, 320px); 1176 + } 1177 + .drawer-toggle:checked ~ * ui-drawer[position='left'], 1178 + .drawer-toggle:checked ~ ui-drawer[position='left'] { 1179 + display: block; 1180 + transform: translateX(0); 1181 + } 1182 + @starting-style { 1183 + .drawer-toggle:checked ~ * ui-drawer[position='left'], 1184 + .drawer-toggle:checked ~ ui-drawer[position='left'] { 1185 + transform: translateX(-100%); 1186 + } 1187 + } 1188 + ui-drawer[position='right'] { 1189 + bottom: 0; 1190 + box-shadow: -4px 0 12px rgba(0, 0, 0, 0.15); 1191 + height: 100vh; 1192 + left: auto; 1193 + right: 0; 1194 + top: 0; 1195 + transform: translateX(100%); 1196 + width: min(80vw, 320px); 1197 + } 1198 + .drawer-toggle:checked ~ * ui-drawer[position='right'], 1199 + .drawer-toggle:checked ~ ui-drawer[position='right'] { 1200 + display: block; 1201 + transform: translateX(0); 1202 + } 1203 + @starting-style { 1204 + .drawer-toggle:checked ~ * ui-drawer[position='right'], 1205 + .drawer-toggle:checked ~ ui-drawer[position='right'] { 1206 + transform: translateX(100%); 1207 + } 1208 + } 1209 + ui-drawer[position='top'] { 1210 + bottom: auto; 1211 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 1212 + height: min(80vh, 400px); 1213 + left: 0; 1214 + right: 0; 1215 + top: 0; 1216 + transform: translateY(-100%); 1217 + width: 100vw; 1218 + } 1219 + .drawer-toggle:checked ~ * ui-drawer[position='top'], 1220 + .drawer-toggle:checked ~ ui-drawer[position='top'] { 1221 + display: block; 1222 + transform: translateY(0); 1223 + } 1224 + @starting-style { 1225 + .drawer-toggle:checked ~ * ui-drawer[position='top'], 1226 + .drawer-toggle:checked ~ ui-drawer[position='top'] { 1227 + transform: translateY(-100%); 1228 + } 1229 + } 1230 + ui-drawer[position='bottom'] { 1231 + bottom: 0; 1232 + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15); 1233 + height: min(80vh, 400px); 1234 + left: 0; 1235 + right: 0; 1236 + top: auto; 1237 + transform: translateY(100%); 1238 + width: 100vw; 1239 + } 1240 + .drawer-toggle:checked ~ * ui-drawer[position='bottom'], 1241 + .drawer-toggle:checked ~ ui-drawer[position='bottom'] { 1242 + display: block; 1243 + transform: translateY(0); 1244 + } 1245 + @starting-style { 1246 + .drawer-toggle:checked ~ * ui-drawer[position='bottom'], 1247 + .drawer-toggle:checked ~ ui-drawer[position='bottom'] { 1248 + transform: translateY(100%); 1249 + } 1250 + } 1251 + ui-bottom-bar { 1252 + align-items: stretch; 1253 + display: flex; 1254 + flex-direction: row; 1255 + gap: 0; 1256 + height: 100%; 1257 + margin: 0; 1258 + padding: 0; 1259 + width: 100%; 1260 + } 1261 + ui-bottom-bar > a, ui-bottom-bar > button { 1262 + align-items: center; 1263 + background: transparent; 1264 + border: none; 1265 + color: inherit; 1266 + cursor: pointer; 1267 + display: flex; 1268 + flex: 1; 1269 + flex-direction: column; 1270 + font-size: var(--f7); 1271 + font-weight: var(--fw-medium); 1272 + gap: 2px; 1273 + justify-content: center; 1274 + opacity: 1; 1275 + padding: var(--s1) var(--s2); 1276 + text-decoration: none; 1277 + transition: opacity var(--transition-base); 1278 + } 1279 + ui-bottom-bar > a:hover, ui-bottom-bar > button:hover { 1280 + opacity: 0.7; 1281 + } 1282 + ui-bottom-bar > button:disabled { 1283 + cursor: not-allowed; 1284 + opacity: 0.3; 1285 + } 1286 + ui-bottom-bar > a[aria-current='page'], 1287 + ui-bottom-bar > button[aria-current='page'] { 1288 + opacity: 0.5; 1289 + } 1290 + ui-bottom-bar .icon, ui-bottom-bar ui-icon { 1291 + font-size: var(--f4); 1292 + line-height: 1; 1293 + } 1294 + ui-bottom-bar[hide-text] > a > span:not(.icon), 1295 + ui-bottom-bar[hide-text] > button > span:not(.icon) { 1296 + display: none; 1297 + } 1298 + ui-bottom-bar[hide-text] > a, ui-bottom-bar[hide-text] > button { 1299 + gap: 0; 1300 + } 1301 + ui-bottom-bar[orientation='horizontal'] > a, 1302 + ui-bottom-bar[orientation='horizontal'] > button { 1303 + flex-direction: row; 1304 + gap: var(--s2); 1305 + } 1306 + ui-pwa-version { 1307 + display: block; 1308 + } 1309 + ui-pwa-version p { 1310 + margin: 0 0 var(--s2) 0; 1311 + } 1312 + ui-pwa-version button { 1313 + margin-top: var(--s1); 1314 + } 1315 + ui-counter { 1316 + align-items: stretch; 1317 + display: flex; 1318 + gap: var(--s2); 1319 + } 1320 + ui-counter button { 1321 + align-items: center; 1322 + display: flex; 1323 + flex-shrink: 0; 1324 + justify-content: center; 1325 + } 1326 + ui-counter input[type='number'] { 1327 + -webkit-appearance: textfield; 1328 + appearance: textfield; 1329 + -moz-appearance: textfield; 1330 + flex: 1; 1331 + margin: 0; 1332 + padding: 0; 1333 + text-align: center; 1334 + } 1335 + ui-counter input[type='number']::-webkit-inner-spin-button, 1336 + ui-counter input[type='number']::-webkit-outer-spin-button { 1337 + -webkit-appearance: none; 1338 + } 1339 + } 1340 + @layer utilities { 1341 + .flex { 1342 + display: flex; 1343 + } 1344 + .flex-column { 1345 + flex-direction: column; 1346 + } 1347 + .flex-row { 1348 + flex-direction: row; 1349 + } 1350 + .items-center { 1351 + align-items: center; 1352 + } 1353 + .items-start { 1354 + align-items: flex-start; 1355 + } 1356 + .items-end { 1357 + align-items: flex-end; 1358 + } 1359 + .justify-center { 1360 + justify-content: center; 1361 + } 1362 + .justify-start { 1363 + justify-content: flex-start; 1364 + } 1365 + .justify-end { 1366 + justify-content: flex-end; 1367 + } 1368 + .justify-between { 1369 + justify-content: space-between; 1370 + } 1371 + .w-100 { 1372 + width: 100%; 1373 + } 1374 + .mw6 { 1375 + max-width: var(--max-width-sm); 1376 + } 1377 + .measure { 1378 + max-width: 30em; 1379 + } 1380 + .container-sm { 1381 + max-width: var(--max-width-sm); 1382 + } 1383 + .container-md, .container-sm { 1384 + margin: 0 auto; 1385 + padding: 0 var(--container-padding); 1386 + } 1387 + .container-md { 1388 + max-width: var(--max-width-md); 1389 + } 1390 + .container-lg { 1391 + max-width: var(--max-width-lg); 1392 + } 1393 + .container-lg, .container-xl { 1394 + margin: 0 auto; 1395 + padding: 0 var(--container-padding); 1396 + } 1397 + .container-xl { 1398 + max-width: var(--max-width-xl); 1399 + } 1400 + .container-fluid { 1401 + padding: 0 var(--container-padding); 1402 + width: 100%; 1403 + } 1404 + .pa0 { 1405 + padding: 0; 1406 + } 1407 + .pa1 { 1408 + padding: var(--s1); 1409 + } 1410 + .pa2 { 1411 + padding: var(--s2); 1412 + } 1413 + .pa3 { 1414 + padding: var(--s3); 1415 + } 1416 + .pa4 { 1417 + padding: var(--s4); 1418 + } 1419 + .ma0 { 1420 + margin: 0; 1421 + } 1422 + .ma1 { 1423 + margin: var(--s1); 1424 + } 1425 + .ma2 { 1426 + margin: var(--s2); 1427 + } 1428 + .ma3 { 1429 + margin: var(--s3); 1430 + } 1431 + .mh-auto { 1432 + margin-left: auto; 1433 + margin-right: auto; 1434 + } 1435 + .mv3 { 1436 + margin-bottom: var(--s3); 1437 + margin-top: var(--s3); 1438 + } 1439 + .mb2 { 1440 + margin-bottom: var(--s2); 1441 + } 1442 + .mb0 { 1443 + margin-bottom: 0; 1444 + } 1445 + .mt3 { 1446 + margin-top: var(--s3); 1447 + } 1448 + .mt2 { 1449 + margin-top: var(--s2); 1450 + } 1451 + .mr3 { 1452 + margin-right: var(--s3); 1453 + } 1454 + .pr3 { 1455 + padding-right: var(--s3); 1456 + } 1457 + .pl2 { 1458 + padding-left: var(--s2); 1459 + } 1460 + .pt2 { 1461 + padding-top: var(--s2); 1462 + } 1463 + .pb4 { 1464 + padding-bottom: var(--s4); 1465 + } 1466 + .pb0 { 1467 + padding-bottom: 0; 1468 + } 1469 + .pb3 { 1470 + padding-bottom: var(--s3); 1471 + } 1472 + .f3 { 1473 + font-size: var(--f3); 1474 + } 1475 + .f4 { 1476 + font-size: var(--f4); 1477 + } 1478 + .f5 { 1479 + font-size: var(--f5); 1480 + } 1481 + .f6 { 1482 + font-size: var(--f6); 1483 + } 1484 + .tc { 1485 + text-align: center; 1486 + } 1487 + .tr { 1488 + text-align: right; 1489 + } 1490 + .b { 1491 + font-weight: var(--fw-bold); 1492 + } 1493 + .br2 { 1494 + border-radius: var(--br2); 1495 + } 1496 + .ba { 1497 + border: 1px solid; 1498 + } 1499 + .b1 { 1500 + border-width: 1px; 1501 + } 1502 + .t-border { 1503 + border: 1px solid transparent; 1504 + } 1505 + .border { 1506 + border: 1px solid; 1507 + } 1508 + .list { 1509 + list-style: none; 1510 + } 1511 + .cursor, .pointer { 1512 + cursor: pointer; 1513 + } 1514 + .o-30 { 1515 + opacity: 0.3; 1516 + } 1517 + .o-40 { 1518 + opacity: 0.4; 1519 + } 1520 + .o-60 { 1521 + opacity: 0.6; 1522 + } 1523 + .underline { 1524 + text-decoration: underline; 1525 + } 1526 + .link { 1527 + text-decoration: none; 1528 + } 1529 + .db { 1530 + display: block; 1531 + } 1532 + .h2 { 1533 + height: 2rem; 1534 + } 1535 + .h3 { 1536 + height: 3rem; 1537 + } 1538 + .input { 1539 + border: 1px solid; 1540 + border-radius: var(--br2); 1541 + padding: var(--s2); 1542 + } 1543 + .input-reset { 1544 + -webkit-appearance: none; 1545 + -moz-appearance: none; 1546 + appearance: none; 1547 + background: transparent; 1548 + } 1549 + .shadow { 1550 + box-shadow: var(--shadow-base); 1551 + } 1552 + .pv2 { 1553 + padding-bottom: var(--s2); 1554 + padding-top: var(--s2); 1555 + } 1556 + .pv1 { 1557 + padding-bottom: var(--s1); 1558 + padding-top: var(--s1); 1559 + } 1560 + .ph2 { 1561 + padding-left: var(--s2); 1562 + padding-right: var(--s2); 1563 + } 1564 + .lh-copy { 1565 + line-height: var(--lh-copy); 1566 + } 1567 + .center { 1568 + text-align: center; 1569 + } 1570 + .flex-wrap { 1571 + flex-wrap: wrap; 1572 + } 1573 + .no-select { 1574 + cursor: default; 1575 + -webkit-user-select: none; 1576 + -moz-user-select: none; 1577 + user-select: none; 1578 + } 1579 + .relative { 1580 + position: relative; 1581 + } 1582 + .absolute { 1583 + position: absolute; 1584 + } 1585 + .fixed { 1586 + position: fixed; 1587 + } 1588 + .absolute-center { 1589 + left: 50%; 1590 + position: fixed; 1591 + top: 50%; 1592 + transform: translate(-50%, -50%); 1593 + } 1594 + .overflow-hidden, .text-ellipsis { 1595 + overflow: hidden; 1596 + } 1597 + .text-ellipsis { 1598 + text-overflow: ellipsis; 1599 + white-space: nowrap; 1600 + } 1601 + .z-dropdown { 1602 + z-index: var(--z-dropdown); 1603 + } 1604 + .z-modal { 1605 + z-index: var(--z-modal); 1606 + } 1607 + .z-tooltip { 1608 + z-index: var(--z-tooltip); 1609 + } 1610 + }
+2 -1
www/static/styles/theme.css
··· 83 83 --shadow-xl: var(--black) 3px 3px 0px 0px; 84 84 85 85 /* Hanzi-specific shadows */ 86 - --purple-shadow: hsl(var(--purpleH), var(--purpleS), var(--purpleL)) 3px 3px 0px 0px; 86 + --purple-shadow: hsl(var(--purpleH), var(--purpleS), var(--purpleL)) 3px 3px 87 + 0px 0px; 87 88 --blue-shadow: hsl(var(--blueH), var(--blueS), var(--blueL)) 3px 3px 0px 0px; 88 89 --pink-shadow: hsl(var(--pinkH), var(--pinkS), var(--pinkL)) 3px 3px 0px 0px; 89 90
+271 -1
www/static/styles/utilities.min.css
··· 1 - @layer utilities{.flex{display:flex}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.w-100{width:100%}.mw6{max-width:var(--max-width-sm)}.measure{max-width:30em}.container-sm{max-width:var(--max-width-sm)}.container-md,.container-sm{margin:0 auto;padding:0 var(--container-padding)}.container-md{max-width:var(--max-width-md)}.container-lg{max-width:var(--max-width-lg)}.container-lg,.container-xl{margin:0 auto;padding:0 var(--container-padding)}.container-xl{max-width:var(--max-width-xl)}.container-fluid{padding:0 var(--container-padding);width:100%}.pa0{padding:0}.pa1{padding:var(--s1)}.pa2{padding:var(--s2)}.pa3{padding:var(--s3)}.pa4{padding:var(--s4)}.ma0{margin:0}.ma1{margin:var(--s1)}.ma2{margin:var(--s2)}.ma3{margin:var(--s3)}.mh-auto{margin-left:auto;margin-right:auto}.mv3{margin-bottom:var(--s3);margin-top:var(--s3)}.mb2{margin-bottom:var(--s2)}.mb0{margin-bottom:0}.mt3{margin-top:var(--s3)}.mt2{margin-top:var(--s2)}.mr3{margin-right:var(--s3)}.pr3{padding-right:var(--s3)}.pl2{padding-left:var(--s2)}.pt2{padding-top:var(--s2)}.pb4{padding-bottom:var(--s4)}.pb0{padding-bottom:0}.pb3{padding-bottom:var(--s3)}.f3{font-size:var(--f3)}.f4{font-size:var(--f4)}.f5{font-size:var(--f5)}.f6{font-size:var(--f6)}.tc{text-align:center}.tr{text-align:right}.b{font-weight:var(--fw-bold)}.br2{border-radius:var(--br2)}.ba{border:1px solid}.b1{border-width:1px}.t-border{border:1px solid transparent}.border{border:1px solid}.list{list-style:none}.cursor,.pointer{cursor:pointer}.o-30{opacity:.3}.o-40{opacity:.4}.o-60{opacity:.6}.underline{text-decoration:underline}.link{text-decoration:none}.db{display:block}.h2{height:2rem}.h3{height:3rem}.input{border:1px solid;border-radius:var(--br2);padding:var(--s2)}.input-reset{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent}.shadow{box-shadow:var(--shadow-base)}.pv2{padding-bottom:var(--s2);padding-top:var(--s2)}.pv1{padding-bottom:var(--s1);padding-top:var(--s1)}.ph2{padding-left:var(--s2);padding-right:var(--s2)}.lh-copy{line-height:var(--lh-copy)}.center{text-align:center}.flex-wrap{flex-wrap:wrap}.no-select{cursor:default;-webkit-user-select:none;-moz-user-select:none;user-select:none}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.absolute-center{left:50%;position:fixed;top:50%;transform:translate(-50%,-50%)}.overflow-hidden,.text-ellipsis{overflow:hidden}.text-ellipsis{text-overflow:ellipsis;white-space:nowrap}.z-dropdown{z-index:var(--z-dropdown)}.z-modal{z-index:var(--z-modal)}.z-tooltip{z-index:var(--z-tooltip)}} 1 + @layer utilities { 2 + .flex { 3 + display: flex; 4 + } 5 + .flex-column { 6 + flex-direction: column; 7 + } 8 + .flex-row { 9 + flex-direction: row; 10 + } 11 + .items-center { 12 + align-items: center; 13 + } 14 + .items-start { 15 + align-items: flex-start; 16 + } 17 + .items-end { 18 + align-items: flex-end; 19 + } 20 + .justify-center { 21 + justify-content: center; 22 + } 23 + .justify-start { 24 + justify-content: flex-start; 25 + } 26 + .justify-end { 27 + justify-content: flex-end; 28 + } 29 + .justify-between { 30 + justify-content: space-between; 31 + } 32 + .w-100 { 33 + width: 100%; 34 + } 35 + .mw6 { 36 + max-width: var(--max-width-sm); 37 + } 38 + .measure { 39 + max-width: 30em; 40 + } 41 + .container-sm { 42 + max-width: var(--max-width-sm); 43 + } 44 + .container-md, .container-sm { 45 + margin: 0 auto; 46 + padding: 0 var(--container-padding); 47 + } 48 + .container-md { 49 + max-width: var(--max-width-md); 50 + } 51 + .container-lg { 52 + max-width: var(--max-width-lg); 53 + } 54 + .container-lg, .container-xl { 55 + margin: 0 auto; 56 + padding: 0 var(--container-padding); 57 + } 58 + .container-xl { 59 + max-width: var(--max-width-xl); 60 + } 61 + .container-fluid { 62 + padding: 0 var(--container-padding); 63 + width: 100%; 64 + } 65 + .pa0 { 66 + padding: 0; 67 + } 68 + .pa1 { 69 + padding: var(--s1); 70 + } 71 + .pa2 { 72 + padding: var(--s2); 73 + } 74 + .pa3 { 75 + padding: var(--s3); 76 + } 77 + .pa4 { 78 + padding: var(--s4); 79 + } 80 + .ma0 { 81 + margin: 0; 82 + } 83 + .ma1 { 84 + margin: var(--s1); 85 + } 86 + .ma2 { 87 + margin: var(--s2); 88 + } 89 + .ma3 { 90 + margin: var(--s3); 91 + } 92 + .mh-auto { 93 + margin-left: auto; 94 + margin-right: auto; 95 + } 96 + .mv3 { 97 + margin-bottom: var(--s3); 98 + margin-top: var(--s3); 99 + } 100 + .mb2 { 101 + margin-bottom: var(--s2); 102 + } 103 + .mb0 { 104 + margin-bottom: 0; 105 + } 106 + .mt3 { 107 + margin-top: var(--s3); 108 + } 109 + .mt2 { 110 + margin-top: var(--s2); 111 + } 112 + .mr3 { 113 + margin-right: var(--s3); 114 + } 115 + .pr3 { 116 + padding-right: var(--s3); 117 + } 118 + .pl2 { 119 + padding-left: var(--s2); 120 + } 121 + .pt2 { 122 + padding-top: var(--s2); 123 + } 124 + .pb4 { 125 + padding-bottom: var(--s4); 126 + } 127 + .pb0 { 128 + padding-bottom: 0; 129 + } 130 + .pb3 { 131 + padding-bottom: var(--s3); 132 + } 133 + .f3 { 134 + font-size: var(--f3); 135 + } 136 + .f4 { 137 + font-size: var(--f4); 138 + } 139 + .f5 { 140 + font-size: var(--f5); 141 + } 142 + .f6 { 143 + font-size: var(--f6); 144 + } 145 + .tc { 146 + text-align: center; 147 + } 148 + .tr { 149 + text-align: right; 150 + } 151 + .b { 152 + font-weight: var(--fw-bold); 153 + } 154 + .br2 { 155 + border-radius: var(--br2); 156 + } 157 + .ba { 158 + border: 1px solid; 159 + } 160 + .b1 { 161 + border-width: 1px; 162 + } 163 + .t-border { 164 + border: 1px solid transparent; 165 + } 166 + .border { 167 + border: 1px solid; 168 + } 169 + .list { 170 + list-style: none; 171 + } 172 + .cursor, .pointer { 173 + cursor: pointer; 174 + } 175 + .o-30 { 176 + opacity: 0.3; 177 + } 178 + .o-40 { 179 + opacity: 0.4; 180 + } 181 + .o-60 { 182 + opacity: 0.6; 183 + } 184 + .underline { 185 + text-decoration: underline; 186 + } 187 + .link { 188 + text-decoration: none; 189 + } 190 + .db { 191 + display: block; 192 + } 193 + .h2 { 194 + height: 2rem; 195 + } 196 + .h3 { 197 + height: 3rem; 198 + } 199 + .input { 200 + border: 1px solid; 201 + border-radius: var(--br2); 202 + padding: var(--s2); 203 + } 204 + .input-reset { 205 + -webkit-appearance: none; 206 + -moz-appearance: none; 207 + appearance: none; 208 + background: transparent; 209 + } 210 + .shadow { 211 + box-shadow: var(--shadow-base); 212 + } 213 + .pv2 { 214 + padding-bottom: var(--s2); 215 + padding-top: var(--s2); 216 + } 217 + .pv1 { 218 + padding-bottom: var(--s1); 219 + padding-top: var(--s1); 220 + } 221 + .ph2 { 222 + padding-left: var(--s2); 223 + padding-right: var(--s2); 224 + } 225 + .lh-copy { 226 + line-height: var(--lh-copy); 227 + } 228 + .center { 229 + text-align: center; 230 + } 231 + .flex-wrap { 232 + flex-wrap: wrap; 233 + } 234 + .no-select { 235 + cursor: default; 236 + -webkit-user-select: none; 237 + -moz-user-select: none; 238 + user-select: none; 239 + } 240 + .relative { 241 + position: relative; 242 + } 243 + .absolute { 244 + position: absolute; 245 + } 246 + .fixed { 247 + position: fixed; 248 + } 249 + .absolute-center { 250 + left: 50%; 251 + position: fixed; 252 + top: 50%; 253 + transform: translate(-50%, -50%); 254 + } 255 + .overflow-hidden, .text-ellipsis { 256 + overflow: hidden; 257 + } 258 + .text-ellipsis { 259 + text-overflow: ellipsis; 260 + white-space: nowrap; 261 + } 262 + .z-dropdown { 263 + z-index: var(--z-dropdown); 264 + } 265 + .z-modal { 266 + z-index: var(--z-modal); 267 + } 268 + .z-tooltip { 269 + z-index: var(--z-tooltip); 270 + } 271 + }
+2
www/worker.ts
··· 1 1 import { 2 2 init, 3 + withBackgroundPrecache, 3 4 withCleanup, 4 5 withFetchStrategy, 5 6 withPrecache, ··· 52 53 '/static/styles/theme.css', 53 54 '/manifest.json', 54 55 ]), 56 + withBackgroundPrecache(), 55 57 withCleanup(), 56 58 withUpdatePolling(), 57 59 withFetchStrategy(),